-
-
Save rightfold/3169ead17eb7d3620249 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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