如何将带有有效Erlang表达式的字符串转换为抽象语法树(AST)?

如何将带有有效Erlang表达式的字符串转换为抽象语法树(AST)?,erlang,abstract-syntax-tree,Erlang,Abstract Syntax Tree,我想将一个包含有效Erlang表达式的字符串转换为其抽象语法树表示形式,但迄今为止没有任何成功 下面是我想做的一个例子。编译后,allingz:z()。生成模块zed,该模块通过调用zed:zed()。返回对给定列表应用lists:reverse的结果 -module(z). -export([z/0]). z() -> ModuleAST = erl_syntax:attribute(erl_syntax:atom(module),

我想将一个包含有效Erlang表达式的字符串转换为其抽象语法树表示形式,但迄今为止没有任何成功

下面是我想做的一个例子。编译后,alling
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.

EDIT2

此解决方案适用于以下简单术语:

{ok, Ts, _} = erl_scan:string(String),
{ok, Term} = erl_parse:parse_term(Ts),
ListAST = erl_syntax:abstract(Term),
佐尔坦

以下是我们获取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> 
在您的编辑示例中:

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_语法(它接受所有erl_解析树)无关;只是在ListAST周围有一个额外的列表包装器,这导致编译器呕吐。

我脑海中的一些评论

我没有真正使用erl_语法库,但我确实认为它们使阅读和“查看”您试图构建的内容变得困难。我可能会导入函数或定义自己的API以使其更简短易读。但我通常倾向于选择更短的函数和变量名

由erl_语法创建的AST和由erl_parse创建并在编译器中使用的“标准”AST是不同的,不能混用。因此您必须选择其中一个并坚持使用

第二次编辑中的示例适用于术语,但不适用于更一般的情况:

{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_expr返回AST。将它们放入erl_语法:abstract中会做一些有趣的事情

根据您试图做的事情,实际编写和编译erlang文件可能比直接使用抽象形式更容易。这与我根深蒂固的感觉背道而驰,但生成erlang AST并不简单。您打算生成什么类型的代码

如果你不害怕列表,你可以尝试使用LFE(lisp风格的erlang)来生成代码,因为所有lisp都没有特殊的抽象形式,它都是同形的,更容易使用


我已经尝试过类似的东西。当我将它放入使用erl\u语法构建的AST时,这根本不起作用。它使
编译:forms()
throup…@Gordon,我扩展了我问题中的示例。当我使用
erl\u语法创建列表时,它非常有效。但是用
erl\u parse
东西替换它,不幸的是不起作用。现在我看了代码,我显然混淆了erl\u语法和erl\u解析格式…仍然不知道如何做到这一点虽然(典型的太多bejgli错误)。是的,如果你将你的ListAST与erl_语法生成的ListAST进行比较,它们看起来并不一样:(42>ListAST。[{cons,1,{integer,1,1},{cons,1,2},{integer,1,2},{nil,1}}]43>erl_语法:list([1,2,3])。{tree,list,{attr,0,[],none},{list,[1,2,3],]}}44>因此,我需要一种方法使
erl_语法
兼容AST脱离字符串,或者一种方法将占位符放入
erl_语法
内容中,并在调用
revert()
后替换它。或者我缺少一些明显的东西……请在erl_语法手册中添加以下注释:“这意味着所有erl_解析树都是有效的抽象语法树……”不,我只是认为它没有正确标记,所以你把[{cons,1,…]]放进去,它期待着{tree,something},其中某物与[{cons,1,…}]有某种关联:(谢谢Richard!我想我应该已经弄明白了…:\谢谢你的答案,Robert。同时我从Richard那里得到了答案:erl_解析树可以混合到erl_语法树中。然后调用
erl_语法:revert()
从混合中创建了一个干净的erl_解析树。我唯一的错误是没有注意到,
erl_解析:parse_exprs()
的结果被包装在一个列表中…起初我还打算在一个临时文件中构建源代码,并以通常的方式编译它。现在我将其改为构建一个iolist()不幸的是,我丢失了一些很好的功能,比如代码:get_object_code,beam_lib:get_chunks,hipe:compile,但我可以接受这些。顺便说一句,我只是在玩从模板文件生成模块的游戏,同时让自己在模板中使用Erlang代码。
{ok, Ts, _} = erl_scan:string(String),
{ok, Term} = erl_parse:parse_term(Ts),
ListAST = erl_syntax:abstract(Term),