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')];