# HG changeset patch # User Mike Pavone # Date 1333430928 25200 # Node ID 4d87c38404d616fd3dbdc75e2247ce8f452c3e0e # Parent fe3533494ce987d28f58040a0e28b2e40965f223 List literals, fixes to implicit self property lookup, import statement and editor improvements diff -r fe3533494ce9 -r 4d87c38404d6 compiler.js --- 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) { diff -r fe3533494ce9 -r 4d87c38404d6 editor.css --- 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; diff -r fe3533494ce9 -r 4d87c38404d6 editor.js --- 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); } diff -r fe3533494ce9 -r 4d87c38404d6 editor.tp --- 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 diff -r fe3533494ce9 -r 4d87c38404d6 jsbackend.js --- 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; }; diff -r fe3533494ce9 -r 4d87c38404d6 mquery.js --- 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) diff -r fe3533494ce9 -r 4d87c38404d6 parser.js --- 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); };' + diff -r fe3533494ce9 -r 4d87c38404d6 scripttags.js --- 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(); }