-
Notifications
You must be signed in to change notification settings - Fork 1
DSL Builder
The Runiq package offers a simple way to define your own language keywords -- essentially your own mini-language or DSL.
The Runiq libraries are actually defined as a fairly straightforward dictionary of callback-passing-style functions. You may recall that we pass a library object to the Runiq interpreter:
var interpreter = new RuniqInterpreter(libraryObject);
That library object looks something like this:
{
functions: {
'+': function(a, b, cb) {
return cb(null, a + b);
},
'-': function(a, b, cb) {
return cb(null, a - b);
}
}
}
More or less, that's the implementation of +
and -
in Runiq.
When you write code such as this:
(+ 1 (- 2 3))
Runiq is actually calling those functions.
Runiq provides a DSL builder to make creating your own runtime library easy.
First, we instantiate a new DSL:
var DSL = require('runiq/dsl');
var dsl = new DSL();
Then, we add functions to it:
dsl.defineFunction('foo', {
signature: [['number'],'number'],
implementation: function(n, cb) {
return cb(null, n * 3);
}
});
Above, we define a function foo
that multiplies a number by 3
.
Notice that the function specification has two parts:
- a required
signature
, describing the function signature for type checking - a required
implementation
, the function that actually does the work
At runtime, your implementation
will be passed the arguments, and a continuation callback that you must call. The continuation callback takes two arguments: an error object (or null
if no error), and the result of the function.
You can define as many functions as you want. The DSL builder will warn you if you've accidentally clobbered a function already in the library.
Once you've finished adding functions, you can export the library object and hand it over to your interpreter instance:
var library = dsl.exportLibrary();
var interpreter = new RuniqInterpreter(library);
Say you want to expand on a preexisting library such as the core library that ships with the Runiq package. Simply create a new DSL
object, and pass the existing library to the constructor:
var existingLibrary = require('./runiq/lib')(); // Note the function call
var dsl = new DSL(existingLibrary);
dsl.defineFunction('foo', ...);
The only rule you must follow when defining Runiq library functions is this:
Your functions should only accept JSON-serializable parameters, and only return JSON-serializable values.
That is, no function should return any object that would prevent the program state from being serialized at that point.
For side effects, and return values that correctly represent the result of the side effects, so that a downstream function can continue from that point if needed.