我想将包含有效Erlang表达式的字符串转换为其抽象语法树表示,到目前为止没有任何成功。
以下是我想做的一个例子。编译完成后,所有z:z().
生成模块zed
,通过调用zed:zed().
返回在给定列表中应用lists:reverse
的结果。
-module(z).
-export([z/0]).
z() ->
ModuleAST = erl_syntax:attribute(erl_syntax:atom(module),
[erl_syntax:atom("zed")]),
ExportAST = erl_syntax:attribute(erl_syntax:atom(export),
[erl_syntax:list(
[erl_syntax:arity_qualifier(
erl_syntax:atom("zed"),
erl_syntax:integer(0))])]),
%ListAST = ?(String), % This is where I would put my AST
ListAST = erl_syntax:list([erl_syntax:integer(1), erl_syntax:integer(2)]),
FunctionAST = erl_syntax:function(erl_syntax:atom("zed"),
[erl_syntax:clause(
[], none,
[erl_syntax:application(
erl_syntax:atom(lists),
erl_syntax:atom(reverse),
[ListAST]
)])]),
Forms = [erl_syntax:revert(AST) || AST <- [ModuleAST, ExportAST, FunctionAST]],
case compile:forms(Forms) of
{ok,ModuleName,Binary} -> code:load_binary(ModuleName, "z", Binary);
{ok,ModuleName,Binary,_Warnings} -> code:load_binary(ModuleName, "z", Binary)
end.
String
可以是"[1,2,3]."
,也可以是"begin A=4, B=2+3, [A,B] end."
,或类似的任何内容。
(请注意,这只是我想要做的一个例子,所以评估String
不是我的选择。)
修改:
如下所示指定ListAST会生成一个巨大的dict-digraph-error-monster,并说“lint_module中的内部错误”。
String = "[1,2,3].",
{ok, Ts, _} = erl_scan:string(String),
{ok, ListAST} = erl_parse:parse_exprs(Ts),
EDIT2 :
此解决方案适用于简单的术语:
{ok, Ts, _} = erl_scan:string(String),
{ok, Term} = erl_parse:parse_term(Ts),
ListAST = erl_syntax:abstract(Term),
答案 0 :(得分:5)
在您的编辑示例中:
String = "[1,2,3].",
{ok, Ts, _} = erl_scan:string(String),
{ok, ListAST} = erl_parse:parse_exprs(Ts),
ListAST实际上是AST:s的列表(因为parse_exprs,如名称所示,解析多个表达式(每个表达式以句点结尾)。由于您的字符串包含单个表达式,因此您获得了一个元素的列表。你需要做的就是匹配:
{ok, [ListAST]} = erl_parse:parse_exprs(Ts),
所以它与erl_syntax(接受所有erl_parse树)无关;只是你在ListAST周围有一个额外的列表包装器,这导致编译器呕吐。
答案 1 :(得分:3)
我头顶的一些评论。
我还没有真正使用erl_syntax库,但我确实认为它们难以阅读并“看到”您正在尝试构建的内容。我可能会导入函数或定义我自己的API,使其更短,更清晰。但是我通常倾向于选择较短的函数和变量名。
由erl_syntax创建的AST和由erl_parse创建并在编译器中使用的“标准”AST是不同的,不能混合。所以你必须选择其中一个并坚持下去。
第二个EDIT中的示例适用于术语,但不适用于更一般的情况:
{ok, Ts, _} = erl_scan:string(String),
{ok, Term} = erl_parse:parse_term(Ts),
ListAST = erl_syntax:abstract(Term),
这是因为erl_parse:parse_term / 1返回标记所代表的实际术语,而另一个erl_parse函数parse_form和parse_exprs返回AST。将它们放入erl_syntax:abstract会做有趣的事情。
根据您的尝试,实际写出和编写erlang文件并编译它可能实际上更容易,而不是直接使用抽象表单。这违背了我根深蒂固的感受,但生成erlang AST并非易事。你打算生产什么类型的代码?
<shameless_plug>
如果您不害怕列表,您可能会尝试使用LFE(lisp风格的erlang)来生成代码,就像所有lisps一样,没有特殊的抽象形式,它都是同质的,更容易使用。
</shameless_plug>
答案 2 :(得分:2)
佐尔坦
这就是我们获取AST的方式:
11> String = "fun() -> io:format(\"blah~n\") end.".
"fun() -> io:format(\"blah~n\") end."
12> {ok, Tokens, _} = erl_scan:string(String).
{ok,[{'fun',1},
{'(',1},
{')',1},
{'->',1},
{atom,1,io},
{':',1},
{atom,1,format},
{'(',1},
{string,1,"blah~n"},
{')',1},
{'end',1},
{dot,1}],
1}
13> {ok, AbsForm} = erl_parse:parse_exprs(Tokens).
{ok,[{'fun',1,
{clauses,[{clause,1,[],[],
[{call,1,
{remote,1,{atom,1,io},{atom,1,format}},
[{string,1,"blah~n"}]}]}]}}]}
14>