Mercurial > repos > tabletprog
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 }; |