2009-12-29 10 views
10

me gustaría convertir una cadena que contiene una expresión válida de Erlang a su representación abstracta árbol de sintaxis, sin ningún éxito hasta el momento.cadena de árbol de sintaxis abstracta

A continuación se muestra un ejemplo de lo que me gustaría hacer. Después de compilar, Alling z:z(). genera módulo zed, que llamando zed:zed(). devuelve el resultado de aplicar lists:reverse en la lista dada.

-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 podría ser "[1,2,3]." o "begin A=4, B=2+3, [A,B] end.", ni nada parecido.

(Tenga en cuenta que esto es sólo un ejemplo de lo que me gustaría hacer, por lo que la evaluación de String no es una opción para mí.)


EDITAR:

Especificación ListAST de la siguiente genera un enorme monstruo dict-digraph-error-monstruo, y dice "error interno en lint_module".

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

Edit2:

Esta solución funciona para términos simples:

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

Ahora que miro el código, obviamente, mezclando erl_syntax y formatos erl_parse ... todavía no puede encontrar la manera de hacer esto, sin embargo (típico exceso de error bejgli). – Zed

+0

Sí, si compara su ListAST con una hecha por erl_syntax, no se ven igual :( 42> ListAST. [{Cons, 1, {integer, 1,1}, {cons, 1, {integer, 1,2}, {nil, 1}}}] 43> erl_syntax: list ([1, 2, 3], []). {tree, list, {attr, 0, [], none}, {list , [1,2,3], []}} 44> –

+0

Así que, o necesito una forma de hacer una AST compatible con 'erl_syntax' fuera de la cadena, o una forma de poner un marcador de posición a las cosas 'erl_syntax', y reemplácelo después de llamar a 'revert()'. O me falta algo obvio ... – Zed

Respuesta

5

En su ejemplo EDIT:

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

la ListAST es en realidad una lista de AST: s (porque parse_exprs, como su nombre indica, lo analiza múltiples expresiones (cada uno terminado por un período). Como su cadena contiene una sola expresión, tiene una lista de un elemento. Todo lo que hay que hacer es que coincida con la salida:

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

por lo que no tiene nada que ver con erl_syntax (que acepta todos los árboles erl_parse); es solo que tenía un contenedor de lista adicional alrededor de ListAST, lo que causó que el compilador vomitara.

+0

Gracias Richard! Creo que debería haber entendido esto ...: \ – Zed

2

Zoltan

Así es como se obtiene la 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

Ya probé cosas como esta. Esto simplemente no funciona cuando pongo esto en mi AST construido con erl_syntax. Hace que 'compile: forms()' vomite ... – Zed

+0

@Gordon, extendí el ejemplo en mi pregunta. Funciona perfectamente cuando creo la lista con 'erl_syntax'. Pero reemplazarlo con el material 'erl_parse' no funciona desafortunadamente. – Zed

3

Algunos comentarios de la parte superior de mi cabeza.

realmente no he utilizado las bibliotecas erl_syntax pero yo creo que hacen que sea difícil de leer y "ver" lo que está tratando de construir. Probablemente importaría las funciones o definiría mi propia API para hacerla más corta y más legible. Pero generalmente tiendo a preferir nombres de función y variable más cortos.

El AST creado por erl_syntax y el "estándar" creado por erl_parse y utilizado en el compilador son diferentes y no se pueden mezclar. Entonces debes elegir uno de ellos y quedarte con él.

El ejemplo en su segundo EDITAR trabajará para los términos, pero no en el caso más general:

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

Esto porque erl_parse: vuelve parse_term/1 el término real representado por las fichas mientras que el otro funciona erl_parse parse_form y parse_exprs devuelven los AST. Poniéndolos en erl_syntax: abstract hará cosas divertidas.

Dependiendo de lo que esté tratando de hacer, en realidad podría ser más fácil escribir y crear un archivo erlang y compilarlo en lugar de trabajar directamente con los formularios abstractos. Esto va en contra de mis sentimientos arraigados, pero generar los AST de erlang no es trivial. ¿Qué tipo de código pretendes producir?

<shameless_plug>

Si no tienen miedo a las listas puede intentar utilizar LFE (Lisp con sabor Erlang) para generar el código como con todos Lisps no hay forma de resumen especial, que es todo homoiconic y mucho más fácil de trabajar.

</shameless_plug>

+0

Gracias por la respuesta, Robert. Mientras tanto, recibí la respuesta de Richard: los árboles erl_parse se pueden mezclar en árboles erl_syntax. Luego, al llamar a 'erl_syntax: revert()' crea un árbol limpio de erl_parse fuera de la mezcla. Mi único error fue no darme cuenta de que el resultado de 'erl_parse: parse_exprs()' está envuelto en una lista ... – Zed

+0

Al principio también fui a compilar el código fuente en un archivo temporal y compilarlo de la forma habitual. Ahora lo cambié a la construcción de un iolist() en su lugar, y uso parse_forms en eso, entonces todo se hace en la memoria. Desafortunadamente, perdí algunas características agradables, como el código: get_object_code, beam_lib: get_chunks, hipe: compilar, pero puedo vivir con eso. – Zed

+0

BTW Estoy jugando con la generación de módulos a partir de archivos de plantilla, mientras me permitía usar el código de Erlang dentro de las plantillas. – Zed

Cuestiones relacionadas