Skip to content

Instantly share code, notes, and snippets.

@rightfold
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rightfold/3169ead17eb7d3620249 to your computer and use it in GitHub Desktop.
Save rightfold/3169ead17eb7d3620249 to your computer and use it in GitHub Desktop.
var mori = require('mori');
function Symbol() { }
function ModuleDefSymbol() {};
ModuleDefSymbol.prototype = Object.create(Symbol.prototype);
function ModuleLazySymbol() {};
ModuleLazySymbol.prototype = Object.create(Symbol.prototype);
function LocalValSymbol() {};
LocalValSymbol.prototype = Object.create(Symbol.prototype);
function GlobalValSymbol() {};
GlobalValSymbol.prototype = Object.create(Symbol.prototype);
function notImplemented(condition) {
if (condition) {
throw Error('not implemented');
}
}
var lastID = 0;
function newID(prefix) {
return prefix + '$lasagnascript$id_' + ++lastID;
}
function esIdentifier(name) {
return { type: 'Identifier', name: name };
}
function esLiteral(value) {
return { type: 'Literal', value: value };
}
function esGlobal() {
return {
type: 'CallExpression',
callee: esIdentifier('$lasagnascript$global'),
arguments: [],
};
}
function esModule() {
return esIdentifier('$lasagnascript$module');
}
function warning(message) {
console.log('warning: %s', message);
}
function error(message) {
console.log('error: %s', message);
throw Error();
}
function compileDecl(decl, symbols) {
switch (decl.type) {
case 'DefDecl':
notImplemented(decl.parameterLists.length !== 1);
symbols = mori.assoc(symbols, decl.name, new ModuleDefSymbol());
decl.parameterLists[0].forEach(function(parameter) {
if (parameter === decl.name) {
warning("parameter '" + parameter + "' shadows its function");
}
symbols = mori.assoc(symbols, parameter, new LocalValSymbol());
});
var esBody = compileExpr(decl.body, symbols);
var esDecl = {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: esModule(),
property: esIdentifier(decl.name),
computed: false
},
right: {
type: 'FunctionExpression',
id: null,
params: decl.parameterLists[0].map(esIdentifier),
defaults: [],
rest: null,
body: {
type: 'BlockStatement',
body: [{ type: 'ReturnStatement', argument: esBody }]
},
generator: false,
expression: false
}
}
};
return {
esDecl: esDecl,
symbols: symbols
};
case 'LazyDecl':
symbols = mori.assoc(symbols, decl.name, new ModuleLazySymbol());
var esBody = compileExpr(decl.body, symbols);
var id = newID(decl.name);
var esDecl = {
type: 'ExpressionStatement',
expression: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
object: {
type: 'MemberExpression',
object: esGlobal(),
property: esIdentifier('Object'),
computed: false
},
property: esIdentifier('createProperty'),
computed: false
},
arguments: [
esModule(),
esLiteral(decl.name),
{
type: 'ObjectExpression',
properties: [
{
type: 'Property',
key: esIdentifier('get'),
value: {
type: 'FunctionExpression',
id: null,
params: [],
defaults: [],
rest: null,
body: {
type: 'BlockStatement',
body: [
{
type: 'ReturnStatement',
argument: {
type: 'ConditionalExpression',
test: {
type: 'BinaryExpression',
operator: 'in',
left: esLiteral(id),
right: { type: 'ThisExpression' }
},
consequent: {
type: 'MemberExpression',
object: { type: 'ThisExpression' },
property: esIdentifier(id),
computed: false
},
alternate: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: { type: 'ThisExpression' },
property: esIdentifier(id),
computed: false
},
right: esBody
}
}
}
]
},
generator: false,
expression: false
},
kind: 'init'
}
]
}
]
}
};
return {
esDecl: esDecl,
symbols: symbols
};
default:
throw Error('invalid decl type ' + decl.type);
}
}
function compileExpr(expr, symbols) {
switch (expr.type) {
case 'NameExpr':
if (!mori.has_key(symbols, expr.name)) {
error("name '" + expr.name + "' not in scope");
}
var symbol = mori.get(symbols, expr.name);
if (symbol instanceof ModuleDefSymbol) {
return {
type: 'MemberExpression',
object: esModule(),
property: esIdentifier(expr.name),
computed: false
};
} else if (symbol instanceof LocalValSymbol) {
return esIdentifier(expr.name);
} else if (symbol instanceof GlobalValSymbol) {
return {
type: 'MemberExpression',
object: esGlobal(),
property: esIdentifier(expr.name),
computed: false
};
} else {
throw Error('ICE');
}
case 'GlobalExpr':
return esGlobal();
case 'CallExpr':
var esCallee = compileExpr(expr.callee, symbols);
var esArguments = expr.arguments.map(function(argument) {
return compileExpr(argument, symbols);
});
return {
type: 'CallExpression',
callee: esCallee,
arguments: esArguments
};
default:
throw Error('invalid expr type ' + expr.type);
}
}
module.exports = function(decls) {
var esDecls = [];
var symbols = mori.hash_map(
'$lasagnascript$asterisk', new GlobalValSymbol()
);
decls.forEach(function(decl) {
var result = compileDecl(decl, symbols);
esDecls.push(result.esDecl);
symbols = result.symbols;
});
return {
type: 'Program',
body: esDecls
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment