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 };