chaîne d'arbre de syntaxe abstraite

je voudrais convertir une chaîne contenant une expression Erlang valide à sa représentation d'arbre de syntaxe abstraite, sans aucun succès jusqu'à présent.

ci-Dessous est un exemple de ce que je voudrais faire. Après compilation, alling z:z(). génère le module zed, qui en appelant zed:zed(). retourne le résultat de l'application lists:reverse sur la liste donnée.

-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 peut être "[1,2,3].", ou "begin A=4, B=2+3, [A,B] end.", ou quelque chose de même.

(notez que c'est juste un exemple de ce que je voudrais faire, donc évaluer String n'est pas une option pour moi.)


EDIT:

spécifier ListAST comme ci-dessous génère un énorme monstre dict-digraph-error, et dit "Erreur interne dans lint_module".

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

EDIT2:

Cette solution fonctionne pour des termes simples:

{ok, Ts, _} = erl_scan:string(String),
{ok, Term} = erl_parse:parse_term(Ts),
ListAST = erl_syntax:abstract(Term),
10
demandé sur jldupont 2009-12-29 14:44:40

3 réponses

dans votre exemple D'édition:

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

la ListAST est en fait une liste de AST:s (parce que parse_exprs, comme son nom l'indique, analyse plusieurs expressions (chacune terminée par une période). Puisque votre chaîne contenait une seule expression, vous avez une liste d'un élément. Tout ce que vous devez faire est de correspondre à ça:

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

donc, cela n'a rien à voir avec erl_syntax (qui accepte tous les arbres erl_parse); c'est juste que vous aviez un enveloppeur de liste supplémentaire autour du ListAST, qui causé le compilateur de vomir.

5
répondu RichardC 2009-12-30 15:07:41

Quelques commentaires du haut de ma tête.

Je n'ai pas vraiment utilisé les bibliothèques erl_syntax mais je pense qu'elles rendent difficile la lecture et la "vue" de ce que vous essayez de construire. Je voudrais probablement importer les fonctions ou définir ma propre API pour la rendre plus courte et plus lisible. Mais alors j'ai généralement tendance à préférer des fonctions plus courtes et des noms de variables.

L'AST créé par erl_syntax et le "standard" créé par erl_parse et utilisé dans le compilateur sont différents et ne peut pas être mélangés. Donc, vous devez choisir l'un d'entre eux et de rester avec elle.

L'exemple dans votre deuxième édition fonctionnera pour les Termes mais pas dans le cas plus général:

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

ceci parce que erl_parse:parse_term / 1 renvoie le terme réel représenté par les tokens tandis que les autres fonctions erl_parse parse_form et parse_exprs renvoient les AST. Les mettre dans erl_syntax: abstract fera de drôles de choses.

selon ce que vous essayez de faire faire il pourrait en fait être plus facile de réellement écrire et fichier erlang et compiler plutôt que de travailler directement avec les formes abstraites. Cela va à l'encontre de mes sentiments ancrés, mais générer les ASTs d'erlang n'est pas anodin. Ce type de code avez-vous l'intention de produire?

<shameless_plug>

si vous n'avez pas peur des listes, vous pouvez essayer D'utiliser LFE (lisp flavoured erlang) pour générer du code car avec tous les lisps il n'y a pas de forme abstraite spéciale, tout est homoiconique et beaucoup plus facile pour travailler avec.

</shameless_plug>

3
répondu rvirding 2009-12-31 01:40:12

Zoltan

Voici comment nous obtenons 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> 
2
répondu Gordon Guthrie 2009-12-29 13:56:28