Mercurial > repos > tabletprog
view jsbackend.js @ 349:60292f131de9
Make map actually work right on hashmaps
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 10 Apr 2015 01:19:10 -0700 |
parents | a83989115028 |
children |
line wrap: on
line source
var mainModule; var modules = {}; function toobj(val) { return (typeof val == "boolean") ? (val ? module_true : module_false) : val; } function importSym(obj, src, key) { if(!(key in src)) { throw new Error(key +' not found in source object for import'); } if(key in obj) { throw new Error(key +' already exists in target object for import') } obj[key] = src[key]; } function doImport(obj, src, symlist) { if (symlist === undefined) { each(src, function(key,val) { if (key != 'parent') { importSym(obj, src, key); } }); } else { for (var i = 0; i < symlist.length; ++i) { importSym(obj, src, symlist[i]); } } return obj; } op.prototype.toJS = function(isReceiver) { if (this.op == '&&') { var ret = 'toobj(' + this.left.toJS() + ').sif(' + this.right.toJS() + ')'; } else if(this.op == '||') { var ret = 'toobj(' + this.left.toJS() + ').ifnot(' + this.right.toJS() + ')'; } else { var opmap = {'=': '==', '.': '+'}; var ret = '(' + this.left.toJS() +' '+ (this.op in opmap ? opmap[this.op] : this.op) +' '+ this.right.toJS() + ')'; } return ret; }; function escapeJSName(name) { name = name.replace("_", "UN_").replace(":", "CN_").replace("!", "EX_").replace('?', 'QS_').replace('@', 'AT_'); var reserved = {'true': true, 'false': true, 'this': true, 'if': true, 'else': true, 'NaN': true}; if (name in reserved) { name = 's' + name; } return name; } symbol.prototype.toJS = function() { var name = this.cleanName(); if (name == 'self') { return this.symbols.selfVar(); } var info = this.symbols.find(name); if (!info) { throw new Error('symbol ' + name + ' not found'); } var pre = ''; if (info.type == 'self') { pre = this.symbols.selfVar() + '.'; } else if(info.type == 'parent') { pre = 'this'; for (var i = 0; i < funinfo.depth; ++i) { pre += '.parent'; } } else if (info.type == 'toplevel') { return toplevel.moduleVar(name); } return pre + escapeJSName(name); } intlit.prototype.toJS = function() { return this.val.toString(); } floatlit.prototype.toJS = function() { return this.val.toString(); } strlit.prototype.toJS = function() { return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"'; } listlit.prototype.toJS = function() { var ret = '['; each(this.val, function(idx, el) { ret += (idx ? ', ' : '') + el.toJS(); }); return ret + ']'; } funcall.prototype.toJS = function() { var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; if (name == 'foreign') { if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) { return null; } else if(this.args[0] instanceof symbol) { return this.args[0].name; } else { throw new Error("Unexpected AST type for foreign:"); } } var args = this.args.slice(0, this.args.length); if (this.receiver) { args.splice(0, 0, this.receiver); } var funinfo = this.symbols.find(name); if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') { var receiver = args[0]; args.splice(0, 1); for (var i in args) { args[i] = args[i].toJS(); } var rJS = (funinfo ? '' : 'toobj(') + receiver.toJS(true) + (funinfo ? '' : ')') ; if ((name[name.length-1] == '!' && args.length == 1) || (funinfo && funinfo.def instanceof setter)) { return '(' + rJS + '.' + escapeJSName(name.substr(0, name.length-1)) + ' = ' + args[0] + ', ' + rJS + ')'; } else { var callee = rJS + '.' + escapeJSName(name); if (args.length == 0) { return callee; } else { return callee + '(' + args.join(', ') + ')'; } } } var ret = ''; switch(funinfo.type) { case 'self': if (args.length < funinfo.def.args.length || funinfo.def.args.length == 0 || funinfo.def.args[0].name != 'self') { var receiver = new symbol('self', this.symbols); } else { var receiver = args[0]; args.splice(0, 1); if (args.length == 0) { var rJS = receiver.toJS(true); var callee = rJS + '.' + escapeJSName(name); return '(' + callee + ' instanceof Function ? ' + callee + '() : ' + callee + ')'; } } ret = receiver.toJS(true) + '.'; break; case 'parent': var receiver = new symbol('self', this.symbols); ret = receiver.toJS(true); for (var i = 0; i < funinfo.depth; ++i) { ret += '.parent'; } break; } for (var i in args) { args[i] = args[i].toJS(); } return ret + escapeJSName(name) + '(' + args.join(', ') + ')'; } object.prototype.toJS = function() { var messages = this.messages; var compiled = []; var imports = [] for (var i in messages) { if (messages[i] instanceof funcall) { if (messages[i].name == 'import:' && messages[i].args.length == 1) { imports.push({symbols: false, src: messages[i].args[0]}); } else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) { var importsyms = []; each(messages[i].args[0].val, function(i, el) { if (!(el instanceof symbol)) { throw new Error('Names in import:from statement must be symbols'); } importsyms.push(new strlit(el.name)); }); imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]}); } else { throw new Error('Only import and import:from calls allowed in object context'); } } else { var js = messages[i].toJSObject(); if (js) { compiled.push(indent(js)); } } } var pre = ''; var post = ''; for (var i = imports.length-1; i >= 0; i--) { pre += 'doImport('; post += ', ' + imports[i].src.toJS(); if (imports[i].symbols) { post += ', ' + imports[i].symbols.toJS(); } post += ')'; } return pre+'{\n\tparent: ' + this.symbols.parentObject() + ',\n\t' + compiled.join(',\n\t') + '\n}'+post; } object.prototype.toJSModule = function() { this.populateSymbols(toplevel); return '(function () {\n\tvar module = ' + indent(this.toJS()) + ';\n\treturn module;\n})();' } lambda.prototype.toJS = function() { var args = this.args ? this.args.slice(0, this.args.length) : []; if (args.length && args[0].cleanName() == 'self') { args.splice(0, 1); } var exprs = this.expressions; for (var i in args) { args[i] = args[i].toJS(); } var compiled = [] for (var i in exprs) { var js = exprs[i].toJS(); if (js) { compiled.push(indent(js)); } } exprs = compiled; if (exprs.length) { exprs[exprs.length-1] = 'return ' + exprs[exprs.length-1] + ';'; } return 'function (' + args.join(', ') + ') {\n\t' + (this.symbols.needsSelfVar ? 'var self = this;\n\t' : '') + exprs.join(';\n\t') + '\n}' }; lambda.prototype.nonSelfArgs = function() { var args = this.args ? this.args.slice(0, this.args.length) : []; if (args.length && args[0].cleanName() == 'self') { args.splice(0, 1); } return args; }; lambda.prototype.toJSModule = function() { this.populateSymbols(toplevel); return this.toJS() + '();'; } modulefile.prototype.toJSModule = function(){ return this.ast.toJSModule(); }; function processUsedToplevelJS(toplevel) { var alwaysused = ['true', 'false']; var ret = ''; var modulenum = 0; var visited = {}; for (var i = 0; i < alwaysused.length; i++) { toplevel.used[alwaysused[i]] = true; } var newused = Object.keys(toplevel.used); var allused = newused; while (newused.length) { for (var i = 0; i < newused.length; i++) { console.log(i, newused[i]); toplevel.names[newused[i]].populateSymbols(toplevel); visited[newused[i]] = true; } newused = []; for (var symbol in toplevel.used) { if (!(symbol in visited)) { newused.push(symbol); allused.push(symbol); } } } for (var i = allused.length-1; i >= 0; i--) { var symbol = allused[i]; ret += 'var ' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toJSModule() + '\n'; } return ret; } function asyncProcessTopLevelJS(toplevel, whenDone) { var alwaysused = ['true', 'false']; var ret = ''; var modulenum = 0; var visited = {}; for (var i = 0; i < alwaysused.length; i++) { toplevel.used[alwaysused[i]] = true; } var newused = Object.keys(toplevel.used); var allused = newused; var i = -1; var handler = function() { i++; while(newused.length) { if (i < newused.length) { visited[newused[i]] = true; toplevel.names[newused[i]].popuplateSymbolsAsync(toplevel, handler); return; } else { newused = []; for (var symbol in toplevel.used) { if (!(symbol in visited)) { newused.push(symbol); allused.push(symbol); } } i = 0; } } whenDone(); }; handler(); } function makeJSProg(mainmodule) { return processUsedToplevelJS(toplevel) + 'main_module = ' + mainmodule.toJSModule() + '\n' + 'Number.prototype.__defineGetter__("string", function() { return "" + this; });\n' + 'String.prototype.__defineGetter__("string", function() { return this; });\n' + 'String.prototype.__defineGetter__("print", function() { write(this); });\n' + 'Object.defineProperty(Array.prototype, "foreach", {value: function(action) { var ret = module_false; for (var i = 0; i < this.length; i++) { ret = action(i, this[i]) }; return ret; }});\n' + 'Function.prototype.whileCN_do = function(action) { var ret = module_false; while(toobj(this()) == module_true) { ret = action(); } return ret; };\n' + 'module_true.valueOf = function() { return true; }\n' + 'module_false.valueOf = function() { return false; }\n' + 'function toobj(val) {\n' + ' return (typeof val == "boolean") ? (val ? module_true : module_false) : val;\n' + '}\n' + 'var m = main_module.main;\n' + 'if (m instanceof Function) {\n' + ' m(arguments);\n' + '}\n'; } assignment.prototype.toJS = function() { var existing = this.symbols.find(this.symbol.name); var prefix = ''; /*if (!existing) { prefix = 'var '; } else { switch (existing.type) { case 'self': var self = new symbol('self', this.symbols); prefix = self.toJS() + '.'; break; case 'parent': var self = new symbol('self', this.symbols); prefix = self.toJS() + '.'; for (var i = 0; i < existing.depth; ++i) { prefix += 'parent.'; } break; } }*/ var val = this.expression.toJS(); if (val === null) { return null; } if ((existing.type == 'local' || existing.type == 'closedover') && !existing.isdeclared) { prefix = 'var '; this.symbols.declareVar(this.symbol.name); } return prefix + this.symbol.toJS() + ' = ' + val; }; function removeInitialFunction(str) { var f = 'function'; str = str.trim(); if (str.substr(0, f.length) == f) { return str.substr(f.length); } return str; } assignment.prototype.toJSObject = function() { var val = this.expression.toJS(); if (val === null) { return null; } if (this.expression instanceof lambda) { var args = this.expression.nonSelfArgs(); if (args.length == 0) { return 'get ' + escapeJSName(this.symbol.name) + removeInitialFunction(val); } else if(args.length == 1 && this.symbol.name[this.symbol.name.length-1] == '!') { return 'set ' + escapeJSName(this.symbol.name.substr(0, this.symbol.name.length-1)) + removeInitialFunction(val); } } return escapeJSName(this.symbol.name) + ': ' + val; };