Mercurial > repos > tabletprog
view parser.js @ 7:8af72f11714e
Initial version of FFI
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 21 Mar 2012 20:12:12 -0700 |
parents | 554602d4cbc6 |
children | 04ae32e91598 |
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 indent(str) { return str.split('\n').join('\n\t'); } function osymbols(parent) { this.parent = parent; this.names = {}; this.lastname = null; } osymbols.prototype.find = function(name) { if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { return { type: 'foreign', def: this.names[name] }; } return { type: 'self', def: this.names[name], }; } else if(this.parent) { var ret = this.parent.find(name); if (ret) { if(ret.type == 'self') { ret.type = 'parent'; ret.depth = 1; } else if(ret.type == 'parent') { ret.depth++; } } return ret; } return null; }; osymbols.prototype.defineMsg = function(name, def) { this.lastname = name; this.names[name] = def; } osymbols.prototype.parentObject = function() { if (!this.parent) { return 'null'; } return 'this'; } function lsymbols(parent) { this.parent = parent; this.names = {}; this.lastname = null; this.needsSelfVar = false; } lsymbols.prototype.find = function(name) { if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { return { type: 'foreign', def: this.names[name] }; } return { type: 'local', def: this.names[name] }; } else if(this.parent) { var ret = this.parent.find(name); if (ret && ret.type == 'local') { ret.type = 'upvar'; } return ret; } return null; }; lsymbols.prototype.defineVar = function(name, def) { this.lastname = name; this.names[name] = def; }; 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; } }; function op(left, op, right) { this.left = left; this.op = op; this.right = right; } op.prototype.toJS = function(symbols, isReceiver) { var ret = '(' + this.left.toJS(symbols) +' '+ (this.op == '=' ? '==' : this.op) +' '+ this.right.toJS(symbols) + ')'; if (isReceiver) { ret = 'toobj' + ret; } return ret; }; function symbol(name) { this.name = name; } symbol.prototype.toJS = function(symbols) { var name = this.cleanName(); if (name == 'self') { return 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; } symbol.prototype.cleanName = function() { return this.name[0] == ':' ? this.name.substr(1) : this.name; } function intlit(val) { this.val = val; } intlit.prototype.toJS = function(symbols) { return this.val.toString(); } function floatlit(val) { this.val = val; } floatlit.prototype.toJS = function(symbols) { return this.val.toString(); } function strlit(val) { this.val = val; } strlit.prototype.toJS = function(symbols) { console.log('string:', this.val); return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"'; } function funcall(name, args) { this.name = name; this.args = args; this.receiver = null; } funcall.prototype.toJS = function(symbols) { var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; var args = this.args.slice(0, this.args.length); if (this.receiver) { args.splice(0, 0, this.receiver); } var funinfo = symbols.find(name); if (!funinfo) { var receiver = args[0]; args.splice(0, 1); for (var i in args) { args[i] = args[i].toJS(symbols); } return receiver.toJS(symbols, true) + '.' + (new symbol(name)).toJS(symbols) + '(' + 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'); } else { var receiver = args[0]; args.splice(0, 1); } for (var i in args) { args[i] = args[i].toJS(symbols); } return receiver.toJS(symbols, true) + '.' + (new symbol(name)).toJS(symbols) + '(' + 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(symbols); } ret += (new symbol(name)).toJS(symbols) + '(' + args.join(', ') + ')'; return ret; case 'local': case 'upvar': case 'foreign': for (var i in args) { args[i] = args[i].toJS(symbols); } return (new symbol(name)).toJS(symbols) + '(' + args.join(', ') + ')'; } } function object(messages) { this.messages = messages; } object.prototype.toJS = function(symbols) { var messages = this.messages; symbols = new osymbols(symbols); var compiled = [] for (var i in messages) { var js = messages[i].toJSObject(symbols); if (js) { compiled.push(indent(js)); } } return '{\n\tparent: ' + symbols.parentObject() + ',\n\t' + compiled.join(',\n\t') + '\n}'; } object.prototype.toJSModule = function() { return '(function () {\n\tvar module = ' + indent(this.toJS(null)) + ';\n\treturn module;\n})' } function lambda(args, expressions) { this.args = args; this.expressions = expressions; } lambda.prototype.toJS = function(symbols) { 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; symbols = new lsymbols(symbols); for (var i in args) { symbols.defineVar(args[i].cleanName(), null); args[i] = args[i].toJS(symbols); } var compiled = [] for (var i in exprs) { var js = exprs[i].toJS(symbols); 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' + (symbols.needsSelfVar ? 'var self = this;\n\t' : '') + exprs.join(';\n\t') + '\n}' }; lambda.prototype.toJSModule = lambda.prototype.toJS function assignment(sym, expr) { this.symbol = sym; this.expression = expr; } assignment.prototype.toJS = function(symbols) { var existing = symbols.find(this.symbol.name); var prefix = ''; if (!existing) { symbols.defineVar(this.symbol.name, this.expression); 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; } } if (this.expression instanceof funcall && this.expression.name == 'foreign:') { return null; } return prefix + this.symbol.toJS(symbols) + ' = ' + this.expression.toJS(symbols); }; assignment.prototype.toJSObject = function(symbols) { symbols.defineMsg(this.symbol.name, this.expression); if (this.expression instanceof funcall && this.expression.name == 'foreign:') { return null; } return this.symbol.toJS(symbols) + ': ' + this.expression.toJS(symbols); }; var grammar = 'start = ws module:(object / lambda) ws { return module; };' + 'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n")*;' + 'hws = ([ \\t] / "/*" ([^*] / "*" ! "/")* "*/" )*;' + 'expr = e:(funcall / methcall / opexpr) ws { return e; };' + '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; };' + '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* "}" { return new object(messages); };' + 'assignment = hws 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); };' + 'funcallpart = fun:funpart args:opexpr* hws { return { name: fun, args: args}; };' + 'funpart = chars:[a-zA-Z_!?@]+ middle:[a-zA-Z_!?@0-9]* ":" & [ \\t\\n\\r] { return chars.join("") + middle.join("") + ":"; };' + 'methcall = receiver:opexpr hws info:methcallrest { info.receiver = receiver; return info; };' + 'methcallrest = funcall / unarymeth;' + 'unarymeth = name:symbol { return new funcall(name.name, []); };'; var parser = PEG.buildParser(grammar); //var parser = PEG.buildParser('start = expr; expr = int; int = digits:[0-9]+ { return parseInt(digits.join(""), 10); }'); onReady(function() { q('#parse').onclick = function() { var text = q('textarea').value; try { var parsed = parser.parse(text); q('pre').innerHTML = text + "\n\n" + JSON.stringify(parsed); console.log(parsed); } catch(e) { q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column; } } q('#tojs').onclick = function() { var text = q('textarea').value; //try { var parsed = parser.parse(text); var js = parsed.toJSModule(); q('pre').innerHTML = js; console.log(parsed); /*} catch(e) { q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column; }*/ } q('#run').onclick = function() { var text = q('textarea').value; //try { var parsed = parser.parse(text); var js = parsed.toJSModule(); mainModule = eval(js)(); q('pre').innerHTML = mainModule.main(); /*} catch(e) { q('pre').innerHTML = e.message + '\nLine: ' + e.line + '\nCol: ' + e.column; }*/ } });