view compiler.js @ 54:976a0924e1d4

Fix closure over self var
author Mike Pavone <pavone@retrodev.com>
date Fri, 13 Jul 2012 19:22:39 -0700
parents 18ab96287c3a
children 93ddb4ad6fcb
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 = {};
	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 > -1) {
					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';
}

function osymbols(parent)
{
	this.parent = parent;
	this.names = {};
	this.needsenv = false;
}
osymbols.prototype.find = function(name, nestedcall) {
	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',
			def: this.names[name],
		};
	} else if(this.parent) {
		var ret = this.parent.find(name, nestedcall);
		if (ret) {
			if(ret.type == 'self') {
				ret.type = 'parent';
				ret.depth = 1;
			} else if(ret.type == 'parent') {
				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.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';
}
lsymbols.prototype.find = function(name, nestedcall) {
	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;
			} 
			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);
		if (ret) {
			if (ret.type == 'closedover') {
				ret.type = 'upvar';
				ret.depth = 1;
			} else if (ret.type == 'upvar') {
				if (Object(this.closedover).keys.length) {
					ret.depth++;
				} else {
					this.passthruenv = true;
				}
			}
		}
	} else {
		return null;
	}
	debugprint('\t//symbol type:', ret ? ret.type : 'null');
	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.type == 'self') {
		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:");
		}
	}
	this.symbols = symbols;
	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.parent);
}

object.prototype.populateSymbols = function(symbols) {
	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) {
	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) {
	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')];