Mercurial > repos > tabletprog
comparison interp.js @ 217:adad61ea2f3a
Switched to a less hacky implementation of hygiene and exposed more AST properties to macros
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 21 Dec 2013 12:07:51 -0800 |
parents | e00a8bc6361b |
children | b70be565d54c |
comparison
equal
deleted
inserted
replaced
216:2dac67e9d18b | 217:adad61ea2f3a |
---|---|
117 if (name in this.modules) { | 117 if (name in this.modules) { |
118 return this.modules[name]; | 118 return this.modules[name]; |
119 } | 119 } |
120 if (name in this.names) { | 120 if (name in this.names) { |
121 var parsed = parseFile(this.names[name]); | 121 var parsed = parseFile(this.names[name]); |
122 this.modules[name] = parsed.macroexpand(this).eval(this); | 122 var ret = parsed.macroexpand(this).eval(this); |
123 return this.modules[name]; | 123 if (typeof ret == 'function') { |
124 ret = ret(); | |
125 } | |
126 this.modules[name] = ret; | |
127 return ret; | |
124 } | 128 } |
125 return null; | 129 return null; |
126 }, | 130 }, |
127 findNoTop: function(name) { | 131 findNoTop: function(name) { |
128 return null; | 132 return null; |
244 } else { | 248 } else { |
245 if (name == '&&') { | 249 if (name == '&&') { |
246 name = 'if' | 250 name = 'if' |
247 } else if (name == '||') { | 251 } else if (name == '||') { |
248 name = 'ifnot' | 252 name = 'ifnot' |
253 } else if (name == '|') { | |
254 return r['tpmeth_|'](l); | |
249 } | 255 } |
250 ret = l['tpmeth_'+name](r); | 256 ret = l['tpmeth_'+name](r); |
251 } | 257 } |
252 return ret; | 258 return ret; |
253 }; | 259 }; |
262 var left = this.left.quote(env); | 268 var left = this.left.quote(env); |
263 var right = this.right.quote(env); | 269 var right = this.right.quote(env); |
264 return new op(left, this.op, right); | 270 return new op(left, this.op, right); |
265 }; | 271 }; |
266 | 272 |
273 op.prototype.makeHygienic = function(env) { | |
274 var left = this.left.makeHygienic(env); | |
275 var right = this.right.makeHygienic(env); | |
276 return new op(left, this.op, right); | |
277 }; | |
278 | |
267 op.prototype.tpmeth_nodeType = function() { | 279 op.prototype.tpmeth_nodeType = function() { |
268 return "op"; | 280 return "op"; |
269 }; | 281 }; |
270 | 282 |
271 op.prototype.tpmeth_left = function() { | 283 op.prototype.tpmeth_left = function() { |
292 | 304 |
293 symbol.prototype.quote = function(env) { | 305 symbol.prototype.quote = function(env) { |
294 var val = env.find(this.name); | 306 var val = env.find(this.name); |
295 if (val) { | 307 if (val) { |
296 var newnode = makeASTNode(val); | 308 var newnode = makeASTNode(val); |
297 if (!(newnode instanceof symbol)) { | |
298 if ('quote' in newnode) { | |
299 newnode = newnode.quote(env); | |
300 } else { | |
301 throw new Error('Symbol ' + this.name + ' is not bound to a valid AST value, instead it is bound to an object with keys ' + JSON.stringify(Object.keys(newnode))); | |
302 } | |
303 } | |
304 return newnode; | 309 return newnode; |
305 } else { | 310 } else { |
306 var hygenic = env.findQuoteTrans(this.name); | 311 this.dirty = true; |
307 return hygenic ? new symbol(hygenic, this.symbols) : this; | 312 return this; |
313 } | |
314 }; | |
315 | |
316 symbol.prototype.makeHygienic = function(env) { | |
317 if (this.dirty) { | |
318 var hygenic = env.findQuoteTrans(this.cleanName()); | |
319 if (hygenic) | |
320 { | |
321 return new symbol(hygenic, this.symbols); | |
322 } else { | |
323 return this; | |
324 } | |
325 } else { | |
326 return this; | |
308 } | 327 } |
309 }; | 328 }; |
310 | 329 |
311 symbol.prototype.tpmeth_nodeType = function() { | 330 symbol.prototype.tpmeth_nodeType = function() { |
312 return "symbol"; | 331 return "symbol"; |
313 }; | 332 }; |
314 | 333 |
315 symbol.prototype.tpmeth_name = function() { | 334 symbol.prototype.tpmeth_name = function() { |
316 return this.cleanName(); | 335 return this.cleanName(); |
336 }; | |
337 symbol.prototype['tpmeth_name!'] = function(val) { | |
338 this.name = val; | |
339 return this; | |
317 }; | 340 }; |
318 | 341 |
319 intlit.prototype.eval = | 342 intlit.prototype.eval = |
320 floatlit.prototype.eval = | 343 floatlit.prototype.eval = |
321 strlit.prototype.eval = | 344 strlit.prototype.eval = |
322 arraylit.prototype.eval = function(env) { | 345 arraylit.prototype.eval = function(env) { |
323 return this.val; | 346 return this.val; |
324 }; | 347 }; |
325 | 348 |
349 listlit.prototype.eval = function(env) { | |
350 var cur = tplist.tpmeth_empty(); | |
351 for (var idx = this.val.length - 1; idx >= 0; --idx) { | |
352 cur = tplist['tpmeth_node:withTail'](this.val[idx].eval(env), cur); | |
353 } | |
354 return cur; | |
355 }; | |
356 | |
326 symbol.prototype.macroexpand = | 357 symbol.prototype.macroexpand = |
327 intlit.prototype.macroexpand = | 358 intlit.prototype.macroexpand = |
328 floatlit.prototype.macroexpand = | 359 floatlit.prototype.macroexpand = |
329 strlit.prototype.macroexpand = function(env) { | 360 strlit.prototype.macroexpand = function(env) { |
330 return this; | 361 return this; |
342 intlit.prototype.quote = | 373 intlit.prototype.quote = |
343 floatlit.prototype.quote = | 374 floatlit.prototype.quote = |
344 strlit.prototype.quote = | 375 strlit.prototype.quote = |
345 arraylit.prototype.quote = | 376 arraylit.prototype.quote = |
346 listlit.prototype.quote = function(env) { | 377 listlit.prototype.quote = function(env) { |
378 return this; | |
379 }; | |
380 | |
381 intlit.prototype.makeHygienic = | |
382 floatlit.prototype.makeHygienic = | |
383 strlit.prototype.makeHygienic = | |
384 arraylit.prototype.makeHygienic = | |
385 listlit.prototype.makeHygienic = function(env) { | |
347 return this; | 386 return this; |
348 }; | 387 }; |
349 | 388 |
350 intlit.prototype.tpmeth_nodeType = function() { | 389 intlit.prototype.tpmeth_nodeType = function() { |
351 return "intlit"; | 390 return "intlit"; |
455 args.push(this.receiver); | 494 args.push(this.receiver); |
456 } | 495 } |
457 for (var i = 0; i < this.args.length; i++) { | 496 for (var i = 0; i < this.args.length; i++) { |
458 args.push(this.args[i]); | 497 args.push(this.args[i]); |
459 } | 498 } |
460 return makeASTNode(macro.apply(null, args)); | 499 var ret = makeASTNode(macro.apply(null, args)); |
500 ret = ret.makeHygienic(env); | |
501 quote_prefix++; | |
502 return ret; | |
461 }; | 503 }; |
462 | 504 |
463 funcall.prototype.quote = function(env) { | 505 funcall.prototype.quote = function(env) { |
464 var receiver = this.receiver ? this.receiver.quote(env) : null; | 506 var receiver = this.receiver ? this.receiver.quote(env) : null; |
465 var args = []; | 507 var args = []; |
477 name = fun.cleanName(); | 519 name = fun.cleanName(); |
478 } else if (fun instanceof lambda) { | 520 } else if (fun instanceof lambda) { |
479 throw new Error('FIXME'); | 521 throw new Error('FIXME'); |
480 } | 522 } |
481 } else { | 523 } else { |
482 var hygenic = env.findQuoteTrans(this.name); | 524 this.dirty = true; |
483 if (hygenic) { | |
484 name = hygenic; | |
485 } | |
486 } | 525 } |
487 var ret = new funcall(name, args); | 526 var ret = new funcall(name, args); |
488 ret.receiver = receiver; | 527 ret.receiver = receiver; |
489 return ret; | 528 return ret; |
490 }; | 529 }; |
491 | 530 |
531 funcall.prototype.makeHygienic = function(env) { | |
532 var receiver = this.receiver ? this.receiver.makeHygienic(env) : null; | |
533 var args = []; | |
534 for (var i = 0; i < this.args.length; i++) { | |
535 args.push(this.args[i].makeHygienic(env)); | |
536 } | |
537 var name = this.name; | |
538 if (name[name.length-1] == ":") { | |
539 name = name.substr(0, name.length-1); | |
540 } | |
541 var hygienic = env.findQuoteTrans(name); | |
542 if (hygienic) { | |
543 name = hygienic; | |
544 } | |
545 var ret = new funcall(name, args); | |
546 ret.receiver = receiver; | |
547 return ret; | |
548 }; | |
549 | |
492 funcall.prototype.tpmeth_nodeType = function() { | 550 funcall.prototype.tpmeth_nodeType = function() { |
493 return "funcall"; | 551 return "funcall"; |
552 }; | |
553 | |
554 funcall.prototype['tpmeth_args!'] = function(arglist) { | |
555 this.args = []; | |
556 //TODO: Finish this | |
494 }; | 557 }; |
495 | 558 |
496 object.prototype.eval = function(parentenv) { | 559 object.prototype.eval = function(parentenv) { |
497 var env = new environment(parentenv); | 560 var env = new environment(parentenv); |
498 var obj = {env: env}; | 561 var obj = {env: env}; |
579 var env = new environment(parentenv); | 642 var env = new environment(parentenv); |
580 var outmessages = []; | 643 var outmessages = []; |
581 for (var i = 0; i < this.messages.length; i++) { | 644 for (var i = 0; i < this.messages.length; i++) { |
582 var msg = this.messages[i]; | 645 var msg = this.messages[i]; |
583 if (msg instanceof assignment) { | 646 if (msg instanceof assignment) { |
584 //Make sure method names don't get renamed for hygene | |
585 env.syms[msg.symbol.name] = null; | 647 env.syms[msg.symbol.name] = null; |
586 env.quotetrans[msg.symbol.name] = msg.symbol.name; | 648 if (!(msg.expression instanceof lambda)) { |
587 if (msg.expression instanceof lambda) { | |
588 env.syms[msg.symbol.name + '!'] = null; | 649 env.syms[msg.symbol.name + '!'] = null; |
589 env.quotetrans[msg.symbol.name + '!'] = msg.symbol.name + '!'; | |
590 } | 650 } |
591 } | 651 } |
592 } | 652 } |
593 for (var i = 0; i < this.messages.length; i++) { | 653 for (var i = 0; i < this.messages.length; i++) { |
594 var msg = this.messages[i]; | 654 var msg = this.messages[i]; |
599 } | 659 } |
600 } | 660 } |
601 return new object(outmessages); | 661 return new object(outmessages); |
602 }; | 662 }; |
603 | 663 |
664 object.prototype.makeHygienic = function(parentenv) { | |
665 var env = new environment(parentenv); | |
666 var outmessages = []; | |
667 for (var i = 0; i < this.messages.length; i++) { | |
668 var msg = this.messages[i]; | |
669 if (msg instanceof assignment) { | |
670 env.syms[msg.symbol.name] = null; | |
671 //make sure method names don't get translated for hygiene | |
672 env.quotetrans[msg.symbol.name] = null; | |
673 if (!(msg.expression instanceof lambda)) { | |
674 env.syms[msg.symbol.name + '!'] = null; | |
675 env.quotetrans[msg.symbol.name + '!'] = null; | |
676 } | |
677 } | |
678 } | |
679 for (var i = 0; i < this.messages.length; i++) { | |
680 var msg = this.messages[i]; | |
681 if (msg instanceof assignment) { | |
682 outmessages.push(new assignment(msg.symbol, msg.expression.makeHygienic(env))); | |
683 } else { | |
684 outmessages.push(msg.makeHygienic(env)); | |
685 } | |
686 } | |
687 return new object(outmessages); | |
688 }; | |
689 | |
604 object.prototype.tpmeth_nodeType = function() { | 690 object.prototype.tpmeth_nodeType = function() { |
605 return "object"; | 691 return "object"; |
692 }; | |
693 | |
694 object.prototype.tpmeth_addMessage = function(message) { | |
695 this.messages.push(makeASTNode(message)); | |
696 return this; | |
606 }; | 697 }; |
607 | 698 |
608 lambda.prototype.eval = function(parentenv) { | 699 lambda.prototype.eval = function(parentenv) { |
609 var args = this.args; | 700 var args = this.args; |
610 var exprs = this.expressions; | 701 var exprs = this.expressions; |
652 var args = []; | 743 var args = []; |
653 var expressions = []; | 744 var expressions = []; |
654 var env = new environment(parentenv); | 745 var env = new environment(parentenv); |
655 for (var i = 0; i < this.args.length; i++) { | 746 for (var i = 0; i < this.args.length; i++) { |
656 env.syms[this.args[i].cleanName()] = null; | 747 env.syms[this.args[i].cleanName()] = null; |
657 var hygenic = '' + quote_prefix + this.args[i].cleanName(); | 748 var sym = new symbol(this.args[i].name, this.args[i].symbols); |
658 env.quotetrans[this.args[i].cleanName()] = hygenic; | 749 sym.dirty = true; |
659 args.push(new symbol(hygenic, this.args[i].symbols)); | 750 args.push(sym); |
660 } | 751 } |
661 for (var i = 0; i < this.expressions.length; i++) { | 752 for (var i = 0; i < this.expressions.length; i++) { |
662 expressions.push(this.expressions[i].quote(env)); | 753 expressions.push(this.expressions[i].quote(env)); |
754 } | |
755 return new lambda(args, expressions); | |
756 }; | |
757 | |
758 lambda.prototype.makeHygienic = function(parentenv) { | |
759 var args = []; | |
760 var expressions = []; | |
761 var env = new environment(parentenv); | |
762 for (var i = 0; i < this.args.length; i++) { | |
763 env.syms[this.args[i].cleanName()] = null; | |
764 if (this.args[i].dirty) { | |
765 var hygienic = '' + quote_prefix + this.args[i].cleanName(); | |
766 env.quotetrans[this.args[i].cleanName()] = hygienic; | |
767 args.push(new symbol(hygienic, this.args[i].symbols)); | |
768 } else { | |
769 args.push(this.args[i]); | |
770 } | |
771 } | |
772 for (var i = 0; i < this.expressions.length; i++) { | |
773 expressions.push(this.expressions[i].makeHygienic(env)); | |
663 } | 774 } |
664 return new lambda(args, expressions); | 775 return new lambda(args, expressions); |
665 }; | 776 }; |
666 | 777 |
667 lambda.prototype.numArgs = function() { | 778 lambda.prototype.numArgs = function() { |
674 | 785 |
675 lambda.prototype.tpmeth_nodeType = function() { | 786 lambda.prototype.tpmeth_nodeType = function() { |
676 return "lambda"; | 787 return "lambda"; |
677 }; | 788 }; |
678 | 789 |
790 lambda.prototype.tpmeth_args = function() { | |
791 var cur = tplist.tpmeth_empty(); | |
792 for (var idx = this.args.length - 1; idx >= 0; --idx) { | |
793 cur = tplist['tpmeth_node:withTail'](this.args[idx], cur); | |
794 } | |
795 return cur; | |
796 }; | |
797 | |
798 lambda.prototype.tpmeth_expressions = function() { | |
799 var cur = tplist.tpmeth_empty(); | |
800 for (var idx = this.expressions.length - 1; idx >= 0; --idx) { | |
801 cur = tplist['tpmeth_node:withTail'](this.expressions[idx], cur); | |
802 } | |
803 return cur; | |
804 }; | |
805 | |
806 lambda.prototype['tpmeth_expressions!'] = function(exprs) { | |
807 var expressions = []; | |
808 while (!exprs['tpmeth_empty?']().valueOf()) { | |
809 expressions.push(makeASTNode(exprs.tpmeth_value())); | |
810 exprs = exprs.tpmeth_tail(); | |
811 } | |
812 this.expressions = expressions; | |
813 return this; | |
814 }; | |
815 | |
816 lambda.prototype.tpmeth_addExpression = function(expr) { | |
817 this.expressions.push(makeASTNode(expr)); | |
818 return this; | |
819 }; | |
820 | |
679 assignment.prototype.eval = function(env) { | 821 assignment.prototype.eval = function(env) { |
680 var val = this.expression.eval(env); | 822 var val = this.expression.eval(env); |
681 env.findSet(this.symbol.name, val); | 823 env.findSet(this.symbol.name, val); |
682 return val; | 824 return val; |
683 }; | 825 }; |
687 return this; | 829 return this; |
688 }; | 830 }; |
689 | 831 |
690 assignment.prototype.quote = function(env) { | 832 assignment.prototype.quote = function(env) { |
691 var name = this.symbol.cleanName(); | 833 var name = this.symbol.cleanName(); |
834 var val = env.find(name); | |
835 var dirty = true; | |
836 if (val) { | |
837 var node = makeASTNode(val); | |
838 if (!(node instanceof symbol)) { | |
839 throw new Error('Left hand side of assignment expression must be a symbol!'); | |
840 } | |
841 name = node.cleanName(); | |
842 dirty = node.dirty; | |
843 } | |
692 env.syms[name] = null; | 844 env.syms[name] = null; |
693 var hygenic = '' + quote_prefix + name; | 845 var sym = new symbol(name, this.symbol.symbols); |
694 env.quotetrans[name] = hygenic; | 846 sym.dirty = dirty; |
695 return new assignment(new symbol(hygenic, this.symbol.symbols), this.expression.quote(env)); | 847 return new assignment(sym, this.expression.quote(env)); |
848 }; | |
849 | |
850 assignment.prototype.makeHygienic = function(env) { | |
851 var name = this.symbol.cleanName(); | |
852 env.syms[name] = null; | |
853 if (this.symbol.dirty) { | |
854 name = env.quotetrans[name] = '' + quote_prefix + name; | |
855 } | |
856 return new assignment(new symbol(name, this.symbol.symbols), this.expression.makeHygienic(env)); | |
696 }; | 857 }; |
697 | 858 |
698 assignment.prototype.tpmeth_nodeType = function() { | 859 assignment.prototype.tpmeth_nodeType = function() { |
699 return "assignment"; | 860 return "assignment"; |
700 }; | 861 }; |
862 | |
863 assignment.prototype.tpmeth_symbol = function() { | |
864 return this.symbol; | |
865 }; | |
866 | |
867 assignment.prototype['tpmeth_symbol!'] = function(val) { | |
868 this.symbol = makeASTNode(val); | |
869 return this; | |
870 }; | |
871 | |
872 assignment.prototype.tpmeth_expression = function() { | |
873 return this.expression; | |
874 }; | |
875 | |
876 assignment.prototype['tpmeth_expression!'] = function(val) { | |
877 this.expression = makeASTNode(val); | |
878 return this; | |
879 }; |