Elixir:AST扩展期间未定义的变量

时间:2018-11-29 23:47:17

标签: macros elixir abstract-syntax-tree

我有一个像这样的模块,/dev/nullStarted POST "/profile/sign_in" for 127.0.0.1 at 2018-11-29 15:36:09 -0800 Started POST "/profile/sign_in" for 127.0.0.1 at 2018-11-29 15:36:09 -0800 Processing by ProfileController#sign_in as JS Processing by ProfileController#sign_in as JS 看起来一样,但是第二个模块中的ast1出现错误。有人可以解释这个问题吗?

ast2

更新

  • 我想做的是,给定rest undefined生成的defmodule PacketDef do pk_def = {:pk_name, [ {:unk_int1, :int}, {:unk_int2, :int}, ]} {pkn, field_defs} = pk_def field_decs = Enum.map(field_defs, fn ({var_name, var_type}) when var_type in [:int] -> rest = Macro.var(:rest, __MODULE__) dec_name = String.to_atom("decode_#{var_type}") xvar_name = Macro.var(var_name, __MODULE__) quote do {:ok, unquote(xvar_name), unquote(rest)} = unquote(dec_name)(unquote(rest)) end (_field_def) -> nil end) ast1 = quote do def decode(unquote(pkn), rest) do {:ok, unk_int1, rest} = decode_int(rest) {:ok, unk_int2, rest} = decode_int(rest) {:ok, rest} end end ast2 = quote do def decode(unquote(pkn), rest) do unquote_splicing(field_decs) {:ok, rest} end end IO.puts("ast1") IO.inspect(ast1, width: 100) IO.puts("ast2") IO.inspect(ast2, width: 100) def decode(unquote(pkn), rest) do {:ok, unk_int1, rest} = decode_int(rest) {:ok, unk_int2, rest} = decode_int(rest) {:ok, rest} end # why get error *rest* here def decode(unquote(pkn), rest) do unquote_splicing(field_decs) {:ok, rest} end def decode_int(<<b::32-little, rest::binary>>) do {:ok, b, rest} end end 函数,就像pk_def一样,但是decode是动态生成的。

1 个答案:

答案 0 :(得分:2)

问题出在函数定义而不是标题,特别是以下行:

unquote_splicing(field_decs)

如果删除此行,代码将起作用。原因是当使用field_decs扩展unquote_splicing AST时,它将进行子调用以尝试取消引用失败的rest变量。修正AST评估方式也将解决此问题。


在我看来,这就像是XY Problem。我不确定您要在此处做什么,但是在处理语言扩展和自定义DSL时,应将其分解为多个较小的可组合Macros(大多数功能在私有函数中实现)并应注意宏观卫生。这将大大降低您的代码复杂性,并使得处理代码扩展变得更容易,因为您不必直接处理AST。