我有一个像这样的模块,/dev/null
和Started 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
是动态生成的。答案 0 :(得分:2)
问题出在函数定义而不是标题,特别是以下行:
unquote_splicing(field_decs)
如果删除此行,代码将起作用。原因是当使用field_decs
扩展unquote_splicing
AST时,它将进行子调用以尝试取消引用失败的rest
变量。修正AST评估方式也将解决此问题。
在我看来,这就像是XY Problem。我不确定您要在此处做什么,但是在处理语言扩展和自定义DSL时,应将其分解为多个较小的可组合Macros(大多数功能在私有函数中实现)并应注意宏观卫生。这将大大降低您的代码复杂性,并使得处理代码扩展变得更容易,因为您不必直接处理AST。