Подтвердить что ты не робот

Строка для абстрактного дерева синтаксиса

Я хотел бы преобразовать строку, содержащую действительное выражение Erlang, в его абстрактное древовидное представление синтаксиса без каких-либо успехов.

Ниже приведен пример того, что я хотел бы сделать. После компиляции alling 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),
4b9b3361

Ответ 1

В примере EDIT:

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, из-за которой компилятор пошатнулся.

Ответ 2

Некоторые комментарии верхней части моей головы.

Я не использовал библиотеки erl_syntax, но я думаю, что они затрудняют чтение и "видят" то, что вы пытаетесь создать. Я бы, вероятно, импортировал функции или определил свой собственный API, чтобы сделать его более коротким и четким. Но тогда я обычно предпочитаю более короткие имена функций и переменных.

AST, созданный erl_syntax и "стандартный", созданный erl_parse и используемый в компиляторе, отличается и не может смешиваться. Поэтому вы должны выбрать один из них и придерживаться его.

Пример во втором EDIT будет работать для терминов, но не в более общем случае:

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

Это потому, что erl_parse: parse_term/1 возвращает фактический термин, представленный токенами, в то время как другие функции erse_parse parse_form и parse_exprs возвращают AST. Вводя их в erl_syntax: абстрактный будет делать забавные вещи.

В зависимости от того, что вы пытаетесь сделать, на самом деле может быть проще фактически выписать и erlang файл и скомпилировать его, а не напрямую работать с абстрактными формами. Это противоречит моим укоренившимся чувствам, но генерация erlang AST не является тривиальной. Какой тип кода вы собираетесь производить?

<shameless_plug>

Если вы не боитесь списков, вы можете попробовать использовать LFE (lisp flavored erlang) для генерации кода, поскольку со всеми лишами нет специальной абстрактной формы, все это гомоиконно и намного проще работать.

</shameless_plug>

Ответ 3

Золтан

Вот как мы получаем 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>