diff jsbackend.js @ 25:4d87c38404d6

List literals, fixes to implicit self property lookup, import statement and editor improvements
author Mike Pavone <pavone@retrodev.com>
date Mon, 02 Apr 2012 22:28:48 -0700
parents 068d63627b16
children 608eb70fe261
line wrap: on
line diff
--- a/jsbackend.js	Tue Mar 27 00:39:32 2012 -0700
+++ b/jsbackend.js	Mon Apr 02 22:28:48 2012 -0700
@@ -16,6 +16,33 @@
 	throw new Error("can't make val into object");
 }
 
+function importSym(obj, src, key)
+{
+	if(!(key in src)) {
+		throw new Error(key +' not found in source object for import');
+	}
+	if(key in obj) {
+		throw new Error(key +' already exists in target object for import')
+	}
+	obj[key] = src[key];
+}
+
+function doImport(obj, src, symlist)
+{
+	if (symlist === undefined) {
+		each(src, function(key,val) {
+			if (key != 'parent') {
+				importSym(obj, src, key);
+			}
+		});
+	} else {
+		for (var i = 0; i < symlist.length; ++i) {
+			importSym(obj, src, symlist[i]);
+		}
+	}
+	return obj;
+}
+
 op.prototype.toJS = function(isReceiver) {
 	var ret = '(' + this.left.toJS() +' '+ (this.op == '=' ? '==' : this.op) +' '+ this.right.toJS() + ')';
 	if (isReceiver) {
@@ -24,11 +51,8 @@
 	return ret;
 };
 
-symbol.prototype.toJS = function() {
-	var name = this.cleanName();
-	if (name == 'self') {
-		return this.symbols.selfVar();
-	}
+function escapeJSName(name)
+{
 	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) {
@@ -37,6 +61,27 @@
 	return name;
 }
 
+symbol.prototype.toJS = function() {
+	var name = this.cleanName();
+	if (name == 'self') {
+		return this.symbols.selfVar();
+	}
+	var info = this.symbols.find(name);
+	if (!info) {
+		throw new Error('symbol ' + name + ' not found');
+	}
+	var pre = '';
+	if (info.type == 'self') {
+		pre = this.symbols.selfVar() + '.';
+	} else if(info.type == 'parent') {
+		pre = 'this';
+		for (var i = 0; i < funinfo.depth; ++i) {
+			pre += '.parent';
+		}
+	}
+	return pre + escapeJSName(name);
+}
+
 intlit.prototype.toJS = function() {
 	return this.val.toString();
 }
@@ -49,6 +94,14 @@
 	return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"';
 }
 
+listlit.prototype.toJS = function() {
+	var ret = '[';
+	each(this.val, function(idx, el) {
+		ret += (idx ? ', ' : '') + el.toJS();
+	});
+	return ret + ']';
+}
+
 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') {
@@ -73,10 +126,9 @@
 		}
 		var rJS = receiver.toJS(true);
 		if ((name[name.length-1] == '!' && args.length == 1) || (funinfo && funinfo.def instanceof setter)) {
-			console.log(name.substr(0, name.length-1));
-			return  '(' + rJS + '.' + (new symbol(name.substr(0, name.length-1), this.symbols)).toJS()  + ' = ' + args[0] + ', ' + rJS + ')'
+			return  '(' + rJS + '.' + escapeJSName(name.substr(0, name.length-1))  + ' = ' + args[0] + ', ' + rJS + ')';
 		} else {
-			var callee = rJS + '.' + (new symbol(name, this.symbols)).toJS();
+			var callee = rJS + '.' + escapeJSName(name);
 			var callCode = callee + '(' + args.join(', ') + ')';
 			if (args.length == 0) {
 				return '(' + callee + ' instanceof Function ? ' + callCode + ' : ' + callee + ')';
@@ -107,19 +159,47 @@
 	for (var i in args) {
 		args[i] = args[i].toJS();
 	}
-	return ret + (new symbol(name, this.symbols)).toJS() + '(' + args.join(', ') + ')';
+	return ret + escapeJSName(name) + '(' + args.join(', ') + ')';
 }
 
 object.prototype.toJS = function() {
 	var messages = this.messages;
-	var compiled = []
+	var compiled = [];
+	var imports = []
 	for (var i in messages) {
-		var js = messages[i].toJSObject();
-		if (js) {
-			compiled.push(indent(js));
+		if (messages[i] instanceof funcall) {
+			if (messages[i].name == 'import:' && messages[i].args.length == 1) {
+				imports.push({symbols: false, src: messages[i].args[0]});
+			} else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) {
+				var importsyms = [];
+				each(messages[i].args[0].val, function(i, el) {
+					if (!(el instanceof symbol)) {
+						throw new Error('Names in import:from statement must be symbols');
+					}
+					importsyms.push(new strlit(el.name));
+				});
+				imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]});
+			} else {
+				throw new Error('Only import and import:from calls allowed in object context');
+			}
+		} else {
+			var js = messages[i].toJSObject();
+			if (js) {
+				compiled.push(indent(js));
+			}
 		}
 	}
-	return '{\n\tparent: ' + this.symbols.parentObject() + ',\n\t' + compiled.join(',\n\t') + '\n}';
+	var pre = '';
+	var post = '';
+	for (var i = imports.length-1; i >= 0; i--) {
+		pre += 'doImport(';
+		post += ', ' + imports[i].src.toJS();
+		if (imports[i].symbols) {
+			post += ', ' + imports[i].symbols.toJS();
+		}
+		post += ')';
+	}
+	return pre+'{\n\tparent: ' + this.symbols.parentObject() + ',\n\t' + compiled.join(',\n\t') + '\n}'+post;
 }
 
 object.prototype.toJSModule = function() {
@@ -184,5 +264,5 @@
 	if (val === null) {
 		return null;
 	}
-	return this.symbol.toJS() + ': ' + val;
+	return escapeJSName(this.symbol.name) + ': ' + val;
 };