2009-12-29 112 views
10

我想將包含有效的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如下會生成一個巨大的字典 - 圖形錯誤 - 怪物,並說「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

現在,我在看代碼,我顯然混淆了erl_syntax和erl_parse格式......仍然無法弄清楚如何做到這一點,但(典型太多bejgli錯誤)。 – Zed 2009-12-29 13:39:39

+0

是的,如果你比較ListList和erl_syntax創建的列表,他們看起來並不相似:( 42> ListAST。[{cons,1,{integer,1,1},{cons,1,{integer, {},{nil,1}}}] 43> erl_syntax:list([1,2,3],[])。 ,[1,2,3],[]}} 44> – 2009-12-29 14:02:34

+0

所以我需要一種方法來使'erl_syntax'兼容的AST脫離字符串,或者將佔位符放入'erl_syntax'的東西,並在調用'revert()'之後替換它,或者我缺少一些明顯的... – Zed 2009-12-29 14:07:33

回答

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中有一個額外的列表包裝,導致編譯器嘔吐。

+0

謝謝理查!我想我應該知道這個了......:\ – Zed 2009-12-30 15:34:11

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> 
+0

我已經嘗試過這樣的東西。當我把這個放到用erl_syntax構建的AST中時,這根本就行不通。它使'compile:forms()'拋出... – Zed 2009-12-29 12:02:22

+0

@戈登,我在我的問題中擴展了這個例子。當我用'erl_syntax'創建列表時,它完美地工作。但是,用erl_parse替換它是不行的。 – Zed 2009-12-29 13:31:58

3

我的頭頂有些評論。

我還沒有真正使用erl_syntax庫,但我確實認爲它們難以閱讀和「看到」你想要構建的東西。我可能會導入這些函數或定義我自己的API,以使它更短,更清晰。但是,我通常傾向於選擇較短的函數和變量名稱。

由erl_syntax創建的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_exprs返回AST。將它們放入erl_syntax:abstract將會做有趣的事情。

根據你想要做的事情,實際上可能更容易實際編寫出erlang文件並進行編譯,而不是直接使用抽象表單。這違背了我根深蒂固的感覺,但生成erlang ASTs並不是微不足道的。你打算生成什麼類型​​的代碼?

<shameless_plug>

如果你不害怕列表您可以嘗試使用LFE(LISP的調味二郎)來生成代碼與所有的Lisp也沒有特別的抽象形式,它的所有homoiconic,更容易的工作。

</shameless_plug>

+0

謝謝你的回答,羅伯特。在此期間,我從Richard得到了答案:erl_parse樹可以混合到erl_syntax樹中。然後調用'erl_syntax:revert()'從混合中創建一個乾淨的erl_parse樹。我唯一的錯誤是沒有注意到'erl_parse:parse_exprs()'的結果被封裝在一個列表中...... – Zed 2009-12-31 11:50:54

+0

起初我還去建立一個臨時文件中的源代碼,並按通常的方式編譯它。現在我改變了建立一個iolist(),並使用parse_forms,所以一切都在內存中完成。不幸的是,我失去了一些很好的功能,如代碼:get_object_code,beam_lib:get_chunks,hipe:compile,但我可以忍受。 – Zed 2009-12-31 12:23:39

+0

順便說一句我只是在模板文件中生成模塊,同時讓我自己在模板中使用Erlang代碼。 – Zed 2009-12-31 12:25:50