view jsbackend.js @ 19:132c7756860e

Use populateSymbols to generate symbol tables during compilation rather than populating them as we go. This change allows us to refer to symbols defined later in the input stream and also gives the symbol table logic a single home that can be used both by the compiler and editor.
author Mike Pavone <pavone@retrodev.com>
date Sun, 25 Mar 2012 16:11:19 -0700
parents 6e4851a204a5
children bf03c9f0dd55
line wrap: on
line source

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");
}

function setP(o, p, val)
{
	o[p] = val;
	return o;
}

function getP(o, p)
{
	return o[p];
}

op.prototype.toJS = function(isReceiver) {
	var ret = '(' + this.left.toJS() +' '+ (this.op == '=' ? '==' : this.op) +' '+ this.right.toJS() + ')';
	if (isReceiver) {
		ret = 'toobj' + ret;
	}
	return ret;
};

symbol.prototype.toJS = function() {
	var name = this.cleanName();
	if (name == 'self') {
		return this.symbols.selfVar();
	}
	name = name.replace("_", "UN_").replace(":", "CN_").replace("!", "EX_").replace('?', 'QS_').replace('@', 'AT_');
	var reserved = {'true': true, 'false': true, 'this': true, 'if': true, 'else': true, 'NaN': true};
	if (name in reserved) {
		name = 's' + name;
	}
	return name;
}

intlit.prototype.toJS = function() {
	return this.val.toString();
}

floatlit.prototype.toJS = function() {
	return this.val.toString();
}

strlit.prototype.toJS = function() {
	return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"';
}

funcall.prototype.toJS = function() {
	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
	if (name == 'foreign') {
		if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) {
			return null;
		} else if(this.args[0] instanceof symbol) {
			return this.args[0].name;
		} else {
			throw new Error("Unexpected AST type for foreign:");
		}
	}
	var args = this.args.slice(0, this.args.length);
	if (this.receiver) {
		args.splice(0, 0, this.receiver);
	}
	var funinfo = this.symbols.find(name);
	if (!funinfo) {
		var receiver = args[0];
		args.splice(0, 1);
		for (var i in args) {
			args[i] = args[i].toJS();
		}
		return receiver.toJS(true) + '.' + (new symbol(name, this.symbols)).toJS() + '(' + args.join(', ') + ')';
	}
	switch(funinfo.type)
	{
	case 'self':
		if (args.length < funinfo.def.args.length || funinfo.def.args[0].name != 'self') {
			var receiver = new symbol('self', this.symbols);
		} else {
			var receiver = args[0];
			args.splice(0, 1);
		}
		for (var i in args) {
			args[i] = args[i].toJS();
		}
		return receiver.toJS(true) + '.' + (new symbol(name, this.symbols)).toJS() + '(' + args.join(', ') + ')';
	case 'parent':
		var ret = 'this';
		for (var i = 0; i < funinfo.depth; ++i) {
			ret += '.parent';
		}
		for (var i in args) {
			args[i] = args[i].toJS();
		}
		ret += (new symbol(name, this.symbols)).toJS() + '(' + args.join(', ') + ')';
		return ret;
	case 'local':
	case 'upvar':
	case 'foreign':
		for (var i in args) {
			args[i] = args[i].toJS();
		}
		return (new symbol(name, this.symbols)).toJS() + '(' + args.join(', ') + ')';
	}
}

object.prototype.toJS = function() {
	var messages = this.messages;
	var compiled = []
	for (var i in messages) {
		var js = messages[i].toJSObject();
		if (js) {
			compiled.push(indent(js));
		}
	}
	return '{\n\tparent: ' + this.symbols.parentObject() + ',\n\t' + compiled.join(',\n\t') + '\n}';
}

object.prototype.toJSModule = function() {
	this.populateSymbols(null);
	return '(function () {\n\tvar module = ' + indent(this.toJS()) + ';\n\treturn module;\n})'
}

lambda.prototype.toJS = function() {
	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;
	for (var i in args) {
		args[i] = args[i].toJS();
	}
	var compiled = []
	for (var i in exprs) {
		var js = exprs[i].toJS();
		if (js) {
			compiled.push(indent(js));
		}
	}
	exprs = compiled;
	if (exprs.length) {
		exprs[exprs.length-1] = 'return ' + exprs[exprs.length-1] + ';';
	}
	return 'function (' + args.join(', ') + ') {\n\t' + (this.symbols.needsSelfVar ? 'var self = this;\n\t' : '') + exprs.join(';\n\t') + '\n}'
};
lambda.prototype.toJSModule = function() {
	this.populateSymbols(null);
	return this.toJS();
}

assignment.prototype.toJS = function() {
	var existing = this.symbols.find(this.symbol.name);
	var prefix = '';
	if (!existing) {
		prefix =  'var ';
	} else {
		switch (existing.type)
		{
		case 'self':
			prefix = 'this.';
			break;
		case 'parent':
			prefix = 'this.';
			for (var i = 0; i < existing.depth; ++i) {
				prefix += 'parent.';
			}
			break;
		}
	}
	var val = this.expression.toJS();
	if (val === null) {
		return null;
	}
	return prefix + this.symbol.toJS() + ' = ' + val;
};
assignment.prototype.toJSObject = function() {
	var val = this.expression.toJS();
	if (val === null) {
		return null;
	}
	return this.symbol.toJS() + ': ' + val;
};