Skip to content

Commit

Permalink
Add macro support to JS built cljs analyzer/compiler.
Browse files Browse the repository at this point in the history
This doesn't yet enable defining and immediate use of macros during
JVM based analyze/compile of cljs.

In other words, in the live Node.js and web REPL you can define new
macros and use them via the REPL. However, you can't define them yet
in a file, analyzer/compile it and then use them during or after
analyze/compile.
  • Loading branch information
kanaka committed Dec 8, 2012
1 parent 437658f commit fbfcfc5
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 22 deletions.
35 changes: 13 additions & 22 deletions src/cljs/cljs/analyzer.cljs
Expand Up @@ -864,27 +864,20 @@
(assoc ret :op :var :info lb)
(assoc ret :op :var :info (resolve-existing-var env sym)))))

(defn is-macro? [sym]
(let [var (resolve-existing-var (empty-env) sym)
ns (:ns var)
name (symbol (name (:name var)))]
(get-in @namespaces [:macros ns name])))

(defn get-expander [sym env]
(let [mvar
(when-not (or (-> env :locals sym) ;locals hide macros
(and (or (-> env :ns :excludes sym)
(get-in @namespaces [(-> env :ns :name) :excludes sym]))
(not (or (-> env :ns :uses-macros sym)
(get-in @namespaces [(-> env :ns :name) :uses-macros sym])))))
(if-let [nstr (namespace sym)]
(when-let [ns (cond
(= "clojure.core" nstr) (find-ns 'cljs.core)
(>= (.indexOf nstr ".") 0) (find-ns (symbol nstr))
:else
(-> env :ns :requires-macros (get (symbol nstr))))]
(.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym))))
(if-let [nsym (-> env :ns :uses-macros sym)]
(.findInternedVar ^clojure.lang.Namespace (find-ns nsym) sym)
(.findInternedVar ^clojure.lang.Namespace (find-ns 'cljs.core) sym))))]
(when (and mvar (.isMacro ^clojure.lang.Var mvar))
@mvar)))

;; JOELM: we will need this eventually
(let [var (resolve-existing-var (empty-env) sym)
ns (:ns var)
name (symbol (name (:name var)))]
;(println "// get-expander:" sym ns name)
(when (is-macro? sym)
(js/eval (str ns "." name)))))

(defn macroexpand-1 [env form]
(let [op (first form)]
(if (specials op)
Expand All @@ -904,8 +897,6 @@
:else form))
form)))))

(defn macroexpand-1 [env form] form)

(defn analyze-seq
[env form name]
(let [env (assoc env :line
Expand Down
60 changes: 60 additions & 0 deletions src/cljs/cljs/core.cljs
Expand Up @@ -7217,3 +7217,63 @@ reduces them without incurring seq initialization"
(def namespaces (atom '{cljs.core {:name cljs.core}
cljs.user {:name cljs.user}}))

(defn setMacro [sym]
(let [ns (symbol (or (namespace sym)
(try cljs.analyzer/*cljs-ns*
(catch js/Error e 'cljs.core))))
name (symbol (name sym))]
(swap! namespaces assoc-in [:macros ns name] true))
nil)

(def

^{:doc "Like defn, but the resulting function name is declared as a
macro and will be used as a macro by the compiler when it is
called."
:arglists '([name doc-string? attr-map? [params*] body]
[name doc-string? attr-map? ([params*] body)+ attr-map?])
:added "1.0"}
defmacro (fn [&form &env
name & args]
(let [prefix (loop [p (list name) args args]
(let [f (first args)]
(if (string? f)
(recur (cons f p) (next args))
(if (map? f)
(recur (cons f p) (next args))
p))))
fdecl (loop [fd args]
(if (string? (first fd))
(recur (next fd))
(if (map? (first fd))
(recur (next fd))
fd)))
fdecl (if (vector? (first fdecl))
(list fdecl)
fdecl)
add-implicit-args (fn [fd]
(let [args (first fd)]
(cons (vec (cons '&form (cons '&env args))) (next fd))))
add-args (fn [acc ds]
(if (nil? ds)
acc
(let [d (first ds)]
(if (map? d)
(conj acc d)
(recur (conj acc (add-implicit-args d)) (next ds))))))
fdecl (seq (add-args [] fdecl))
decl (loop [p prefix d fdecl]
(if p
(recur (next p) (cons (first p) d))
d))]
(prn "defmacro here1: ")
(list 'do
(list 'def (first decl) (cons `fn* (first (rest decl))))
#_(cons `defn decl)
#_(list '. (list 'var name) '(setMacro))
(list 'cljs.core/setMacro (list 'quote name))
#_(list 'var name)))))

#_(. (var defmacro) (setMacro))
(setMacro 'cljs.core/defmacro)

0 comments on commit fbfcfc5

Please sign in to comment.