Overview
Implementations of SubScript will consist generally of two parts:
- a compiler, or rather an extension of a compiler of the base language
- the SubScript Virtual Machine, probably written in the base language, which takes care of executing the scripts
Currently we are still working on the SubScript extension to the Scala language. The example GUI program does run, but the implementation does not yet offer support for the typical mathematical like syntax.
The SubScript Virtual Machine supports part of the language: enough to let the Life GUI controller work. Most notably, process communication is not yet supported. The current VM has been written in about 2000 lines of Scala code; this may well double for a complete and robust language implementation.
Once the Scala implementation is sufficiently ready, other base languages could be supported. Java would not be too hard to do, since it cooperates well with Scala. Also JavaScript should well be possible, because the Scala compiler optionally produces JavaScript rather than JVM bytecode.
Internal Script Language
The SubScript compiler translates scripts and script expressions into methods in the “Internal Script Language” (ISL). This comes down to establishing the template trees, as outlined in the Operational Model page.
- T_script – for a script definition
- T_n_ary – for an n-ary node, e.g. an operator such as “;”
- T_0_ary_code – for a 0-ary node (AKA leaf node), with associated code
For instance, this simple Hello World script with 2 method calls
main(args: Array[String]) = print,"Hello" print,"World"
is translated into something that does effectively this ISL code:
def main(caller: N_call, args: Array[String]) = caller.calls(T_script("script", T_n_ary(";", T_0_ary_code("{}", (here:CallGraphNodeTrait[TemplateNode]) =>print("Hello ")), T_0_ary_code("{}", (here:CallGraphNodeTrait[TemplateNode]) =>println("world!")) ), "main", new FormalInputParameter("args")), args)
It appeared that some extra functions could bring this ISL to a higher level, so that the HelloWorld code would be:
def _main(_args: FormalInputParameter[Array[String]]) = _script(this, 'main, _args~'args) { _seq({print("Hello ")}, {println("world!") }) }
This is possible through definitions in the singleton object subscript.DSL
:
type _scriptType = N_call=>Unit def _script (owner: AnyRef, name: Symbol, p: FormalParameter_withName[_]*) (_t: TemplateChildNode): _scriptType = { (_c: N_call) => _c.calls(T_script(owner, "script", name, _t), p:_*) } def _codeFragmentKind1[N<:N_atomic_action[N]](opSymbol: String, cf: (N=>Unit)): T_0_ary_code[N] = T_0_ary_code(opSymbol, () => cf) implicit def _normal(cf: => (N_code_normal =>Unit)) = _codeFragmentKind1("{}",cf) def _op(opSymbol: String)(children: TemplateChildNode*) = T_n_ary(opSymbol, children:_*) def _seq = _op(";")_
Creating these definitions in the DSL object was a strange process. First we wanted a higher level interface, to avoid repetitions in the ISL code. This went very smoothly thanks to the support for closures in Scala. The resulting DSL comes surprisingly close to the genuine SubScript syntax.