Skip to content

DSL Builder

Matthew Trost edited this page Dec 19, 2015 · 2 revisions

The Runiq package offers a simple way to define your own language keywords -- essentially your own mini-language or DSL.

Introduction

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.

Creating a DSL

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

Extending an existing 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 Golden Rule of Runiq DSL Building:

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.

Clone this wiki locally