changeset 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 fe3533494ce9
children fe593c1df568
files compiler.js editor.css editor.js editor.tp jsbackend.js mquery.js parser.js scripttags.js
diffstat 8 files changed, 214 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/compiler.js	Tue Mar 27 00:39:32 2012 -0700
+++ b/compiler.js	Mon Apr 02 22:28:48 2012 -0700
@@ -130,17 +130,6 @@
 	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.populateSymbols = function(symbols, isReceiver) {
 	this.left.populateSymbols(symbols);
 	this.right.populateSymbols(symbols);
@@ -159,8 +148,13 @@
 strlit.prototype.populateSymbols = function(symbols) {
 }
 
+listlit.prototype.populateSymbols = function(symbols) {
+	each(this.val, function(i,el) {
+		el.populateSymbols(symbols);
+	});
+}
+
 funcall.prototype.populateSymbols = function(symbols) {
-	if(this.name == 'q:') { console.log('populateSymbols', this); }
 	if (this.name == 'foreign:') {
 		if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object) || (this.args[0] instanceof symbol)) {
 			return;
@@ -177,6 +171,10 @@
 	}
 }
 
+funcall.prototype.populateSymbolsObject = function(symbols) {
+	this.populateSymbols(symbols.parent);
+}
+
 object.prototype.populateSymbols = function(symbols) {
 	symbols = new osymbols(symbols);
 	for (var i in this.messages) {
--- a/editor.css	Tue Mar 27 00:39:32 2012 -0700
+++ b/editor.css	Mon Apr 02 22:28:48 2012 -0700
@@ -33,6 +33,16 @@
 	height: 100%;
 }
 
+#src span:focus
+{
+	background-color: yellow;
+}
+
+#src span.selected
+{
+	background-color: yellow;
+}
+
 #editor
 {
 	display: none;
--- a/editor.js	Tue Mar 27 00:39:32 2012 -0700
+++ b/editor.js	Mon Apr 02 22:28:48 2012 -0700
@@ -69,18 +69,25 @@
 strlit.prototype.toHTML = function(node) {
 	node.appendChild(newEl('span', {
 		className: 'string',
+		contentEditable: 'true',
 		textContent: this.val
 	}));
 };
 
 funcall.prototype.toHTML = function(node) {
+	var astNode = this;
 	var base = newEl('div', {
 		className: 'funcall'
 	});
 	var parts = this.name.split(':');
 	for (var i in parts ) {
 		if(parts[i]) {
-			base.appendChild(newEl('span', {textContent: parts[i] + ':', className: 'funpart'}));
+			base.appendChild(newEl('span', {
+				textContent: parts[i] + ':',
+				className: 'funpart',
+				onclick: function() {
+					return mainModule.funClick(this, astNode);
+				}}));
 			if (this.args[i]) {
 				this.args[i].toHTML(base);
 			}
--- a/editor.tp	Tue Mar 27 00:39:32 2012 -0700
+++ b/editor.tp	Mon Apr 02 22:28:48 2012 -0700
@@ -12,12 +12,13 @@
 parser <- foreign: #{
 	parse <- foreign: :str {}
 }
+isLambda <- foreign: :astnode {}
 
 //js builtins
 console <- foreign: #{
 	log <- foreign: :val {}
 }
-window <- #{}
+window <- foreign: #{}
 Object <- foreign: #{
 	keys <- foreign: :object {}
 }
@@ -35,6 +36,17 @@
   }
 }
 
+filter <- :arr pred {
+	output <- arr slice: 0 0
+	each: arr :idx el {
+		if: (pred: el) {
+			output push: el
+		} else: {}
+	}
+	output
+}
+
+//editor code
 editFile <- :path {
 	get: path :request {
 		addClass: (q: "body") "editorMode"
@@ -47,19 +59,64 @@
 	}
 }
 
-symbolClick <- :domnode astnode {
+selectNode <- :node {
+	each: (qall: ".selected") :idx el {
+		removeClass: el "selected"
+	}
+	addClass: node "selected"
+}
+
+selectQuery <- :selector {
+	selectQuery: selector in: (foreign: undefined)
+}
+
+selectQuery:in <- :selector :context {
+	each: (qall: ".selected") :idx el {
+		removeClass: el "selected"
+	}
+	each: (qall: selector context) :idx el {
+		addClass: el "selected"
+	}
+}
+
+selectParent <- :node {
+	each: (qall: ".selectParent") :idx el {
+		removeClass: el "selected"
+	}
+	addClass: (node parentNode) "selectParent"
+}
+
+popInscope:onClick <- :syms :handler {
 	inscope <- q: "#inscope"
 	inscope innerHTML!: ""
-	console log: astnode
-	syms <- (astnode symbols) allSymbols
 	each: syms :idx key {
 		inscope appendChild: (newEl: "li" #{
 			textContent <- key
+			onclick <- { handler: key }
 		})
 	}
 }
 
-//editor code
+symbolClick <- :domnode astnode {
+	console log: astnode
+	selectNode: domnode
+	popInscope: ((astnode symbols) allSymbols) onClick: :key {
+		domnode textContent!: key
+		astnode name!: key
+	}
+	
+}
+
+funClick <- :domnode astnode {
+	selectParent: domnode
+	selectQuery: ".selectParent > .funpart" in: (domnode parentNode)
+	symtable <- astnode symbols
+	syms <- filter: (symtable allSymbols) :sym {
+		isLambda: ((symtable find: sym) def)
+	}
+	popInscope: syms
+}
+
 main <- {
 	//bind handlers for file browser links
 	each: (qall: "a") :idx el {
@@ -81,7 +138,9 @@
 		}
 	}
 	(q: "#ops_button") onclick!: :event {
-		addClass: (q: ".controls") "showops"
+		each: (qall: ".controls") :idx el {
+			addClass: el "showops"
+		}
 	}
 	
 	path <- (window location) pathname
--- 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;
 };
--- a/mquery.js	Tue Mar 27 00:39:32 2012 -0700
+++ b/mquery.js	Mon Apr 02 22:28:48 2012 -0700
@@ -13,13 +13,13 @@
 	}
 }
 
-function q(query)
+function q(query, el)
 {
-	return document.querySelector(query);
+	return el === undefined ? document.querySelector(query) : el.querySelector(query);
 }
-function qall(query)
+function qall(query, el)
 {
-	return document.querySelectorAll(query);
+	return el === undefined ? document.querySelectorAll(query)  : el.querySelectorAll(query);
 }
 
 function hide(el)
--- a/parser.js	Tue Mar 27 00:39:32 2012 -0700
+++ b/parser.js	Mon Apr 02 22:28:48 2012 -0700
@@ -30,6 +30,11 @@
 	this.val = val;
 }
 
+function listlit(val)
+{
+	this.val = val;
+}
+
 function funcall(name, args)
 {
 	this.name = name;
@@ -54,6 +59,11 @@
 	this.expression = expr;
 }
 
+function isLambda(node)
+{
+	return node instanceof lambda;
+}
+
 var grammar = 
 'start = ws module:(object / lambda) ws { return module; };' +
 'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n")*;' +
@@ -62,13 +72,14 @@
 'opexpr = left:addsub hws opn:("<=" / ">=" / "<" / ">" / "=") hws right:opexpr { return new op(left, opn, right); } / addsub;' +
 'addsub = left:muldiv hws opn:("+"/"-") hws right:addsub { return new op(left, opn, right); } / muldiv;'+
 'muldiv = left:primlitsym hws opn:("*"/"/") hws right:muldiv { return new op(left, opn, right); } / primlitsym;'+
-'primlitsym = hws val:(float / int / string / symbol / object / lambda / "(" expr:expr hws ")" { return expr; }) { return val; };' +
+'primlitsym = hws val:(float / int / string / symbol / object / list / lambda / "(" expr:expr hws ")" { return expr; }) { return val; };' +
 'symbol = chars:[a-zA-Z_!?@]+ trailing:(":"? [a-zA-Z_!?@0-9])* ! ":" { for (var i in trailing) { trailing[i] = trailing[i].join(""); } return new symbol(chars.join("") + trailing.join("")); };' +
 'float = digits:[0-9]+ "." decimals:[0-9]+ { return new floatlit(parseFloat(digits.join("") + "." + decimals.join(""))); };' +
 'int = digits:[0-9]+ { return new intlit(parseInt(digits.join(""), 10)); };' +
 'string = "\\"" text:[^\\"]* "\\"" { return new strlit(text.join("")); };' +
 'object = "#{" ws messages:(assignment / funcall)* "}" { return new object(messages); };' +
-'assignment = hws sym:symbol hws "<-" expr:expr ws { return new assignment(sym, expr); }' +
+'list = "[" ws els:opexpr* "]" { return new listlit(els); };' +
+'assignment = ws sym:symbol hws "<-" expr:expr ws { return new assignment(sym, expr); }' +
 'lambda = args:((& ":") argname+  )? "{" ws exprs:(assignment / expr)* "}" { return new lambda(args[1], exprs); };' +
 'argname = init:":"? chars:[a-zA-Z_!?@]+ trailing:[a-zA-Z_!?@0-9]* hws { return new symbol(init + chars.join("") + trailing.join("")); };' +
 'funcall = hws parts: funcallpart+ { var fun = ""; var args = []; for (var i in parts) { fun += parts[i].name; args = args.concat(parts[i].args); } return new funcall(fun, args); };' +
--- a/scripttags.js	Tue Mar 27 00:39:32 2012 -0700
+++ b/scripttags.js	Mon Apr 02 22:28:48 2012 -0700
@@ -4,6 +4,15 @@
 	var ast = parser.parse(src);
 	var js = ast.toJSModule();
 	mainModule = eval(js)();
+	if (mainModule.strue) {
+		each(mainModule.strue, function(key, val) {
+			if(val instanceof Function) {
+				Boolean.prototype[key] = function() {
+					return this.valueOf() ? mainModule.strue[key].apply(mainModule.strue, arguments) : mainModule.sfalse[key].apply(mainModule.sfalse, arguments);
+				};
+			}
+		});
+	}
 	mainModule.main();
 }