Mercurial > repos > tabletprog
view compiler.js @ 84:9811040704ac
Add support for llMessage:withVars:andCode and llProperty:withType for specifying low level code without having to stick C inside the compiler. Redo array built-in type to use this feature.
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 21 Jul 2012 22:30:21 -0700 |
parents | 8a9b96888b7d |
children | 926b65fe92b4 |
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; } var toplevel = new topsymbols([]); function topsymbols(moduledirs) { this.names = null; this.used = {}; this.nextmodulenum = 0; var self = this; if (typeof window === "object") { get('/src/', function(data) { self.names = {}; var fakeEl = newEl("div", { innerHTML: data.response }); each(qall('a', fakeEl), function(idx, a) { var tpidx = a.textContent.indexOf('.tp'); if (tpidx > -1) { self.names[a.textContent.substr(0, tpidx)] = true; } }); }); } 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') { return 'module_' + name; } if (!this.names[name].modulevar) { this.names[name].modulevar = 'module_' + this.nextmodulenum++ } return this.names[name].modulevar; } 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) { 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) { switch(typeof val) { case 'boolean': if(val) { return mainModule.strue; } else { return mainModule.sfalse; } case 'number': return mainModule.snumber(val); } throw new Error("can't make val into object"); } op.prototype.populateSymbols = function(symbols, isReceiver) { this.left.populateSymbols(symbols); 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) { 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]); } 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); } if (this.receiver) { this.receiver.populateSymbols(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].populateSymbolsObject(symbols); } this.symbols = symbols; } lambda.prototype.populateSymbols = function(symbols, isobject) { 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) { symbols.defineVar(this.symbol.name, this.expression); } this.symbol.populateSymbols(symbols); this.expression.populateSymbols(symbols); this.symbols = symbols; }; assignment.prototype.populateSymbolsObject = 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)); } 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')];