Pull to refresh

Компиляция «на ходу» в эрланге

Reading time2 min
Views1.9K
В некоторых случаях бывает удобно скомпилировать определенные части программы во время работы приложения. Например, в мире явы так происходит компиляция веб сервером .jsp страниц в сервлеты. Другими возможными применениями подобной техники являются разные языки шаблонов, регулярные выражения и т.д.

В эрланге транслятор встроен в библиотеку времени исполнения, и позволяет полностью управлять всеми стадиями трансляции.

Как это сделать


К примеру, создадим модуль, содержащий одну функцию. Пусть это будет — сложение двух чисел.

Итак. Формируем строку, содержащую текст модуля.

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.
Tags:
Hubs:
Total votes 24: ↑23 and ↓1+22
Comments19

Articles