bakpakin / Fennel

Lua Lisp Language
https://fennel-lang.org
MIT License
2.42k stars 124 forks source link

Strange compile error occurred when iterating a table generated by macro in another macro #447

Closed moxuze closed 1 year ago

moxuze commented 1 year ago

Codes Here:

; Just a simple compile-time merging macro.
(macro merge [a b]
  (let [out {}]
    (each [_ t (ipairs [a b])]
      (each [k v (pairs t)]
        (tset out k v)))
    out))

; Compiled to `local t = { a = "A", b = "B" }`, that is what I want.
(local t (merge { :a "A" } { :b "B" }))

; Now we define a macro `b` who can generate a table.
(macro b [] { :b "B" })

; Failed with message `Compile error in 'b': tried to reference a macro without calling it`.
; Expected output: `local t = { a = "A", b = "B" }`.
(local t (merge { :a "A" } (b)))

; Futher more, even this can be compiled to a strange statement with metadata of a macro:
; `local t0 = {"b", a = "A", byteend = 363, bytestart = 363, col = 27, endcol = 27, filename = "unknown", line = 13}`.
(local t (merge { :a "A" } b))

Is that a bug of Fennel? I really love this language, thanks for discussion.

technomancy commented 1 year ago
(local t (merge { :a "A" } (b)))

It seems you're expecting macros to be expanded from the inside out, but they are actually expanded from the outside in. The merge macro cannot see the expansion of b; it just sees the call to b as a literal list instead, and uses pairs to loop over that list, resulting in an attempt to merge with {1 b} which cannot compile.

The error message is confusing, but I can't think of a way to improve it here.

(local t (merge { :a "A" } b))

The strange results here come from the fact that symbols are tables, even if they don't look like it. So your merge macro is looping over the fields of the symbol, which include source data about where the symbol was read from.

moxuze commented 1 year ago

Thank you, that is clear. I find that macroexpand can solve my demand although its document is somewhat ambiguous.

technomancy commented 1 year ago

If you have suggestions for how to make it clearer, please let me know.