comparison interp.js @ 207:60eff5f81d9a

Basic implementation of macros is now working
author Mike Pavone <pavone@retrodev.com>
date Tue, 19 Nov 2013 22:02:11 -0800
parents b4a9d4e405c5
children a1b4a2bc8d72
comparison
equal deleted inserted replaced
206:b4a9d4e405c5 207:60eff5f81d9a
77 if (name in this.modules) { 77 if (name in this.modules) {
78 return this.modules[name]; 78 return this.modules[name];
79 } 79 }
80 if (name in this.names) { 80 if (name in this.names) {
81 var parsed = parseFile(this.names[name]); 81 var parsed = parseFile(this.names[name]);
82 this.modules[name] = parsed.eval(this); 82 this.modules[name] = parsed.macroexpand(this).eval(this);
83 return this.modules[name]; 83 return this.modules[name];
84 } 84 }
85 return null; 85 return null;
86 }, 86 },
87 findNoTop: function(name) { 87 findNoTop: function(name) {
88 return null; 88 return null;
89 }, 89 },
90 findSetPresent: function(name, val) { 90 findSetPresent: function(name, val) {
91 return false; 91 return false;
92 } 92 },
93 findMacro: function(name) {
94 return null;
95 },
93 } 96 }
94 97
95 function environment(parent) 98 function environment(parent)
96 { 99 {
97 this.parent = parent; 100 this.parent = parent;
98 this.syms = {}; 101 this.syms = {};
102 this.macros = {};
99 } 103 }
100 104
101 environment.prototype = { 105 environment.prototype = {
102 find: function(name) { 106 find: function(name) {
103 if (name in this.syms) { 107 if (name in this.syms) {
131 } 135 }
132 if (this.parent) { 136 if (this.parent) {
133 return this.parent.findSetPresent(name, val); 137 return this.parent.findSetPresent(name, val);
134 } 138 }
135 return false; 139 return false;
136 } 140 },
137 }; 141 findMacro: function(name) {
142 if (name in this.syms) {
143 if (name in this.macros) {
144 return this.syms[name];
145 }
146 return null;
147 }
148 if (this.parent) {
149 return this.parent.findMacro(name);
150 }
151 return null;
152 },
153 defMacro: function(name, def) {
154 this.syms[name] = def;
155 this.macros[name] = true;
156 }
157 };
158
159 function makeASTNode(val)
160 {
161 if (typeof val == 'number') {
162 return new intlit(val);
163 }
164 if (typeof val == 'string') {
165 return new stringlit(val);
166 }
167 if (val instanceof Array) {
168 return new arraylit(val);
169 }
170 if (val == tptrue) {
171 return new symbol('true');
172 }
173 if (val == tpfalse) {
174 return new symbol('false');
175 }
176 return val;
177 }
138 178
139 op.prototype.eval = function(env) { 179 op.prototype.eval = function(env) {
140 var l = this.left.eval(env); 180 var l = this.left.eval(env);
141 var r = this.right.eval(env); 181 var r = this.right.eval(env);
142 var name = this.op; 182 var name = this.op;
148 ret = l['tpmeth_'+name](r); 188 ret = l['tpmeth_'+name](r);
149 } 189 }
150 return ret; 190 return ret;
151 }; 191 };
152 192
193 op.prototype.macroexpand = function(env) {
194 this.left = this.left.macroexpand(env);
195 this.right = this.right.macroexpand(env);
196 return this;
197 };
198
153 symbol.prototype.eval = function(env) { 199 symbol.prototype.eval = function(env) {
154 return env.find(this.name); 200 return env.find(this.name);
155 }; 201 };
156 202
157 intlit.prototype.eval = 203 intlit.prototype.eval =
159 strlit.prototype.eval = 205 strlit.prototype.eval =
160 arraylit.prototype.eval = function(env) { 206 arraylit.prototype.eval = function(env) {
161 return this.val; 207 return this.val;
162 }; 208 };
163 209
210 symbol.prototype.macroexpand =
211 intlit.prototype.macroexpand =
212 floatlit.prototype.macroexpand =
213 strlit.prototype.macroexpand =
214 arraylit.prototype.macroexpand =
215 listlit.prototype.macroexpand = function(env) {
216 return this;
217 };
218
164 funcall.prototype.eval = function(env) { 219 funcall.prototype.eval = function(env) {
165 var args = []; 220 var args = [];
166 var name = this.name; 221 var name = this.name;
167 if (name[name.length-1] == ":") { 222 if (name[name.length-1] == ":") {
168 name = name.substr(0, name.length-1); 223 name = name.substr(0, name.length-1);
169 } 224 }
225 if (name == 'quote') {
226 if (this.receiver) {
227 return this.receiver;
228 }
229 if (this.args.length) {
230 return this.args[0];
231 }
232 throw new Error('quote takes an argument');
233 }
234 if (name == 'macro') {
235 return null;
236 }
170 if (this.receiver) { 237 if (this.receiver) {
171 args.push(this.receiver.eval(env)); 238 args.push(this.receiver.eval(env));
172 } 239 }
173 for (var i = 0; i < this.args.length; i++) { 240 for (var i = 0; i < this.args.length; i++) {
174 args.push(this.args[i].eval(env)); 241 args.push(this.args[i].eval(env));
242 }
243 if (name == 'eval:else') {
244 try {
245 var res = args[0].eval(env);
246 } catch(e) {
247 return args[2].call(null);
248 }
249 return args[1].call(null, res);
175 } 250 }
176 var fun = env.findNoTop(name); 251 var fun = env.findNoTop(name);
177 if (fun) { 252 if (fun) {
178 return fun.apply(null, args); 253 return fun.apply(null, args);
179 } else { 254 } else {
180 return args[0]['tpmeth_'+name].apply(args[0], args.slice(1)); 255 //if (typeof args[0]'tpmeth_'+name in args[0]) {
181 } 256 //try {
257 return args[0]['tpmeth_'+name].apply(args[0], args.slice(1));
258 /*} catch(e) {
259 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])));
260 }*/
261 /*} else {JSON.stringify
262 throw new Error('No method named ' + name + ' on object ' + JSON.stringify(args[0]));
263 }*/
264 }
265 };
266
267 funcall.prototype.macroexpand = function(env) {
268 var name = this.name;
269 if (name[name.length-1] == ":") {
270 name = name.substr(0, name.length-1);
271 }
272 var macro = env.findMacro(name);
273 if (this.receiver) {
274 this.receiver = this.receiver.macroexpand(env);
275 }
276 for (var i = 0; i < this.args.length; i++) {
277 this.args[i] = this.args[i].macroexpand(env);
278 }
279 if (!macro) {
280 return this;
281 }
282 var args = [];
283 if (this.receiver) {
284 args.push(this.receiver);
285 }
286 for (var i = 0; i < this.args.length; i++) {
287 args.push(this.args[i]);
288 }
289 return makeASTNode(macro.apply(null, args));
182 }; 290 };
183 291
184 object.prototype.eval = function(parentenv) { 292 object.prototype.eval = function(parentenv) {
185 var env = new environment(parentenv); 293 var env = new environment(parentenv);
186 var obj = {env: env}; 294 var obj = {env: env};
214 } 322 }
215 } 323 }
216 return obj; 324 return obj;
217 }; 325 };
218 326
327 object.prototype.macroexpand = function(parentenv) {
328 var env = new environment(parentenv);
329 var obj = {env: env};
330 var outmessages = [];
331 for (var i = 0; i < this.messages.length; i++) {
332 var msg = this.messages[i].macroexpand(env);
333 if (msg instanceof assignment) {
334 if (msg.expression instanceof lambda) {
335 (function(name, expr) {
336 env.syms[name] = function() {
337 if (!obj['tpmeth_' + name]) {
338 obj['tpmeth_' + name] = expr.eval(env);
339 }
340 return obj['tpmeth_' + name].apply(obj, arguments);
341 };
342 })(msg.symbol.name, msg.expression);
343 outmessages.push(msg);
344 } else if (msg.expression instanceof funcall && msg.expression.name == 'macro:') {
345 env.defMacro(msg.symbol.name, msg.expression.args[0].eval(env));
346 } else {
347 outmessages.push(msg);
348 /*
349 var makeProp = function(obj, name) {
350 obj['tprop_' + name] = msg.expression.eval(env);
351 name = 'tpmeth_' + name;
352 obj[name] = function() {
353 return this[name];
354 };
355 var setname = name+'!';
356 obj[setname] = function(val) {
357 this[setname] = val;
358 return this;
359 };
360 };
361 makeProp(obj, msg.symbol.name);*/
362 }
363 } else {
364 outmessages.push(msg);
365 //throw new Error('pseudo function calls in object definitions not implemented yet');
366 }
367 }
368 this.messages = outmessages;
369 return this;
370 };
371
219 lambda.prototype.eval = function(parentenv) { 372 lambda.prototype.eval = function(parentenv) {
220 var args = this.args; 373 var args = this.args;
221 var exprs = this.expressions; 374 var exprs = this.expressions;
222 return function() { 375 return function() {
223 var env = new environment(parentenv); 376 var env = new environment(parentenv);
236 } 389 }
237 return res; 390 return res;
238 }; 391 };
239 }; 392 };
240 393
394 lambda.prototype.macroexpand = function(parentenv) {
395 var env = new environment(parentenv);
396 for (var i = 0; i < this.args.length; i++) {
397 env.syms[this.args[i].cleanName()] = {};
398 }
399 for (var i = 0; i < this.expressions.length; i++) {
400 var expr = this.expressions[i];
401 if (expr instanceof assignment) {
402 if (expr.expression instanceof funcall && expr.expression.name == 'macro:') {
403 env.defMacro(expr.symbol.name, exp.expression.args[0].eval(env));
404 } else {
405 env.syms[expr.symbol.cleanName()] = {};
406 this.expressions[i] = expr.macroexpand(env);
407 }
408 } else {
409 this.expressions[i] = expr.macroexpand(env);
410 }
411 }
412 return this;
413 };
414
241 assignment.prototype.eval = function(env) { 415 assignment.prototype.eval = function(env) {
242 var val = this.expression.eval(env); 416 var val = this.expression.eval(env);
243 env.findSet(this.symbol.name, val); 417 env.findSet(this.symbol.name, val);
244 return val; 418 return val;
245 }; 419 };
420
421 assignment.prototype.macroexpand = function(env) {
422 this.expression = this.expression.macroexpand(env);
423 return this;
424 };