Skip to content

Instantly share code, notes, and snippets.

@fogus
Forked from alandipert/macro_eval.clj
Last active August 29, 2015 14:14
Show Gist options
  • Save fogus/c045ef2298dedeee7b5e to your computer and use it in GitHub Desktop.
Save fogus/c045ef2298dedeee7b5e to your computer and use it in GitHub Desktop.
(require '[clojure.walk :refer [macroexpand-all postwalk]]
'[clojure.pprint :refer [pprint]])
(def ^:dynamic *platform* :clj)
(defmacro +clj [form]
(if (= :clj *platform*)
form
::elide))
(defmacro +cljs [form]
(if (= :cljs *platform*)
form
::elide))
(defn elide
"Removes occurrences of ::elide in operation forms."
[form]
(postwalk
(fn [f]
(if (and (seq? f) (not= 'quote (first f)))
(filter (complement #{::elide}) f)
f))
form))
(defn make-weird!
"Turns a macro into one that expands arguments and performs elision.
Arguments are expanded, and those that expanded to an elision are
filtered from the argument list.
Demonstrates a macro evaluation semantic capable of performing
elision the way the reader can, in order to aid macro composition."
[macro-var]
(alter-var-root
macro-var
(fn [macro-fn]
(fn [env form & args]
(apply macro-fn env form
(->> args
(map (comp elide macroexpand-all))
(filter (complement #{::elide}))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro do+
"Wraps the do special form in a weird macro. In a Lisp that worked
this way, maybe the special forms would be weird too."
[& forms]
`(do ~@forms))
(make-weird! #'do+)
(pprint
(macroexpand
'(do+
(+clj (.print System/out "clj!"))
(+cljs (.log js/console "cljs!")))))
;=> (do (. System/out print "clj!"))
;;; cljs specific code elided
(binding [*platform* :cljs]
(pprint
(macroexpand
'(do+
(+clj (.print System/out "clj!"))
(+cljs (.log js/console "cljs!"))))))
;=> (do (. js/console log "cljs!"))
;;; clj code elided
(make-weird! #'ns) ;here it comes
(pprint
(macroexpand
'(ns portable-ns
(+clj (:require clojure.core))
(+cljs (:require cljs.core)))))
;=>
;; (do
;; (clojure.core/in-ns 'portable-ns)
;; (clojure.core/with-loading-context
;; (clojure.core/refer 'clojure.core) ;-----------------------------+
;; (clojure.core/require 'clojure.core)) ;<- because *platform* = :clj |
;; (if ;-----------------------------+
;; (.equals 'portable-ns 'clojure.core)
;; nil
;; (do
;; (clojure.core/dosync
;; (clojure.core/commute
;; @#'clojure.core/*loaded-libs*
;; clojure.core/conj
;; 'portable-ns))
;; nil)))
(binding [*platform* :cljs]
(pprint
(macroexpand
'(ns portable-ns
(+clj (:require clojure.core))
(+cljs (:require cljs.core))))))
;=>
;; (do
;; (clojure.core/in-ns 'portable-ns)
;; (clojure.core/with-loading-context
;; (clojure.core/refer 'clojure.core) ;------------------------------+
;; (clojure.core/require 'cljs.core)) ;<- because *platform* = :cljs |
;; (if ;------------------------------+
;; (.equals 'portable-ns 'clojure.core)
;; nil
;; (do
;; (clojure.core/dosync
;; (clojure.core/commute
;; @#'clojure.core/*loaded-libs*
;; clojure.core/conj
;; 'portable-ns))
;; nil)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment