view compiler.js @ 42:4e983fe32047

Fix closures as methods so that private vars work
author Mike Pavone <pavone@retrodev.com>
date Thu, 12 Jul 2012 20:14:15 -0700
parents e7be612fd3ae
children 18ab96287c3a
line wrap: on
line source

function indent(str)
{
	return str.split('\n').join('\n\t');
}

var toplevel = new topsymbols();
function topsymbols()
{
	this.names = null;
	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 = {};
	}
}
topsymbols.prototype.find = function(name) {
	if (!this.names) {
		throw new Error('data not ready');
	}
	if (name in this.names) {
		return {
			type: 'toplevel',
			def: null
		};
	}
	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) {
	if (name in this.names) {
		if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
			return {
				type: 'foreign',
				def: this.names[name]
			};
		}
		return {
			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;
			}
		}
		return ret;
	}
	return null;
};
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) {
	if (name in this.names) {
		if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') {
			return {
				type: 'foreign',
				def: this.names[name]
			};
		}
		if (nestedcall) {
			this.closedover[name] = true;
		} 
		if (name in this.closedover) {
			return {
				type: 'closedover',
				def: this.names[name]
			};
		}
		return {
			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;
				}
			}
		}
		return ret;
	}
	return null;
};
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;
	symbols.find(this.cleanName());
}

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) {
	var args = this.args ? this.args.slice(0, this.args.length) : [];
	if (args.length && args[0].cleanName() == 'self') {
		args.splice(0, 1);
	}
	var exprs = this.expressions;
	symbols = new lsymbols(symbols);
	for (var i in args) {
		symbols.defineVar(args[i].cleanName(), null);
		args[i].populateSymbols(symbols);
	}
	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);
	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')];