В некоторых случаях бывает удобно скомпилировать определенные части программы во время работы приложения. Например, в мире явы так происходит компиляция веб сервером .jsp страниц в сервлеты. Дру��ими возможными применениями подобной техники являются разные языки шаблонов, регулярные выражения и т.д.
В эрланге транслятор встроен в библиотеку времени исполнения, и позволяет полностью управлять всеми стадиями трансляции.
Как это сделать
К примеру, создадим модуль, содержащий одну функцию. Пусть это будет — сложение двух чисел.
Итак. Формируем строку, содержащую текст модуля.
Далее нам необходимо сделать лексический анализ,
В результате в Lexems получаем список лексем, что-то вроде
Синтаксический анализ
На этом этапе Parsed будет содержать внутреннее представление нашей программы. Между прочим, в читаемом виде.
Так, что в некоторых простых случаях может быть разумно строить сразу такое атрибутное дерево без предварительного лексического и синтаксического анализа.
Осталась мелочь. Преобразовать атрибутное дерево в бинарный код виртуальной машины,
И загрузить этот код из строки в виртуальную машину.
Проверяем,
Все работает! Мы сформировали в памяти модуль, странслировали его, загрузили, и теперь он ничем не отличается от других модулей эрланга. Его можно вызывать откуда угодно, менять (спасибо эрлангу за эту возможность), и, между прочим, он у нас был скомпилирован с опцией native — то есть в код процессора, с помощью hipe.
В эрланге транслятор встроен в библиотеку времени исполнения, и позволяет полностью управлять всеми стадиями трансляции.
Как это сделать
К примеру, создадим модуль, содержащий одну функцию. Пусть это будет — сложение двух чисел.
Итак. Формируем строку, содержащую текст модуля.
Src = [ "-module(testmodule).", "-export([add_two_numbers/2]).", "-compile(native)." , "add_two_numbers(X,Y) -> X+Y."].
Далее нам необходимо сделать лексический анализ,
Lexems = lists:map(fun(X) -> {ok,Scanned,_} = erl_scan:string(X), Scanned end, Src).
В результате в Lexems получаем список лексем, что-то вроде
[[{'-',1}, {atom,1,module}, {'(',1}, {atom,1,testmodule}, {')',1}, {dot,1}], [{'-',1}, {atom,1,export}, {'(',1}, {'[',1}, {atom,1,add_two_numbers}, ... И т.д.
Синтаксический анализ
Parsed = lists:map(fun(X) -> {ok,P} = erl_parse:parse_form(X), P end, Lexems).
На этом этапе Parsed будет содержать внутреннее представление нашей программы. Между прочим, в читаемом виде.
[{attribute,1,module,testmodule}, {attribute,1,export,[{add_two_numbers,2}]}, {attribute,1,compile,native}, {function,1,add_two_numbers,2, [{clause,1, [{var,1,'X'},{var,1,'Y'}], [], [{op,1,'+',{var,1,'X'},{var,1,'Y'}}]}]}]
Так, что в некоторых простых случаях может быть разумно строить сразу такое атрибутное дерево без предварительного лексического и синтаксического анализа.
Осталась мелочь. Преобразовать атрибутное дерево в бинарный код виртуальной машины,
{ok,_,B} = compile:forms(Parsed).
И загрузить этот код из строки в виртуальную машину.
code:load_binary(testmodule,"nofile",B).
Проверяем,
19> testmodule:add_two_numbers(5,8). 13
Все работает! Мы сформировали в памяти модуль, странслировали его, загрузили, и теперь он ничем не отличается от других модулей эрланга. Его можно вызывать откуда угодно, менять (спасибо эрлангу за эту возможность), и, между прочим, он у нас был скомпилирован с опцией native — то есть в код процессора, с помощью hipe.