Macrodebug is currently implemented as
(fn macrodebug* [form return?]
"Print the resulting form after performing macroexpansion.
With a second argument, returns expanded form as a string instead of printing."
(let [handle (if return? `do `print)]
`(,handle ,(view (macroexpand form _SCOPE)))))
The _SCOPE
variable it's passing to macroexpand
is the scope in which the macro itself was defined, but it should be (get-scope)
, the scope at the point the macro is invoked.
This likely hasn't come up because most of what depends on correct scope in a macro relates to when it comes time to compile to lua - gensym for hygiene and the like. Still, it should be fixed.
Tests on a fix suggest I may be misremembering the difference between
_SCOPE
and(get-scope)
.. I remember(get-scope)
being created distinctly for a reason, though. Will look into this further before I close it.
For clarity, it's this change to the tests that seems to still pass without changing
macrodebug
to useget-scope
:diff --git a/test/macro.fnl b/test/macro.fnl index 983c3d5..514d9f3 100644 --- a/test/macro.fnl +++ b/test/macro.fnl @@ -127,9 +127,13 @@ (let [eval-normalize #(-> (pick-values 1 (fennel.eval $1 $2)) (: :gsub "table: 0x[0-9a-f]+" "#<TABLE>") (: :gsub "\n%s*" " ")) - code "(macrodebug (when (= 1 1) (let [x :X] {: x})) true)" - expected "(if (= 1 1) (do (let [x \"X\"] {:x x})))"] - (t.= (eval-normalize code) expected))) + cases [["(macrodebug (when (= 1 1) (let [x :X] {: x})) true)" + "(if (= 1 1) (do (let [x \"X\"] {:x x})))"] + ["(macro reflect-parent [] (next (. (get-scope) :parent :parent :macros))) + (do (do (macrodebug (reflect-parent) true)))" + "\"reflect-parent\""]]] + (each [_ [code expected] (ipairs cases)] + (t.= expected (eval-normalize code))))) ;; many of these are copied wholesale from test-match, pending implementation, ;; if match is implemented via case then it should be reasonable to remove much
Yeah... this looks wrong, but as far as I can tell ... it works right?
(let [x 9] (macrodebug (match [9] [x] :hi)) (macrodebug (match [9] [y] :hi))) ; -> (let [(_1_) [9]] (if (and (= (_G.type _1_) "table") (= (. _1_ 1) x)) (let {} "hi"))) (let [(_2_) [9]] (if (and (= (_G.type _2_) "table") (not= nil (. _2_ 1))) (let [y (. _2_ 1)] "hi")))It can tell that
x
is in scope, buty
isn't.