(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))

Nie można przekazać artycznie z makra barowego, aby zablokować makro i zdobyć java.lang.NullPointerException. Reszta kodu wykonuje, jeśli skomentuję połączenie blokowe z makra barowego.

(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     
                )
              )
            )
        )
  )

Po komentarzu poniżej jest wyjście linii debugowania:

"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

Zgodnie z liniami debugowania drukowane powyżej, w pasku Makro nie jestem w stanie rozwiązać symbolu pierwszego arity'ego i jest drukowane zero (nie znam powodu, dlaczego). Celem jest prawidłowe przekazywanie ARTY z makra barowego do makra Block i będzie w stanie uzyskać dostęp i drukować wartość Start i Etype1 w Action1 Macro.

2
Jai 22 luty 2019, 15:06

2 odpowiedzi

Najlepsza odpowiedź

Jak wskazano @ i0st3D, może to być rozwiązanie po modyfikującym definicji baru, a Foo_multi stanie się podobnie jak rozwiązanie 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))

W barze Macro otrzymasz dostęp do wszystkich params ARTY, dzięki czemu można utworzyć mapę Vars Vary, w razie potrzeby w razie potrzeby. Twój (niech [myarr ["X" 100 200 {"P" 1 "Q" 2}]] (Zastosuj Foo Myarr)) będzie również działać zgodnie z oczekiwaniami.

1
al.moorthi 25 marzec 2019, 09:29

Dostaję nullpointer za pomocą makro action1 nil i makro block próbuje wykonać odpowiedź action1. Cytat ten naprawił. Również wygląda na mnie, jakby jest zbyt wiele cytatów na wartościach wiązań w {X4}}, więc też ich zabrałem.

(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)))

Po drugie, clojure.core/resolve Will tylko wygląd W górę Vars w przestrzeni nazw, a nie mieszkańcy utworzone przez clojure.core/let lub clojure.core/fn. Więc jeśli spróbujesz i rozwiązujesz lokalny, otrzymasz 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))))))

Więc ta część foo_multi teraz biegnie.

(block {toc "c"} (block {c1 "d"} (action1)) "end")
;;=>
"action1" :start :etype1
"end"

W 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))))))))

Filtr (filter #(= (get % (some named-top? (keys %))) ~'etype) '~ctxv) wydaje się, że to błąd, ponieważ etype nie będzie istniał, chyba że zostanie zmuszony być w argumencie metadata. Ogólnie jest zły pomysł tworzenia tych magicznych mieszkańców za pomocą ~'idiom, ponieważ nigdy nie wiesz, co jesteś cieniowany, a to tylko straszna akcja na odległość. Lepiej jest użyć funkcji gensym {x6}} local# local# jako opisany tutaj.

Już jako komentarz o strategii debugowania, próbując wydobyć uproszczoną minimalną sprawę, która prawdopodobnie pomogłaby zrozumieć, co się dzieje. Ten kod jest raczej zdezorientowany. Jest cała grupa rzeczy zhakowana razem. Odczytuje się, jak opierasz się o Clojure i Macros i gryziesz za dużo w jednym Idź. Myślę, że próbujesz powtórzyć zakres leksykalny z tymi makrami, ale nie jestem całkowicie pewien, jaki jest koniec koniec. Może czytanie przez Pomoże to pomoże.

Podejrzewam też, że wpadniesz w kłopoty, gdy odkryjesz, że clojure.core/for jest leniwy.

foo_multi Zwraca funkcję zwracającą listę funkcji. Aby faktycznie wykonać większość napisanego kodu, musisz zadzwonić do tych funkcji.

(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))

Jeśli próbujesz powtórzyć zakres leksykalny przy użyciu map, a nie wektory, być może ten fragment kodu pomoże Ci przemyśleć podejście:

(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]))
5
l0st3d 5 marzec 2019, 15:39