(defmacro action1 [] `(prn "action1" ~'start ~'etype1))
(defmacro block [bindings & body]
`(let [~@(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `'~v]) (cond
(map? bindings) bindings
(symbol? bindings) (var-get (resolve bindings))
:else (throw (Exception. "bindings must be map or symbol"))))]
~body))
(defmacro bar [ctx arity & expr]
`(let [~'t1 "lv" ~'np (prn "bar_1st_let" '~ctx ~ctx '~arity ~arity '~arity(resolve (first '~arity)) )
]
(block ~ctx ;;~ctx = {start "s1" top "x"}
(fn '~arity ;; '~arity = [etype1 cid1 id1 pl1]
(let [~'__execonceresult 1]
(do ~@expr)
)
)
)
)
)
(defmacro foo_multi [metadata ctxv aritym & expr]
`(let [~@(mapcat (fn [[k v]] [k `~v]) metadata) ~'np (prn "foo_multi_1st_let" '~aritym)]
(fn ~aritym
(for [~'ctx (filter #(= (% (some (fn [~'m] (if (= (name ~'m) "top") ~'m)) (keys %))) ~'etype) '~ctxv)]
(do (prn "foo_multi_b4_case" ~'ctx ~'etype ~aritym)
(case ~'etype
"x"
(let [[~'etype1 ~'cid1 ~'id1 ~'pl1] ~aritym ~'np (prn "foo_multi_2nd_let" ~'ctx ~'etype1 ~'cid1 ~'id1 ~'pl1)]
(bar ~'ctx [~'etype1 ~'cid1 ~'id1 ~'pl1] ~@expr))
"y"
(let [[~'etype2 ~'cid2 ~'id2 ~'pl2] ~aritym]
(bar ~'ctx [~'etype2 ~'cid2 ~'id2 ~'pl2] ~@expr))
))))))
(def foo (foo_multi { meta1 "m1" meta2 "m2" } [{start "s1" top "x"}
{start "s3" top "x"} {start "s2" top "y"}] [etype a1 a2 a3] (block {toc "c"}
(block {c1 "d"} (action1)) "end"))
)
(let [myarr ["x" 100 200 {"p" 1 "q" 2}]] (apply foo myarr))
无法将条形从bar宏传递到block宏并获得java.lang.NullPointerException
。
如果我注释了bar宏中的块调用,则会执行其余代码。
(defmacro bar [ctx arity & expr]
`(let [~'t1 "lv" ~'np (prn "bar_1st_let" '~ctx ~ctx '~arity ~arity '~arity(resolve (first '~arity)) )
]
(comment block ~ctx ;;~ctx = {start "s1" top "x"}
(fn '~arity ;; etype specific ~arity eg: [etype1 cid1 id1 pl1]
(let [~'__execonceresult 1]
(do ~@expr) ;; uses etype1
)
)
)
)
)
下面的注释是调试行的输出:
"foo_multi_1st_let" [etype a1 a2 a3]
"foo_multi_b4_case" {start "s1", top "x"} "x" ["x" 100 200 {"p" 1, "q" 2}]
"foo_multi_2nd_let" {start "s1", top "x"} "x" 100 200 {"p" 1, "q" 2}
"bar_1st_let" ctx {start "s1", top "x"} [etype1 cid1 id1 pl1] ["x" 100 200 {"p" 1, "q" 2}] [etype1 cid1 id1 pl1] nil
"foo_multi_b4_case" {start "s3", top "x"} "x" ["x" 100 200 {"p" 1, "q" 2}]
"foo_multi_2nd_let" {start "s3", top "x"} "x" 100 200 {"p" 1, "q" 2}
"bar_1st_let" ctx {start "s3", top "x"} [etype1 cid1 id1 pl1] ["x" 100 200 {"p" 1, "q" 2}] [etype1 cid1 id1 pl1] nil
按照上面打印的调试行,在bar宏中,我无法解析第一个arity符号,并且将其打印为nil(不知道原因)。目的是将arity正确地从bar宏传递到block宏,并能够访问和打印action1宏中的start和etype1值。
答案 0 :(得分:5)
我得到一个NullPointer,原因是您的action1
宏返回了nil
,而block
宏试图执行来自action1
的响应。拼接报价可以解决该问题。另外,在我看来,block
中绑定值的引号太多,所以我也将它们删除了。
(defmacro block [bindings & body]
(let [bs (->>
(cond
(map? bindings) bindings
(symbol? bindings) []
:else (throw (Exception. "bindings must be map or symbol")))
(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) v])))]
`(let [~@bs]
~@body)))
第二,clojure.core/resolve
将only look up vars in a namespace,而不是clojure.core/let
或clojure.core/fn
创建的本地人。因此,如果您尝试解决本地问题,将会得到nil
。
(defmacro bar [bindings arity & expr]
`(block ~bindings ;;~bindings = {start "s1" top "x"}
(fn ~arity ;; '~arity = [etype1 cid1 id1 pl1]
(let [~'__execonceresult 1]
(do ~@expr)))))
(macroexpand-1 '(bar {start "s1" top "x"} [etype1 cid1 id1 pl1] (action1)))
;; =>
(do
(user/block
{start "s1", top "x"}
(clojure.core/fn
[etype1 cid1 id1 pl1]
(clojure.core/let [__execonceresult 1] (do (action1))))))
因此foo_multi
的这一部分现在可以运行。
(block {toc "c"} (block {c1 "d"} (action1)) "end")
;;=>
"action1" :start :etype1
"end"
在foo_multi
中:
(defn named-top? [m]
(when (= (name m) "top")
m))
(defmacro foo_multi [metadata ctxv aritym & expr]
(prn "foo_multi" (map #(get % (some named-top? (keys %))) ctxv))
`(let [~@(mapcat (fn [[k v]] [k v]) metadata)]
(prn "foo_multi_1st_let" '~aritym)
(fn ~aritym
(for [~'ctx (filter #(= (get % (some named-top? (keys %))) ~'etype) '~ctxv)]
(do #_ (prn "foo_multi_b4_case" ~'ctx ~'etype ~aritym)
(case ~'etype
"x"
(let [[~'etype1 ~'cid1 ~'id1 ~'pl1] ~aritym ~'np (prn "foo_multi_2nd_let" ~'ctx ~'etype1 ~'cid1 ~'id1 ~'pl1)]
(bar ~'ctx [~'etype1 ~'cid1 ~'id1 ~'pl1] ~@expr))
"y"
(let [[~'etype2 ~'cid2 ~'id2 ~'pl2] ~aritym]
(bar ~'ctx [~'etype2 ~'cid2 ~'id2 ~'pl2] ~@expr))))))))
过滤器(filter #(= (get % (some named-top? (keys %))) ~'etype) '~ctxv)
似乎会出错,因为除非强制将etype
放在参数metadata
中,否则~'idiom
不存在。用gensym
创建这些魔术局部对象通常是一个坏主意,因为您永远不知道自己要遮盖什么,并且只是在很远的地方进行怪异的动作。最好将syntax-quote
local#
的{{1}}功能用作described here。
就像对调试策略的评论一样,尝试提取简化的最小用例可能会帮助您了解所发生的情况。我认为这段代码很混乱。有很多东西被黑在一起。听起来好像您是在关注Clojure和宏,并且一口气咬了太多东西。我认为您正在尝试使用这些宏复制词汇范围,但我不确定最终目标是什么。也许通读this会有所帮助。
此外,我怀疑您发现clojure.core/for
懒惰时会遇到麻烦。
foo_multi
返回一个函数,该函数返回一个函数列表。因此,要实际执行您编写的大部分代码,您需要调用这些函数。
(let [start :start
etype1 :etype1
foo (foo_multi {meta1 "m1" meta2 "m2" }
[{start "s1" top "x"}
{start "s3" top "x"}
{start "s2" top "y"}]
[etype a1 a2 a3]
(block {toc "c"}
(block {c1 "d"} (action1))
"end"))
args ["x" 100 200 {"p" 1 "q" 2}]
fns (apply foo args)]
(map #(apply % args) fns))
如果您尝试使用地图而不是向量来复制词法范围,那么以下代码片段可能会帮助您重新思考方法:
(defmacro my-let [bindings & body]
(let [bs (vec (mapcat (fn [[k v]] [k v]) bindings))]
`(let ~bs
~@body)))
(defmacro my-multi-let [bindings-list & body]
(->> bindings-list
(map (fn [b] `(my-let ~b ~@body)))
(cons `list)))
(macroexpand-1 '(my-let {a "a1" b "b1"} [a b]))
(macroexpand-1 '(my-multi-let [{a "a1" b "b1"} {a "a2" b "b2"}] [a b]))
答案 1 :(得分:1)
正如@ I0st3d所指出的,这可能是修改条形定义后的解决方案,而foo_multi将变得像上面的解决方案my-multi-let。
(defmacro action1 [] `(prn "action1" ~'start ~'etype))
(defn named-type? [m] (when (= (name m) "top") m))
(defmacro block [ctx & expr]
`(let [~@(mapcat (fn [[k v]] [k `~v]) ctx)] ~@expr))
(defmacro bar [bindings & body]
`(block ~bindings (if (= ~'top ~'etype) (do
~@body))))
(defmacro foo_multi [metadata bindings-list arity & body]
(let [fns (->> bindings-list
(map (fn [b] `(bar ~b ~arity ~@body))))
] `(block ~metadata (fn ~arity (do ~@fns)))
))
(def foo (foo_multi {meta1 "m1" meta2 "m2"} [{start "s1" top "x"}
{start "s2" top "y"}] [etype a1 a2 a3]
(block {toc "c"} (block {c1 "d"} (action1)) "end")
))
(let [myarr ["x" 100 200 {"p" 1 "q" 2}]] (apply foo myarr))
在bar宏中,您将可以访问所有arity参数,因此您也可以根据需要创建针对etype特定符号而变化的map var。 您的(让[myarr [“ x” 100200 {“ p” 1“ q” 2}]](应用foo myarr))也将按预期工作。