2009-12-29 13 views
10

Vorrei convertire una stringa contenente un'espressione di Erlang valida nella sua rappresentazione astratta della struttura della sintassi, senza alcun risultato fino ad ora.stringa ad albero di sintassi astratto

Di seguito è riportato un esempio di ciò che vorrei fare. Dopo la compilazione, alling z:z(). genera il modulo zed, che chiamando zed:zed(). restituisce il risultato dell'applicazione di lists:reverse nell'elenco fornito.

-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 potrebbe essere "[1,2,3].", o "begin A=4, B=2+3, [A,B] end.", o qualcosa di simile.

(Si noti che questo è solo un esempio di quello che vorrei fare, in modo da valutare String non è un'opzione per me.)


EDIT:

Specifica ListAST come di seguito genera un enorme monster-error-errore dict-digraph e dice "error interno in lint_module".

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

EDIT2:

Questa soluzione funziona per termini semplici:

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

Ora che guardo il codice, io ovviamente mescolare erl_syntax e formati erl_parse ... ancora non riesco a capire come fare questo però (tipica troppo errore bejgli). – Zed

+0

Sì, se confronti il ​​tuo ListAST con uno fatto da erl_syntax, non hanno lo stesso aspetto :( 42> ListAST. [{Cons, 1, {intero, 1,1}, {cons, 1, {intero, 1,2}, {nil, 1}}}] 43> erl_syntax: list ([1, 2, 3], []). {tree, list, {attr, 0, [], none}, {list [1,2,3], []}} 44> –

+0

Quindi ho bisogno di un modo per rendere un AST compatibile con 'erl_syntax' dalla stringa, o un modo per mettere un segnaposto nella roba' erl_syntax', e sostituirlo dopo aver chiamato 'revert()'. O mi manca qualcosa di ovvio ... – Zed

risposta

5

Nel tuo esempio EDIT:

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

il ListAST è in realtà un elenco di AST: s (perché parse_exprs, come indica il nome, analizza molteplici espressioni (ogni terminati da un punto). Dato che la tua stringa conteneva una singola espressione, hai ottenuto un elenco di un elemento. Tutto quello che devi fare è che corrisponda a quello fuori:

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

quindi non ha nulla a che fare con erl_syntax (che accetta tutti gli alberi erl_parse); è solo che hai avuto un extra list wrapper attorno a ListAST, che ha causato il vomito del compilatore.

+0

Grazie Richard! Immagino che avrei dovuto capirlo ...: \ – Zed

2

Zoltan

In questo modo si ottiene l'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

Ho già provato cose del genere. Questo semplicemente non funziona quando inserisco questo nel mio AST compilato con erl_syntax. Rende 'compile: forms()' throw up ... – Zed

+0

@Gordon, ho esteso l'esempio nella mia domanda. Funziona perfettamente quando creo la lista con 'erl_syntax'. Ma sostituirlo con la roba 'erl_parse' non funziona purtroppo. – Zed

3

Alcuni commenti in cima alla mia testa.

Non ho davvero utilizzato le librerie erl_syntax ma penso che rendono difficile leggere e "vedere" ciò che si sta tentando di creare. Probabilmente importerei le funzioni o definirò la mia API per renderla più breve e più leggibile. Ma in genere io preferisco tendenzialmente funzioni più brevi e nomi variabili.

L'AST creato da erl_syntax e quello "standard" creato da erl_parse e utilizzato nel compilatore sono diversi e non può essere impostato su. Quindi devi scegliere uno di loro e seguirlo.

L'esempio nel vostro secondo EDIT funzionerà per i termini, ma non nel caso più generale:

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

Questo perché erl_parse: ritorna parse_term/1 il termine effettivo rappresentato dai gettoni, mentre l'altro funzioni erl_parse parse_form e parse_exprs restituiscono gli AST. Inserendoli in erl_syntax: l'abstract farà cose divertenti.

A seconda di ciò che si sta tentando di fare, potrebbe essere effettivamente più semplice scrivere ed estrarre il file e compilarlo piuttosto che lavorare direttamente con i moduli astratti. Questo va contro i miei sentimenti radicati, ma generare gli AST di erlang non è banale. Che tipo di codice intendi produrre?

<shameless_plug>

Se non hai paura di liste si potrebbe provare a utilizzare LFE (lisp aromatizzato Erlang) per generare il codice come con tutte le lische non v'è alcuna forma astratta speciale, è tutto homoiconic e molto più facile da lavorare.

</shameless_plug>

+0

Grazie per la risposta, Robert. Nel frattempo ho ricevuto la risposta di Richard: erl_parse trees può essere mescolato con alberi erl_syntax. Quindi chiamando 'erl_syntax: revert()' crea un albero erl_parse pulito fuori dal mix. Il mio unico errore non è stato notare che il risultato di 'erl_parse: parse_exprs()' è racchiuso in un elenco ... – Zed

+0

All'inizio ho anche pensato di creare il codice sorgente in un file temporaneo e di compilarlo nel solito modo. Ora l'ho cambiato a costruire un iolist(), invece, e uso parse_forms su quello, quindi tutto è fatto in memoria. Purtroppo ho perso alcune caratteristiche interessanti, come il codice: get_object_code, beam_lib: get_chunks, hipe: compile, ma posso conviverci. – Zed

+0

BTW Sto solo giocando con la generazione di moduli da file di modello, mentre mi lascio usare il codice di Erlang all'interno dei modelli. – Zed

Problemi correlati