Napkin-Syntax DSL + nodejs text template code generation to any language

I’ve been looking for a way to keep domain types specified in a very simple, “napkin-syntax” like way:

Project

  Name
  Address
  SomeValue :num

And with string descriptions for use in application(s):

Project
  Id :key
  Name
    "Descriptive name"
  Address
    "Full address"
    "I.e Street 123"
  City

The reasons for this are

1) to keep the domain type specifications above everything else, dependent on nothing

2) to be able to easily communicate the specifications with the domain knowers (my clients) in a human understandable – and not cluttered – language

3) to be able to use the code to generate necessary types and code in whatever syntax necessary; C#, Typescript and Javascript for example

4) to make it possible for easy iterations of “top-level changes” – for me or the business persons themselves to see the results of adding and changing details immediately. Without involving the ordinary developer process.

A short (2 mins) demonstration:

Github repository https://github.com/joeriks/napkin-sample

Example of editing DSL code and get the generated code automatically immediately on save. The templates (for cs and html in the example) are written with standard text template syntax.

steps

DSL + a code generation template

The solution I’m trying here is to use a parser generator tool to define a domain specific language (DSL), and then pass the code files to language and application specific templates for the code generation.

steps

First I looked at the MPS tool (Meta Programming System) from Jetbrains. It seems to be the most sophisticated way to do this, and is completely free. However I hoped to be able to keep this in node to be able to combine it with other nice tools I use. So I found and choose to go on with Peg.js http://pegjs.majda.cz/ Parser Generator for javascript, made by David Majda.

With Peg.js you define a set of syntax rules (grammar) for your DSL and then Peg generates a parser for you.

The PEGjs DSL grammar

I was a bit tricky to understand how the grammar syntax works, but I found the online playground(s) available to be very helpful. With that, some examples in the wild and the js console I managed to create a grammar that works fairly well for a first attempt in a few hours. From there to a more useful version (as in the video) it took me a little while longer, but the syntax was not very hard to learn.

Use the generated parser

After I created my grammar, I let PEG.js generate my parser (as a javascript file) which I need to use it on my domain type syntax files.

To be able to run the parser easily I used the quickpeg.js npm package which is a tiny wrapper around peg. With that I created and used this node script to generate a json object for my source:

var qp = require("quickpeg");
qp("dsl.grammar",{cache:false},function(err,parser){
  parser.parseFile("project.txt",function(err,data){    console.log(data);
  });
});

(dsl.grammar is the file with my dsl grammar). When I run it with my project.txt source file (as above) the created object is:

{ text: 'Project',
  type: '',
  children:
   [ { text: 'Id', type: 'key' },
     { text: 'Name', type: '' },
     { text: 'Address', type: '' },
     { text: 'SomeValue', type: 'num' } ] }

Run the created object with a template

With this object the next step is to combine it with a text template to generate the actual code.

I found that the “swig” template engine is a popular one on node and decided to give it a go. I actually never heard of it before, but the syntax is very mustache like and it was easy to create what I needed:

public class {{ text }} { {% for child in children %}
 {% if child.type=="key" || child.type=="num" %}int{% else %}string{% endif %} {{ child.text }} {get;set;}{% endfor %}
}

The complete node script

Here is all the node code I needed to be able to parse a piece of DSL code and combine it with a template to generate a C# class file:

var fs = require("fs");
var qp = require("quickpeg");
var swig = require("swig");
var args = process.argv.splice(2);
var tpl = swig.compileFile(args[0]);
qp("dsl.grammar",{cache:false},function(err,parser){
  parser.parseFile(args[1],function(err,data){
    fs.writeFile(args[2],tpl(data),function(err,data) {
        console.log("Saved to " + args[2]);
    });
  }); 
});

I run it with command line arguments

node runqp cstemplate.swig project.txt project.cs

and the generated code (project.cs) looks like this

public class Project { 
 public int Id {get;set;}
 public string Name {get;set;}
 public string Address {get;set;}
 public int SomeValue {get;set;}
}

I found this useful and will continue to work with my DSL to generate both C#, javascript / typescript and possibly even SQL schema from it.

Advertisements

2 thoughts on “Napkin-Syntax DSL + nodejs text template code generation to any language

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s