Инструментарий виртуальных миров и OMeta (Ohm)

Прошел год как первый прототип интеграции Virtual World Framework & Ometa был представлен на международной конференции SPLASH'14 на семинаре по будщему программирования Future Programming Workshop. В 2015 году вышла в свет новая версия объектно-ориентированного языка для создания языков Ometa под именем Ohm. Как и OMeta, новый язык так же поддерживает объектно-ориентированное создание грамматик, распознавание строк и структурных типов данных, но теперь грамматики полностью отделены от семантических действий над ними.

В этом сообщении я хотел бы представить вашему вниманию обновленный прототип, который базируется на новом языке Ohm!

Чтобы попробовать прототип в действии, просто откройте эту ссылку в браузере и войдите в виртуальный мир, содержащий генератор L-систем. Вы можете модифицировать этот мир, скопировав его или создав новый. В библиотеке "Content Library" располагающейся слева в инструментарии миров, находятся примеры L-систем от треугольника Серпинского до снежинки Коха.

Вы можете модифицировать параметры выбранного объекта L-системы в реальном времени: угол, количество итераций, аксиомы и правила. Более того в редакторе скриптов "Script Editor" вы можете изменить грамматику выбранного объекта, не нарушая работу других объектов мира.


Первые шаги по работе с прототипом

Ниже приведены шаги, выполнив которые вы сможете попробовать самостоятельно как прикрепить грамматику к объекту в виртуальном пространстве и добавить соответствующие семантические действия, используя версию прототип Крестьянство SDK, объединяющий инструментарий виртуальных миров ADL Sandbox и язык Ohm. В качестве примера мы возьмем грамматику для арифметического калькулятора, которая доступна из обучающих материалов по языку Ohm.
Итак, сначала мы определим грамматику для распознавания простых арифметических выражений типа "(5+7)*4"; далее мы добавим семантические действия, которые позволят вычислять распознанные выражения на языке JavaScript; далее мы прикрепим созданную грамматику к объекту виртуального мира, так чтобы результат вычислений отображался на самом же объекте.
Работающий виртуальный мир с грамматикой арифметического калькулятора доступен здесь: http://live.net.ru/adl/sandbox/world/WRpbHEuHfYGmUm54

Для начала работы, зарегистрируйтесь пожалуйста на сайте виртуальных миров: http://live.net.ru проекта http://www.krestianstvo.org.
Будучи зарегистрированными, вы сможете создавать свои миры или клонировать существующие для изменения и экспериментов.

1. Создайте новый мир и войдите в него:

2. Внутри мира создайте объект "3D Text" для последующего присоединения грамматики к нему и визуализации результата:

3. Откройте редактор скриптов "Script Editor" для выбранного объекта (3D text) и создайте новый параметр с именем: ohmCalc. Скопируйте содержимое исходного кода грамматики в редактор. Важно чтобы имя начиналось с префикса 'ohm'! Это укажет среде виртуальных миров, что для параметра должны будут создаться автоматически другие необходимые параметры и методы (язык Omh доступен после загрузки мира по глобальной переменной _ohm). После создания параметра, объект грамматики автоматически сгенерируется и будет сохранен под именем Calc.

Исходный код грамматики для арифметического калькулятора:

Arithmetic {
  Exp
    = AddExp
    
  AddExp
    = AddExp "+" MulExp  -- plus
    | AddExp "-" MulExp  -- minus
    | MulExp

  MulExp
    = MulExp "*" ExpExp  -- times
    | MulExp "/" ExpExp  -- divide
    | ExpExp

  ExpExp
    = PriExp "^" ExpExp  -- power
    | PriExp

  PriExp
    = "(" Exp ")"  -- paren
    | "+" PriExp   -- pos
    | "-" PriExp   -- neg
    | ident
    | number
    
  ident  (an identifier)
    = letter alnum*

  number  (a number)
    = digit* "." digit+  -- fract
    | digit+             -- whole
}

4. Создайте метод для выбранного объекта (3D text), в котором мы определим семантические действия для грамматики. Объект семантики доступен по автоматически сгенерированнму параметру Engine.getProperty(this.id, 'semanticsCalc'). Скопируйте ниже приведенный код операции 'interpret' в новый метод с именем addOperationCalc

Исходный код: addOperationCalc

function addOperationCalc()
{   
 
    var constants = {pi: Math.PI, e: Math.E};
    var s = _LangManager.ohmLangs[this.id]["Calc"]["semantics"];

s.addOperation('interpret', {
      Exp: function(e) {
        return e.interpret();  
      },
      AddExp: function(e) {
        return e.interpret();
      },
      AddExp_plus: function(x, _, y) {
        return x.interpret() + y.interpret();
      },
      AddExp_minus: function(x, _, y) {
        return x.interpret() - y.interpret();
      },
      MulExp:        function(e)       { return e.interpret(); },
      MulExp_times:  function(x, _, y) { return x.interpret() * y.interpret(); },
      MulExp_divide: function(x, _, y) { return x.interpret() / y.interpret(); },
      ExpExp:        function(e)       { return e.interpret(); },
      ExpExp_power:  function(x, _, y) { return Math.pow(x.interpret(), y.interpret()); },
      PriExp:        function(e)       { return e.interpret(); },
      PriExp_paren:  function(_, e, _) { return e.interpret(); },
      PriExp_pos:    function(_, e)    { return e.interpret(); },
      PriExp_neg:    function(_, e)    { return -e.interpret(); },
      ident: function(_, _) {
        return constants[this.interval.contents] || 0;
      },
      number: function(_) {
        return parseFloat(this.interval.contents);
      }
    });
}

5. Далее нам нужно добавить вызов функции addOperationCalc() из метода initGrammarCalc() и вызвать его для инициализации семантических действий. После добавления кода, нажмите кнопку Call Method.

function initGrammarCalc()
{
    //console.log('Init grammar: Calc');
    this.addOperationCalc();
}

6. Теперь, мы готовы протестировать грамматику. Создайте новый метод с именем "test" и скопируйте код для него. Нажмите кнопку Call Method.

function test()
{
    var str = '(5+7)*4';
    var gram = _LangManager.ohmLangs[this.id]["Calc"]["grammar"];
    var sem = _LangManager.ohmLangs[this.id]["Calc"]["semantics"];
    var r = gram.match(str);
    var n = sem(r);
    this.text = n.interpret();
}

Если все в порядке, то вы должны увидеть результат распознавания и вычисления арифметического действия из строки "(5+7)*4"

Если что-то не работает, посмотрите виртуальный мир с подключенной грамматикой арифметического калькулятора здесь