Skip to content

Instantly share code, notes, and snippets.

@tj
Last active December 11, 2015 08:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tj/4574520 to your computer and use it in GitHub Desktop.
Save tj/4574520 to your computer and use it in GitHub Desktop.
quick hack of an example of how to add some type enforcement via pre-processing
def greet(first:string, last:string) {
ret "Hello " + first + " " + last
}
function scan(str) {
return str.match(/(\bdef\b|\bret\b|[+:{}(),]|"(.*?)"|'(.*?)'|[_\w]+)/g);
}
function parse(str) {
var toks = scan(str);
return stmts();
function stmts() {
var stmts = [];
while (toks.length) stmts.push(stmt());
return { type: 'stmts', stmts: stmts };
}
function stmt() {
var tok = toks.shift();
switch (tok) {
case 'def': return def();
case 'ret': return ret();
default:
throw new Error('unexpected ' + tok);
}
}
function ret() {
return {
type: 'return',
expr: expr()
}
}
function expr() {
var node = { type: 'expr', val: toks.shift() };
if ('+' == toks[0]) {
node = {
type: 'op',
op: toks.shift(),
left: node,
right: expr()
};
}
return node;
}
function def() {
var id = toks.shift();
toks.shift(); // (
var p = params();
toks.shift(); // )
var b = block();
return {
type: 'function',
id: id,
params: p,
body: b
}
}
function accept(tok) {
if (tok == toks[0]) {
return toks.shift();
}
}
function params() {
var params = [];
if (')' == toks[0]) return params;
do {
var id = toks.shift();
toks.shift(); // :
var type = toks.shift();
params.push({ id: id, type: type });
} while (accept(','));
return params;
}
function block() {
var stmts = [];
toks.shift(); // {
while ('}' != toks[0]) stmts.push(stmt());
toks.shift(); // }
return { type: 'stmts', stmts: stmts };
}
}
function compile(ast) {
return visit(ast);
function visit(node) {
switch (node.type) {
case 'stmts': return stmts(node);
case 'function': return fn(node);
case 'return': return ret(node);
case 'expr': return expr(node);
case 'op': return op(node);
default: throw new Error('unhandled ' + node.type);
}
}
function ret(node) {
return ' return ' + visit(node.expr);
}
function expr(node) {
return node.val;
}
function op(node) {
return visit(node.left) + ' ' + node.op + ' ' + visit(node.right);
}
function fn(node) {
var asserts = [];
var params = node.params.map(function(p){
asserts.push(' assertType(' + p.id + ', "' + p.id + '", "' + p.type + '")');
return p.id;
}).join(', ');
return 'function '
+ node.id + '(' + params + ') {\n'
+ asserts.join('\n') + '\n'
+ visit(node.body)
+ '\n}';
}
function stmts(node) {
return node.stmts.map(visit).join('\n');
}
}
var fs = require('fs');
var vm = require('vm');
var str = fs.readFileSync('example.js', 'utf8');
var ast = parse(str);
// console.log(require('util').inspect(ast, false, 15, true));
var js = compile(ast);
console.log(js);
// js += '\ngreet("tobi", "ferret")';
js += '\ngreet("tobi", 5)';
var ret = vm.runInNewContext(js, {
assertType: function(val, name, type){
var actual = typeof val;
if (actual != type) {
throw new TypeError(name + ' must be of type '+ type + ', a ' + actual + '(' + val + ') was given');
}
}
});
console.log(ret);
function greet(first, last) {
assertType(first, "first", "string")
assertType(last, "last", "string")
return "Hello " + first + " " + last
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment