2009-12-29 10 views
10

Ich möchte einen String, der einen gültigen Erlang-Ausdruck enthält, in seine abstrakte Syntaxbaumdarstellung konvertieren, bisher jedoch ohne Erfolg.String zum abstrakten Syntaxbaum

Unten ist ein Beispiel, was ich gerne machen würde. Nach dem Kompilieren generiert alling z:z(). das Modul zed, das durch Aufruf von zed:zed(). das Ergebnis der Anwendung lists:reverse auf die angegebene Liste zurückgibt.

-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 konnte gleich "[1,2,3]." oder "begin A=4, B=2+3, [A,B] end.", oder irgendetwas sein.

(Beachten Sie, dass dies nur ein Beispiel dafür, was Ich mag, so zu tun, würde String Bewertung ist keine Option für mich.)


EDIT:

Angeben ListAST wie unten erzeugt ein riesiges Dict-Digraph-Fehler-Monster und sagt "interner Fehler in lint_module".

String = "[1,2,3].", 
{ok, Ts, _} = erl_scan:string(String), 
{ok, ListAST} = erl_parse:parse_exprs(Ts), 

EDIT2:

Diese Lösung funktioniert für einfache Worte:

{ok, Ts, _} = erl_scan:string(String), 
{ok, Term} = erl_parse:parse_term(Ts), 
ListAST = erl_syntax:abstract(Term), 
+0

Jetzt, wo ich den Code anschauen, mischen ich natürlich bis erl_syntax und erl_parse Formate ... immer noch nicht herausfinden können, wie dies aber tun (typisch zu viel bejgli Fehler). – Zed

+0

Ja, wenn Sie Ihre ListAST mit der von erl_syntax vergleichen, sehen sie nicht gleich aus :( 42> ListAST. [{Cons, 1, {integer, 1,1}, {cons, 1, {integer, 1,2}, {nil, 1}}}] 43> erl_syntax: list ([1, 2, 3], []). {Baum, Liste, {attr, 0, [], none}, {list , [1,2,3], []}} 44> –

+0

Also muss ich entweder einen 'erl_syntax' kompatiblen AST aus der Zeichenkette machen oder einen Platzhalter für' erl_syntax' verwenden, und ersetze es nach dem Aufruf von 'revert()'. Oder ich vermisse etwas Offensichtliches ... – Zed

Antwort

5

In Ihrem EDIT Beispiel:

String = "[1,2,3].", 
{ok, Ts, _} = erl_scan:string(String), 
{ok, ListAST} = erl_parse:parse_exprs(Ts), 

die ListAST ist eigentlich eine Liste von AST: s (weil parse_exprs, wie der Name schon sagt, mehrere Ausdrücke (jeweils durch einen Punkt beendet parst). Da Ihre Zeichenfolge einen einzelnen Ausdruck enthielt, haben Sie eine Liste mit einem Element erhalten. Alles, was Sie tun müssen, ist, dass passen heraus:

{ok, [ListAST]} = erl_parse:parse_exprs(Ts), 

so hat es nichts mit erl_syntax zu tun (die alle erl_parse Bäume akzeptiert); Es ist nur so, dass Sie einen zusätzlichen Listen-Wrapper um den ListAST herum hatten, der den Compiler zum Kotzen brachte.

+0

Danke Richard! Ich denke, ich hätte das herausfinden sollen ...: – Zed

2

Zoltan

Dies ist, wie wir das AST erhalten:

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

Ich habe schon solche Sachen probiert. Das geht einfach nicht, wenn ich das in meinen AST mit erl_syntax lege. Es macht 'compile: forms()' kotzen ... – Zed

+0

@Gordon, habe ich das Beispiel in meiner Frage erweitert. Es funktioniert perfekt, wenn ich die Liste mit 'erl_syntax' erstelle. Aber es mit dem 'erl_parse'-Zeug zu ersetzen funktioniert leider nicht. – Zed

3

Einige Kommentare von der Spitze meines Kopfes.

Ich habe die Bibliotheken von erl_syntax nicht wirklich benutzt, aber ich denke, sie machen es schwierig zu lesen und zu sehen, was Sie zu bauen versuchen. Ich würde wahrscheinlich die Funktionen importieren oder meine eigene API definieren, um sie kürzer und lesbarer zu machen. Aber dann tendiere ich im Allgemeinen dazu, kürzere Funktions- und Variablennamen zu bevorzugen.

Der AST erstellt von erl_syntax und der "Standard" erstellt von erl_parse und verwendet im Compiler sind unterschiedlich und kann nicht gemischt werden. Sie müssen also eine von ihnen wählen und dabei bleiben.aber nicht in dem allgemeineren Fall

Das Beispiel in Ihrem zweiten EDIT wird nach Begriffen arbeiten: Diese

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

weil erl_parse: parse_term/1 kehrt die tatsächliche Laufzeit durch die Token dargestellt, während die andere erl_parse Funktionen parse_form und parse_exprs geben die ASTs zurück. Putting sie in erl_syntax: Zusammenfassung wird lustige Dinge tun.

Je nachdem, was Sie versuchen zu tun, könnte es tatsächlich einfacher zu schreiben und Erlang-Datei tatsächlich und kompilieren, anstatt direkt mit den abstrakten Formen arbeiten. Das widerspricht meinen tief verwurzelten Gefühlen, aber die Erlang-ASTs zu erzeugen, ist nicht trivial. Welche Art von Code möchten Sie produzieren?

<shameless_plug>

Wenn Sie keine Angst vor Listen sind Sie könnten versuchen, LFE mit (Lispeln aromatisierte erlang) Code zu erzeugen, wie mit allen lispelt es keine spezielle abstrakte Form ist, es ist alles homoiconic und viel einfacher zu handhaben.

</shameless_plug>

+0

Danke für die Antwort, Robert. In der Zwischenzeit habe ich die Antwort von Richard bekommen: erl_parse Bäume können in erl_syntax Bäume gemischt werden. Dann ruft 'erl_syntax: revert()' einen sauberen erl_parse Baum aus dem Mix. Mein einziger Fehler war, dass ich nicht bemerkte, dass das Ergebnis von 'erl_parse: parse_exprs()' in eine Liste eingeschlossen wurde ... – Zed

+0

Zuerst habe ich auch den Quellcode in eine temporäre Datei geschrieben und kompiliert. Jetzt habe ich das geändert, um stattdessen eine iolist() zu erstellen und parse_forms dafür zu verwenden, also wird alles im Speicher ausgeführt. Leider habe ich einige nette Features verloren, wie zum Beispiel Code: get_object_code, beam_lib: get_chunks, hipe: compile, aber damit kann ich leben. – Zed

+0

Übrigens spiele ich gerade mit dem Generieren von Modulen aus Vorlagendateien, während ich Erlang-Code in den Vorlagen verwenden kann. – Zed

Verwandte Themen