# HG changeset patch # User Mike Pavone # Date 1384927331 28800 # Node ID 60eff5f81d9ad768e0b2b0e315db7047390c2021 # Parent b4a9d4e405c5eef9dcfe0a3d6078300cb00194d5 Basic implementation of macros is now working diff -r b4a9d4e405c5 -r 60eff5f81d9a compiler.js --- a/compiler.js Wed Oct 23 19:10:03 2013 -0700 +++ b/compiler.js Tue Nov 19 22:02:11 2013 -0800 @@ -13,7 +13,7 @@ modulefile.prototype.populateSymbols = function (toplevel) { if (!this.ast) { - this.ast = parseFile(this.path + '/' + this.file); + this.ast = parseFile(this.path + '/' + this.file).macroexpand(new topenv(toplevel.moduledirs)); this.ast.populateSymbols(toplevel); } }; @@ -22,6 +22,7 @@ if (!this.ast) { var self = this; get(this.path + '/' + this.file, function(data) { + //TODO: macro expand in the async case self.ast = parser.parse(data.responseText); self.ast.populateSymbols(toplevel); whenDone(); @@ -48,6 +49,7 @@ var toplevel = new topsymbols([]); function topsymbols(moduledirs) { + this.moduledirs = moduledirs; this.names = null; this.used = {}; this.nextmodulenum = 0; diff -r b4a9d4e405c5 -r 60eff5f81d9a interp.js --- a/interp.js Wed Oct 23 19:10:03 2013 -0700 +++ b/interp.js Tue Nov 19 22:02:11 2013 -0800 @@ -79,7 +79,7 @@ } if (name in this.names) { var parsed = parseFile(this.names[name]); - this.modules[name] = parsed.eval(this); + this.modules[name] = parsed.macroexpand(this).eval(this); return this.modules[name]; } return null; @@ -89,13 +89,17 @@ }, findSetPresent: function(name, val) { return false; - } + }, + findMacro: function(name) { + return null; + }, } function environment(parent) { this.parent = parent; this.syms = {}; + this.macros = {}; } environment.prototype = { @@ -133,9 +137,45 @@ return this.parent.findSetPresent(name, val); } return false; + }, + findMacro: function(name) { + if (name in this.syms) { + if (name in this.macros) { + return this.syms[name]; + } + return null; + } + if (this.parent) { + return this.parent.findMacro(name); + } + return null; + }, + defMacro: function(name, def) { + this.syms[name] = def; + this.macros[name] = true; } }; +function makeASTNode(val) +{ + if (typeof val == 'number') { + return new intlit(val); + } + if (typeof val == 'string') { + return new stringlit(val); + } + if (val instanceof Array) { + return new arraylit(val); + } + if (val == tptrue) { + return new symbol('true'); + } + if (val == tpfalse) { + return new symbol('false'); + } + return val; +} + op.prototype.eval = function(env) { var l = this.left.eval(env); var r = this.right.eval(env); @@ -150,6 +190,12 @@ return ret; }; +op.prototype.macroexpand = function(env) { + this.left = this.left.macroexpand(env); + this.right = this.right.macroexpand(env); + return this; +}; + symbol.prototype.eval = function(env) { return env.find(this.name); }; @@ -161,24 +207,86 @@ return this.val; }; +symbol.prototype.macroexpand = + intlit.prototype.macroexpand = + floatlit.prototype.macroexpand = + strlit.prototype.macroexpand = + arraylit.prototype.macroexpand = + listlit.prototype.macroexpand = function(env) { + return this; +}; + funcall.prototype.eval = function(env) { var args = []; var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } + if (name == 'quote') { + if (this.receiver) { + return this.receiver; + } + if (this.args.length) { + return this.args[0]; + } + throw new Error('quote takes an argument'); + } + if (name == 'macro') { + return null; + } if (this.receiver) { args.push(this.receiver.eval(env)); } for (var i = 0; i < this.args.length; i++) { args.push(this.args[i].eval(env)); } + if (name == 'eval:else') { + try { + var res = args[0].eval(env); + } catch(e) { + return args[2].call(null); + } + return args[1].call(null, res); + } var fun = env.findNoTop(name); if (fun) { return fun.apply(null, args); } else { - return args[0]['tpmeth_'+name].apply(args[0], args.slice(1)); + //if (typeof args[0]'tpmeth_'+name in args[0]) { + //try { + return args[0]['tpmeth_'+name].apply(args[0], args.slice(1)); + /*} catch(e) { + throw new Error('Error, \n\t' + e.message.split('\n').join('\n\t') + '\ninvoking method ' + name + ' on object ' + args[0] + ' ' + JSON.stringify(Object.keys(args[0]))); + }*/ + /*} else {JSON.stringify + throw new Error('No method named ' + name + ' on object ' + JSON.stringify(args[0])); + }*/ + } +}; + +funcall.prototype.macroexpand = function(env) { + var name = this.name; + if (name[name.length-1] == ":") { + name = name.substr(0, name.length-1); } + var macro = env.findMacro(name); + if (this.receiver) { + this.receiver = this.receiver.macroexpand(env); + } + for (var i = 0; i < this.args.length; i++) { + this.args[i] = this.args[i].macroexpand(env); + } + if (!macro) { + return this; + } + var args = []; + if (this.receiver) { + args.push(this.receiver); + } + for (var i = 0; i < this.args.length; i++) { + args.push(this.args[i]); + } + return makeASTNode(macro.apply(null, args)); }; object.prototype.eval = function(parentenv) { @@ -216,6 +324,51 @@ return obj; }; +object.prototype.macroexpand = function(parentenv) { + var env = new environment(parentenv); + var obj = {env: env}; + var outmessages = []; + for (var i = 0; i < this.messages.length; i++) { + var msg = this.messages[i].macroexpand(env); + if (msg instanceof assignment) { + if (msg.expression instanceof lambda) { + (function(name, expr) { + env.syms[name] = function() { + if (!obj['tpmeth_' + name]) { + obj['tpmeth_' + name] = expr.eval(env); + } + return obj['tpmeth_' + name].apply(obj, arguments); + }; + })(msg.symbol.name, msg.expression); + outmessages.push(msg); + } else if (msg.expression instanceof funcall && msg.expression.name == 'macro:') { + env.defMacro(msg.symbol.name, msg.expression.args[0].eval(env)); + } else { + outmessages.push(msg); + /* + var makeProp = function(obj, name) { + obj['tprop_' + name] = msg.expression.eval(env); + name = 'tpmeth_' + name; + obj[name] = function() { + return this[name]; + }; + var setname = name+'!'; + obj[setname] = function(val) { + this[setname] = val; + return this; + }; + }; + makeProp(obj, msg.symbol.name);*/ + } + } else { + outmessages.push(msg); + //throw new Error('pseudo function calls in object definitions not implemented yet'); + } + } + this.messages = outmessages; + return this; +}; + lambda.prototype.eval = function(parentenv) { var args = this.args; var exprs = this.expressions; @@ -238,8 +391,34 @@ }; }; +lambda.prototype.macroexpand = function(parentenv) { + var env = new environment(parentenv); + for (var i = 0; i < this.args.length; i++) { + env.syms[this.args[i].cleanName()] = {}; + } + for (var i = 0; i < this.expressions.length; i++) { + var expr = this.expressions[i]; + if (expr instanceof assignment) { + if (expr.expression instanceof funcall && expr.expression.name == 'macro:') { + env.defMacro(expr.symbol.name, exp.expression.args[0].eval(env)); + } else { + env.syms[expr.symbol.cleanName()] = {}; + this.expressions[i] = expr.macroexpand(env); + } + } else { + this.expressions[i] = expr.macroexpand(env); + } + } + return this; +}; + assignment.prototype.eval = function(env) { var val = this.expression.eval(env); env.findSet(this.symbol.name, val); return val; }; + +assignment.prototype.macroexpand = function(env) { + this.expression = this.expression.macroexpand(env); + return this; +}; diff -r b4a9d4e405c5 -r 60eff5f81d9a tpc.js --- a/tpc.js Wed Oct 23 19:10:03 2013 -0700 +++ b/tpc.js Tue Nov 19 22:02:11 2013 -0800 @@ -92,6 +92,7 @@ PEG = module.exports; load(basedir + 'parser.js'); load(basedir + 'compiler.js'); + load(basedir + 'interp.js'); if (backend == 'C') { load(basedir + 'cbackend.js'); } else if (backend == 'IL') { @@ -104,6 +105,7 @@ if (debugmode) { debugprint = print; } + parsed = parsed.macroexpand(new topenv(includes)); toplevel = new topsymbols(includes); switch(backend) { diff -r b4a9d4e405c5 -r 60eff5f81d9a tpi.js --- a/tpi.js Wed Oct 23 19:10:03 2013 -0700 +++ b/tpi.js Tue Nov 19 22:02:11 2013 -0800 @@ -98,6 +98,7 @@ toplevel = new topenv(includes); + parsed = parsed.macroexpand(toplevel); var mainModule = parsed.eval(toplevel); return mainModule.tpmeth_main(); }