Mercurial > repos > tabletprog
view compiler.js @ 251:2557ce4e671f
Fix a couple of compiler bugs. topenv was getting initialized in multiple places. This resulted in multiple copies of modules getting created which caused problems for macro expansion. Additionally, arguments were not being marked as declared during code generation so assigning to an argument that was not closed over generated invalid C code.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 11 Apr 2014 22:29:32 -0700 |
parents | 60eff5f81d9a |
children | d6a4c9e7716e |
line wrap: on
line source
var debugprint = function() {}; function indent(str) { return str.split('\n').join('\n\t'); } function modulefile(path, file) { this.path = path; this.file = file; } modulefile.prototype.populateSymbols = function (toplevel) { if (!this.ast) { this.ast = parseFile(this.path + '/' + this.file).macroexpand(toplevel.topenv); this.ast.populateSymbols(toplevel); } }; modulefile.prototype.popuplateSymbolsAsync = function(toplevel, whenDone) { if (!this.ast) { var self = this; get(this.path + '/' + this.file, function(data) { //TODO: macro expand in the async case self.ast = parser.parse(data.responseText); self.ast.populateSymbols(toplevel); whenDone(); }); } else { whenDone(); } }; function getfileshtml(path, data, names) { var fakeEl = newEl("div", { innerHTML: data.response }); each(qall('a', fakeEl), function(idx, a) { var tpidx = a.textContent.indexOf('.tp'); var modname = a.textContent.substr(0, tpidx); if (tpidx > -1) { names[modname] = new modulefile(path, modname + '.tp'); } }); } var toplevel = null;//new topsymbols([]); function topsymbols(moduledirs, top) { if (!top) { top = new topenv(moduledirs); } this.topenv = top; this.moduledirs = moduledirs; this.names = null; this.used = {}; this.nextmodulenum = 0; this.onready = null; var self = this; if (typeof window === "object") { get('/modules/', function(data) { var names = {} getfileshtml('/modules', data, names); get('/src/', function(data) { getfileshtml('/src', data, names); self.names = names; if (self.onready) { self.onready(); } }); }); } else { this.names = {}; for (var dirnum in moduledirs) { var results = os.system("ls", [moduledirs[dirnum]]).split('\n'); for (var i in results) { var tpidx = results[i].indexOf('.tp') if (tpidx > 0 && tpidx == results[i].length - 3) { this.names[results[i].substr(0, tpidx)] = new modulefile(moduledirs[dirnum], results[i]); } } } } } topsymbols.prototype.find = function(name) { debugprint('//topsymbols.find', name, name in this.names); if (!this.names) { throw new Error('data not ready'); } if (name in this.names) { this.used[name] = true; return { type: 'toplevel', def: this.names[name] }; } return null; }; topsymbols.prototype.getEnvType = function() { return 'void'; }; topsymbols.prototype.moduleVar = function(name) { if (!(name in this.names)) { throw new Error('symbol ' + name + ' not found at toplevel'); } if (name == 'true' || name == 'false' || name == 'list') { return 'module_' + name; } if (!this.names[name].modulevar) { this.names[name].modulevar = 'module_' + this.nextmodulenum++ } return this.names[name].modulevar; }; topsymbols.prototype.onReady = function(fun) { if (this.names) { fun(); return; } if (!this.onready) { this.onready = fun; } else { var oldready = this.onready; this.onready = function() { oldready(); fun(); }; } }; function osymbols(parent) { this.parent = parent; this.names = {}; this.llnames = {}; this.needsenv = false; this.typename = null; this.needsparent = false; } osymbols.prototype.find = function(name, nestedcall, allowll) { debugprint('//osymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { return { type: 'foreign', def: this.names[name] }; } var ret = { type: 'self', isll: false, def: this.names[name], selftype: this.typename }; } else if(allowll && name in this.llnames) { return { type: 'self', isll: true, selftype: this.typename }; } else if(this.parent) { var ret = this.parent.find(name, nestedcall, allowll); if (ret) { if(ret.type == 'self') { ret.type = 'parent'; ret.depth = 1; this.needsparent = true; } else if(ret.type == 'parent') { this.needsparent = true; ret.depth++; } else if(ret.type == 'closedover' || ret.type == 'upvar') { this.needsenv = true; } } } else { return null; } debugprint('\t//symbol type:', ret ? ret.type : 'null'); return ret; }; osymbols.prototype.defineMsg = function(name, def) { this.names[name] = def; } osymbols.prototype.defineLLProperty = function(name) { this.llnames[name] = true; } osymbols.prototype.parentObject = function() { if (!this.parent) { return 'null'; } return 'this'; } osymbols.prototype.allSymbols = function(curlist, cursyms) { if (curlist === undefined) { curlist = []; cursyms = {}; } var keys = Object.keys(this.names).sort(); for (var i in keys) { if (!(keys[i] in cursyms)) { curlist.push(keys[i]); cursyms[keys[i]] = true; } } if (this.parent) { return this.parent.allSymbols(curlist, cursyms); } return curlist; } osymbols.prototype.getEnvType = function() { return this.parent.getEnvType(); } osymbols.prototype.envVar = function() { return this.parent.envVar(); } function lsymbols(parent) { this.parent = parent; this.names = {}; this.closedover = {}; this.declared = {}; this.needsSelfVar = false; this.passthruenv = false; this.envtype = 'void'; this.needsParentEnv = false; } lsymbols.prototype.find = function(name, nestedcall, allowll) { debugprint('//lsymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { var ret = { type: 'foreign', def: this.names[name] }; } else { if (nestedcall && !(name in this.closedover)) { debugprint('//symbol', name, 'is now closed over'); this.closedover[name] = true; this.passthruenv = false; } if (name in this.closedover) { var ret = { type: 'closedover', def: this.names[name] }; } else { var ret = { type: 'local', def: this.names[name], isdeclared: (name in this.declared) }; } } } else if(this.parent) { var ret = this.parent.find(name, true, allowll); if (ret) { if (ret.type == 'closedover') { ret.type = 'upvar'; ret.depth = 0; } if (ret.type == 'upvar') { if (Object.keys(this.closedover).length) { ret.depth++; ret.startdepth = 1; this.needsParentEnv = true; } else { this.passthruenv = true; ret.startdepth = 0; /*if (ret.depth == 0) { ret.depth = 1; }*/ } } } } else { return null; } var type = ret ? ret.type : 'null'; debugprint('\t//symbol type:', type , type == 'upvar' ? 'depth: ' + ret.depth : ''); return ret; }; lsymbols.prototype.defineVar = function(name, def) { this.names[name] = def; }; lsymbols.prototype.declareVar = function(name) { this.declared[name] = true; } lsymbols.prototype.selfVar = function() { if (this.parent && this.parent instanceof lsymbols) { this.parent.needsSelf(); return 'self'; } else { return 'this'; } }; lsymbols.prototype.needsSelf = function() { if (this.parent && this.parent instanceof lsymbols) { this.parent.needsSelf(); } else { this.needsSelfVar = true; } }; lsymbols.prototype.allSymbols = osymbols.prototype.allSymbols; lsymbols.prototype.parentEnvType = function() { if (!this.parent) { return 'void'; } return this.parent.getEnvType(); }; lsymbols.prototype.getEnvType = function() { if (this.passthruenv) { return this.parent.getEnvType(); } else { return this.envtype; } } lsymbols.prototype.envVar = function() { if (Object.keys(this.closedover).length) { return 'myenv'; } else if(this.passthruenv) { return 'env'; } return null; } var mainModule; function toobj(val) { return (typeof val == "boolean") ? (val ? module_true : module_false) : val; } op.prototype.populateSymbols = function(symbols, isReceiver) { this.left.populateSymbols(symbols); if (this.op == '&&' || this.op == '||') { //&& and || are syntactic sugar for if and ifnot with //the second argument transformed into a lambda to //achieve short-circuit evalutation this.right = new lambda([], [this.right]); } this.right.populateSymbols(symbols); }; symbol.prototype.populateSymbols = function(symbols) { this.symbols = symbols; var ret = symbols.find(this.cleanName()); if (ret && (ret.type == 'self' || ret.type == 'parent')) { symbols.find('self'); } } intlit.prototype.populateSymbols = function(symbols) { } floatlit.prototype.populateSymbols = function(symbols) { } strlit.prototype.populateSymbols = function(symbols) { } listlit.prototype.populateSymbols = function(symbols) { for (var i = 0; i < this.val.length; i++) { this.val[i].populateSymbols(symbols); } } arraylit.prototype.populateSymbols = function(symbols) { for (var i = 0; i < this.val.length; i++) { this.val[i].populateSymbols(symbols); } } funcall.prototype.populateSymbols = function(symbols) { var isll = false; if (this.name == 'foreign:') { if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object) || (this.args[0] instanceof symbol)) { return; } else { throw new Error("Unexpected AST type for foreign:"); } } else if (this.name == 'llProperty:withType:') { if (this.args[0] instanceof symbol) { if ((this.args[1] instanceof symbol) || (this.args[1] instanceof funcall)) { symbols.defineLLProperty(this.args[0].name); return; } else { throw new Error("Second argument to llProperty:withType: must be a symbol or funcall"); } } else { throw new Error("First argument to llProperty:withType: must be a symbol"); } } else if (this.name == 'llMessage:withVars:andCode:') { if (this.args[0] instanceof symbol) { if (this.args[1] instanceof lambda) { if (this.args[2] instanceof lambda) { symbols.defineMsg(this.args[0].name, this.args[2]); isll = true; } else { throw new Error("Third argument to llMessage:withVars:andCode: must be a lambda"); } } else { throw new Error("Second argument to llMessage:withVars:andCode: must be a lambda"); } } else { throw new Error("First argument to llMessage:withVars:andCode: must be a symbol"); } } this.symbols = symbols; var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; var funinfo = symbols.find(name); if (funinfo && (funinfo.type == 'self' || funinfo.type == 'parent')) { symbols.find('self'); } for (var i in this.args) { this.args[i].populateSymbols(symbols, undefined, isll); } if (this.receiver) { this.receiver.populateSymbols(symbols, undefined, isll); } } funcall.prototype.defineSymbolsObject = function(symbols) { } funcall.prototype.populateSymbolsObject = function(symbols) { this.populateSymbols(symbols); } object.prototype.populateSymbols = function(symbols) { var symbols = new osymbols(symbols); for (var i in this.messages) { this.messages[i].defineSymbolsObject(symbols); } for (var i in this.messages) { this.messages[i].populateSymbolsObject(symbols); } this.symbols = symbols; } var lambdanum = 0; lambda.prototype.populateSymbols = function(symbols, isobject, isll) { if (!isll) { this.name = 'lambda_' + lambdanum++; } var args = this.args ? this.args.slice(0, this.args.length) : []; var exprs = this.expressions; var symbols = new lsymbols(symbols); for (var i in args) { symbols.defineVar(args[i].cleanName(), null); args[i].populateSymbols(symbols); } if (isobject && (!args.length || args[0].cleanName() != 'self')) { symbols.defineVar('self', null); } for (var i in exprs) { exprs[i].populateSymbols(symbols); } this.symbols = symbols; }; assignment.prototype.populateSymbols = function(symbols) { debugprint('//assignment', this.symbol.name, 'populateSymbols'); var existing = symbols.find(this.symbol.name); if (!existing || existing.type == 'toplevel') { symbols.defineVar(this.symbol.name, this.expression); } this.symbol.populateSymbols(symbols); this.expression.populateSymbols(symbols); this.symbols = symbols; }; assignment.prototype.defineSymbolsObject = function(symbols) { debugprint('//messagedef', this.symbol.name, 'populateSymbols'); if (this.expression instanceof lambda || (this.expression instanceof funcall & this.expression.name == 'foreign:')) { symbols.defineMsg(this.symbol.name, this.expression); } else { symbols.defineMsg(this.symbol.name, new getter(null)); symbols.defineMsg(this.symbol.name + '!', new setter(null)); } }; assignment.prototype.populateSymbolsObject = function(symbols) { this.symbol.populateSymbols(symbols); this.expression.populateSymbols(symbols, true); this.symbols = symbols; }; function setter(fun) { this.fun = fun; } setter.prototype.args = [new symbol('self'), new symbol('newval')]; function getter(fun) { this.fun = fun; } getter.prototype.args = [new symbol('self')];