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