JSR223 в TermWare – заметки по реализации.


Содержание

Введение.

Доступ к движку скрипт-языка.

Использование движка скрипт языка.

Интерпретация

Поддержка необязательного интерфейса компиляции (Compilable).

Поддержка необязательного интерфейса вызовов (Invocable).

История изменений.


Введение.


TermWare – язык основанный на переписывающих правилах, предназначенный для встраивания в Java системы. Подробности можно посмотреть на домашней странице TermWare http://www.gradsoft.ua

JSR223 это стандарт на API взаимодействия Java со скрипт языками. Спецификация может быть найдена по адресу http://www.jcp.org/jsr223. Реализация JSR223 включена в J2SE начиная с JDK-1.6


Доступ к движку скрипт-языка.


Имена для движка TermWare: 'TermWare' и 'termware'. Тип mime для скриптов на языке termware: Application/X-TermWare-Script. Следующий фрагмент кода показывает, как получить доступ к движку по имени языка:


ScriptEngineManager  scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName('termware');

или по типу mime:


ScriptEngineManager  scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine =
scriptEngineManager.getEngineByMimeType('Application/X-TermWare-Script');



Каждые экземпляр движка сохраняет свое множество переменных (binding) и свой экземпляр системы термов с набором преобразований из general, БД фактов DefaultFacts и стратегией FirstTop. В многопоточных приложениях можно использовать termware в соотвествии с JSR223 политикой “MULTITHREADING” , где несколько экземпляров движка могут быть доступны одновременно; а один – если мы вручную синхронизируем доступ к bindings.


Использование движка скрипт языка.


Интерпретация


После получения, мы можем использовать движок скрипт языка. Самый простой способ -- интерпретация выражений (для нас – редукция терма, переданного как строка) используя семество eval методов ScriptEngine:


 Object o = scriptEngine.eval(“2+2”);
System.out.println(“2+2 is “+o.toString());

Списое предопределенных редукций можно посмотреть в описании системы general в TermWare API. Трансформер Let позволяет нам определять новые правила в текущей системе, т.е. можно написать нечто в стиле:


 (void)scriptEngine.eval(“let square($x) -> $x*$x”);
Object o = scriptEngine.eval(“square(3)”);
System.out.println(“square(3) is “+o.toString());

Также можно вызывать другие системы используя встроенную функциональность apply и определять новые системы с помощью стандартных редукций в sys:


(void)scriptEngine.eval(“sys.system(MyNewSystem,default,”+
“ruleset(P($x) -> $x*$x,Q($x)->$x+$x),FirstTop)”);

Object o = scriptEngine.eval(“MyNewSystem.P(3)”);

Для передачи информации из Java в termware используется интерфейс связывания (bindings):


scriptEngine.put(“a”,3);
Object o = scriptEngine.eval(“MyNewSystem.P($a)”);

Или, что то-же самое:


Bindings bindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put(“a”,3);
Object o = scriptEngine.eval(“MyNewSystem.P($a)”);

Будьте осторожны со связыванием, когда определяете новые правила: следующий фрагмент кода:


  scriptEngine.put(“a”,10);
Object o = scriptEngine.eval(“let square($a) -> $a*$a”);

добавит в систему правило square(10) -> 10*10 вместо ожидаемого, так как подстановка переменных происходит перед собственно интрепретацией. Решение – очищать связывания перед использованием свободных переменных, т.е:


scriptEngine.getBinding(ScriptContext.ENGINE_SCOPE).clear();
Object o = scriptEngine.eval(“let square($a) -> $a*$a”);

Также возможно передать в связывание любой Java объект и потом вызывать его методы из скрипта termware:


scriptEngine.put(“x”,new Point(10,10));
Object o = scriptEngine.eval(“$x.getX()”);

Поддержка необязательного интерфейса компиляции (Compilable).


Иногда нам нужно выполнить несколько раз один и тот-же фрагмент скрипт кода. В таком случае естественно разобрать его только один раз и потом использовать подготовленный уже разобранный фрагмент кода для быстрой интрпретации. TermWarе реализует необязательный интерфейс JSR223 Compilable, который позволяет нам получить откомпилированный скрипт из строки или какого-то входного потока, а потом вызвать для него eval.

Пример:


Compilable compilable = (Compilable)scriptEngine;
CompiledScript cs = compilable.compile(“square($x)”);
scriptEngine.put(“x”,10);
Object o = cs.eval();
scriptEngine.put(“x”,11);
o=cs.eval()

Поддержка необязательного интерфейса вызовов (Invocable).


TermWare также реализует необязательный интерфейс ScriptEngine Invocable. Это позволяет нам определить каке-то преобразования в termware, а потом использовать их из Java с помощью Invocation API. Самый простой случай это использования метода invokeFunction:


 ScriptEngine scriptEngine = scriptEngineManager.getEngineByName(“termware”);
scriptEngine.eval(“let square($x) -> $x*$x;”);
Invocable invocable = (Invocable)scriptEngine;
Object o = invocable.invokeFunction(“square”,10);

Набор функций реализует интерфейс. К примеру, пусть у нас есть интерфейс Summator:


interface Summator
{
int sum(int x, int y);
double sum(double x, double y);
}

Тогда мы можем определить переписывающие правила для sum в TermWare и связать экземпляр ScriptEngine, где определен этот метод, с данным интерфейсом:


ScriptEngine scriptEngine = scriptEngineManager.getEngineByName(“termware”);
scriptEngine.eval(“let sum($x,$y) -> $x+$y;”);
Invocable invocable = (Invocable)scriptEngine;
Summator tSummator = invocable.getInterface(Summator.class);
int five = tSummator.sum(2,3);

Таким же образом мы можем представлять экземпляры объектов TermWare как Java интерфейсы. К примеру, пусть нам требуется получить число и возвратить строку, в котором это число описанно словами (прописью). В файле systems/examples/Number2String.def можно найти систему, которая делает это на английском языке. Теперь сконструируем из нее Java класс с методом numberToString:

Со стороны Java нам необходимо определение интрефейса:


public interface InWords {
String numberInWords(int number);
}


Метод , возвращающий данный интерфейс выглядит следующим образом:


public static InWords createInwords()
{
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName(“termware”);
Invocable invocable = (Invocable)scriptEngine;
return invocable.getInterface(“examples::Number2String”,InWords.class);
}

Правила получения интерфейсов из термов довольно просты: если терм является атомом или составным именем, то termware пытается найти систему с таким именем и связать ее с интерфейсом, иначе если терм, содержвщий Java объект, который реализует целевой интерфейс, то мы этот объект и возвращаем, в противном случае генерируется сообщение об ошибке.


It is possible extend mapping of terms to objects, by using TypeConversions API, available by method TermWareInstance.getTypeConversions() (see API for details).

История изменений.