TermWare: JSR 223 implementation notes.


Table of Contents

Introduction.

Resolving script engine.

Using script engine.

Evaluation

Support of compilable optional interface.

Support of invocable optional interface.

History of changes.


Introduction.


TermWare is a language, based on rewriting rules and designed to be embedded in Java based systems. For general information about TermWare see http://www.gradsoft.ua

JSR223 is a standard for interaction between Java ans scripting language. Specification can be found at http://www.jcp.org/jsr223. JSR223 implementation is included in Java standard edition starting from JKD-1.6


Resolving script engine.


Names of TermWare script engine are 'TermWare' and 'termware'. Mime type for termware script is Application/X-TermWare-Script, Following code fragment shows, how to retrieve script engine by name:


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

or by mime type:


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



Each instance of script engine keep own bindings and own instance of term system, with general set of transformers, default facts and FirstTop strategy. It is safe to use TermWare in multithreading application, according to JSR223 “MULTITHREADING” policy, where few instances of script engine can be accessed concurrently, one instance – if caller manually synchronize access to bindings.



Using script engine.


Evaluation


After obtaining, we can use script engine, evaluating terms with help of eval family of ScriptEngine methods:


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

For list of default transformations you can look at description of general system in TermWare API. Let transformer allows us to define new rules in current system, so we can write something in style:


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

It is possible to call other systems, using apply default transformer and define new systems with help of sys system:


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

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

For passing information from Java to termware engine we can use bindings interface:


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

Or, which the same:


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

Be careful with bindings when defining new rules: next code fragment:


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

will introduce rule square(10) -> 10*10 instead expected, because variables substitution is done before evaluation. The solutions is to clear bindings before using free variables, i. e.:


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

It is possible to pass arbitrary Java object, to call methods of one from termware script:


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

Support of compilable optional interface.


Sometimes we need to use same script code fragment few times. In such cases better parse code fragment only once and then use preparsed script for faster evaluation. TermWare implements optional JSR223 Compilable interface, which allows us to receive compiled script from string or some reader, than call eval on it.

Example:


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()

Support of invocable optional interface.


TermWare also implements optional Invocable interface of ScriptEngine. This allow us to define rule for some names inside system and then use ones via Invocation API. The most simple case is invokeFunction method:


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

Set of function can define interface. For example, let we have interface Summator:


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

Then we can define rewriting rules for 'sum' method in TermWare and bind instance of script engine to this interface:


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);

Also we can map instances of TermWare objects to Java interfaces in the same manner. For example, let we need receive number and return string, which describe this number in natural language. In file systems/examples/Number2String.def we can find system, which do that.

Now, let's construct Java class, which will have method numberToString:

From Java side we need definition of interface:


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


And method, which return this interface:


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

Rules of transforming terms to interfaces are simple: if term is atom or complex name, than termware try to resolve appropriative system and bind one to interface, otherwise if term is Java object which implements target interface, than we return this Java Object.


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


History of changes.