pavone@54: var debugprint = function() {}; pavone@54: pavone@8: function indent(str) pavone@8: { pavone@8: return str.split('\n').join('\n\t'); pavone@8: } pavone@8: pavone@329: var currentModule = null; pavone@48: function modulefile(path, file) pavone@48: { pavone@48: this.path = path; pavone@48: this.file = file; pavone@329: this.dependencies = {}; pavone@48: } pavone@48: pavone@95: modulefile.prototype.populateSymbols = function (toplevel) { pavone@95: if (!this.ast) { pavone@329: currentModule = this; pavone@251: this.ast = parseFile(this.path + '/' + this.file).macroexpand(toplevel.topenv); pavone@95: this.ast.populateSymbols(toplevel); pavone@329: currentModule = null; pavone@95: } pavone@95: }; pavone@95: pavone@104: modulefile.prototype.popuplateSymbolsAsync = function(toplevel, whenDone) { pavone@104: if (!this.ast) { pavone@104: var self = this; pavone@104: get(this.path + '/' + this.file, function(data) { pavone@207: //TODO: macro expand in the async case pavone@329: currentModule = this; pavone@104: self.ast = parser.parse(data.responseText); pavone@104: self.ast.populateSymbols(toplevel); pavone@329: currentModule = null; pavone@104: whenDone(); pavone@104: }); pavone@104: } else { pavone@104: whenDone(); pavone@104: } pavone@104: }; pavone@104: pavone@104: function getfileshtml(path, data, names) pavone@104: { pavone@104: var fakeEl = newEl("div", { pavone@104: innerHTML: data.response pavone@104: }); pavone@104: each(qall('a', fakeEl), function(idx, a) { pavone@104: var tpidx = a.textContent.indexOf('.tp'); pavone@104: var modname = a.textContent.substr(0, tpidx); pavone@104: if (tpidx > -1) { pavone@104: names[modname] = new modulefile(path, modname + '.tp'); pavone@104: } pavone@104: }); pavone@104: } pavone@104: pavone@251: var toplevel = null;//new topsymbols([]); pavone@251: function topsymbols(moduledirs, top) pavone@30: { pavone@251: if (!top) { pavone@251: top = new topenv(moduledirs); pavone@251: } pavone@251: this.topenv = top; pavone@251: this.moduledirs = moduledirs; pavone@30: this.names = null; pavone@48: this.used = {}; pavone@68: this.nextmodulenum = 0; pavone@104: this.onready = null; pavone@31: var self = this; pavone@36: if (typeof window === "object") { pavone@104: get('/modules/', function(data) { pavone@104: var names = {} pavone@104: getfileshtml('/modules', data, names); pavone@104: get('/src/', function(data) { pavone@104: getfileshtml('/src', data, names); pavone@104: self.names = names; pavone@104: if (self.onready) { pavone@104: self.onready(); pavone@36: } pavone@36: }); pavone@31: }); pavone@36: } else { pavone@36: this.names = {}; pavone@48: for (var dirnum in moduledirs) { pavone@48: var results = os.system("ls", [moduledirs[dirnum]]).split('\n'); pavone@48: for (var i in results) { pavone@48: var tpidx = results[i].indexOf('.tp') pavone@68: if (tpidx > 0 && tpidx == results[i].length - 3) { pavone@314: var moduleName = results[i].substr(0, tpidx); pavone@314: if (!(moduleName in this.names)) { pavone@314: this.names[moduleName] = new modulefile(moduledirs[dirnum], results[i]); pavone@314: } pavone@48: } pavone@48: } pavone@48: } pavone@36: } pavone@30: } pavone@30: topsymbols.prototype.find = function(name) { pavone@54: debugprint('//topsymbols.find', name, name in this.names); pavone@30: if (!this.names) { pavone@31: throw new Error('data not ready'); pavone@30: } pavone@30: if (name in this.names) { pavone@329: if (currentModule) { pavone@329: currentModule.dependencies[name] = true; pavone@329: } pavone@48: this.used[name] = true; pavone@30: return { pavone@30: type: 'toplevel', pavone@48: def: this.names[name] pavone@30: }; pavone@30: } pavone@30: return null; pavone@104: }; pavone@34: topsymbols.prototype.getEnvType = function() { pavone@34: return 'void'; pavone@104: }; pavone@68: topsymbols.prototype.moduleVar = function(name) { pavone@68: if (!(name in this.names)) { pavone@68: throw new Error('symbol ' + name + ' not found at toplevel'); pavone@68: } pavone@170: if (name == 'true' || name == 'false' || name == 'list') { pavone@68: return 'module_' + name; pavone@68: } pavone@68: if (!this.names[name].modulevar) { pavone@68: this.names[name].modulevar = 'module_' + this.nextmodulenum++ pavone@68: } pavone@68: return this.names[name].modulevar; pavone@104: }; pavone@104: topsymbols.prototype.onReady = function(fun) { pavone@104: if (this.names) { pavone@104: fun(); pavone@104: return; pavone@104: } pavone@104: if (!this.onready) { pavone@104: this.onready = fun; pavone@104: } else { pavone@104: var oldready = this.onready; pavone@104: this.onready = function() { pavone@104: oldready(); pavone@104: fun(); pavone@104: }; pavone@104: } pavone@104: }; pavone@30: pavone@8: function osymbols(parent) pavone@8: { pavone@8: this.parent = parent; pavone@8: this.names = {}; pavone@84: this.llnames = {}; pavone@42: this.needsenv = false; pavone@57: this.typename = null; pavone@59: this.needsparent = false; pavone@8: } pavone@84: osymbols.prototype.find = function(name, nestedcall, allowll) { pavone@54: debugprint('//osymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); pavone@8: if (name in this.names) { pavone@8: if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { pavone@8: return { pavone@8: type: 'foreign', pavone@8: def: this.names[name] pavone@8: }; pavone@8: } pavone@54: var ret = { pavone@8: type: 'self', pavone@84: isll: false, pavone@8: def: this.names[name], pavone@57: selftype: this.typename pavone@8: }; pavone@84: } else if(allowll && name in this.llnames) { pavone@84: return { pavone@84: type: 'self', pavone@84: isll: true, pavone@84: selftype: this.typename pavone@84: }; pavone@8: } else if(this.parent) { pavone@84: var ret = this.parent.find(name, nestedcall, allowll); pavone@8: if (ret) { pavone@8: if(ret.type == 'self') { pavone@8: ret.type = 'parent'; pavone@8: ret.depth = 1; pavone@59: this.needsparent = true; pavone@8: } else if(ret.type == 'parent') { pavone@59: this.needsparent = true; pavone@8: ret.depth++; pavone@42: } else if(ret.type == 'closedover' || ret.type == 'upvar') { pavone@42: this.needsenv = true; pavone@8: } pavone@8: } pavone@54: } else { pavone@54: return null; pavone@8: } pavone@54: debugprint('\t//symbol type:', ret ? ret.type : 'null'); pavone@54: return ret; pavone@8: }; pavone@8: osymbols.prototype.defineMsg = function(name, def) { pavone@8: this.names[name] = def; pavone@8: } pavone@84: osymbols.prototype.defineLLProperty = function(name) { pavone@84: this.llnames[name] = true; pavone@84: } pavone@8: osymbols.prototype.parentObject = function() { pavone@8: if (!this.parent) { pavone@8: return 'null'; pavone@8: } pavone@8: return 'this'; pavone@8: } pavone@24: osymbols.prototype.allSymbols = function(curlist, cursyms) { pavone@24: if (curlist === undefined) { pavone@24: curlist = []; pavone@24: cursyms = {}; pavone@23: } pavone@24: var keys = Object.keys(this.names).sort(); pavone@24: for (var i in keys) { pavone@24: if (!(keys[i] in cursyms)) { pavone@24: curlist.push(keys[i]); pavone@24: cursyms[keys[i]] = true; pavone@24: } pavone@24: } pavone@24: if (this.parent) { pavone@24: return this.parent.allSymbols(curlist, cursyms); pavone@24: } pavone@24: return curlist; pavone@23: } pavone@34: osymbols.prototype.getEnvType = function() { pavone@34: return this.parent.getEnvType(); pavone@34: } pavone@42: osymbols.prototype.envVar = function() { pavone@42: return this.parent.envVar(); pavone@42: } pavone@8: pavone@8: function lsymbols(parent) pavone@8: { pavone@8: this.parent = parent; pavone@8: this.names = {}; pavone@34: this.closedover = {}; pavone@38: this.declared = {}; pavone@8: this.needsSelfVar = false; pavone@34: this.passthruenv = false; pavone@34: this.envtype = 'void'; pavone@57: this.needsParentEnv = false; pavone@8: } pavone@84: lsymbols.prototype.find = function(name, nestedcall, allowll) { pavone@54: debugprint('//lsymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); pavone@8: if (name in this.names) { pavone@8: if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { pavone@54: var ret = { pavone@8: type: 'foreign', pavone@8: def: this.names[name] pavone@8: }; pavone@54: } else { pavone@196: if (nestedcall && !(name in this.closedover)) { pavone@196: debugprint('//symbol', name, 'is now closed over'); pavone@54: this.closedover[name] = true; pavone@55: this.passthruenv = false; pavone@156: } pavone@54: if (name in this.closedover) { pavone@54: var ret = { pavone@54: type: 'closedover', pavone@54: def: this.names[name] pavone@54: }; pavone@54: } else { pavone@54: var ret = { pavone@54: type: 'local', pavone@54: def: this.names[name], pavone@54: isdeclared: (name in this.declared) pavone@54: }; pavone@54: } pavone@8: } pavone@8: } else if(this.parent) { pavone@84: var ret = this.parent.find(name, true, allowll); pavone@31: if (ret) { pavone@34: if (ret.type == 'closedover') { pavone@31: ret.type = 'upvar'; pavone@55: ret.depth = 0; pavone@55: } pavone@55: if (ret.type == 'upvar') { pavone@55: if (Object.keys(this.closedover).length) { pavone@34: ret.depth++; pavone@57: ret.startdepth = 1; pavone@57: this.needsParentEnv = true; pavone@34: } else { pavone@34: this.passthruenv = true; pavone@57: ret.startdepth = 0; pavone@57: /*if (ret.depth == 0) { pavone@55: ret.depth = 1; pavone@57: }*/ pavone@34: } pavone@31: } pavone@8: } pavone@54: } else { pavone@54: return null; pavone@8: } pavone@57: var type = ret ? ret.type : 'null'; pavone@57: debugprint('\t//symbol type:', type , type == 'upvar' ? 'depth: ' + ret.depth : ''); pavone@54: return ret; pavone@8: }; pavone@8: lsymbols.prototype.defineVar = function(name, def) { pavone@8: this.names[name] = def; pavone@8: }; pavone@38: lsymbols.prototype.declareVar = function(name) { pavone@38: this.declared[name] = true; pavone@38: } pavone@8: lsymbols.prototype.selfVar = function() { pavone@8: if (this.parent && this.parent instanceof lsymbols) { pavone@8: this.parent.needsSelf(); pavone@8: return 'self'; pavone@8: } else { pavone@8: return 'this'; pavone@8: } pavone@8: }; pavone@8: lsymbols.prototype.needsSelf = function() { pavone@8: if (this.parent && this.parent instanceof lsymbols) { pavone@8: this.parent.needsSelf(); pavone@8: } else { pavone@8: this.needsSelfVar = true; pavone@8: } pavone@8: }; pavone@23: lsymbols.prototype.allSymbols = osymbols.prototype.allSymbols; pavone@34: lsymbols.prototype.parentEnvType = function() { pavone@34: if (!this.parent) { pavone@34: return 'void'; pavone@34: } pavone@34: return this.parent.getEnvType(); pavone@34: }; pavone@34: lsymbols.prototype.getEnvType = function() { pavone@34: if (this.passthruenv) { pavone@34: return this.parent.getEnvType(); pavone@34: } else { pavone@34: return this.envtype; pavone@34: } pavone@34: } pavone@42: lsymbols.prototype.envVar = function() { pavone@42: if (Object.keys(this.closedover).length) { pavone@42: return 'myenv'; pavone@42: } else if(this.passthruenv) { pavone@42: return 'env'; pavone@42: } pavone@42: return null; pavone@42: } pavone@8: pavone@16: var mainModule; pavone@16: pavone@16: function toobj(val) pavone@16: { pavone@110: return (typeof val == "boolean") ? (val ? module_true : module_false) : val; pavone@16: } pavone@16: pavone@16: op.prototype.populateSymbols = function(symbols, isReceiver) { pavone@16: this.left.populateSymbols(symbols); pavone@97: if (this.op == '&&' || this.op == '||') { pavone@97: //&& and || are syntactic sugar for if and ifnot with pavone@97: //the second argument transformed into a lambda to pavone@97: //achieve short-circuit evalutation pavone@97: this.right = new lambda([], [this.right]); pavone@97: } pavone@16: this.right.populateSymbols(symbols); pavone@16: }; pavone@16: pavone@16: symbol.prototype.populateSymbols = function(symbols) { pavone@16: this.symbols = symbols; pavone@54: var ret = symbols.find(this.cleanName()); pavone@60: if (ret && (ret.type == 'self' || ret.type == 'parent')) { pavone@54: symbols.find('self'); pavone@54: } pavone@16: } pavone@16: pavone@16: intlit.prototype.populateSymbols = function(symbols) { pavone@16: } pavone@16: pavone@16: floatlit.prototype.populateSymbols = function(symbols) { pavone@16: } pavone@16: pavone@16: strlit.prototype.populateSymbols = function(symbols) { pavone@16: } pavone@16: pavone@25: listlit.prototype.populateSymbols = function(symbols) { pavone@38: for (var i = 0; i < this.val.length; i++) { pavone@38: this.val[i].populateSymbols(symbols); pavone@38: } pavone@38: } pavone@38: pavone@38: arraylit.prototype.populateSymbols = function(symbols) { pavone@38: for (var i = 0; i < this.val.length; i++) { pavone@38: this.val[i].populateSymbols(symbols); pavone@38: } pavone@25: } pavone@25: pavone@16: funcall.prototype.populateSymbols = function(symbols) { pavone@96: var isll = false; pavone@16: if (this.name == 'foreign:') { pavone@16: if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object) || (this.args[0] instanceof symbol)) { pavone@16: return; pavone@16: } else { pavone@16: throw new Error("Unexpected AST type for foreign:"); pavone@16: } pavone@273: } else if (this.name == 'import:from:' && symbols instanceof osymbols && this.args.length == 2) { pavone@273: each(this.args[0].val, function(i, el) { pavone@273: if (!(el instanceof symbol)) { pavone@273: throw new Error('Names in import:from statement must be symbols'); pavone@273: } pavone@273: symbols.defineMsg(el.name, new lambda([], [])); pavone@273: }); pavone@84: } else if (this.name == 'llProperty:withType:') { pavone@84: if (this.args[0] instanceof symbol) { pavone@84: if ((this.args[1] instanceof symbol) || (this.args[1] instanceof funcall)) { pavone@265: symbols.defineLLProperty(this.args[0] instanceof symbol ? this.args[0].name : this.args[0].val); pavone@84: return; pavone@84: } else { pavone@84: throw new Error("Second argument to llProperty:withType: must be a symbol or funcall"); pavone@84: } pavone@84: } else { pavone@265: throw new Error("First argument to llProperty:withType: must be a symbol or string"); pavone@84: } pavone@273: } else if (this.name == 'llMessage:withVars:andCode:' && symbols instanceof osymbols) { pavone@265: if (this.args[0] instanceof symbol || this.args[0] instanceof strlit) { pavone@84: if (this.args[1] instanceof lambda) { pavone@84: if (this.args[2] instanceof lambda) { pavone@314: pavone@84: symbols.defineMsg(this.args[0].name, this.args[2]); pavone@96: isll = true; pavone@84: } else { pavone@84: throw new Error("Third argument to llMessage:withVars:andCode: must be a lambda"); pavone@84: } pavone@84: } else { pavone@84: throw new Error("Second argument to llMessage:withVars:andCode: must be a lambda"); pavone@84: } pavone@84: } else { pavone@84: throw new Error("First argument to llMessage:withVars:andCode: must be a symbol"); pavone@84: } pavone@16: } pavone@20: this.symbols = symbols; pavone@62: var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; pavone@77: var funinfo = symbols.find(name); pavone@77: if (funinfo && (funinfo.type == 'self' || funinfo.type == 'parent')) { pavone@77: symbols.find('self'); pavone@77: } pavone@16: for (var i in this.args) { pavone@96: this.args[i].populateSymbols(symbols, undefined, isll); pavone@16: } pavone@20: if (this.receiver) { pavone@96: this.receiver.populateSymbols(symbols, undefined, isll); pavone@20: } pavone@16: } pavone@16: pavone@196: funcall.prototype.defineSymbolsObject = function(symbols) { pavone@196: } pavone@196: pavone@25: funcall.prototype.populateSymbolsObject = function(symbols) { pavone@84: this.populateSymbols(symbols); pavone@25: } pavone@25: pavone@16: object.prototype.populateSymbols = function(symbols) { pavone@84: var symbols = new osymbols(symbols); pavone@16: for (var i in this.messages) { pavone@196: this.messages[i].defineSymbolsObject(symbols); pavone@196: } pavone@196: for (var i in this.messages) { pavone@16: this.messages[i].populateSymbolsObject(symbols); pavone@16: } pavone@16: this.symbols = symbols; pavone@16: } pavone@96: var lambdanum = 0; pavone@96: lambda.prototype.populateSymbols = function(symbols, isobject, isll) { pavone@96: if (!isll) { pavone@96: this.name = 'lambda_' + lambdanum++; pavone@96: } pavone@16: var args = this.args ? this.args.slice(0, this.args.length) : []; pavone@16: var exprs = this.expressions; pavone@54: var symbols = new lsymbols(symbols); pavone@16: for (var i in args) { pavone@16: symbols.defineVar(args[i].cleanName(), null); pavone@16: args[i].populateSymbols(symbols); pavone@16: } pavone@54: if (isobject && (!args.length || args[0].cleanName() != 'self')) { pavone@54: symbols.defineVar('self', null); pavone@54: } pavone@16: for (var i in exprs) { pavone@16: exprs[i].populateSymbols(symbols); pavone@16: } pavone@16: this.symbols = symbols; pavone@16: }; pavone@16: pavone@16: assignment.prototype.populateSymbols = function(symbols) { pavone@62: debugprint('//assignment', this.symbol.name, 'populateSymbols'); pavone@16: var existing = symbols.find(this.symbol.name); pavone@201: if (!existing || existing.type == 'toplevel') { pavone@16: symbols.defineVar(this.symbol.name, this.expression); pavone@16: } pavone@16: this.symbol.populateSymbols(symbols); pavone@16: this.expression.populateSymbols(symbols); pavone@20: this.symbols = symbols; pavone@16: }; pavone@196: assignment.prototype.defineSymbolsObject = function(symbols) { pavone@62: debugprint('//messagedef', this.symbol.name, 'populateSymbols'); pavone@30: if (this.expression instanceof lambda || (this.expression instanceof funcall & this.expression.name == 'foreign:')) { pavone@30: symbols.defineMsg(this.symbol.name, this.expression); pavone@30: } else { pavone@30: symbols.defineMsg(this.symbol.name, new getter(null)); pavone@20: symbols.defineMsg(this.symbol.name + '!', new setter(null)); pavone@20: } pavone@196: }; pavone@196: assignment.prototype.populateSymbolsObject = function(symbols) { pavone@16: this.symbol.populateSymbols(symbols); pavone@54: this.expression.populateSymbols(symbols, true); pavone@20: this.symbols = symbols; pavone@16: }; pavone@16: pavone@20: function setter(fun) pavone@20: { pavone@20: this.fun = fun; pavone@20: } pavone@30: setter.prototype.args = [new symbol('self'), new symbol('newval')]; pavone@20: function getter(fun) pavone@20: { pavone@20: this.fun = fun; pavone@20: } pavone@30: getter.prototype.args = [new symbol('self')]; pavone@99: