Bläddra i källkod

new interpreter passes the tests

Nicolas Petton 11 år sedan
förälder
incheckning
5c06010269

+ 105 - 33
js/Compiler-AST.deploy.js

@@ -27,61 +27,45 @@ smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "extent",
+selector: "isAssignmentNode",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-$2=self._nextNode();
-if(($receiver = $2) == nil || $receiver == undefined){
-$3=self._parent();
-if(($receiver = $3) == nil || $receiver == undefined){
-$1=$3;
-} else {
-var node;
-node=$receiver;
-$1=_st(node)._extent();
-};
-} else {
-var node;
-node=$receiver;
-$1=_st(node)._position();
-};
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"extent",{},smalltalk.Node)})},
-messageSends: ["ifNil:ifNotNil:", "ifNotNil:", "extent", "parent", "position", "nextNode"]}),
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isAssignmentNode",{},smalltalk.Node)})},
+messageSends: []}),
 smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "isAssignmentNode",
+selector: "isBlockNode",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return false;
-}, function($ctx1) {$ctx1.fill(self,"isAssignmentNode",{},smalltalk.Node)})},
+}, function($ctx1) {$ctx1.fill(self,"isBlockNode",{},smalltalk.Node)})},
 messageSends: []}),
 smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "isBlockNode",
+selector: "isBlockSequenceNode",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return false;
-}, function($ctx1) {$ctx1.fill(self,"isBlockNode",{},smalltalk.Node)})},
+}, function($ctx1) {$ctx1.fill(self,"isBlockSequenceNode",{},smalltalk.Node)})},
 messageSends: []}),
 smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "isBlockSequenceNode",
+selector: "isCascadeNode",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return false;
-}, function($ctx1) {$ctx1.fill(self,"isBlockSequenceNode",{},smalltalk.Node)})},
+}, function($ctx1) {$ctx1.fill(self,"isCascadeNode",{},smalltalk.Node)})},
 messageSends: []}),
 smalltalk.Node);
 
@@ -107,6 +91,19 @@ return false;
 messageSends: []}),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isLastChild",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(self._parent())._nodes())._last()).__eq(self);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isLastChild",{},smalltalk.Node)})},
+messageSends: ["=", "last", "nodes", "parent"]}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isNode",
@@ -307,6 +304,21 @@ return self}, function($ctx1) {$ctx1.fill(self,"position:",{aPosition:aPosition}
 messageSends: []}),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "postCopy",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.Object.fn.prototype._postCopy.apply(_st(self), []);
+_st(self._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._parent_(self);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"postCopy",{},smalltalk.Node)})},
+messageSends: ["postCopy", "do:", "nodes", "parent:"]}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "shouldBeAliased",
@@ -519,6 +531,19 @@ return true;
 messageSends: []}),
 smalltalk.BlockNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextChild",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextChild",{},smalltalk.BlockNode)})},
+messageSends: []}),
+smalltalk.BlockNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nextNode:",
@@ -619,6 +644,17 @@ return $1;
 messageSends: ["visitCascadeNode:"]}),
 smalltalk.CascadeNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isCascadeNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isCascadeNode",{},smalltalk.CascadeNode)})},
+messageSends: []}),
+smalltalk.CascadeNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "receiver",
@@ -1097,6 +1133,19 @@ return self}, function($ctx1) {$ctx1.fill(self,"index:",{anInteger:anInteger},sm
 messageSends: []}),
 smalltalk.SendNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isCascadeSendNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._parent())._isCascadeNode();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isCascadeSendNode",{},smalltalk.SendNode)})},
+messageSends: ["isCascadeNode", "parent"]}),
+smalltalk.SendNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isSendNode",
@@ -1115,14 +1164,21 @@ fn: function (){
 var self=this;
 function $Array(){return smalltalk.Array||(typeof Array=="undefined"?nil:Array)}
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-$2=_st($Array())._withAll_(self._arguments());
-_st($2)._add_(self._receiver());
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
+var $1,$2,$4,$5,$3;
+$1=self._receiver();
+if(($receiver = $1) == nil || $receiver == undefined){
+$2=_st(self._arguments())._copy();
+return $2;
+} else {
+$1;
+};
+$4=_st($Array())._with_(self._receiver());
+_st($4)._addAll_(self._arguments());
+$5=_st($4)._yourself();
+$3=$5;
+return $3;
 }, function($ctx1) {$ctx1.fill(self,"nodes",{},smalltalk.SendNode)})},
-messageSends: ["add:", "receiver", "withAll:", "arguments", "yourself"]}),
+messageSends: ["ifNil:", "receiver", "copy", "arguments", "addAll:", "with:", "yourself"]}),
 smalltalk.SendNode);
 
 smalltalk.addMethod(
@@ -1424,6 +1480,22 @@ return self}, function($ctx1) {$ctx1.fill(self,"value:",{anObject:anObject},smal
 messageSends: []}),
 smalltalk.ValueNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "xxxDoIt",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._stack();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"xxxDoIt",{},smalltalk.ValueNode)})},
+messageSends: ["value", "stack"]}),
+smalltalk.ValueNode);
+
 
 
 smalltalk.addClass('VariableNode', smalltalk.ValueNode, ['assigned', 'binding'], 'Compiler-AST');

+ 141 - 39
js/Compiler-AST.js

@@ -38,47 +38,31 @@ smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "extent",
-category: 'accessing',
+selector: "isAssignmentNode",
+category: 'testing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-$2=self._nextNode();
-if(($receiver = $2) == nil || $receiver == undefined){
-$3=self._parent();
-if(($receiver = $3) == nil || $receiver == undefined){
-$1=$3;
-} else {
-var node;
-node=$receiver;
-$1=_st(node)._extent();
-};
-} else {
-var node;
-node=$receiver;
-$1=_st(node)._position();
-};
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"extent",{},smalltalk.Node)})},
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isAssignmentNode",{},smalltalk.Node)})},
 args: [],
-source: "extent\x0a\x09\x22Answer the line and column of the end position of the receiver in the source code\x22\x0a\x09\x0a\x09^ self nextNode \x0a\x09\x09ifNil: [ self parent ifNotNil: [ :node | node extent ] ]\x0a\x09\x09ifNotNil: [ :node | node position ]",
-messageSends: ["ifNil:ifNotNil:", "ifNotNil:", "extent", "parent", "position", "nextNode"],
+source: "isAssignmentNode\x0a\x09^ false",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "isAssignmentNode",
+selector: "isBlockNode",
 category: 'testing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return false;
-}, function($ctx1) {$ctx1.fill(self,"isAssignmentNode",{},smalltalk.Node)})},
+}, function($ctx1) {$ctx1.fill(self,"isBlockNode",{},smalltalk.Node)})},
 args: [],
-source: "isAssignmentNode\x0a\x09^ false",
+source: "isBlockNode\x0a\x09^false",
 messageSends: [],
 referencedClasses: []
 }),
@@ -86,15 +70,15 @@ smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "isBlockNode",
+selector: "isBlockSequenceNode",
 category: 'testing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return false;
-}, function($ctx1) {$ctx1.fill(self,"isBlockNode",{},smalltalk.Node)})},
+}, function($ctx1) {$ctx1.fill(self,"isBlockSequenceNode",{},smalltalk.Node)})},
 args: [],
-source: "isBlockNode\x0a\x09^false",
+source: "isBlockSequenceNode\x0a\x09^false",
 messageSends: [],
 referencedClasses: []
 }),
@@ -102,15 +86,15 @@ smalltalk.Node);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "isBlockSequenceNode",
+selector: "isCascadeNode",
 category: 'testing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return false;
-}, function($ctx1) {$ctx1.fill(self,"isBlockSequenceNode",{},smalltalk.Node)})},
+}, function($ctx1) {$ctx1.fill(self,"isCascadeNode",{},smalltalk.Node)})},
 args: [],
-source: "isBlockSequenceNode\x0a\x09^false",
+source: "isCascadeNode\x0a\x09^ false",
 messageSends: [],
 referencedClasses: []
 }),
@@ -148,6 +132,24 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isLastChild",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(self._parent())._nodes())._last()).__eq(self);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isLastChild",{},smalltalk.Node)})},
+args: [],
+source: "isLastChild\x0a\x09^ self parent nodes last = self",
+messageSends: ["=", "last", "nodes", "parent"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isNode",
@@ -413,6 +415,26 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "postCopy",
+category: 'copying',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.Object.fn.prototype._postCopy.apply(_st(self), []);
+_st(self._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._parent_(self);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"postCopy",{},smalltalk.Node)})},
+args: [],
+source: "postCopy\x0a\x09super postCopy.\x0a\x09self nodes do: [ :each | each parent: self ]",
+messageSends: ["postCopy", "do:", "nodes", "parent:"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "shouldBeAliased",
@@ -702,6 +724,24 @@ referencedClasses: []
 }),
 smalltalk.BlockNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextChild",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextChild",{},smalltalk.BlockNode)})},
+args: [],
+source: "nextChild\x0a\x09\x22Answer the receiver as we want to avoid eager evaluation\x22\x0a\x09\x0a\x09^ self",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.BlockNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nextNode:",
@@ -838,6 +878,22 @@ referencedClasses: []
 }),
 smalltalk.CascadeNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isCascadeNode",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isCascadeNode",{},smalltalk.CascadeNode)})},
+args: [],
+source: "isCascadeNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.CascadeNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "receiver",
@@ -1497,6 +1553,24 @@ referencedClasses: []
 }),
 smalltalk.SendNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isCascadeSendNode",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._parent())._isCascadeNode();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"isCascadeSendNode",{},smalltalk.SendNode)})},
+args: [],
+source: "isCascadeSendNode\x0a\x09^ self parent isCascadeNode",
+messageSends: ["isCascadeNode", "parent"],
+referencedClasses: []
+}),
+smalltalk.SendNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isSendNode",
@@ -1521,16 +1595,23 @@ fn: function (){
 var self=this;
 function $Array(){return smalltalk.Array||(typeof Array=="undefined"?nil:Array)}
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-$2=_st($Array())._withAll_(self._arguments());
-_st($2)._add_(self._receiver());
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
+var $1,$2,$4,$5,$3;
+$1=self._receiver();
+if(($receiver = $1) == nil || $receiver == undefined){
+$2=_st(self._arguments())._copy();
+return $2;
+} else {
+$1;
+};
+$4=_st($Array())._with_(self._receiver());
+_st($4)._addAll_(self._arguments());
+$5=_st($4)._yourself();
+$3=$5;
+return $3;
 }, function($ctx1) {$ctx1.fill(self,"nodes",{},smalltalk.SendNode)})},
 args: [],
-source: "nodes\x0a\x09^ (Array withAll: self arguments)\x0a\x09\x09add: self receiver;\x0a\x09\x09yourself",
-messageSends: ["add:", "receiver", "withAll:", "arguments", "yourself"],
+source: "nodes\x0a\x09self receiver ifNil: [ ^ self arguments copy ].\x0a\x09\x0a\x09^ (Array with: self receiver)\x0a\x09\x09addAll: self arguments;\x0a\x09\x09yourself",
+messageSends: ["ifNil:", "receiver", "copy", "arguments", "addAll:", "with:", "yourself"],
 referencedClasses: ["Array"]
 }),
 smalltalk.SendNode);
@@ -1942,6 +2023,27 @@ referencedClasses: []
 }),
 smalltalk.ValueNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "xxxDoIt",
+category: 'xxxDoIt',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._stack();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"xxxDoIt",{},smalltalk.ValueNode)})},
+args: [],
+source: "xxxDoIt ^[self stack] value",
+messageSends: ["value", "stack"],
+referencedClasses: []
+}),
+smalltalk.ValueNode);
+
 
 
 smalltalk.addClass('VariableNode', smalltalk.ValueNode, ['assigned', 'binding'], 'Compiler-AST');

+ 139 - 29
js/Compiler-Interpreter.deploy.js

@@ -1,5 +1,5 @@
 smalltalk.addPackage('Compiler-Interpreter');
-smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['outerContext', 'innerContext', 'pc', 'locals', 'method', 'ast', 'interpreter'], 'Compiler-Interpreter');
+smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['outerContext', 'innerContext', 'pc', 'locals', 'method', 'ast', 'interpreter', 'methodContext'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "arguments",
@@ -13,7 +13,7 @@ return self._localAt_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"arguments",{},smalltalk.AIContext)})},
-messageSends: ["collect:", "localAt:", "arguments", "ast"]}),
+messageSends: ["collect:", "arguments", "ast", "localAt:"]}),
 smalltalk.AIContext);
 
 smalltalk.addMethod(
@@ -448,7 +448,7 @@ _st(_st($SemanticAnalyzer())._on_(_st(_st(self._context())._receiver())._class()
 $1=ast;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"buildAST",{ast:ast},smalltalk.ASTDebugger)})},
-messageSends: ["parse:", "source", "method", "current", "visit:", "on:", "class", "receiver", "context"]}),
+messageSends: ["parse:", "current", "source", "method", "visit:", "on:", "class", "receiver", "context"]}),
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
@@ -506,7 +506,7 @@ $2=_st($1)._currentNode();
 next=$2;
 _st(self._interpreter())._interpret_(next);
 return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{ast:ast,next:next},smalltalk.ASTDebugger)})},
-messageSends: ["buildAST", "context:", "context", "new", "visit:", "currentNode", "interpret:", "interpreter"]}),
+messageSends: ["buildAST", "context:", "new", "context", "visit:", "currentNode", "interpret:", "interpreter"]}),
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
@@ -620,7 +620,7 @@ _st(self._interpreter())._step();
 return self._step();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"step",{},smalltalk.ASTDebugger)})},
-messageSends: ["whileFalse:", "step", "interpreter", "or:", "not", "atEnd", "and:", "stopOnStepping", "nextNode", "notNil"]}),
+messageSends: ["whileFalse:", "or:", "and:", "notNil", "nextNode", "interpreter", "stopOnStepping", "not", "atEnd", "step"]}),
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
@@ -679,7 +679,7 @@ $1=_st(self._context())._localAt_put_(_st(aNode)._value(),anObject);
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"assign:to:",{aNode:aNode,anObject:anObject},smalltalk.ASTInterpreter)})},
-messageSends: ["ifTrue:ifFalse:", "instVarAt:put:", "value", "receiver", "context", "localAt:put:", "isInstanceVar", "binding"]}),
+messageSends: ["ifTrue:ifFalse:", "isInstanceVar", "binding", "instVarAt:put:", "receiver", "context", "value", "localAt:put:"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -823,7 +823,7 @@ return self._continue_value_(aBlock,value);
 self._continue_value_(aBlock,aNode);
 };
 return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
-messageSends: ["ifTrue:", "ifTrue:ifFalse:", "interpretNode:continue:", "continue:value:", "isNode"]}),
+messageSends: ["ifTrue:", "ifTrue:ifFalse:", "isNode", "interpretNode:continue:", "continue:value:"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -855,7 +855,7 @@ return self._interpretAll_continue_result_(_st(nodes)._allButFirst(),aBlock,_st(
 }, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
 };
 return self}, function($ctx1) {$ctx1.fill(self,"interpretAll:continue:result:",{nodes:nodes,aBlock:aBlock,aCollection:aCollection},smalltalk.ASTInterpreter)})},
-messageSends: ["ifTrue:ifFalse:", "continue:value:", "interpret:continue:", "first", "interpretAll:continue:result:", "allButFirst", ",", "isEmpty"]}),
+messageSends: ["ifTrue:ifFalse:", "isEmpty", "continue:value:", "interpret:continue:", "first", "interpretAll:continue:result:", "allButFirst", ","]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -925,7 +925,7 @@ return self._continue_value_(aBlock,val);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
 }, function($ctx2) {$ctx2.fillBlock({receiver:receiver},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"interpretCascadeNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
-messageSends: ["interpret:continue:", "receiver", "do:", "receiver:", "nodes", "interpretAll:continue:", "allButLast", "last", "continue:value:"]}),
+messageSends: ["interpret:continue:", "receiver", "do:", "nodes", "receiver:", "interpretAll:continue:", "allButLast", "last", "continue:value:"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -937,7 +937,7 @@ function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"
 return smalltalk.withContext(function($ctx1) { 
 self._continue_value_(aBlock,_st(_st($Smalltalk())._current())._at_(_st(aNode)._value()));
 return self}, function($ctx1) {$ctx1.fill(self,"interpretClassReferenceNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
-messageSends: ["continue:value:", "at:", "value", "current"]}),
+messageSends: ["continue:value:", "at:", "current", "value"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -1047,7 +1047,7 @@ return self._continue_value_(aBlock,self._sendMessage_to_superSend_(message,rece
 }, function($ctx3) {$ctx3.fillBlock({args:args},$ctx2)})}));
 }, function($ctx2) {$ctx2.fillBlock({receiver:receiver},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"interpretSendNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
-messageSends: ["interpret:continue:", "receiver", "interpretAll:continue:", "arguments", "messageFromSendNode:arguments:do:", "pc:", "+", "pc", "context", "continue:value:", "sendMessage:to:superSend:", "superSend"]}),
+messageSends: ["interpret:continue:", "receiver", "interpretAll:continue:", "arguments", "messageFromSendNode:arguments:do:", "pc:", "context", "+", "pc", "continue:value:", "sendMessage:to:superSend:", "superSend"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -1092,7 +1092,7 @@ $3=_st(self._context())._localAt_(_st(aNode)._value());
 };
 _st($1)._continue_value_($2,$3);
 return self}, function($ctx1) {$ctx1.fill(self,"interpretVariableNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
-messageSends: ["continue:value:", "ifTrue:ifFalse:", "instVarAt:", "value", "receiver", "context", "localAt:", "isInstanceVar", "binding"]}),
+messageSends: ["continue:value:", "ifTrue:ifFalse:", "isInstanceVar", "binding", "instVarAt:", "receiver", "context", "value", "localAt:"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -1109,7 +1109,7 @@ _st($1)._arguments_(aCollection);
 $2=_st($1)._yourself();
 self._continue_value_(aBlock,$2);
 return self}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:arguments:do:",{aSendNode:aSendNode,aCollection:aCollection,aBlock:aBlock},smalltalk.ASTInterpreter)})},
-messageSends: ["continue:value:", "selector:", "selector", "new", "arguments:", "yourself"]}),
+messageSends: ["continue:value:", "selector:", "new", "selector", "arguments:", "yourself"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -1186,7 +1186,7 @@ return $6;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
 }, function($ctx1) {$ctx1.fill(self,"sendMessage:to:superSend:",{aMessage:aMessage,anObject:anObject,aBoolean:aBoolean,method:method},smalltalk.ASTInterpreter)})},
-messageSends: ["ifFalse:", "sendTo:", "ifNil:", "messageNotUnderstood:receiver:", "superclass", "class", "at:ifAbsent:", "selector", "methodDictionary", "applyTo:arguments:", "arguments", "fn"]}),
+messageSends: ["ifFalse:", "sendTo:", "ifNil:", "superclass", "class", "messageNotUnderstood:receiver:", "at:ifAbsent:", "methodDictionary", "selector", "applyTo:arguments:", "fn", "arguments"]}),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
@@ -1225,7 +1225,7 @@ self._context_(_st(self._context())._outerContext());
 $3=blockResult;
 return $3;
 }, function($ctx1) {$ctx1.fill(self,"withBlockContext:",{aBlock:aBlock,blockResult:blockResult},smalltalk.ASTInterpreter)})},
-messageSends: ["context:", "outerContext:", "context", "new", "yourself", "value", "outerContext"]}),
+messageSends: ["context:", "outerContext:", "new", "context", "yourself", "value", "outerContext"]}),
 smalltalk.ASTInterpreter);
 
 
@@ -1244,7 +1244,7 @@ return _st(self._nextNode()).__eq_eq(self._currentNode());
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"atEnd",{},smalltalk.ASTSteppingInterpreter)})},
-messageSends: ["or:", "==", "currentNode", "nextNode", "shouldReturn"]}),
+messageSends: ["or:", "shouldReturn", "==", "nextNode", "currentNode"]}),
 smalltalk.ASTSteppingInterpreter);
 
 smalltalk.addMethod(
@@ -1534,6 +1534,18 @@ return self}, function($ctx1) {$ctx1.fill(self,"interpret",{},smalltalk.Interpre
 messageSends: ["visit:", "node"]}),
 smalltalk.Interpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpret:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._node_(aNode);
+self._interpret();
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["node:", "interpret"]}),
+smalltalk.Interpreter);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "messageFromSendNode:arguments:",
@@ -1640,13 +1652,13 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 _st((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(self._node())._notNil();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileTrue_((function(){
+return self._atEnd();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
 return smalltalk.withContext(function($ctx2) {
 return self._step();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"proceed",{},smalltalk.Interpreter)})},
-messageSends: ["whileTrue:", "step", "notNil", "node"]}),
+messageSends: ["whileFalse:", "atEnd", "step"]}),
 smalltalk.Interpreter);
 
 smalltalk.addMethod(
@@ -1664,20 +1676,33 @@ smalltalk.Interpreter);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "returnValue",
+selector: "result",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
-$2=self["@returnValue"];
+$2=self._returnValue();
 if(($receiver = $2) == nil || $receiver == undefined){
 $1=_st(self._context())._receiver();
 } else {
 $1=$2;
 };
 return $1;
+}, function($ctx1) {$ctx1.fill(self,"result",{},smalltalk.Interpreter)})},
+messageSends: ["ifNil:", "returnValue", "receiver", "context"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "returnValue",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@returnValue"];
+return $1;
 }, function($ctx1) {$ctx1.fill(self,"returnValue",{},smalltalk.Interpreter)})},
-messageSends: ["ifNil:", "receiver", "context"]}),
+messageSends: []}),
 smalltalk.Interpreter);
 
 smalltalk.addMethod(
@@ -1733,10 +1758,10 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-$1=_st(self["@returnValue"])._notNil();
+$1=_st(self._returnValue())._notNil();
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"shouldReturn",{},smalltalk.Interpreter)})},
-messageSends: ["notNil"]}),
+messageSends: ["notNil", "returnValue"]}),
 smalltalk.Interpreter);
 
 smalltalk.addMethod(
@@ -1804,10 +1829,50 @@ smalltalk.method({
 selector: "visitAssignmentNode:",
 fn: function (aNode){
 var self=this;
+var value;
 return smalltalk.withContext(function($ctx1) { 
-self._assign_to_(_st(aNode)._left(),self._peek());
-return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode},smalltalk.Interpreter)})},
-messageSends: ["assign:to:", "left", "peek"]}),
+value=self._pop();
+self._pop();
+self._push_(value);
+self._assign_to_(_st(aNode)._left(),value);
+return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode,value:value},smalltalk.Interpreter)})},
+messageSends: ["pop", "push:", "assign:to:", "left"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitBlockNode:",
+fn: function (aNode){
+var self=this;
+var blockNode,blockContext,block,interpreter;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4,$5;
+blockNode=_st(_st(_st(aNode)._nodes())._first())._copy();
+_st(blockNode)._parent_(nil);
+$1=_st(_st(self._context())._class())._new();
+_st($1)._outerContext_(self._context());
+$2=_st($1)._yourself();
+blockContext=$2;
+block=(function(){
+return smalltalk.withContext(function($ctx2) {
+interpreter=_st(self._class())._new();
+interpreter;
+$3=interpreter;
+_st($3)._context_(blockContext);
+_st($3)._node_(_st(blockNode)._nextChild());
+$4=_st($3)._proceed();
+$4;
+self._returnValue_(_st(interpreter)._returnValue());
+$5=_st(_st(interpreter)._stack())._isEmpty();
+if(smalltalk.assert($5)){
+return nil;
+} else {
+return _st(interpreter)._pop();
+};
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+self._push_(block);
+return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode,blockNode:blockNode,blockContext:blockContext,block:block,interpreter:interpreter},smalltalk.Interpreter)})},
+messageSends: ["copy", "first", "nodes", "parent:", "outerContext:", "new", "class", "context", "yourself", "context:", "node:", "nextChild", "proceed", "returnValue:", "returnValue", "ifTrue:ifFalse:", "isEmpty", "stack", "pop", "push:"]}),
 smalltalk.Interpreter);
 
 smalltalk.addMethod(
@@ -1822,6 +1887,41 @@ return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode
 messageSends: ["push:", "at:", "value", "current"]}),
 smalltalk.Interpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitDynamicArrayNode:",
+fn: function (aNode){
+var self=this;
+var array;
+return smalltalk.withContext(function($ctx1) { 
+array=[];
+_st(_st(aNode)._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(array)._addFirst_(self._pop());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+self._push_(array);
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicArrayNode:",{aNode:aNode,array:array},smalltalk.Interpreter)})},
+messageSends: ["do:", "nodes", "addFirst:", "pop", "push:"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitDynamicDictionaryNode:",
+fn: function (aNode){
+var self=this;
+var hashedCollection;
+function $HashedCollection(){return smalltalk.HashedCollection||(typeof HashedCollection=="undefined"?nil:HashedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+hashedCollection=_st($HashedCollection())._new();
+_st(_st(aNode)._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(hashedCollection)._add_(self._pop());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+self._push_(hashedCollection);
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicDictionaryNode:",{aNode:aNode,hashedCollection:hashedCollection},smalltalk.Interpreter)})},
+messageSends: ["new", "do:", "nodes", "add:", "pop", "push:"]}),
+smalltalk.Interpreter);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "visitJSStatementNode:",
@@ -1861,16 +1961,26 @@ fn: function (aNode){
 var self=this;
 var receiver,args,message,result;
 return smalltalk.withContext(function($ctx1) { 
+var $1;
 args=_st(_st(aNode)._arguments())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
 return self._pop();
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 receiver=self._pop();
-message=self._messageFromSendNode_arguments_(aNode,args);
+message=self._messageFromSendNode_arguments_(aNode,_st(args)._reversed());
 result=self._sendMessage_to_superSend_(message,receiver,_st(aNode)._superSend());
+_st(self._context())._pc_(_st(_st(self._context())._pc()).__plus((1)));
+$1=_st(_st(aNode)._isCascadeSendNode())._and_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(aNode)._isLastChild())._not();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+if(smalltalk.assert($1)){
+self._push_(receiver);
+} else {
 self._push_(result);
+};
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode,receiver:receiver,args:args,message:message,result:result},smalltalk.Interpreter)})},
-messageSends: ["collect:", "pop", "arguments", "messageFromSendNode:arguments:", "sendMessage:to:superSend:", "superSend", "push:"]}),
+messageSends: ["collect:", "arguments", "pop", "messageFromSendNode:arguments:", "reversed", "sendMessage:to:superSend:", "superSend", "pc:", "context", "+", "pc", "ifTrue:ifFalse:", "and:", "isCascadeSendNode", "not", "isLastChild", "push:"]}),
 smalltalk.Interpreter);
 
 smalltalk.addMethod(

+ 169 - 34
js/Compiler-Interpreter.js

@@ -1,5 +1,5 @@
 smalltalk.addPackage('Compiler-Interpreter');
-smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['outerContext', 'innerContext', 'pc', 'locals', 'method', 'ast', 'interpreter'], 'Compiler-Interpreter');
+smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['outerContext', 'innerContext', 'pc', 'locals', 'method', 'ast', 'interpreter', 'methodContext'], 'Compiler-Interpreter');
 smalltalk.AIContext.comment="I am like a `MethodContext`, used by the `ASTInterpreter`.\x0aUnlike a `MethodContext`, my instances are not read-only.\x0a\x0aWhen debugging, my instances are created by copying the current `MethodContext` (thisContext)";
 smalltalk.addMethod(
 smalltalk.method({
@@ -17,7 +17,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"arguments",{},smalltalk.AIContext)})},
 args: [],
 source: "arguments\x0a\x09^ self ast arguments collect: [ :each |\x0a\x09\x09self localAt: each ]",
-messageSends: ["collect:", "localAt:", "arguments", "ast"],
+messageSends: ["collect:", "arguments", "ast", "localAt:"],
 referencedClasses: []
 }),
 smalltalk.AIContext);
@@ -593,7 +593,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"buildAST",{ast:ast},smalltalk.ASTDebugger)})},
 args: [],
 source: "buildAST\x0a\x09\x22Build the AST tree from the method source code.\x0a\x09The AST is annotated with a SemanticAnalyzer,\x0a\x09to know the semantics and bindings of each node needed for later debugging\x22\x0a\x09\x0a\x09| ast |\x0a\x09\x0a\x09ast := Smalltalk current parse: self method source.\x0a\x09(SemanticAnalyzer on: self context receiver class)\x0a\x09\x09visit: ast.\x0a\x09\x0a\x09^ ast",
-messageSends: ["parse:", "source", "method", "current", "visit:", "on:", "class", "receiver", "context"],
+messageSends: ["parse:", "current", "source", "method", "visit:", "on:", "class", "receiver", "context"],
 referencedClasses: ["Smalltalk", "SemanticAnalyzer"]
 }),
 smalltalk.ASTDebugger);
@@ -671,7 +671,7 @@ _st(self._interpreter())._interpret_(next);
 return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{ast:ast,next:next},smalltalk.ASTDebugger)})},
 args: [],
 source: "initializeInterpreter\x0a\x09| ast next |\x0a\x09ast := self buildAST.\x0a\x09next := ASTPCNodeVisitor new\x0a\x09\x09context: self context;\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode.\x0a\x09self interpreter interpret: next",
-messageSends: ["buildAST", "context:", "context", "new", "visit:", "currentNode", "interpret:", "interpreter"],
+messageSends: ["buildAST", "context:", "new", "context", "visit:", "currentNode", "interpret:", "interpreter"],
 referencedClasses: ["ASTPCNodeVisitor"]
 }),
 smalltalk.ASTDebugger);
@@ -825,7 +825,7 @@ return self._step();
 return self}, function($ctx1) {$ctx1.fill(self,"step",{},smalltalk.ASTDebugger)})},
 args: [],
 source: "step\x0a\x09\x22The ASTSteppingInterpreter stops at each node interpretation.\x0a\x09One step will interpret nodes until:\x0a\x09- we get at the end\x0a\x09- the next node is a stepping node (send, assignment, etc.)\x22\x0a\x09\x0a\x09[ (self interpreter nextNode notNil and: [ self interpreter nextNode stopOnStepping ])\x0a\x09\x09or: [ self interpreter atEnd not ] ]\x0a\x09\x09\x09whileFalse: [\x0a\x09\x09\x09\x09self interpreter step.\x0a\x09\x09\x09\x09self step ]",
-messageSends: ["whileFalse:", "step", "interpreter", "or:", "not", "atEnd", "and:", "stopOnStepping", "nextNode", "notNil"],
+messageSends: ["whileFalse:", "or:", "and:", "notNil", "nextNode", "interpreter", "stopOnStepping", "not", "atEnd", "step"],
 referencedClasses: []
 }),
 smalltalk.ASTDebugger);
@@ -905,7 +905,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"assign:to:",{aNode:aNode,anObject:anObject},smalltalk.ASTInterpreter)})},
 args: ["aNode", "anObject"],
 source: "assign: aNode to: anObject\x0a\x09^ aNode binding isInstanceVar\x0a\x09\x09ifTrue: [ self context receiver instVarAt: aNode value put: anObject ]\x0a\x09\x09ifFalse: [ self context localAt: aNode value put: anObject ]",
-messageSends: ["ifTrue:ifFalse:", "instVarAt:put:", "value", "receiver", "context", "localAt:put:", "isInstanceVar", "binding"],
+messageSends: ["ifTrue:ifFalse:", "isInstanceVar", "binding", "instVarAt:put:", "receiver", "context", "value", "localAt:put:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1089,7 +1089,7 @@ self._continue_value_(aBlock,aNode);
 return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
 args: ["aNode", "aBlock"],
 source: "interpret: aNode continue: aBlock\x0a\x09shouldReturn ifTrue: [ ^ self ].\x0a\x0a\x09aNode isNode\x0a\x09\x09ifTrue: [\x0a\x09\x09\x09currentNode := aNode.\x0a\x09\x09\x09self interpretNode: aNode continue: [ :value |\x0a\x09\x09\x09\x09self continue: aBlock value: value ] ]\x0a\x09\x09ifFalse: [ self continue: aBlock value: aNode ]",
-messageSends: ["ifTrue:", "ifTrue:ifFalse:", "interpretNode:continue:", "continue:value:", "isNode"],
+messageSends: ["ifTrue:", "ifTrue:ifFalse:", "isNode", "interpretNode:continue:", "continue:value:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1131,7 +1131,7 @@ return self._interpretAll_continue_result_(_st(nodes)._allButFirst(),aBlock,_st(
 return self}, function($ctx1) {$ctx1.fill(self,"interpretAll:continue:result:",{nodes:nodes,aBlock:aBlock,aCollection:aCollection},smalltalk.ASTInterpreter)})},
 args: ["nodes", "aBlock", "aCollection"],
 source: "interpretAll: nodes continue: aBlock result: aCollection\x0a\x09nodes isEmpty\x0a\x09\x09ifTrue: [ self continue: aBlock value: aCollection ]\x0a\x09\x09ifFalse: [\x0a\x09\x09\x09self interpret: nodes first continue: [:value |\x0a\x09\x09\x09\x09self\x0a\x09\x09\x09\x09\x09interpretAll: nodes allButFirst\x0a\x09\x09\x09\x09\x09continue: aBlock\x0a\x09\x09\x09\x09\x09result: aCollection, { value } ] ]",
-messageSends: ["ifTrue:ifFalse:", "continue:value:", "interpret:continue:", "first", "interpretAll:continue:result:", "allButFirst", ",", "isEmpty"],
+messageSends: ["ifTrue:ifFalse:", "isEmpty", "continue:value:", "interpret:continue:", "first", "interpretAll:continue:result:", "allButFirst", ","],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1221,7 +1221,7 @@ return self._continue_value_(aBlock,val);
 return self}, function($ctx1) {$ctx1.fill(self,"interpretCascadeNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
 args: ["aNode", "aBlock"],
 source: "interpretCascadeNode: aNode continue: aBlock\x0a\x09\x22TODO: Handle super sends\x22\x0a\x09\x0a\x09self interpret: aNode receiver continue: [ :receiver |\x0a\x09\x09\x22Only interpret the receiver once\x22\x0a\x09\x09aNode nodes do: [ :each | each receiver: receiver ].\x0a\x0a\x09\x09self\x0a\x09\x09\x09interpretAll: aNode nodes allButLast\x0a\x09\x09\x09continue: [\x0a\x09\x09\x09\x09self\x0a\x09\x09\x09\x09\x09interpret: aNode nodes last\x0a\x09\x09\x09\x09\x09continue: [ :val | self continue: aBlock value: val ] ] ]",
-messageSends: ["interpret:continue:", "receiver", "do:", "receiver:", "nodes", "interpretAll:continue:", "allButLast", "last", "continue:value:"],
+messageSends: ["interpret:continue:", "receiver", "do:", "nodes", "receiver:", "interpretAll:continue:", "allButLast", "last", "continue:value:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1238,7 +1238,7 @@ self._continue_value_(aBlock,_st(_st($Smalltalk())._current())._at_(_st(aNode)._
 return self}, function($ctx1) {$ctx1.fill(self,"interpretClassReferenceNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
 args: ["aNode", "aBlock"],
 source: "interpretClassReferenceNode: aNode continue: aBlock\x0a\x09self continue: aBlock value: (Smalltalk current at: aNode value)",
-messageSends: ["continue:value:", "at:", "value", "current"],
+messageSends: ["continue:value:", "at:", "current", "value"],
 referencedClasses: ["Smalltalk"]
 }),
 smalltalk.ASTInterpreter);
@@ -1383,7 +1383,7 @@ return self._continue_value_(aBlock,self._sendMessage_to_superSend_(message,rece
 return self}, function($ctx1) {$ctx1.fill(self,"interpretSendNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
 args: ["aNode", "aBlock"],
 source: "interpretSendNode: aNode continue: aBlock\x0a\x09self interpret: aNode receiver continue: [ :receiver |\x0a\x09\x09self interpretAll: aNode arguments continue: [ :args |\x0a\x09\x09\x09self\x0a\x09\x09\x09\x09messageFromSendNode: aNode\x0a\x09\x09\x09\x09arguments: args\x0a\x09\x09\x09\x09do: [ :message |\x0a\x09\x09\x09\x09\x09self context pc: self context pc + 1.\x0a\x09\x09\x09\x09\x09self\x0a\x09\x09\x09\x09\x09\x09continue: aBlock\x0a\x09\x09\x09\x09\x09\x09value: (self sendMessage: message to: receiver superSend: aNode superSend) ] ] ]",
-messageSends: ["interpret:continue:", "receiver", "interpretAll:continue:", "arguments", "messageFromSendNode:arguments:do:", "pc:", "+", "pc", "context", "continue:value:", "sendMessage:to:superSend:", "superSend"],
+messageSends: ["interpret:continue:", "receiver", "interpretAll:continue:", "arguments", "messageFromSendNode:arguments:do:", "pc:", "context", "+", "pc", "continue:value:", "sendMessage:to:superSend:", "superSend"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1443,7 +1443,7 @@ _st($1)._continue_value_($2,$3);
 return self}, function($ctx1) {$ctx1.fill(self,"interpretVariableNode:continue:",{aNode:aNode,aBlock:aBlock},smalltalk.ASTInterpreter)})},
 args: ["aNode", "aBlock"],
 source: "interpretVariableNode: aNode continue: aBlock\x0a\x09self\x0a\x09\x09continue: aBlock\x0a\x09\x09value: (aNode binding isInstanceVar\x0a\x09\x09\x09ifTrue: [ self context receiver instVarAt: aNode value ]\x0a\x09\x09\x09ifFalse: [ self context localAt: aNode value ])",
-messageSends: ["continue:value:", "ifTrue:ifFalse:", "instVarAt:", "value", "receiver", "context", "localAt:", "isInstanceVar", "binding"],
+messageSends: ["continue:value:", "ifTrue:ifFalse:", "isInstanceVar", "binding", "instVarAt:", "receiver", "context", "value", "localAt:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1465,7 +1465,7 @@ self._continue_value_(aBlock,$2);
 return self}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:arguments:do:",{aSendNode:aSendNode,aCollection:aCollection,aBlock:aBlock},smalltalk.ASTInterpreter)})},
 args: ["aSendNode", "aCollection", "aBlock"],
 source: "messageFromSendNode: aSendNode arguments: aCollection do: aBlock\x0a\x09self\x0a\x09\x09continue: aBlock\x0a\x09\x09value: (Message new\x0a\x09\x09\x09selector: aSendNode selector;\x0a\x09\x09\x09arguments: aCollection;\x0a\x09\x09\x09yourself)",
-messageSends: ["continue:value:", "selector:", "selector", "new", "arguments:", "yourself"],
+messageSends: ["continue:value:", "selector:", "new", "selector", "arguments:", "yourself"],
 referencedClasses: ["Message"]
 }),
 smalltalk.ASTInterpreter);
@@ -1562,7 +1562,7 @@ catch(e) {if(e===$early)return e[0]; throw e}
 }, function($ctx1) {$ctx1.fill(self,"sendMessage:to:superSend:",{aMessage:aMessage,anObject:anObject,aBoolean:aBoolean,method:method},smalltalk.ASTInterpreter)})},
 args: ["aMessage", "anObject", "aBoolean"],
 source: "sendMessage: aMessage to: anObject superSend: aBoolean\x0a\x09| method |\x0a\x09\x0a\x09aBoolean ifFalse: [ ^ aMessage sendTo: anObject ].\x0a\x09anObject class superclass ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x0a\x09method := anObject class superclass methodDictionary\x0a\x09\x09at: aMessage selector\x0a\x09\x09ifAbsent: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x09\x0a\x09^ method fn applyTo: anObject arguments: aMessage arguments\x0a\x09\x09\x0a\x09\x0a\x09",
-messageSends: ["ifFalse:", "sendTo:", "ifNil:", "messageNotUnderstood:receiver:", "superclass", "class", "at:ifAbsent:", "selector", "methodDictionary", "applyTo:arguments:", "arguments", "fn"],
+messageSends: ["ifFalse:", "sendTo:", "ifNil:", "superclass", "class", "messageNotUnderstood:receiver:", "at:ifAbsent:", "methodDictionary", "selector", "applyTo:arguments:", "fn", "arguments"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -1611,7 +1611,7 @@ return $3;
 }, function($ctx1) {$ctx1.fill(self,"withBlockContext:",{aBlock:aBlock,blockResult:blockResult},smalltalk.ASTInterpreter)})},
 args: ["aBlock"],
 source: "withBlockContext: aBlock\x0a\x09\x22Evaluate aBlock with a BlockContext:\x0a\x09- a context is pushed before aBlock evaluation.\x0a\x09- the context is poped after aBlock evaluation\x0a\x09- the result of aBlock evaluation is answered\x22\x0a\x09\x0a\x09| blockResult |\x0a\x09\x09\x09\x0a\x09self context: (AIContext new\x0a\x09\x09outerContext: self context;\x0a\x09\x09yourself).\x0a\x09\x0a\x09blockResult := aBlock value.\x0a\x09\x0a\x09self context: self context outerContext.\x0a\x09^ blockResult",
-messageSends: ["context:", "outerContext:", "context", "new", "yourself", "value", "outerContext"],
+messageSends: ["context:", "outerContext:", "new", "context", "yourself", "value", "outerContext"],
 referencedClasses: ["AIContext"]
 }),
 smalltalk.ASTInterpreter);
@@ -1636,7 +1636,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"atEnd",{},smalltalk.ASTSteppingInterpreter)})},
 args: [],
 source: "atEnd\x0a\x09^ self shouldReturn or: [ self nextNode == self currentNode ]",
-messageSends: ["or:", "==", "currentNode", "nextNode", "shouldReturn"],
+messageSends: ["or:", "shouldReturn", "==", "nextNode", "currentNode"],
 referencedClasses: []
 }),
 smalltalk.ASTSteppingInterpreter);
@@ -2024,6 +2024,23 @@ referencedClasses: []
 }),
 smalltalk.Interpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpret:",
+category: 'interpreting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._node_(aNode);
+self._interpret();
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "interpret: aNode\x0a\x09self node: aNode.\x0a\x09self interpret",
+messageSends: ["node:", "interpret"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "messageFromSendNode:arguments:",
@@ -2166,15 +2183,15 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 _st((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(self._node())._notNil();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileTrue_((function(){
+return self._atEnd();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
 return smalltalk.withContext(function($ctx2) {
 return self._step();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"proceed",{},smalltalk.Interpreter)})},
 args: [],
-source: "proceed\x0a\x09\x22Eagerly evaluate the ast\x22\x0a\x09\x0a\x09[ self node notNil ] whileTrue: [ \x0a\x09\x09self step ]",
-messageSends: ["whileTrue:", "step", "notNil", "node"],
+source: "proceed\x0a\x09\x22Eagerly evaluate the ast\x22\x0a\x09\x0a\x09[ self atEnd ] whileFalse: [ \x0a\x09\x09self step ]",
+messageSends: ["whileFalse:", "atEnd", "step"],
 referencedClasses: []
 }),
 smalltalk.Interpreter);
@@ -2199,23 +2216,41 @@ smalltalk.Interpreter);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "returnValue",
+selector: "result",
 category: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
-$2=self["@returnValue"];
+$2=self._returnValue();
 if(($receiver = $2) == nil || $receiver == undefined){
 $1=_st(self._context())._receiver();
 } else {
 $1=$2;
 };
 return $1;
+}, function($ctx1) {$ctx1.fill(self,"result",{},smalltalk.Interpreter)})},
+args: [],
+source: "result\x0a\x09^ self returnValue ifNil: [ self context receiver ]",
+messageSends: ["ifNil:", "returnValue", "receiver", "context"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "returnValue",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@returnValue"];
+return $1;
 }, function($ctx1) {$ctx1.fill(self,"returnValue",{},smalltalk.Interpreter)})},
 args: [],
-source: "returnValue\x0a\x09^ returnValue ifNil: [ self context receiver ]",
-messageSends: ["ifNil:", "receiver", "context"],
+source: "returnValue\x0a\x09^ returnValue",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.Interpreter);
@@ -2284,12 +2319,12 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-$1=_st(self["@returnValue"])._notNil();
+$1=_st(self._returnValue())._notNil();
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"shouldReturn",{},smalltalk.Interpreter)})},
 args: [],
-source: "shouldReturn\x0a\x09^ returnValue notNil",
-messageSends: ["notNil"],
+source: "shouldReturn\x0a\x09^ self returnValue notNil",
+messageSends: ["notNil", "returnValue"],
 referencedClasses: []
 }),
 smalltalk.Interpreter);
@@ -2380,12 +2415,57 @@ selector: "visitAssignmentNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
+var value;
+return smalltalk.withContext(function($ctx1) { 
+value=self._pop();
+self._pop();
+self._push_(value);
+self._assign_to_(_st(aNode)._left(),value);
+return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode,value:value},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitAssignmentNode: aNode\x0a\x09| value |\x0a\x09\x0a\x09value := self pop.\x0a\x09\x0a\x09\x22Pop the left side of the assignment.\x0a\x09It already has been visited, and we don't need its value.\x22\x0a\x09self pop.\x0a\x09\x0a\x09self push: value.\x0a\x09self assign: aNode left to: value",
+messageSends: ["pop", "push:", "assign:to:", "left"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitBlockNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+var blockNode,blockContext,block,interpreter;
 return smalltalk.withContext(function($ctx1) { 
-self._assign_to_(_st(aNode)._left(),self._peek());
-return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode},smalltalk.Interpreter)})},
+var $1,$2,$3,$4,$5;
+blockNode=_st(_st(_st(aNode)._nodes())._first())._copy();
+_st(blockNode)._parent_(nil);
+$1=_st(_st(self._context())._class())._new();
+_st($1)._outerContext_(self._context());
+$2=_st($1)._yourself();
+blockContext=$2;
+block=(function(){
+return smalltalk.withContext(function($ctx2) {
+interpreter=_st(self._class())._new();
+interpreter;
+$3=interpreter;
+_st($3)._context_(blockContext);
+_st($3)._node_(_st(blockNode)._nextChild());
+$4=_st($3)._proceed();
+$4;
+self._returnValue_(_st(interpreter)._returnValue());
+$5=_st(_st(interpreter)._stack())._isEmpty();
+if(smalltalk.assert($5)){
+return nil;
+} else {
+return _st(interpreter)._pop();
+};
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+self._push_(block);
+return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode,blockNode:blockNode,blockContext:blockContext,block:block,interpreter:interpreter},smalltalk.Interpreter)})},
 args: ["aNode"],
-source: "visitAssignmentNode: aNode\x0a\x09\x22Do not pop as the pushed value would be the right node\x22\x0a\x09\x0a\x09self assign: aNode left to: self peek",
-messageSends: ["assign:to:", "left", "peek"],
+source: "visitBlockNode: aNode\x0a\x09\x22Do not evaluate the block node.\x0a\x09Instead, put all instructions into a block that we push to the stack for later evaluation\x22\x0a\x09\x0a\x09| blockNode blockContext block interpreter |\x0a\x09\x0a\x09\x22Copy the sequence node without the parent to avoid evaluating nodes up the block. \x0a\x09#nextNode should not go anymore up than the block itself\x22\x0a\x09blockNode := aNode nodes first copy.\x0a\x09blockNode parent: nil.\x0a\x09\x09\x0a\x09blockContext := self context class new\x0a\x09\x09outerContext: self context;\x0a\x09\x09yourself.\x0a\x09\x0a\x09block := [\x0a\x09\x09interpreter := self class new.\x0a\x09\x09interpreter\x0a\x09\x09\x09context: blockContext;\x0a\x09\x09\x09node: blockNode nextChild;\x0a\x09\x09\x09proceed.\x0a\x09\x09\x0a\x09\x09\x22Non local returns hanlding\x22\x0a\x09\x09self returnValue: interpreter returnValue.\x0a\x09\x09\x0a\x09\x09\x22Answer the last evaluation of the block or nil\x22\x0a\x09\x09interpreter stack isEmpty\x0a\x09\x09\x09ifTrue: [ nil ]\x0a\x09\x09\x09ifFalse: [ interpreter pop ] ].\x0a\x09\x0a\x09self push: block",
+messageSends: ["copy", "first", "nodes", "parent:", "outerContext:", "new", "class", "context", "yourself", "context:", "node:", "nextChild", "proceed", "returnValue:", "returnValue", "ifTrue:ifFalse:", "isEmpty", "stack", "pop", "push:"],
 referencedClasses: []
 }),
 smalltalk.Interpreter);
@@ -2407,6 +2487,51 @@ referencedClasses: ["Smalltalk"]
 }),
 smalltalk.Interpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitDynamicArrayNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+var array;
+return smalltalk.withContext(function($ctx1) { 
+array=[];
+_st(_st(aNode)._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(array)._addFirst_(self._pop());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+self._push_(array);
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicArrayNode:",{aNode:aNode,array:array},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitDynamicArrayNode: aNode\x0a\x09| array |\x0a\x09\x0a\x09array := #().\x0a\x09aNode nodes do: [ :each |\x0a\x09\x09array addFirst: self pop ].\x0a\x09\x0a\x09self push: array",
+messageSends: ["do:", "nodes", "addFirst:", "pop", "push:"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitDynamicDictionaryNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+var hashedCollection;
+function $HashedCollection(){return smalltalk.HashedCollection||(typeof HashedCollection=="undefined"?nil:HashedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+hashedCollection=_st($HashedCollection())._new();
+_st(_st(aNode)._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(hashedCollection)._add_(self._pop());
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+self._push_(hashedCollection);
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicDictionaryNode:",{aNode:aNode,hashedCollection:hashedCollection},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitDynamicDictionaryNode: aNode\x0a\x09| hashedCollection |\x0a\x09\x0a\x09hashedCollection := HashedCollection new.\x0a\x09aNode nodes do: [ :each | \x0a\x09\x09hashedCollection add: self pop ].\x0a\x09\x0a\x09self push: hashedCollection",
+messageSends: ["new", "do:", "nodes", "add:", "pop", "push:"],
+referencedClasses: ["HashedCollection"]
+}),
+smalltalk.Interpreter);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "visitJSStatementNode:",
@@ -2462,18 +2587,28 @@ fn: function (aNode){
 var self=this;
 var receiver,args,message,result;
 return smalltalk.withContext(function($ctx1) { 
+var $1;
 args=_st(_st(aNode)._arguments())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
 return self._pop();
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 receiver=self._pop();
-message=self._messageFromSendNode_arguments_(aNode,args);
+message=self._messageFromSendNode_arguments_(aNode,_st(args)._reversed());
 result=self._sendMessage_to_superSend_(message,receiver,_st(aNode)._superSend());
+_st(self._context())._pc_(_st(_st(self._context())._pc()).__plus((1)));
+$1=_st(_st(aNode)._isCascadeSendNode())._and_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(aNode)._isLastChild())._not();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+if(smalltalk.assert($1)){
+self._push_(receiver);
+} else {
 self._push_(result);
+};
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode,receiver:receiver,args:args,message:message,result:result},smalltalk.Interpreter)})},
 args: ["aNode"],
-source: "visitSendNode: aNode\x0a\x09| receiver args message result |\x0a\x09\x0a\x09args := aNode arguments collect: [ :each |\x0a\x09\x09self pop ].\x0a\x09receiver := self pop.\x0a\x09\x0a\x09message := self\x0a\x09\x09messageFromSendNode: aNode\x0a\x09\x09arguments: args.\x0a\x09\x0a\x09result := self sendMessage: message to: receiver superSend: aNode superSend.\x0a\x09\x0a\x09self push: result",
-messageSends: ["collect:", "pop", "arguments", "messageFromSendNode:arguments:", "sendMessage:to:superSend:", "superSend", "push:"],
+source: "visitSendNode: aNode\x0a\x09| receiver args message result |\x0a\x09\x0a\x09args := aNode arguments collect: [ :each | self pop ].\x0a\x09receiver := self pop.\x0a\x09\x0a\x09message := self\x0a\x09\x09messageFromSendNode: aNode\x0a\x09\x09arguments: args reversed.\x0a\x09\x0a\x09result := self sendMessage: message to: receiver superSend: aNode superSend.\x0a\x09\x0a\x09self context pc: self context pc + 1.\x0a\x09\x0a\x09\x22For cascade sends, push the reciever if the send is not the last one\x22\x0a\x09(aNode isCascadeSendNode and: [ aNode isLastChild not ])\x0a\x09\x09ifTrue: [ self push: receiver ]\x0a\x09\x09ifFalse: [ self push: result ]",
+messageSends: ["collect:", "arguments", "pop", "messageFromSendNode:arguments:", "reversed", "sendMessage:to:superSend:", "superSend", "pc:", "context", "+", "pc", "ifTrue:ifFalse:", "and:", "isCascadeSendNode", "not", "isLastChild", "push:"],
 referencedClasses: []
 }),
 smalltalk.Interpreter);

+ 25 - 29
js/Compiler-Semantic.deploy.js

@@ -10,7 +10,7 @@ return smalltalk.withContext(function($ctx1) {
 _st(self._args())._at_put_(aString,_st($ArgVar())._on_(aString));
 _st(_st(self._args())._at_(aString))._scope_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addArg:",{aString:aString},smalltalk.LexicalScope)})},
-messageSends: ["at:put:", "on:", "args", "scope:", "at:"]}),
+messageSends: ["at:put:", "args", "on:", "scope:", "at:"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -23,7 +23,7 @@ return smalltalk.withContext(function($ctx1) {
 _st(self._temps())._at_put_(aString,_st($TempVar())._on_(aString));
 _st(_st(self._temps())._at_(aString))._scope_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addTemp:",{aString:aString},smalltalk.LexicalScope)})},
-messageSends: ["at:put:", "on:", "temps", "scope:", "at:"]}),
+messageSends: ["at:put:", "temps", "on:", "scope:", "at:"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -49,7 +49,7 @@ var $1;
 $1=_st(_st(self._args())._keys()).__comma(_st(self._temps())._keys());
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"allVariableNames",{},smalltalk.LexicalScope)})},
-messageSends: [",", "keys", "temps", "args"]}),
+messageSends: [",", "keys", "args", "temps"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -91,7 +91,7 @@ return nil;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"bindingFor:",{aStringOrNode:aStringOrNode},smalltalk.LexicalScope)})},
-messageSends: ["at:ifAbsent:", "value", "temps", "args", "pseudoVars"]}),
+messageSends: ["at:ifAbsent:", "pseudoVars", "value", "args", "temps"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -107,7 +107,7 @@ return _st(self._outerScope())._canInlineNonLocalReturns();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"canInlineNonLocalReturns",{},smalltalk.LexicalScope)})},
-messageSends: ["and:", "canInlineNonLocalReturns", "outerScope", "isInlined"]}),
+messageSends: ["and:", "isInlined", "canInlineNonLocalReturns", "outerScope"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -160,7 +160,7 @@ return _st(self._instruction())._isInlined();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"isInlined",{},smalltalk.LexicalScope)})},
-messageSends: ["and:", "isInlined", "instruction", "notNil"]}),
+messageSends: ["and:", "notNil", "instruction", "isInlined"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -198,7 +198,7 @@ $1;
 $3=lookup;
 return $3;
 }, function($ctx1) {$ctx1.fill(self,"lookupVariable:",{aNode:aNode,lookup:lookup},smalltalk.LexicalScope)})},
-messageSends: ["bindingFor:", "ifNil:", "ifNotNil:", "lookupVariable:", "outerScope"]}),
+messageSends: ["bindingFor:", "ifNil:", "ifNotNil:", "outerScope", "lookupVariable:"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -216,7 +216,7 @@ $1=_st(self._outerScope())._methodScope();
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"methodScope",{},smalltalk.LexicalScope)})},
-messageSends: ["ifNotNil:", "methodScope", "outerScope"]}),
+messageSends: ["ifNotNil:", "outerScope", "methodScope"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -301,7 +301,7 @@ return $3;
 $4=_st(_st(self._outerScope())._scopeLevel()).__plus((1));
 return $4;
 }, function($ctx1) {$ctx1.fill(self,"scopeLevel",{},smalltalk.LexicalScope)})},
-messageSends: ["ifNil:", "outerScope", "ifTrue:", "scopeLevel", "isInlined", "+"]}),
+messageSends: ["ifNil:", "outerScope", "ifTrue:", "isInlined", "scopeLevel", "+"]}),
 smalltalk.LexicalScope);
 
 smalltalk.addMethod(
@@ -337,7 +337,7 @@ return smalltalk.withContext(function($ctx1) {
 _st(self._iVars())._at_put_(aString,_st($InstanceVar())._on_(aString));
 _st(_st(self._iVars())._at_(aString))._scope_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addIVar:",{aString:aString},smalltalk.MethodLexicalScope)})},
-messageSends: ["at:put:", "on:", "iVars", "scope:", "at:"]}),
+messageSends: ["at:put:", "iVars", "on:", "scope:", "at:"]}),
 smalltalk.MethodLexicalScope);
 
 smalltalk.addMethod(
@@ -361,7 +361,7 @@ var $1;
 $1=_st(smalltalk.LexicalScope.fn.prototype._allVariableNames.apply(_st(self), [])).__comma(_st(self._iVars())._keys());
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"allVariableNames",{},smalltalk.MethodLexicalScope)})},
-messageSends: [",", "keys", "iVars", "allVariableNames"]}),
+messageSends: [",", "allVariableNames", "keys", "iVars"]}),
 smalltalk.MethodLexicalScope);
 
 smalltalk.addMethod(
@@ -382,7 +382,7 @@ $1=$2;
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"bindingFor:",{aNode:aNode},smalltalk.MethodLexicalScope)})},
-messageSends: ["ifNil:", "at:ifAbsent:", "value", "iVars", "bindingFor:"]}),
+messageSends: ["ifNil:", "bindingFor:", "at:ifAbsent:", "iVars", "value"]}),
 smalltalk.MethodLexicalScope);
 
 smalltalk.addMethod(
@@ -521,8 +521,8 @@ selector: "pseudoVars",
 fn: function (){
 var self=this;
 function $Dictionary(){return smalltalk.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
-function $PseudoVar(){return smalltalk.PseudoVar||(typeof PseudoVar=="undefined"?nil:PseudoVar)}
 function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+function $PseudoVar(){return smalltalk.PseudoVar||(typeof PseudoVar=="undefined"?nil:PseudoVar)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2,$3,$4;
 $1=self["@pseudoVars"];
@@ -542,7 +542,7 @@ $1;
 $4=self["@pseudoVars"];
 return $4;
 }, function($ctx1) {$ctx1.fill(self,"pseudoVars",{},smalltalk.MethodLexicalScope)})},
-messageSends: ["ifNil:", "new", "do:", "at:put:", "scope:", "methodScope", "on:", "yourself", "pseudoVariableNames", "current"]}),
+messageSends: ["ifNil:", "new", "do:", "pseudoVariableNames", "current", "at:put:", "scope:", "on:", "methodScope", "yourself"]}),
 smalltalk.MethodLexicalScope);
 
 smalltalk.addMethod(
@@ -727,7 +727,7 @@ $3=_st($2)._signal();
 $3;
 };
 return self}, function($ctx1) {$ctx1.fill(self,"validateAssignment",{},smalltalk.ScopeVar)})},
-messageSends: ["ifTrue:", "variableName:", "name", "new", "signal", "or:", "isPseudoVar", "isArgVar"]}),
+messageSends: ["ifTrue:", "or:", "isArgVar", "isPseudoVar", "variableName:", "new", "name", "signal"]}),
 smalltalk.ScopeVar);
 
 
@@ -957,7 +957,7 @@ $3;
 _st(_st(_st(self["@currentScope"])._methodScope())._unknownVariables())._add_(_st(aNode)._value());
 };
 return self}, function($ctx1) {$ctx1.fill(self,"errorUnknownVariable:",{aNode:aNode,identifier:identifier},smalltalk.SemanticAnalyzer)})},
-messageSends: ["value", "ifTrue:ifFalse:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope", "and:", "isVariableGloballyUndefined:", "not", "includes:"]}),
+messageSends: ["value", "ifTrue:ifFalse:", "and:", "not", "includes:", "isVariableGloballyUndefined:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1123,7 +1123,7 @@ $1;
 self._errorShadowingVariable_(aString);
 };
 return self}, function($ctx1) {$ctx1.fill(self,"validateVariableScope:",{aString:aString},smalltalk.SemanticAnalyzer)})},
-messageSends: ["ifNotNil:", "errorShadowingVariable:", "lookupVariable:"]}),
+messageSends: ["ifNotNil:", "lookupVariable:", "errorShadowingVariable:"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1155,7 +1155,7 @@ return _st(self["@currentScope"])._addArg_(each);
 smalltalk.NodeVisitor.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
 self._popScope();
 return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["pushScope:", "newBlockScope", "scope:", "node:", "do:", "validateVariableScope:", "addArg:", "parameters", "visitBlockNode:", "popScope"]}),
+messageSends: ["pushScope:", "newBlockScope", "scope:", "node:", "do:", "parameters", "validateVariableScope:", "addArg:", "visitBlockNode:", "popScope"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1165,10 +1165,6 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-_st(_st(aNode)._nodes())._do_((function(each){
-return smalltalk.withContext(function($ctx2) {
-return _st(each)._receiver_(_st(aNode)._receiver());
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 smalltalk.NodeVisitor.fn.prototype._visitCascadeNode_.apply(_st(self), [aNode]);
 $1=_st(_st(_st(aNode)._nodes())._first())._superSend();
 if(smalltalk.assert($1)){
@@ -1178,7 +1174,7 @@ return _st(each)._superSend_(true);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 };
 return self}, function($ctx1) {$ctx1.fill(self,"visitCascadeNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["do:", "receiver:", "receiver", "nodes", "visitCascadeNode:", "ifTrue:", "superSend:", "superSend", "first"]}),
+messageSends: ["visitCascadeNode:", "ifTrue:", "superSend", "first", "nodes", "do:", "superSend:"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1195,7 +1191,7 @@ _st($1)._name_(_st(aNode)._value());
 $2=_st($1)._yourself();
 _st(aNode)._binding_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["add:", "value", "classReferences", "binding:", "name:", "new", "yourself"]}),
+messageSends: ["add:", "classReferences", "value", "binding:", "name:", "new", "yourself"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1224,7 +1220,7 @@ _st($1)._messageSends_(_st(self._messageSends())._keys());
 $2=_st($1)._superSends_(_st(self._superSends())._keys());
 self._popScope();
 return self}, function($ctx1) {$ctx1.fill(self,"visitMethodNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["pushScope:", "newMethodScope", "scope:", "node:", "do:", "addIVar:", "allInstanceVariableNames", "theClass", "validateVariableScope:", "addArg:", "arguments", "visitMethodNode:", "classReferences:", "classReferences", "messageSends:", "keys", "messageSends", "superSends:", "superSends", "popScope"]}),
+messageSends: ["pushScope:", "newMethodScope", "scope:", "node:", "do:", "allInstanceVariableNames", "theClass", "addIVar:", "arguments", "validateVariableScope:", "addArg:", "visitMethodNode:", "classReferences:", "classReferences", "messageSends:", "keys", "messageSends", "superSends:", "superSends", "popScope"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1243,7 +1239,7 @@ _st(_st(self["@currentScope"])._methodScope())._addNonLocalReturn_(self["@curren
 };
 smalltalk.NodeVisitor.fn.prototype._visitReturnNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["scope:", "ifTrue:ifFalse:", "localReturn:", "addNonLocalReturn:", "methodScope", "isMethodScope", "visitReturnNode:"]}),
+messageSends: ["scope:", "ifTrue:ifFalse:", "isMethodScope", "localReturn:", "addNonLocalReturn:", "methodScope", "visitReturnNode:"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1279,7 +1275,7 @@ _st(_st(self._messageSends())._at_(_st(aNode)._selector()))._add_(aNode);
 _st(aNode)._index_(_st(_st(self._messageSends())._at_(_st(aNode)._selector()))._size());
 smalltalk.NodeVisitor.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["ifTrue:ifFalse:", "superSend:", "value:", "receiver", "at:ifAbsentPut:", "selector", "new", "superSends", "add:", "at:", "ifTrue:", "shouldBeInlined:", "shouldBeAliased:", "includes:", "inlinedSelectors", "=", "value", "messageSends", "index:", "size", "visitSendNode:"]}),
+messageSends: ["ifTrue:ifFalse:", "=", "value", "receiver", "superSend:", "value:", "at:ifAbsentPut:", "superSends", "selector", "new", "add:", "at:", "ifTrue:", "includes:", "inlinedSelectors", "shouldBeInlined:", "shouldBeAliased:", "messageSends", "index:", "size", "visitSendNode:"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1295,7 +1291,7 @@ return _st(self["@currentScope"])._addTemp_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 smalltalk.NodeVisitor.fn.prototype._visitSequenceNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["do:", "validateVariableScope:", "addTemp:", "temps", "visitSequenceNode:"]}),
+messageSends: ["do:", "temps", "validateVariableScope:", "addTemp:", "visitSequenceNode:"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(
@@ -1319,7 +1315,7 @@ $2=$3;
 };
 _st($1)._binding_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["binding:", "ifNil:", "errorUnknownVariable:", "name:", "value", "new", "yourself", "lookupVariable:"]}),
+messageSends: ["binding:", "ifNil:", "lookupVariable:", "errorUnknownVariable:", "name:", "new", "value", "yourself"]}),
 smalltalk.SemanticAnalyzer);
 
 

+ 27 - 31
js/Compiler-Semantic.js

@@ -14,7 +14,7 @@ _st(_st(self._args())._at_(aString))._scope_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addArg:",{aString:aString},smalltalk.LexicalScope)})},
 args: ["aString"],
 source: "addArg: aString\x0a\x09self args at: aString put: (ArgVar on: aString).\x0a\x09(self args at: aString) scope: self",
-messageSends: ["at:put:", "on:", "args", "scope:", "at:"],
+messageSends: ["at:put:", "args", "on:", "scope:", "at:"],
 referencedClasses: ["ArgVar"]
 }),
 smalltalk.LexicalScope);
@@ -32,7 +32,7 @@ _st(_st(self._temps())._at_(aString))._scope_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addTemp:",{aString:aString},smalltalk.LexicalScope)})},
 args: ["aString"],
 source: "addTemp: aString\x0a\x09self temps at: aString put: (TempVar on: aString).\x0a\x09(self temps at: aString) scope: self",
-messageSends: ["at:put:", "on:", "temps", "scope:", "at:"],
+messageSends: ["at:put:", "temps", "on:", "scope:", "at:"],
 referencedClasses: ["TempVar"]
 }),
 smalltalk.LexicalScope);
@@ -68,7 +68,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"allVariableNames",{},smalltalk.LexicalScope)})},
 args: [],
 source: "allVariableNames\x0a\x09^ self args keys, self temps keys",
-messageSends: [",", "keys", "temps", "args"],
+messageSends: [",", "keys", "args", "temps"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -120,7 +120,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"bindingFor:",{aStringOrNode:aStringOrNode},smalltalk.LexicalScope)})},
 args: ["aStringOrNode"],
 source: "bindingFor: aStringOrNode\x0a\x09^ self pseudoVars at: aStringOrNode value ifAbsent: [\x0a\x09\x09self args at: aStringOrNode value ifAbsent: [\x0a\x09\x09\x09self temps at: aStringOrNode value ifAbsent: [ nil ]]]",
-messageSends: ["at:ifAbsent:", "value", "temps", "args", "pseudoVars"],
+messageSends: ["at:ifAbsent:", "pseudoVars", "value", "args", "temps"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -141,7 +141,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"canInlineNonLocalReturns",{},smalltalk.LexicalScope)})},
 args: [],
 source: "canInlineNonLocalReturns\x0a\x09^ self isInlined and: [ self outerScope canInlineNonLocalReturns ]",
-messageSends: ["and:", "canInlineNonLocalReturns", "outerScope", "isInlined"],
+messageSends: ["and:", "isInlined", "canInlineNonLocalReturns", "outerScope"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -214,7 +214,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"isInlined",{},smalltalk.LexicalScope)})},
 args: [],
 source: "isInlined\x0a\x09^ self instruction notNil and: [\x0a\x09\x09self instruction isInlined ]",
-messageSends: ["and:", "isInlined", "instruction", "notNil"],
+messageSends: ["and:", "notNil", "instruction", "isInlined"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -262,7 +262,7 @@ return $3;
 }, function($ctx1) {$ctx1.fill(self,"lookupVariable:",{aNode:aNode,lookup:lookup},smalltalk.LexicalScope)})},
 args: ["aNode"],
 source: "lookupVariable: aNode\x0a\x09| lookup |\x0a\x09lookup := (self bindingFor: aNode).\x0a\x09lookup ifNil: [\x0a\x09\x09lookup := self outerScope ifNotNil: [\x0a\x09\x09\x09(self outerScope lookupVariable: aNode) ]].\x0a\x09^ lookup",
-messageSends: ["bindingFor:", "ifNil:", "ifNotNil:", "lookupVariable:", "outerScope"],
+messageSends: ["bindingFor:", "ifNil:", "ifNotNil:", "outerScope", "lookupVariable:"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -285,7 +285,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"methodScope",{},smalltalk.LexicalScope)})},
 args: [],
 source: "methodScope\x0a\x09^ self outerScope ifNotNil: [\x0a\x09\x09self outerScope methodScope ]",
-messageSends: ["ifNotNil:", "methodScope", "outerScope"],
+messageSends: ["ifNotNil:", "outerScope", "methodScope"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -400,7 +400,7 @@ return $4;
 }, function($ctx1) {$ctx1.fill(self,"scopeLevel",{},smalltalk.LexicalScope)})},
 args: [],
 source: "scopeLevel\x0a\x09self outerScope ifNil: [ ^ 1 ].\x0a\x09self isInlined ifTrue: [ ^ self outerScope scopeLevel ].\x0a\x09\x0a\x09^ self outerScope scopeLevel + 1",
-messageSends: ["ifNil:", "outerScope", "ifTrue:", "scopeLevel", "isInlined", "+"],
+messageSends: ["ifNil:", "outerScope", "ifTrue:", "isInlined", "scopeLevel", "+"],
 referencedClasses: []
 }),
 smalltalk.LexicalScope);
@@ -447,7 +447,7 @@ _st(_st(self._iVars())._at_(aString))._scope_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addIVar:",{aString:aString},smalltalk.MethodLexicalScope)})},
 args: ["aString"],
 source: "addIVar: aString\x0a\x09self iVars at: aString put: (InstanceVar on: aString).\x0a\x09(self iVars at: aString) scope: self",
-messageSends: ["at:put:", "on:", "iVars", "scope:", "at:"],
+messageSends: ["at:put:", "iVars", "on:", "scope:", "at:"],
 referencedClasses: ["InstanceVar"]
 }),
 smalltalk.MethodLexicalScope);
@@ -481,7 +481,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"allVariableNames",{},smalltalk.MethodLexicalScope)})},
 args: [],
 source: "allVariableNames\x0a\x09^ super allVariableNames, self iVars keys",
-messageSends: [",", "keys", "iVars", "allVariableNames"],
+messageSends: [",", "allVariableNames", "keys", "iVars"],
 referencedClasses: []
 }),
 smalltalk.MethodLexicalScope);
@@ -507,7 +507,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"bindingFor:",{aNode:aNode},smalltalk.MethodLexicalScope)})},
 args: ["aNode"],
 source: "bindingFor: aNode\x0a\x09^ (super bindingFor: aNode) ifNil: [\x0a\x09\x09self iVars at: aNode value ifAbsent: [ nil ]]",
-messageSends: ["ifNil:", "at:ifAbsent:", "value", "iVars", "bindingFor:"],
+messageSends: ["ifNil:", "bindingFor:", "at:ifAbsent:", "iVars", "value"],
 referencedClasses: []
 }),
 smalltalk.MethodLexicalScope);
@@ -694,8 +694,8 @@ category: 'accessing',
 fn: function (){
 var self=this;
 function $Dictionary(){return smalltalk.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
-function $PseudoVar(){return smalltalk.PseudoVar||(typeof PseudoVar=="undefined"?nil:PseudoVar)}
 function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+function $PseudoVar(){return smalltalk.PseudoVar||(typeof PseudoVar=="undefined"?nil:PseudoVar)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2,$3,$4;
 $1=self["@pseudoVars"];
@@ -717,8 +717,8 @@ return $4;
 }, function($ctx1) {$ctx1.fill(self,"pseudoVars",{},smalltalk.MethodLexicalScope)})},
 args: [],
 source: "pseudoVars\x0a\x09pseudoVars ifNil: [\x0a\x09\x09pseudoVars := Dictionary new.\x0a\x09\x09Smalltalk current pseudoVariableNames do: [ :each |\x0a\x09\x09\x09pseudoVars at: each put: ((PseudoVar on: each)\x0a\x09\x09\x09\x09scope: self methodScope;\x0a\x09\x09\x09\x09yourself) ]].\x0a\x09^ pseudoVars",
-messageSends: ["ifNil:", "new", "do:", "at:put:", "scope:", "methodScope", "on:", "yourself", "pseudoVariableNames", "current"],
-referencedClasses: ["Dictionary", "PseudoVar", "Smalltalk"]
+messageSends: ["ifNil:", "new", "do:", "pseudoVariableNames", "current", "at:put:", "scope:", "on:", "methodScope", "yourself"],
+referencedClasses: ["Dictionary", "Smalltalk", "PseudoVar"]
 }),
 smalltalk.MethodLexicalScope);
 
@@ -973,7 +973,7 @@ $3;
 return self}, function($ctx1) {$ctx1.fill(self,"validateAssignment",{},smalltalk.ScopeVar)})},
 args: [],
 source: "validateAssignment\x0a\x09(self isArgVar or: [ self isPseudoVar ]) ifTrue: [\x0a\x09\x09InvalidAssignmentError new\x0a\x09\x09\x09variableName: self name;\x0a\x09\x09\x09signal]",
-messageSends: ["ifTrue:", "variableName:", "name", "new", "signal", "or:", "isPseudoVar", "isArgVar"],
+messageSends: ["ifTrue:", "or:", "isArgVar", "isPseudoVar", "variableName:", "new", "name", "signal"],
 referencedClasses: ["InvalidAssignmentError"]
 }),
 smalltalk.ScopeVar);
@@ -1286,7 +1286,7 @@ _st(_st(_st(self["@currentScope"])._methodScope())._unknownVariables())._add_(_s
 return self}, function($ctx1) {$ctx1.fill(self,"errorUnknownVariable:",{aNode:aNode,identifier:identifier},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "errorUnknownVariable: aNode\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a\x09We allow four variable names in addition: `jQuery`, `window`, `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a\x09identifier := aNode value.\x0a\x09\x0a\x09((#('jQuery' 'window' 'document' 'process' 'global') includes: identifier) not\x0a\x09\x09and: [ self isVariableGloballyUndefined: identifier ])\x0a\x09\x09\x09ifTrue: [\x0a\x09\x09\x09\x09UnknownVariableError new\x0a\x09\x09\x09\x09\x09variableName: aNode value;\x0a\x09\x09\x09\x09\x09signal ]\x0a\x09\x09\x09ifFalse: [\x0a\x09\x09\x09\x09currentScope methodScope unknownVariables add: aNode value ]",
-messageSends: ["value", "ifTrue:ifFalse:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope", "and:", "isVariableGloballyUndefined:", "not", "includes:"],
+messageSends: ["value", "ifTrue:ifFalse:", "and:", "not", "includes:", "isVariableGloballyUndefined:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope"],
 referencedClasses: ["UnknownVariableError"]
 }),
 smalltalk.SemanticAnalyzer);
@@ -1507,7 +1507,7 @@ self._errorShadowingVariable_(aString);
 return self}, function($ctx1) {$ctx1.fill(self,"validateVariableScope:",{aString:aString},smalltalk.SemanticAnalyzer)})},
 args: ["aString"],
 source: "validateVariableScope: aString\x0a\x09\x22Validate the variable scope in by doing a recursive lookup, up to the method scope\x22\x0a\x0a\x09(currentScope lookupVariable: aString) ifNotNil: [\x0a\x09\x09self errorShadowingVariable: aString ]",
-messageSends: ["ifNotNil:", "errorShadowingVariable:", "lookupVariable:"],
+messageSends: ["ifNotNil:", "lookupVariable:", "errorShadowingVariable:"],
 referencedClasses: []
 }),
 smalltalk.SemanticAnalyzer);
@@ -1549,7 +1549,7 @@ self._popScope();
 return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitBlockNode: aNode\x0a\x09self pushScope: self newBlockScope.\x0a\x09aNode scope: currentScope.\x0a\x09currentScope node: aNode.\x0a\x09\x0a\x09aNode parameters do: [ :each |\x0a\x09\x09self validateVariableScope: each.\x0a\x09\x09currentScope addArg: each ].\x0a\x0a\x09super visitBlockNode: aNode.\x0a\x09self popScope",
-messageSends: ["pushScope:", "newBlockScope", "scope:", "node:", "do:", "validateVariableScope:", "addArg:", "parameters", "visitBlockNode:", "popScope"],
+messageSends: ["pushScope:", "newBlockScope", "scope:", "node:", "do:", "parameters", "validateVariableScope:", "addArg:", "visitBlockNode:", "popScope"],
 referencedClasses: []
 }),
 smalltalk.SemanticAnalyzer);
@@ -1562,10 +1562,6 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-_st(_st(aNode)._nodes())._do_((function(each){
-return smalltalk.withContext(function($ctx2) {
-return _st(each)._receiver_(_st(aNode)._receiver());
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 smalltalk.NodeVisitor.fn.prototype._visitCascadeNode_.apply(_st(self), [aNode]);
 $1=_st(_st(_st(aNode)._nodes())._first())._superSend();
 if(smalltalk.assert($1)){
@@ -1576,8 +1572,8 @@ return _st(each)._superSend_(true);
 };
 return self}, function($ctx1) {$ctx1.fill(self,"visitCascadeNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
-source: "visitCascadeNode: aNode\x0a\x09\x22Populate the receiver into all children\x22\x0a\x09aNode nodes do: [ :each |\x0a\x09\x09each receiver: aNode receiver ].\x0a\x09super visitCascadeNode: aNode.\x0a\x09aNode nodes first superSend ifTrue: [\x0a\x09\x09aNode nodes do: [ :each | each superSend: true ]]",
-messageSends: ["do:", "receiver:", "receiver", "nodes", "visitCascadeNode:", "ifTrue:", "superSend:", "superSend", "first"],
+source: "visitCascadeNode: aNode\x0a\x09super visitCascadeNode: aNode.\x0a\x09aNode nodes first superSend ifTrue: [\x0a\x09\x09aNode nodes do: [ :each | each superSend: true ]]",
+messageSends: ["visitCascadeNode:", "ifTrue:", "superSend", "first", "nodes", "do:", "superSend:"],
 referencedClasses: []
 }),
 smalltalk.SemanticAnalyzer);
@@ -1599,7 +1595,7 @@ _st(aNode)._binding_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitClassReferenceNode: aNode\x0a\x09self classReferences add: aNode value.\x0a\x09aNode binding: (ClassRefVar new name: aNode value; yourself)",
-messageSends: ["add:", "value", "classReferences", "binding:", "name:", "new", "yourself"],
+messageSends: ["add:", "classReferences", "value", "binding:", "name:", "new", "yourself"],
 referencedClasses: ["ClassRefVar"]
 }),
 smalltalk.SemanticAnalyzer);
@@ -1633,7 +1629,7 @@ self._popScope();
 return self}, function($ctx1) {$ctx1.fill(self,"visitMethodNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitMethodNode: aNode\x0a\x09self pushScope: self newMethodScope.\x0a\x09aNode scope: currentScope.\x0a\x09currentScope node: aNode.\x0a\x0a\x09self theClass allInstanceVariableNames do: [:each |\x0a\x09\x09currentScope addIVar: each ].\x0a\x09aNode arguments do: [ :each |\x0a\x09\x09self validateVariableScope: each.\x0a\x09\x09currentScope addArg: each ].\x0a\x0a\x09super visitMethodNode: aNode.\x0a\x0a\x09aNode\x0a\x09\x09classReferences: self classReferences;\x0a\x09\x09messageSends: self messageSends keys;\x0a\x09\x09superSends: self superSends keys.\x0a\x09self popScope",
-messageSends: ["pushScope:", "newMethodScope", "scope:", "node:", "do:", "addIVar:", "allInstanceVariableNames", "theClass", "validateVariableScope:", "addArg:", "arguments", "visitMethodNode:", "classReferences:", "classReferences", "messageSends:", "keys", "messageSends", "superSends:", "superSends", "popScope"],
+messageSends: ["pushScope:", "newMethodScope", "scope:", "node:", "do:", "allInstanceVariableNames", "theClass", "addIVar:", "arguments", "validateVariableScope:", "addArg:", "visitMethodNode:", "classReferences:", "classReferences", "messageSends:", "keys", "messageSends", "superSends:", "superSends", "popScope"],
 referencedClasses: []
 }),
 smalltalk.SemanticAnalyzer);
@@ -1657,7 +1653,7 @@ smalltalk.NodeVisitor.fn.prototype._visitReturnNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitReturnNode: aNode\x0a\x09aNode scope: currentScope.\x0a\x09currentScope isMethodScope\x0a\x09\x09ifTrue: [ currentScope localReturn: true ]\x0a\x09\x09ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].\x0a\x09super visitReturnNode: aNode",
-messageSends: ["scope:", "ifTrue:ifFalse:", "localReturn:", "addNonLocalReturn:", "methodScope", "isMethodScope", "visitReturnNode:"],
+messageSends: ["scope:", "ifTrue:ifFalse:", "isMethodScope", "localReturn:", "addNonLocalReturn:", "methodScope", "visitReturnNode:"],
 referencedClasses: []
 }),
 smalltalk.SemanticAnalyzer);
@@ -1698,7 +1694,7 @@ smalltalk.NodeVisitor.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitSendNode: aNode\x0a\x0a\x09aNode receiver value = 'super'\x0a\x09\x09ifTrue: [\x0a\x09\x09\x09aNode superSend: true.\x0a\x09\x09\x09aNode receiver value: 'self'.\x0a\x09\x09\x09self superSends at: aNode selector ifAbsentPut: [ Set new ].\x0a\x09\x09\x09(self superSends at: aNode selector) add: aNode ]\x0a\x09\x09\x0a\x09\x09ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [\x0a\x09\x09\x09aNode shouldBeInlined: true.\x0a\x09\x09\x09aNode receiver shouldBeAliased: true ] ].\x0a\x0a\x09self messageSends at: aNode selector ifAbsentPut: [ Set new ].\x0a\x09(self messageSends at: aNode selector) add: aNode.\x0a\x0a\x09aNode index: (self messageSends at: aNode selector) size.\x0a\x0a\x09super visitSendNode: aNode",
-messageSends: ["ifTrue:ifFalse:", "superSend:", "value:", "receiver", "at:ifAbsentPut:", "selector", "new", "superSends", "add:", "at:", "ifTrue:", "shouldBeInlined:", "shouldBeAliased:", "includes:", "inlinedSelectors", "=", "value", "messageSends", "index:", "size", "visitSendNode:"],
+messageSends: ["ifTrue:ifFalse:", "=", "value", "receiver", "superSend:", "value:", "at:ifAbsentPut:", "superSends", "selector", "new", "add:", "at:", "ifTrue:", "includes:", "inlinedSelectors", "shouldBeInlined:", "shouldBeAliased:", "messageSends", "index:", "size", "visitSendNode:"],
 referencedClasses: ["Set", "IRSendInliner"]
 }),
 smalltalk.SemanticAnalyzer);
@@ -1719,7 +1715,7 @@ smalltalk.NodeVisitor.fn.prototype._visitSequenceNode_.apply(_st(self), [aNode])
 return self}, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitSequenceNode: aNode\x0a\x09aNode temps do: [ :each |\x0a\x09\x09self validateVariableScope: each.\x0a\x09\x09currentScope addTemp: each ].\x0a\x0a\x09super visitSequenceNode: aNode",
-messageSends: ["do:", "validateVariableScope:", "addTemp:", "temps", "visitSequenceNode:"],
+messageSends: ["do:", "temps", "validateVariableScope:", "addTemp:", "visitSequenceNode:"],
 referencedClasses: []
 }),
 smalltalk.SemanticAnalyzer);
@@ -1748,7 +1744,7 @@ _st($1)._binding_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
 source: "visitVariableNode: aNode\x0a\x09\x22Bind a ScopeVar to aNode by doing a lookup in the current scope.\x0a\x09If no ScopeVar is found, bind a UnknowVar and throw an error\x22\x0a\x0a\x09aNode binding: ((currentScope lookupVariable: aNode) ifNil: [\x0a\x09\x09self errorUnknownVariable: aNode.\x0a\x09\x09UnknownVar new name: aNode value; yourself ])",
-messageSends: ["binding:", "ifNil:", "errorUnknownVariable:", "name:", "value", "new", "yourself", "lookupVariable:"],
+messageSends: ["binding:", "ifNil:", "lookupVariable:", "errorUnknownVariable:", "name:", "new", "value", "yourself"],
 referencedClasses: ["UnknownVar"]
 }),
 smalltalk.SemanticAnalyzer);

+ 216 - 0
js/Compiler-Tests.deploy.js

@@ -491,6 +491,222 @@ smalltalk.ASTSteppingInterpreterTest);
 
 
 
+smalltalk.addClass('InterpreterTest', smalltalk.AbstractASTInterpreterTest, [], 'Compiler-Tests');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpret:receiver:withArguments:",
+fn: function (aString,anObject,aDictionary){
+var self=this;
+var ctx;
+function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+ctx=_st($AIContext())._new();
+_st(ctx)._receiver_(anObject);
+_st(aDictionary)._keysAndValuesDo_((function(key,value){
+return smalltalk.withContext(function($ctx2) {
+return _st(ctx)._localAt_put_(key,value);
+}, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1)})}));
+$2=self._interpreter();
+_st($2)._context_(ctx);
+_st($2)._interpret_(_st(self._parse_forClass_(aString,_st(anObject)._class()))._nextChild());
+_st($2)._proceed();
+$3=_st($2)._result();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx},smalltalk.InterpreterTest)})},
+messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "nextChild", "parse:forClass:", "class", "proceed", "result"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpreter",
+fn: function (){
+var self=this;
+function $Interpreter(){return smalltalk.Interpreter||(typeof Interpreter=="undefined"?nil:Interpreter)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st($Interpreter())._new();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpreter",{},smalltalk.InterpreterTest)})},
+messageSends: ["new"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testBinarySend",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ 2+3+4"),(9));
+return self}, function($ctx1) {$ctx1.fill(self,"testBinarySend",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testBlockLiteral",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ true ifTrue: [ 1 ] ifFalse: [ 2 ]"),(1));
+self._assert_equals_(self._interpret_("foo true ifTrue: [ ^ 1 ] ifFalse: [ 2 ]"),(1));
+self._assert_equals_(self._interpret_("foo ^ false ifTrue: [ 1 ] ifFalse: [ 2 ]"),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testBlockLiteral",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testCascade",
+fn: function (){
+var self=this;
+function $OrderedCollection(){return smalltalk.OrderedCollection||(typeof OrderedCollection=="undefined"?nil:OrderedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ OrderedCollection new add: 2; add: 3; yourself"),_st($OrderedCollection())._with_with_((2),(3)));
+return self}, function($ctx1) {$ctx1.fill(self,"testCascade",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:", "with:with:"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testDynamicArray",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ {1+1. 2+2}"),[(2), (4)]);
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicArray",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testDynamicDictionary",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ #{1->1. 2->3}"),smalltalk.HashedCollection._fromPairs_([(1).__minus_gt((1)),(2).__minus_gt((3))]));
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicDictionary",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:", "->"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testInlinedJSStatement",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo <return 2+3>"),(5));
+self._assert_equals_(self._interpret_withArguments_("foo: anInteger <return 2 + anInteger>",smalltalk.HashedCollection._fromPairs_(["anInteger".__minus_gt((3))])),(5));
+return self}, function($ctx1) {$ctx1.fill(self,"testInlinedJSStatement",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:", "interpret:withArguments:", "->"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testInstVarAccess",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo ^ x",(2).__at((3)),smalltalk.HashedCollection._fromPairs_([])),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAccess",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "@"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testInstVarAssignment",
+fn: function (){
+var self=this;
+function $Point(){return smalltalk.Point||(typeof Point=="undefined"?nil:Point)}
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo: anInteger x := anInteger. ^ x",_st($Point())._new(),smalltalk.HashedCollection._fromPairs_(["anInteger".__minus_gt((2))])),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAssignment",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "new", "->"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testKeywordSend",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ Point x: 1 y: 2"),(1).__at((2)));
+return self}, function($ctx1) {$ctx1.fill(self,"testKeywordSend",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:", "@"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testNonlocalReturn",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo true ifTrue: [ ^ 1 ]. ^2"),(1));
+return self}, function($ctx1) {$ctx1.fill(self,"testNonlocalReturn",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testReceiver",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo ^ self",(2).__at((3)),smalltalk.HashedCollection._fromPairs_([])),(2).__at((3)));
+return self}, function($ctx1) {$ctx1.fill(self,"testReceiver",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "@"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testSuper",
+fn: function (){
+var self=this;
+function $Dictionary(){return smalltalk.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo ^ super isBoolean",true,_st($Dictionary())._new()),false);
+return self}, function($ctx1) {$ctx1.fill(self,"testSuper",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "new"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testTempAssignment",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo | a | a := 2. ^ a"),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testTempAssignment",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testThisContext",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_(_st(_st(self._interpret_("foo ^ thisContext"))._outerContext())._isNil());
+self._assert_(_st(_st(self._interpret_("foo ^ [ thisContext ] value"))._outerContext())._notNil());
+self._assert_(self._interpret_("foo ^ [ thisContext ] value outerContext == thisContext"));
+return self}, function($ctx1) {$ctx1.fill(self,"testThisContext",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:", "isNil", "outerContext", "interpret:", "notNil"]}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testUnarySend",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ 1 asString"),"1");
+return self}, function($ctx1) {$ctx1.fill(self,"testUnarySend",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
+
+
 smalltalk.addClass('CodeGeneratorTest', smalltalk.TestCase, ['receiver'], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({

+ 301 - 0
js/Compiler-Tests.js

@@ -651,6 +651,307 @@ smalltalk.ASTSteppingInterpreterTest);
 
 
 
+smalltalk.addClass('InterpreterTest', smalltalk.AbstractASTInterpreterTest, [], 'Compiler-Tests');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpret:receiver:withArguments:",
+category: 'as yet unclassified',
+fn: function (aString,anObject,aDictionary){
+var self=this;
+var ctx;
+function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+ctx=_st($AIContext())._new();
+_st(ctx)._receiver_(anObject);
+_st(aDictionary)._keysAndValuesDo_((function(key,value){
+return smalltalk.withContext(function($ctx2) {
+return _st(ctx)._localAt_put_(key,value);
+}, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1)})}));
+$2=self._interpreter();
+_st($2)._context_(ctx);
+_st($2)._interpret_(_st(self._parse_forClass_(aString,_st(anObject)._class()))._nextChild());
+_st($2)._proceed();
+$3=_st($2)._result();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx},smalltalk.InterpreterTest)})},
+args: ["aString", "anObject", "aDictionary"],
+source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx |\x0a\x09\x0a\x09ctx := AIContext new.\x0a\x09ctx receiver: anObject.\x0a\x09aDictionary keysAndValuesDo: [ :key :value |\x0a\x09\x09ctx localAt: key put: value ].\x0a\x09\x0a\x09^ self interpreter\x0a\x09\x09context: ctx;\x0a\x09\x09interpret: (self parse: aString forClass: anObject class) nextChild;\x0a\x09\x09proceed;\x0a\x09\x09result",
+messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "nextChild", "parse:forClass:", "class", "proceed", "result"],
+referencedClasses: ["AIContext"]
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpreter",
+category: 'accessing',
+fn: function (){
+var self=this;
+function $Interpreter(){return smalltalk.Interpreter||(typeof Interpreter=="undefined"?nil:Interpreter)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st($Interpreter())._new();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpreter",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "interpreter\x0a\x09^ Interpreter new",
+messageSends: ["new"],
+referencedClasses: ["Interpreter"]
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testBinarySend",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ 2+3+4"),(9));
+return self}, function($ctx1) {$ctx1.fill(self,"testBinarySend",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testBinarySend\x0a\x09self assert: (self interpret: 'foo ^ 2+3+4') equals: 9",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testBlockLiteral",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ true ifTrue: [ 1 ] ifFalse: [ 2 ]"),(1));
+self._assert_equals_(self._interpret_("foo true ifTrue: [ ^ 1 ] ifFalse: [ 2 ]"),(1));
+self._assert_equals_(self._interpret_("foo ^ false ifTrue: [ 1 ] ifFalse: [ 2 ]"),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testBlockLiteral",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testBlockLiteral\x0a\x09self assert: (self interpret: 'foo ^ true ifTrue: [ 1 ] ifFalse: [ 2 ]') equals: 1.\x0a\x09self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ] ifFalse: [ 2 ]') equals: 1.\x0a\x09self assert: (self interpret: 'foo ^ false ifTrue: [ 1 ] ifFalse: [ 2 ]') equals: 2",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testCascade",
+category: 'tests',
+fn: function (){
+var self=this;
+function $OrderedCollection(){return smalltalk.OrderedCollection||(typeof OrderedCollection=="undefined"?nil:OrderedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ OrderedCollection new add: 2; add: 3; yourself"),_st($OrderedCollection())._with_with_((2),(3)));
+return self}, function($ctx1) {$ctx1.fill(self,"testCascade",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testCascade\x0a\x09self assert: (self interpret: 'foo ^ OrderedCollection new add: 2; add: 3; yourself') equals: (OrderedCollection with: 2 with: 3)",
+messageSends: ["assert:equals:", "interpret:", "with:with:"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testDynamicArray",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ {1+1. 2+2}"),[(2), (4)]);
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicArray",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testDynamicArray\x0a\x09self assert: (self interpret: 'foo ^ {1+1. 2+2}') equals: #(2 4)",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testDynamicDictionary",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ #{1->1. 2->3}"),smalltalk.HashedCollection._fromPairs_([(1).__minus_gt((1)),(2).__minus_gt((3))]));
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicDictionary",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testDynamicDictionary\x0a\x09self assert: (self interpret: 'foo ^ #{1->1. 2->3}') equals: #{1->1. 2->3}",
+messageSends: ["assert:equals:", "interpret:", "->"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testInlinedJSStatement",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo <return 2+3>"),(5));
+self._assert_equals_(self._interpret_withArguments_("foo: anInteger <return 2 + anInteger>",smalltalk.HashedCollection._fromPairs_(["anInteger".__minus_gt((3))])),(5));
+return self}, function($ctx1) {$ctx1.fill(self,"testInlinedJSStatement",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testInlinedJSStatement\x0a\x09self assert: (self interpret: 'foo <return 2+3>') equals: 5.\x0a\x09\x0a\x09self\x0a\x09\x09assert: (self\x0a\x09\x09\x09interpret: 'foo: anInteger <return 2 + anInteger>'\x0a\x09\x09\x09withArguments: #{ 'anInteger' -> 3})\x0a\x09\x09equals: 5",
+messageSends: ["assert:equals:", "interpret:", "interpret:withArguments:", "->"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testInstVarAccess",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo ^ x",(2).__at((3)),smalltalk.HashedCollection._fromPairs_([])),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAccess",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testInstVarAccess\x0a\x09self\x0a\x09\x09assert: (self\x0a\x09\x09\x09interpret: 'foo ^ x'\x0a\x09\x09\x09receiver: 2@3\x0a\x09\x09\x09withArguments: #{})\x0a\x09\x09equals: 2",
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "@"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testInstVarAssignment",
+category: 'tests',
+fn: function (){
+var self=this;
+function $Point(){return smalltalk.Point||(typeof Point=="undefined"?nil:Point)}
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo: anInteger x := anInteger. ^ x",_st($Point())._new(),smalltalk.HashedCollection._fromPairs_(["anInteger".__minus_gt((2))])),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAssignment",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testInstVarAssignment\x0a\x09self\x0a\x09\x09assert: (self\x0a\x09\x09\x09interpret: 'foo: anInteger x := anInteger. ^ x'\x0a\x09\x09\x09receiver: Point new\x0a\x09\x09\x09withArguments: #{'anInteger' -> 2})\x0a\x09\x09equals: 2",
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "new", "->"],
+referencedClasses: ["Point"]
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testKeywordSend",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ Point x: 1 y: 2"),(1).__at((2)));
+return self}, function($ctx1) {$ctx1.fill(self,"testKeywordSend",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testKeywordSend\x0a\x09self assert: (self interpret: 'foo ^ Point x: 1 y: 2') equals: 1@2",
+messageSends: ["assert:equals:", "interpret:", "@"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testNonlocalReturn",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo true ifTrue: [ ^ 1 ]. ^2"),(1));
+return self}, function($ctx1) {$ctx1.fill(self,"testNonlocalReturn",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testNonlocalReturn\x0a\x09self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ]. ^2') equals: 1",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testReceiver",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo ^ self",(2).__at((3)),smalltalk.HashedCollection._fromPairs_([])),(2).__at((3)));
+return self}, function($ctx1) {$ctx1.fill(self,"testReceiver",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testReceiver\x0a\x09self\x0a\x09\x09assert: (self\x0a\x09\x09\x09interpret: 'foo ^ self'\x0a\x09\x09\x09receiver: 2@3\x0a\x09\x09\x09withArguments: #{})\x0a\x09\x09equals: 2@3",
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "@"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testSuper",
+category: 'tests',
+fn: function (){
+var self=this;
+function $Dictionary(){return smalltalk.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_receiver_withArguments_("foo ^ super isBoolean",true,_st($Dictionary())._new()),false);
+return self}, function($ctx1) {$ctx1.fill(self,"testSuper",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testSuper\x0a\x09self \x0a\x09\x09assert: (self \x0a\x09\x09\x09interpret: 'foo ^ super isBoolean' \x0a\x09\x09\x09receiver: true \x0a\x09\x09\x09withArguments: Dictionary new) \x0a\x09\x09equals: false",
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "new"],
+referencedClasses: ["Dictionary"]
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testTempAssignment",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo | a | a := 2. ^ a"),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testTempAssignment",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testTempAssignment\x0a\x09self assert: (self interpret: 'foo | a | a := 2. ^ a') equals: 2",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testThisContext",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_(_st(_st(self._interpret_("foo ^ thisContext"))._outerContext())._isNil());
+self._assert_(_st(_st(self._interpret_("foo ^ [ thisContext ] value"))._outerContext())._notNil());
+self._assert_(self._interpret_("foo ^ [ thisContext ] value outerContext == thisContext"));
+return self}, function($ctx1) {$ctx1.fill(self,"testThisContext",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testThisContext\x0a\x09self assert: (self interpret: 'foo ^ thisContext') outerContext isNil.\x0a\x09self assert: (self interpret: 'foo ^ [ thisContext ] value') outerContext notNil.\x0a\x09self assert: (self interpret: 'foo ^ [ thisContext ] value outerContext == thisContext')",
+messageSends: ["assert:", "isNil", "outerContext", "interpret:", "notNil"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testUnarySend",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ 1 asString"),"1");
+return self}, function($ctx1) {$ctx1.fill(self,"testUnarySend",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testUnarySend\x0a\x09self assert: (self interpret: 'foo ^ 1 asString') equals: '1'",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
+
+
 smalltalk.addClass('CodeGeneratorTest', smalltalk.TestCase, ['receiver'], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({

+ 40 - 10
st/Compiler-AST.st

@@ -16,14 +16,6 @@ addNode: aNode
 	aNode parent: self
 !
 
-extent
-	"Answer the line and column of the end position of the receiver in the source code"
-	
-	^ self nextNode 
-		ifNil: [ self parent ifNotNil: [ :node | node extent ] ]
-		ifNotNil: [ :node | node position ]
-!
-
 nextChild
 	"Answer the next node after aNode.
 	Recurse into the possible children of the receiver to answer the next node to be evaluated"
@@ -97,6 +89,13 @@ position: aPosition
 	position := aPosition
 ! !
 
+!Node methodsFor: 'copying'!
+
+postCopy
+	super postCopy.
+	self nodes do: [ :each | each parent: self ]
+! !
+
 !Node methodsFor: 'testing'!
 
 isAssignmentNode
@@ -111,6 +110,10 @@ isBlockSequenceNode
 	^false
 !
 
+isCascadeNode
+	^ false
+!
+
 isImmutable
 	^false
 !
@@ -119,6 +122,10 @@ isJSStatementNode
 	^ false
 !
 
+isLastChild
+	^ self parent nodes last = self
+!
+
 isNode
 	^ true
 !
@@ -200,6 +207,12 @@ I represent an block closure node.!
 
 !BlockNode methodsFor: 'accessing'!
 
+nextChild
+	"Answer the receiver as we want to avoid eager evaluation"
+	
+	^ self
+!
+
 nextNode: aNode
 	"Answer the receiver as we want to avoid eager evaluation"
 	
@@ -254,6 +267,12 @@ receiver: aNode
 	receiver := aNode
 ! !
 
+!CascadeNode methodsFor: 'testing'!
+
+isCascadeNode
+	^ true
+! !
+
 !CascadeNode methodsFor: 'visiting'!
 
 accept: aVisitor
@@ -458,8 +477,10 @@ index: anInteger
 !
 
 nodes
-	^ (Array withAll: self arguments)
-		add: self receiver;
+	self receiver ifNil: [ ^ self arguments copy ].
+	
+	^ (Array with: self receiver)
+		addAll: self arguments;
 		yourself
 !
 
@@ -502,6 +523,10 @@ valueForReceiver: anObject
 
 !SendNode methodsFor: 'testing'!
 
+isCascadeSendNode
+	^ self parent isCascadeNode
+!
+
 isSendNode
 	^ true
 !
@@ -606,6 +631,11 @@ accept: aVisitor
 	^ aVisitor visitValueNode: self
 ! !
 
+!ValueNode methodsFor: 'xxxDoIt'!
+
+xxxDoIt ^[self stack] value
+! !
+
 ValueNode subclass: #VariableNode
 	instanceVariableNames: 'assigned binding'
 	package: 'Compiler-AST'!

+ 83 - 10
st/Compiler-Interpreter.st

@@ -1,6 +1,6 @@
 Smalltalk current createPackage: 'Compiler-Interpreter'!
 NodeVisitor subclass: #AIContext
-	instanceVariableNames: 'outerContext innerContext pc locals method ast interpreter'
+	instanceVariableNames: 'outerContext innerContext pc locals method ast interpreter methodContext'
 	package: 'Compiler-Interpreter'!
 !AIContext commentStamp!
 I am like a `MethodContext`, used by the `ASTInterpreter`.
@@ -703,8 +703,12 @@ node: aNode
 	node := aNode
 !
 
+result
+	^ self returnValue ifNil: [ self context receiver ]
+!
+
 returnValue
-	^ returnValue ifNil: [ self context receiver ]
+	^ returnValue
 !
 
 returnValue: anObject
@@ -723,6 +727,11 @@ interpret
 	self visit: self node
 !
 
+interpret: aNode
+	self node: aNode.
+	self interpret
+!
+
 next
 	self node: self node nextNode
 !
@@ -730,7 +739,7 @@ next
 proceed
 	"Eagerly evaluate the ast"
 	
-	[ self node notNil ] whileTrue: [ 
+	[ self atEnd ] whileFalse: [ 
 		self step ]
 !
 
@@ -831,7 +840,7 @@ atEnd
 !
 
 shouldReturn
-	^ returnValue notNil
+	^ self returnValue notNil
 ! !
 
 !Interpreter methodsFor: 'visiting'!
@@ -841,15 +850,75 @@ visit: aNode
 !
 
 visitAssignmentNode: aNode
-	"Do not pop as the pushed value would be the right node"
+	| value |
+	
+	value := self pop.
 	
-	self assign: aNode left to: self peek
+	"Pop the left side of the assignment.
+	It already has been visited, and we don't need its value."
+	self pop.
+	
+	self push: value.
+	self assign: aNode left to: value
+!
+
+visitBlockNode: aNode
+	"Do not evaluate the block node.
+	Instead, put all instructions into a block that we push to the stack for later evaluation"
+	
+	| blockNode blockContext block interpreter |
+	
+	"Copy the sequence node without the parent to avoid evaluating nodes up the block. 
+	#nextNode should not go anymore up than the block itself"
+	blockNode := aNode nodes first copy.
+	blockNode parent: nil.
+		
+	blockContext := self context class new
+		outerContext: self context;
+		yourself.
+	
+	block := [
+		interpreter := self class new.
+		interpreter
+			context: blockContext;
+			node: blockNode nextChild;
+			proceed.
+		
+		"Non local returns hanlding"
+		self returnValue: interpreter returnValue.
+		
+		"Answer the last evaluation of the block or nil"
+		interpreter stack isEmpty
+			ifTrue: [ nil ]
+			ifFalse: [ interpreter pop ] ].
+	
+	self push: block
 !
 
 visitClassReferenceNode: aNode
 	self push: (Smalltalk current at: aNode value)
 !
 
+visitDynamicArrayNode: aNode
+	| array |
+	
+	array := #().
+	aNode nodes do: [ :each |
+		array addFirst: self pop ].
+	
+	self push: array
+!
+
+visitDynamicDictionaryNode: aNode
+	| hashedCollection |
+	
+	hashedCollection := HashedCollection new.
+	aNode nodes do: [ :each | 
+		hashedCollection add: self pop ].
+	
+	self push: hashedCollection
+!
+
 visitJSStatementNode: aNode
 	self returnValue: (self eval: aNode source)
 !
@@ -865,17 +934,21 @@ visitReturnNode: aNode
 visitSendNode: aNode
 	| receiver args message result |
 	
-	args := aNode arguments collect: [ :each |
-		self pop ].
+	args := aNode arguments collect: [ :each | self pop ].
 	receiver := self pop.
 	
 	message := self
 		messageFromSendNode: aNode
-		arguments: args.
+		arguments: args reversed.
 	
 	result := self sendMessage: message to: receiver superSend: aNode superSend.
 	
-	self push: result
+	self context pc: self context pc + 1.
+	
+	"For cascade sends, push the reciever if the send is not the last one"
+	(aNode isCascadeSendNode and: [ aNode isLastChild not ])
+		ifTrue: [ self push: receiver ]
+		ifFalse: [ self push: result ]
 !
 
 visitValueNode: aNode

+ 0 - 3
st/Compiler-Semantic.st

@@ -498,9 +498,6 @@ visitBlockNode: aNode
 !
 
 visitCascadeNode: aNode
-	"Populate the receiver into all children"
-	aNode nodes do: [ :each |
-		each receiver: aNode receiver ].
 	super visitCascadeNode: aNode.
 	aNode nodes first superSend ifTrue: [
 		aNode nodes do: [ :each | each superSend: true ]]

+ 121 - 0
st/Compiler-Tests.st

@@ -292,6 +292,127 @@ testSimpleStepping
 	self assert: self interpreter result equals: 1
 ! !
 
+AbstractASTInterpreterTest subclass: #InterpreterTest
+	instanceVariableNames: ''
+	package: 'Compiler-Tests'!
+
+!InterpreterTest methodsFor: 'accessing'!
+
+interpreter
+	^ Interpreter new
+! !
+
+!InterpreterTest methodsFor: 'as yet unclassified'!
+
+interpret: aString receiver: anObject withArguments: aDictionary
+	"The food is a methodNode. Interpret the sequenceNode only"
+	
+	| ctx |
+	
+	ctx := AIContext new.
+	ctx receiver: anObject.
+	aDictionary keysAndValuesDo: [ :key :value |
+		ctx localAt: key put: value ].
+	
+	^ self interpreter
+		context: ctx;
+		interpret: (self parse: aString forClass: anObject class) nextChild;
+		proceed;
+		result
+! !
+
+!InterpreterTest methodsFor: 'tests'!
+
+testBinarySend
+	self assert: (self interpret: 'foo ^ 2+3+4') equals: 9
+!
+
+testBlockLiteral
+	self assert: (self interpret: 'foo ^ true ifTrue: [ 1 ] ifFalse: [ 2 ]') equals: 1.
+	self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ] ifFalse: [ 2 ]') equals: 1.
+	self assert: (self interpret: 'foo ^ false ifTrue: [ 1 ] ifFalse: [ 2 ]') equals: 2
+!
+
+testCascade
+	self assert: (self interpret: 'foo ^ OrderedCollection new add: 2; add: 3; yourself') equals: (OrderedCollection with: 2 with: 3)
+!
+
+testDynamicArray
+	self assert: (self interpret: 'foo ^ {1+1. 2+2}') equals: #(2 4)
+!
+
+testDynamicDictionary
+	self assert: (self interpret: 'foo ^ #{1->1. 2->3}') equals: #{1->1. 2->3}
+!
+
+testInlinedJSStatement
+	self assert: (self interpret: 'foo <return 2+3>') equals: 5.
+	
+	self
+		assert: (self
+			interpret: 'foo: anInteger <return 2 + anInteger>'
+			withArguments: #{ 'anInteger' -> 3})
+		equals: 5
+!
+
+testInstVarAccess
+	self
+		assert: (self
+			interpret: 'foo ^ x'
+			receiver: 2@3
+			withArguments: #{})
+		equals: 2
+!
+
+testInstVarAssignment
+	self
+		assert: (self
+			interpret: 'foo: anInteger x := anInteger. ^ x'
+			receiver: Point new
+			withArguments: #{'anInteger' -> 2})
+		equals: 2
+!
+
+testKeywordSend
+	self assert: (self interpret: 'foo ^ Point x: 1 y: 2') equals: 1@2
+!
+
+testNonlocalReturn
+	self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ]. ^2') equals: 1
+!
+
+testReceiver
+	self
+		assert: (self
+			interpret: 'foo ^ self'
+			receiver: 2@3
+			withArguments: #{})
+		equals: 2@3
+!
+
+testSuper
+	self 
+		assert: (self 
+			interpret: 'foo ^ super isBoolean' 
+			receiver: true 
+			withArguments: Dictionary new) 
+		equals: false
+!
+
+testTempAssignment
+	self assert: (self interpret: 'foo | a | a := 2. ^ a') equals: 2
+!
+
+testThisContext
+	self assert: (self interpret: 'foo ^ thisContext') outerContext isNil.
+	self assert: (self interpret: 'foo ^ [ thisContext ] value') outerContext notNil.
+	self assert: (self interpret: 'foo ^ [ thisContext ] value outerContext == thisContext')
+!
+
+testUnarySend
+	self assert: (self interpret: 'foo ^ 1 asString') equals: '1'
+! !
+
 TestCase subclass: #CodeGeneratorTest
 	instanceVariableNames: 'receiver'
 	package: 'Compiler-Tests'!