Browse Source

Use continuations in ASTInterpreter for future stepping

Nicolas Petton 11 years ago
parent
commit
0d947e962f

+ 22 - 0
js/Compiler-AST.deploy.js

@@ -68,6 +68,17 @@ return smalltalk.withContext(function($ctx1) { 
return false;
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+"_isNode",
+smalltalk.method({
+selector: "isNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isNode",{}, smalltalk.Node)})}
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 "_isReturnNode",
 smalltalk.method({
@@ -1299,3 +1310,14 @@ smalltalk.ClassReferenceNode);
 
 
 
+smalltalk.addMethod(
+"_isNode",
+smalltalk.method({
+selector: "isNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return false;
+}, function($ctx1) {$ctx1.fill(self,"isNode",{}, smalltalk.Object)})}
+}),
+smalltalk.Object);
+

+ 32 - 0
js/Compiler-AST.js

@@ -99,6 +99,22 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+"_isNode",
+smalltalk.method({
+selector: "isNode",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isNode",{}, smalltalk.Node)})},
+args: [],
+source: "isNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 "_isReturnNode",
 smalltalk.method({
@@ -1780,3 +1796,19 @@ smalltalk.ClassReferenceNode);
 
 
 
+smalltalk.addMethod(
+"_isNode",
+smalltalk.method({
+selector: "isNode",
+category: '*Compiler-AST',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return false;
+}, function($ctx1) {$ctx1.fill(self,"isNode",{}, smalltalk.Object)})},
+args: [],
+source: "isNode\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Object);
+

+ 1 - 1
js/Compiler-Core.deploy.js

@@ -342,7 +342,7 @@ selector: "visitAll:",
 fn: function (aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(aCollection)._do_((function(each){
+$1=_st(aCollection)._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
return _st(self)._visit_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return $1;

+ 3 - 3
js/Compiler-Core.js

@@ -461,14 +461,14 @@ category: 'visiting',
 fn: function (aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(aCollection)._do_((function(each){
+$1=_st(aCollection)._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
return _st(self)._visit_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"visitAll:",{aCollection:aCollection}, smalltalk.NodeVisitor)})},
 args: ["aCollection"],
-source: "visitAll: aCollection\x0a\x09^ aCollection do: [ :each | self visit: each ]",
-messageSends: ["do:", "visit:"],
+source: "visitAll: aCollection\x0a\x09^ aCollection collect: [ :each | self visit: each ]",
+messageSends: ["collect:", "visit:"],
 referencedClasses: []
 }),
 smalltalk.NodeVisitor);

+ 204 - 100
js/Compiler-Interpreter.deploy.js

@@ -188,7 +188,7 @@ return $1;
 smalltalk.AIContext.klass);
 
 
-smalltalk.addClass('ASTInterpreter', smalltalk.NodeVisitor, ['currentNode', 'context', 'shouldReturn'], 'Compiler-Interpreter');
+smalltalk.addClass('ASTInterpreter', smalltalk.NodeVisitor, ['currentNode', 'context', 'shouldReturn', 'currentValue'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 "_assign_to_",
 smalltalk.method({
@@ -237,6 +237,30 @@ return self}, function($ctx1) {$ctx1.fill(self,"context:",{anAIContext:anAIConte
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_continue_",
+smalltalk.method({
+selector: "continue:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@currentValue"]=anObject;
+return self}, function($ctx1) {$ctx1.fill(self,"continue:",{anObject:anObject}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_currentValue",
+smalltalk.method({
+selector: "currentValue",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@currentValue"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"currentValue",{}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_eval_",
 smalltalk.method({
@@ -283,44 +307,84 @@ smalltalk.method({
 selector: "interpret:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@shouldReturn"]=false;
-$1=_st(self)._interpretNode_(aNode);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpret:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
self["@shouldReturn"]=false;
+_st(self)._interpret_continue_(aNode,(function(value){
+return smalltalk.withContext(function($ctx2) {
self["@currentValue"]=value;
+return self["@currentValue"];
+}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
-"_interpretNode_",
+"_interpret_continue_",
 smalltalk.method({
-selector: "interpretNode:",
-fn: function (aNode){
+selector: "interpret:continue:",
+fn: function (aNode,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+$1=self["@shouldReturn"];
+if(smalltalk.assert($1)){
+$2=self;
+return $2;
+};
+$3=_st(aNode)._isNode();
+if(smalltalk.assert($3)){
+_st(self)._visit_(aNode);
+} else {
+self["@currentValue"]=aNode;
+self["@currentValue"];
+};
+_st(aBlock)._value_(_st(self)._currentValue());
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_interpretAll_continue_",
+smalltalk.method({
+selector: "interpretAll:continue:",
+fn: function (aCollection,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_result_(aCollection,aBlock,_st((smalltalk.OrderedCollection || OrderedCollection))._new());
+return self}, function($ctx1) {$ctx1.fill(self,"interpretAll:continue:",{aCollection:aCollection,aBlock:aBlock}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_interpretAll_continue_result_",
+smalltalk.method({
+selector: "interpretAll:continue:result:",
+fn: function (nodes,aBlock,aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@currentNode"]=aNode;
-$1=_st(self)._visit_(aNode);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpretNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+$1=_st(nodes)._isEmpty();
+if(smalltalk.assert($1)){
+_st(aBlock)._value_(aCollection);
+} else {
+_st(self)._interpret_continue_(_st(nodes)._first(),(function(value){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._interpretAll_continue_result_(_st(nodes)._allButFirst(),aBlock,_st(aCollection).__comma([value]));
+}, 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)})}
 }),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
-"_messageFromSendNode_",
+"_messageFromSendNode_do_",
 smalltalk.method({
-selector: "messageFromSendNode:",
-fn: function (aSendNode){
+selector: "messageFromSendNode:do:",
+fn: function (aSendNode,aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
-$2=_st((smalltalk.Message || Message))._new();
-_st($2)._selector_(_st(aSendNode)._selector());
-_st($2)._arguments_(_st(_st(aSendNode)._arguments())._collect_((function(each){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._interpretNode_(each);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})));
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:",{aSendNode:aSendNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+_st(self)._interpretAll_continue_(_st(aSendNode)._arguments(),(function(args){
+return smalltalk.withContext(function($ctx2) {
$1=_st((smalltalk.Message || Message))._new();
+_st($1)._selector_(_st(aSendNode)._selector());
+_st($1)._arguments_(args);
+$2=_st($1)._yourself();
+return _st(aBlock)._value_($2);
+}, function($ctx2) {$ctx2.fillBlock({args:args},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:do:",{aSendNode:aSendNode,aBlock:aBlock}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -330,10 +394,10 @@ smalltalk.method({
 selector: "visitAssignmentNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(self)._assign_to_(_st(aNode)._left(),_st(self)._interpretNode_(_st(aNode)._right()));
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(aNode)._right(),(function(value){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._continue_(_st(self)._assign_to_(_st(aNode)._left(),value));
+}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -343,12 +407,14 @@ smalltalk.method({
 selector: "visitBlockNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._interpretNode_(_st(_st(aNode)._nodes())._first());
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+_st(self)._continue_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=self;
+_st($1)._interpret_(_st(_st(aNode)._nodes())._first());
+$2=_st($1)._currentValue();
+return $2;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -358,15 +424,17 @@ smalltalk.method({
 selector: "visitCascadeNode:",
 fn: function (aNode){
 var self=this;
-var receiver;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-receiver=_st(self)._interpretNode_(_st(aNode)._receiver());
-_st(_st(_st(aNode)._nodes())._allButLast())._do_((function(each){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(self)._messageFromSendNode_(each))._sendTo_(receiver);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
-$1=_st(_st(self)._messageFromSendNode_(_st(_st(aNode)._nodes())._last()))._sendTo_(receiver);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitCascadeNode:",{aNode:aNode,receiver:receiver}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(aNode)._receiver(),(function(receiver){
+return smalltalk.withContext(function($ctx2) {
_st(_st(aNode)._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx3) {
return _st(each)._receiver_(receiver);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx1)})}));
+return _st(self)._interpretAll_continue_(_st(_st(aNode)._nodes())._allButLast(),(function(){
+return smalltalk.withContext(function($ctx3) {
return _st(self)._interpret_continue_(_st(_st(aNode)._nodes())._last(),(function(val){
+return smalltalk.withContext(function($ctx4) {
return _st(self)._continue_(val);
+}, function($ctx4) {$ctx4.fillBlock({val:val},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({receiver:receiver},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitCascadeNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -376,10 +444,40 @@ smalltalk.method({
 selector: "visitClassReferenceNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(_st(aNode)._value());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._continue_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(_st(aNode)._value()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_visitDynamicArrayNode_",
+smalltalk.method({
+selector: "visitDynamicArrayNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_(_st(aNode)._nodes(),(function(array){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._continue_(array);
+}, function($ctx2) {$ctx2.fillBlock({array:array},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicArrayNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_visitDynamicDictionaryNode_",
+smalltalk.method({
+selector: "visitDynamicDictionaryNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_(_st(aNode)._nodes(),(function(array){
+var hashedCollection;
+return smalltalk.withContext(function($ctx2) {
hashedCollection=_st((smalltalk.HashedCollection || HashedCollection))._new();
+hashedCollection;
+_st(array)._do_((function(each){
+return smalltalk.withContext(function($ctx3) {
return _st(hashedCollection)._add_(each);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx1)})}));
+return _st(self)._continue_(hashedCollection);
+}, function($ctx2) {$ctx2.fillBlock({array:array,hashedCollection:hashedCollection},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicDictionaryNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -389,11 +487,9 @@ smalltalk.method({
 selector: "visitJSStatementNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@shouldReturn"]=true;
-$1=_st(self)._eval_(_st(aNode)._source());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
self["@shouldReturn"]=true;
+_st(self)._continue_(_st(self)._eval_(_st(aNode)._source()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -403,11 +499,12 @@ smalltalk.method({
 selector: "visitReturnNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@shouldReturn"]=true;
-$1=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._first());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(_st(aNode)._nodes())._first(),(function(value){
+return smalltalk.withContext(function($ctx2) {
self["@shouldReturn"]=true;
+self["@shouldReturn"];
+return _st(self)._continue_(value);
+}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -417,10 +514,13 @@ smalltalk.method({
 selector: "visitSendNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(_st(self)._messageFromSendNode_(aNode))._sendTo_(_st(self)._interpretNode_(_st(aNode)._receiver()));
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(aNode)._receiver(),(function(receiver){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._messageFromSendNode_do_(aNode,(function(message){
+return smalltalk.withContext(function($ctx3) {
_st(_st(self)._context())._pc_(_st(_st(_st(self)._context())._pc()).__plus((1)));
+return _st(self)._continue_(_st(message)._sendTo_(receiver));
+}, function($ctx3) {$ctx3.fillBlock({message:message},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({receiver:receiver},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -430,26 +530,10 @@ smalltalk.method({
 selector: "visitSequenceNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2,$5;
-var $early={};
-try {
-$1=_st(_st(aNode)._nodes())._allButLast();
-$2=(function(each){
-var value;
-return smalltalk.withContext(function($ctx2) {
value=_st(self)._interpretNode_(each);
-value;
-$3=self["@shouldReturn"];
-if(smalltalk.assert($3)){
-$4=value;
-throw $early=[$4];
-};
-}, function($ctx2) {$ctx2.fillBlock({each:each,value:value},$ctx1)})});
-_st($1)._do_($2);
-$5=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._last());
-return $5;
-}
-catch(e) {if(e===$early)return e[0]; throw e}
-}, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_(_st(aNode)._nodes(),(function(array){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._continue_(_st(array)._last());
+}, function($ctx2) {$ctx2.fillBlock({array:array},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
@@ -459,30 +543,50 @@ smalltalk.method({
 selector: "visitValueNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(aNode)._value();
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitValueNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
_st(self)._continue_(_st(aNode)._value());
+return self}, function($ctx1) {$ctx1.fill(self,"visitValueNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
 }),
 smalltalk.ASTInterpreter);
 
+
+
+smalltalk.addClass('ASTDebugger', smalltalk.ASTInterpreter, ['continuation'], 'Compiler-Interpreter');
 smalltalk.addMethod(
-"_visitVariableNode_",
+"_initialize",
 smalltalk.method({
-selector: "visitVariableNode:",
-fn: function (aNode){
+selector: "initialize",
+fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$1;
-$2=_st(_st(aNode)._binding())._isInstanceVar();
-if(smalltalk.assert($2)){
-$1=_st(_st(_st(self)._context())._receiver())._instVarAt_(_st(aNode)._value());
-} else {
-$1=_st(_st(self)._context())._localAt_(_st(aNode)._value());
-};
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+return smalltalk.withContext(function($ctx1) { 
smalltalk.ASTInterpreter.fn.prototype._initialize.apply(_st(self), []);
+self["@continuation"]=(function(){
+return smalltalk.withContext(function($ctx2) {
}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.ASTDebugger)})}
 }),
-smalltalk.ASTInterpreter);
+smalltalk.ASTDebugger);
+
+smalltalk.addMethod(
+"_interpret_continue_",
+smalltalk.method({
+selector: "interpret:continue:",
+fn: function (aNode,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@continuation"]=(function(){
+return smalltalk.withContext(function($ctx2) {
return smalltalk.ASTInterpreter.fn.prototype._interpret_continue_.apply(_st(self), [aNode,aBlock]);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock}, smalltalk.ASTDebugger)})}
+}),
+smalltalk.ASTDebugger);
+
+smalltalk.addMethod(
+"_stepOver",
+smalltalk.method({
+selector: "stepOver",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@continuation"])._value();
+return self}, function($ctx1) {$ctx1.fill(self,"stepOver",{}, smalltalk.ASTDebugger)})}
+}),
+smalltalk.ASTDebugger);
 
 
 

+ 274 - 130
js/Compiler-Interpreter.js

@@ -253,7 +253,7 @@ referencedClasses: []
 smalltalk.AIContext.klass);
 
 
-smalltalk.addClass('ASTInterpreter', smalltalk.NodeVisitor, ['currentNode', 'context', 'shouldReturn'], 'Compiler-Interpreter');
+smalltalk.addClass('ASTInterpreter', smalltalk.NodeVisitor, ['currentNode', 'context', 'shouldReturn', 'currentValue'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 "_assign_to_",
 smalltalk.method({
@@ -317,6 +317,40 @@ referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_continue_",
+smalltalk.method({
+selector: "continue:",
+category: 'interpreting',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@currentValue"]=anObject;
+return self}, function($ctx1) {$ctx1.fill(self,"continue:",{anObject:anObject}, smalltalk.ASTInterpreter)})},
+args: ["anObject"],
+source: "continue: anObject\x0a\x09currentValue := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_currentValue",
+smalltalk.method({
+selector: "currentValue",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@currentValue"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"currentValue",{}, smalltalk.ASTInterpreter)})},
+args: [],
+source: "currentValue\x0a\x09^ currentValue",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_eval_",
 smalltalk.method({
@@ -374,57 +408,107 @@ selector: "interpret:",
 category: 'interpreting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@shouldReturn"]=false;
-$1=_st(self)._interpretNode_(aNode);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpret:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
self["@shouldReturn"]=false;
+_st(self)._interpret_continue_(aNode,(function(value){
+return smalltalk.withContext(function($ctx2) {
self["@currentValue"]=value;
+return self["@currentValue"];
+}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "interpret: aNode\x0a\x09shouldReturn := false.\x0a    ^ self interpretNode: aNode",
-messageSends: ["interpretNode:"],
+source: "interpret: aNode\x0a\x09shouldReturn := false.\x0a    self interpret: aNode continue: [ :value |\x0a    \x09currentValue := value ]",
+messageSends: ["interpret:continue:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
-"_interpretNode_",
+"_interpret_continue_",
 smalltalk.method({
-selector: "interpretNode:",
+selector: "interpret:continue:",
 category: 'interpreting',
-fn: function (aNode){
+fn: function (aNode,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+$1=self["@shouldReturn"];
+if(smalltalk.assert($1)){
+$2=self;
+return $2;
+};
+$3=_st(aNode)._isNode();
+if(smalltalk.assert($3)){
+_st(self)._visit_(aNode);
+} else {
+self["@currentValue"]=aNode;
+self["@currentValue"];
+};
+_st(aBlock)._value_(_st(self)._currentValue());
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock}, smalltalk.ASTInterpreter)})},
+args: ["aNode", "aBlock"],
+source: "interpret: aNode continue: aBlock\x0a\x0a\x09shouldReturn ifTrue: [ ^ self ].\x0a\x0a\x09aNode isNode \x0a    \x09ifTrue: [ self visit: aNode ]\x0a        ifFalse: [ currentValue := aNode ].\x0a\x09aBlock value: self currentValue",
+messageSends: ["ifTrue:", "ifTrue:ifFalse:", "visit:", "isNode", "value:", "currentValue"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_interpretAll_continue_",
+smalltalk.method({
+selector: "interpretAll:continue:",
+category: 'interpreting',
+fn: function (aCollection,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_result_(aCollection,aBlock,_st((smalltalk.OrderedCollection || OrderedCollection))._new());
+return self}, function($ctx1) {$ctx1.fill(self,"interpretAll:continue:",{aCollection:aCollection,aBlock:aBlock}, smalltalk.ASTInterpreter)})},
+args: ["aCollection", "aBlock"],
+source: "interpretAll: aCollection continue: aBlock\x0a\x09self \x0a    \x09interpretAll: aCollection \x0a        continue: aBlock \x0a        result: OrderedCollection new",
+messageSends: ["interpretAll:continue:result:", "new"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_interpretAll_continue_result_",
+smalltalk.method({
+selector: "interpretAll:continue:result:",
+category: 'interpreting',
+fn: function (nodes,aBlock,aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@currentNode"]=aNode;
-$1=_st(self)._visit_(aNode);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpretNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
-args: ["aNode"],
-source: "interpretNode: aNode\x0a\x09currentNode := aNode.\x0a    ^ self visit: aNode",
-messageSends: ["visit:"],
+$1=_st(nodes)._isEmpty();
+if(smalltalk.assert($1)){
+_st(aBlock)._value_(aCollection);
+} else {
+_st(self)._interpret_continue_(_st(nodes)._first(),(function(value){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._interpretAll_continue_result_(_st(nodes)._allButFirst(),aBlock,_st(aCollection).__comma([value]));
+}, 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)})},
+args: ["nodes", "aBlock", "aCollection"],
+source: "interpretAll: nodes continue: aBlock result: aCollection\x0a\x09nodes isEmpty \x0a    \x09ifTrue: [ aBlock value: aCollection ]\x0a    \x09ifFalse: [\x0a    \x09\x09self interpret: nodes first continue: [:value |\x0a    \x09\x09\x09self \x0a                \x09interpretAll: nodes allButFirst \x0a                    continue: aBlock\x0a  \x09\x09\x09\x09\x09result: aCollection, { value } ] ]",
+messageSends: ["ifTrue:ifFalse:", "value:", "interpret:continue:", "first", "interpretAll:continue:result:", "allButFirst", ",", "isEmpty"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
 smalltalk.addMethod(
-"_messageFromSendNode_",
+"_messageFromSendNode_do_",
 smalltalk.method({
-selector: "messageFromSendNode:",
+selector: "messageFromSendNode:do:",
 category: 'interpreting',
-fn: function (aSendNode){
+fn: function (aSendNode,aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
-$2=_st((smalltalk.Message || Message))._new();
-_st($2)._selector_(_st(aSendNode)._selector());
-_st($2)._arguments_(_st(_st(aSendNode)._arguments())._collect_((function(each){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._interpretNode_(each);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})})));
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:",{aSendNode:aSendNode}, smalltalk.ASTInterpreter)})},
-args: ["aSendNode"],
-source: "messageFromSendNode: aSendNode\x0a\x09^ Message new\x0a    \x09selector: aSendNode selector;\x0a        arguments: (aSendNode arguments collect: [ :each |\x0a        \x09self interpretNode: each ]);\x0a        yourself",
-messageSends: ["selector:", "selector", "new", "arguments:", "collect:", "interpretNode:", "arguments", "yourself"],
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+_st(self)._interpretAll_continue_(_st(aSendNode)._arguments(),(function(args){
+return smalltalk.withContext(function($ctx2) {
$1=_st((smalltalk.Message || Message))._new();
+_st($1)._selector_(_st(aSendNode)._selector());
+_st($1)._arguments_(args);
+$2=_st($1)._yourself();
+return _st(aBlock)._value_($2);
+}, function($ctx2) {$ctx2.fillBlock({args:args},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:do:",{aSendNode:aSendNode,aBlock:aBlock}, smalltalk.ASTInterpreter)})},
+args: ["aSendNode", "aBlock"],
+source: "messageFromSendNode: aSendNode do: aBlock\x0a\x09self interpretAll: aSendNode arguments continue: [ :args |\x0a    \x09aBlock value: (Message new\x0a    \x09\x09selector: aSendNode selector;\x0a        \x09arguments: args;\x0a        \x09yourself) ]",
+messageSends: ["interpretAll:continue:", "arguments", "value:", "selector:", "selector", "new", "arguments:", "yourself"],
 referencedClasses: ["Message"]
 }),
 smalltalk.ASTInterpreter);
@@ -436,13 +520,13 @@ selector: "visitAssignmentNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(self)._assign_to_(_st(aNode)._left(),_st(self)._interpretNode_(_st(aNode)._right()));
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(aNode)._right(),(function(value){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._continue_(_st(self)._assign_to_(_st(aNode)._left(),value));
+}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitAssignmentNode: aNode\x0a\x09^ self assign: aNode left to: (self interpretNode: aNode right)",
-messageSends: ["assign:to:", "left", "interpretNode:", "right"],
+source: "visitAssignmentNode: aNode\x0a\x09self interpret: aNode right continue: [ :value |\x0a    \x09self continue: (self assign: aNode left to: value) ]",
+messageSends: ["interpret:continue:", "right", "continue:", "assign:to:", "left"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -454,15 +538,17 @@ selector: "visitBlockNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(self)._interpretNode_(_st(_st(aNode)._nodes())._first());
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+_st(self)._continue_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=self;
+_st($1)._interpret_(_st(_st(aNode)._nodes())._first());
+$2=_st($1)._currentValue();
+return $2;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitBlockNode: aNode\x0a    ^ [ self interpretNode: aNode nodes first ]",
-messageSends: ["interpretNode:", "first", "nodes"],
+source: "visitBlockNode: aNode\x0a\x09\x22TODO: Context should be set\x22\x0a    self continue: [ self interpret: aNode nodes first; currentValue ]",
+messageSends: ["continue:", "interpret:", "first", "nodes", "currentValue"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -474,18 +560,20 @@ selector: "visitCascadeNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-var receiver;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-receiver=_st(self)._interpretNode_(_st(aNode)._receiver());
-_st(_st(_st(aNode)._nodes())._allButLast())._do_((function(each){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(self)._messageFromSendNode_(each))._sendTo_(receiver);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
-$1=_st(_st(self)._messageFromSendNode_(_st(_st(aNode)._nodes())._last()))._sendTo_(receiver);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitCascadeNode:",{aNode:aNode,receiver:receiver}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(aNode)._receiver(),(function(receiver){
+return smalltalk.withContext(function($ctx2) {
_st(_st(aNode)._nodes())._do_((function(each){
+return smalltalk.withContext(function($ctx3) {
return _st(each)._receiver_(receiver);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx1)})}));
+return _st(self)._interpretAll_continue_(_st(_st(aNode)._nodes())._allButLast(),(function(){
+return smalltalk.withContext(function($ctx3) {
return _st(self)._interpret_continue_(_st(_st(aNode)._nodes())._last(),(function(val){
+return smalltalk.withContext(function($ctx4) {
return _st(self)._continue_(val);
+}, function($ctx4) {$ctx4.fillBlock({val:val},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({receiver:receiver},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitCascadeNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitCascadeNode: aNode\x0a\x09\x22TODO: Handle super sends\x22\x0a\x09| receiver |\x0a    \x0a    receiver := self interpretNode: aNode receiver.\x0a\x0a    aNode nodes allButLast\x0a    \x09do: [ :each | \x0a        \x09(self messageFromSendNode: each)\x0a            \x09sendTo: receiver ].\x0a\x0a    ^ (self messageFromSendNode: aNode nodes last)\x0a            \x09sendTo: receiver",
-messageSends: ["interpretNode:", "receiver", "do:", "sendTo:", "messageFromSendNode:", "allButLast", "nodes", "last"],
+source: "visitCascadeNode: aNode\x0a\x09\x22TODO: Handle super sends\x22\x0a\x09\x0a    self interpret: aNode receiver continue: [ :receiver |\x0a\x09\x09\x22Only interpret the receiver once\x22\x0a        aNode nodes do: [ :each | each receiver: receiver ].\x0a  \x0a    \x09self \x0a        \x09interpretAll: aNode nodes allButLast\x0a    \x09\x09continue: [\x0a              \x09self \x0a                \x09interpret: aNode nodes last\x0a                \x09continue: [ :val | self continue: val ] ] ]",
+messageSends: ["interpret:continue:", "receiver", "do:", "receiver:", "nodes", "interpretAll:continue:", "allButLast", "last", "continue:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -497,17 +585,57 @@ selector: "visitClassReferenceNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(_st(aNode)._value());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._continue_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(_st(aNode)._value()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitClassReferenceNode: aNode\x0a\x09^ Smalltalk current at: aNode value",
-messageSends: ["at:", "value", "current"],
+source: "visitClassReferenceNode: aNode\x0a\x09self continue: (Smalltalk current at: aNode value)",
+messageSends: ["continue:", "at:", "value", "current"],
 referencedClasses: ["Smalltalk"]
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_visitDynamicArrayNode_",
+smalltalk.method({
+selector: "visitDynamicArrayNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_(_st(aNode)._nodes(),(function(array){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._continue_(array);
+}, function($ctx2) {$ctx2.fillBlock({array:array},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicArrayNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+args: ["aNode"],
+source: "visitDynamicArrayNode: aNode\x0a\x0a\x09self interpretAll: aNode nodes continue: [ :array |\x0a    \x09self continue: array ]",
+messageSends: ["interpretAll:continue:", "nodes", "continue:"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+"_visitDynamicDictionaryNode_",
+smalltalk.method({
+selector: "visitDynamicDictionaryNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_(_st(aNode)._nodes(),(function(array){
+var hashedCollection;
+return smalltalk.withContext(function($ctx2) {
hashedCollection=_st((smalltalk.HashedCollection || HashedCollection))._new();
+hashedCollection;
+_st(array)._do_((function(each){
+return smalltalk.withContext(function($ctx3) {
return _st(hashedCollection)._add_(each);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx1)})}));
+return _st(self)._continue_(hashedCollection);
+}, function($ctx2) {$ctx2.fillBlock({array:array,hashedCollection:hashedCollection},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitDynamicDictionaryNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+args: ["aNode"],
+source: "visitDynamicDictionaryNode: aNode\x0a\x09\x0a    self interpretAll: aNode nodes continue: [ :array | | hashedCollection |\x0a    \x09hashedCollection := HashedCollection new.\x0a        array do: [ :each | hashedCollection add: each ].\x0a        self continue: hashedCollection ]",
+messageSends: ["interpretAll:continue:", "nodes", "new", "do:", "add:", "continue:"],
+referencedClasses: ["HashedCollection"]
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_visitJSStatementNode_",
 smalltalk.method({
@@ -515,14 +643,12 @@ selector: "visitJSStatementNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@shouldReturn"]=true;
-$1=_st(self)._eval_(_st(aNode)._source());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
self["@shouldReturn"]=true;
+_st(self)._continue_(_st(self)._eval_(_st(aNode)._source()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitJSStatementNode: aNode\x0a\x09shouldReturn := true.\x0a\x09^ self eval: aNode source",
-messageSends: ["eval:", "source"],
+source: "visitJSStatementNode: aNode\x0a\x09shouldReturn := true.\x0a\x09self continue: (self eval: aNode source)",
+messageSends: ["continue:", "eval:", "source"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -534,14 +660,15 @@ selector: "visitReturnNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-self["@shouldReturn"]=true;
-$1=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._first());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(_st(aNode)._nodes())._first(),(function(value){
+return smalltalk.withContext(function($ctx2) {
self["@shouldReturn"]=true;
+self["@shouldReturn"];
+return _st(self)._continue_(value);
+}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitReturnNode: aNode\x0a\x09shouldReturn := true.\x0a    ^ self interpretNode: aNode nodes first",
-messageSends: ["interpretNode:", "first", "nodes"],
+source: "visitReturnNode: aNode\x0a    self interpret: aNode nodes first continue: [ :value |\x0a    \x09shouldReturn := true.\x0a\x09\x09self continue: value ]",
+messageSends: ["interpret:continue:", "first", "nodes", "continue:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -553,13 +680,16 @@ selector: "visitSendNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(_st(self)._messageFromSendNode_(aNode))._sendTo_(_st(self)._interpretNode_(_st(aNode)._receiver()));
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_continue_(_st(aNode)._receiver(),(function(receiver){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._messageFromSendNode_do_(aNode,(function(message){
+return smalltalk.withContext(function($ctx3) {
_st(_st(self)._context())._pc_(_st(_st(_st(self)._context())._pc()).__plus((1)));
+return _st(self)._continue_(_st(message)._sendTo_(receiver));
+}, function($ctx3) {$ctx3.fillBlock({message:message},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({receiver:receiver},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitSendNode: aNode\x0a\x09\x22TODO: Handle super sends\x22\x0a    \x0a    ^ (self messageFromSendNode: aNode)\x0a    \x09sendTo: (self interpretNode: aNode receiver)",
-messageSends: ["sendTo:", "interpretNode:", "receiver", "messageFromSendNode:"],
+source: "visitSendNode: aNode\x0a\x09\x22TODO: Handle super sends\x22\x0a    \x0a    self interpret: aNode receiver continue: [ :receiver |\x0a    \x09self messageFromSendNode: aNode do: [ :message |\x0a        \x09self context pc: self context pc + 1.\x0a        \x09self continue: (message sendTo: receiver) ] ]",
+messageSends: ["interpret:continue:", "receiver", "messageFromSendNode:do:", "pc:", "+", "pc", "context", "continue:", "sendTo:"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -571,29 +701,13 @@ selector: "visitSequenceNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2,$5;
-var $early={};
-try {
-$1=_st(_st(aNode)._nodes())._allButLast();
-$2=(function(each){
-var value;
-return smalltalk.withContext(function($ctx2) {
value=_st(self)._interpretNode_(each);
-value;
-$3=self["@shouldReturn"];
-if(smalltalk.assert($3)){
-$4=value;
-throw $early=[$4];
-};
-}, function($ctx2) {$ctx2.fillBlock({each:each,value:value},$ctx1)})});
-_st($1)._do_($2);
-$5=_st(self)._interpretNode_(_st(_st(aNode)._nodes())._last());
-return $5;
-}
-catch(e) {if(e===$early)return e[0]; throw e}
-}, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpretAll_continue_(_st(aNode)._nodes(),(function(array){
+return smalltalk.withContext(function($ctx2) {
return _st(self)._continue_(_st(array)._last());
+}, function($ctx2) {$ctx2.fillBlock({array:array},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitSequenceNode: aNode\x0a\x0a\x09aNode nodes allButLast do: [ :each | | value |\x0a        value := self interpretNode: each.\x0a\x09\x09shouldReturn ifTrue: [ ^ value ] ].\x0a        \x0a    ^ self interpretNode: aNode nodes last",
-messageSends: ["do:", "interpretNode:", "ifTrue:", "allButLast", "nodes", "last"],
+source: "visitSequenceNode: aNode\x0a\x09self interpretAll: aNode nodes continue: [ :array |\x0a    \x09self continue: array last ]",
+messageSends: ["interpretAll:continue:", "nodes", "continue:", "last"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
@@ -605,39 +719,69 @@ selector: "visitValueNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(aNode)._value();
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitValueNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+return smalltalk.withContext(function($ctx1) { 
_st(self)._continue_(_st(aNode)._value());
+return self}, function($ctx1) {$ctx1.fill(self,"visitValueNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
 args: ["aNode"],
-source: "visitValueNode: aNode\x0a\x09^ aNode value",
-messageSends: ["value"],
+source: "visitValueNode: aNode\x0a\x09self continue: aNode value",
+messageSends: ["continue:", "value"],
 referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
+
+
+smalltalk.addClass('ASTDebugger', smalltalk.ASTInterpreter, ['continuation'], 'Compiler-Interpreter');
 smalltalk.addMethod(
-"_visitVariableNode_",
+"_initialize",
 smalltalk.method({
-selector: "visitVariableNode:",
-category: 'visiting',
-fn: function (aNode){
+selector: "initialize",
+category: 'initialization',
+fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$1;
-$2=_st(_st(aNode)._binding())._isInstanceVar();
-if(smalltalk.assert($2)){
-$1=_st(_st(_st(self)._context())._receiver())._instVarAt_(_st(aNode)._value());
-} else {
-$1=_st(_st(self)._context())._localAt_(_st(aNode)._value());
-};
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
-args: ["aNode"],
-source: "visitVariableNode: aNode\x0a\x09^ aNode binding isInstanceVar\x0a    \x09ifTrue: [ self context receiver instVarAt: aNode value ]\x0a      \x09ifFalse: [ self context localAt: aNode value ]",
-messageSends: ["ifTrue:ifFalse:", "instVarAt:", "value", "receiver", "context", "localAt:", "isInstanceVar", "binding"],
+return smalltalk.withContext(function($ctx1) { 
smalltalk.ASTInterpreter.fn.prototype._initialize.apply(_st(self), []);
+self["@continuation"]=(function(){
+return smalltalk.withContext(function($ctx2) {
}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.ASTDebugger)})},
+args: [],
+source: "initialize\x0a\x09super initialize.\x0a    continuation := [  ]",
+messageSends: ["initialize"],
 referencedClasses: []
 }),
-smalltalk.ASTInterpreter);
+smalltalk.ASTDebugger);
+
+smalltalk.addMethod(
+"_interpret_continue_",
+smalltalk.method({
+selector: "interpret:continue:",
+category: 'interpreting',
+fn: function (aNode,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@continuation"]=(function(){
+return smalltalk.withContext(function($ctx2) {
return smalltalk.ASTInterpreter.fn.prototype._interpret_continue_.apply(_st(self), [aNode,aBlock]);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock}, smalltalk.ASTDebugger)})},
+args: ["aNode", "aBlock"],
+source: "interpret: aNode continue: aBlock\x0a\x09continuation := [ super interpret: aNode continue: aBlock ]",
+messageSends: ["interpret:continue:"],
+referencedClasses: []
+}),
+smalltalk.ASTDebugger);
+
+smalltalk.addMethod(
+"_stepOver",
+smalltalk.method({
+selector: "stepOver",
+category: 'stepping',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@continuation"])._value();
+return self}, function($ctx1) {$ctx1.fill(self,"stepOver",{}, smalltalk.ASTDebugger)})},
+args: [],
+source: "stepOver\x0a\x09continuation value",
+messageSends: ["value"],
+referencedClasses: []
+}),
+smalltalk.ASTDebugger);
 
 
 

+ 35 - 1
js/Compiler-Tests.deploy.js

@@ -42,7 +42,8 @@ return smalltalk.withContext(function($ctx2) {
return _st(ctx)._localAt_put_(key
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1)})}));
 $2=_st(self)._interpreter();
 _st($2)._context_(ctx);
-$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,_st(anObject)._class()))._nodes())._first());
+_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,_st(anObject)._class()))._nodes())._first());
+$3=_st($2)._currentValue();
 $1=$3;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx}, smalltalk.ASTInterpreterTest)})}
@@ -136,6 +137,28 @@ return self}, function($ctx1) {$ctx1.fill(self,"testCascade",{}, smalltalk.ASTIn
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testDynamicArray",
+smalltalk.method({
+selector: "testDynamicArray",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo ^ {1+1. 2+2}"),[(2), (4)]);
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicArray",{}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_testDynamicDictionary",
+smalltalk.method({
+selector: "testDynamicDictionary",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo ^ #{1->1. 2->3}"),smalltalk.HashedCollection._fromPairs_([_st((1)).__minus_gt((1)),_st((2)).__minus_gt((3))]));
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicDictionary",{}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
 smalltalk.addMethod(
 "_testInlinedJSStatement",
 smalltalk.method({
@@ -159,6 +182,17 @@ return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAssignment",{}, small
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testNonlocalReturn",
+smalltalk.method({
+selector: "testNonlocalReturn",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo true ifTrue: [ ^ 1 ]. ^2"),(1));
+return self}, function($ctx1) {$ctx1.fill(self,"testNonlocalReturn",{}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
 smalltalk.addMethod(
 "_testTempAssignment",
 smalltalk.method({

+ 52 - 3
js/Compiler-Tests.js

@@ -53,13 +53,14 @@ return smalltalk.withContext(function($ctx2) {
return _st(ctx)._localAt_put_(key
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1)})}));
 $2=_st(self)._interpreter();
 _st($2)._context_(ctx);
-$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,_st(anObject)._class()))._nodes())._first());
+_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,_st(anObject)._class()))._nodes())._first());
+$3=_st($2)._currentValue();
 $1=$3;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx}, smalltalk.ASTInterpreterTest)})},
 args: ["aString", "anObject", "aDictionary"],
-source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a    \x0a    | ctx |\x0a    \x0a    ctx := AIContext new.\x0a    ctx receiver: anObject.\x0a    aDictionary keysAndValuesDo: [ :key :value |\x0a    \x09ctx localAt: key put: value ].\x0a    \x0a    ^ self interpreter\x0a    \x09context: ctx;\x0a    \x09interpret: (self parse: aString forClass: anObject class) \x0a        \x09nodes first",
-messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "first", "nodes", "parse:forClass:", "class"],
+source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a    \x0a    | ctx |\x0a    \x0a    ctx := AIContext new.\x0a    ctx receiver: anObject.\x0a    aDictionary keysAndValuesDo: [ :key :value |\x0a    \x09ctx localAt: key put: value ].\x0a    \x0a    ^ self interpreter\x0a    \x09context: ctx;\x0a    \x09interpret: (self parse: aString forClass: anObject class) \x0a        \x09nodes first;\x0a        currentValue",
+messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "first", "nodes", "parse:forClass:", "class", "currentValue"],
 referencedClasses: ["AIContext"]
 }),
 smalltalk.ASTInterpreterTest);
@@ -186,6 +187,38 @@ referencedClasses: ["OrderedCollection"]
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testDynamicArray",
+smalltalk.method({
+selector: "testDynamicArray",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo ^ {1+1. 2+2}"),[(2), (4)]);
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicArray",{}, smalltalk.ASTInterpreterTest)})},
+args: [],
+source: "testDynamicArray\x0a\x09self assert: (self interpret: 'foo ^ {1+1. 2+2}') equals: #(2 4)",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_testDynamicDictionary",
+smalltalk.method({
+selector: "testDynamicDictionary",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo ^ #{1->1. 2->3}"),smalltalk.HashedCollection._fromPairs_([_st((1)).__minus_gt((1)),_st((2)).__minus_gt((3))]));
+return self}, function($ctx1) {$ctx1.fill(self,"testDynamicDictionary",{}, smalltalk.ASTInterpreterTest)})},
+args: [],
+source: "testDynamicDictionary\x0a\x09self assert: (self interpret: 'foo ^ #{1->1. 2->3}') equals: #{1->1. 2->3}",
+messageSends: ["assert:equals:", "interpret:", "->"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreterTest);
+
 smalltalk.addMethod(
 "_testInlinedJSStatement",
 smalltalk.method({
@@ -219,6 +252,22 @@ referencedClasses: ["Point"]
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testNonlocalReturn",
+smalltalk.method({
+selector: "testNonlocalReturn",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo true ifTrue: [ ^ 1 ]. ^2"),(1));
+return self}, function($ctx1) {$ctx1.fill(self,"testNonlocalReturn",{}, smalltalk.ASTInterpreterTest)})},
+args: [],
+source: "testNonlocalReturn\x0a\x09self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ]. ^2') equals: 1",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreterTest);
+
 smalltalk.addMethod(
 "_testTempAssignment",
 smalltalk.method({

+ 10 - 4
st/Compiler-AST.st

@@ -65,6 +65,10 @@ isImmutable
 	^false
 !
 
+isNode
+	^ true
+!
+
 isReturnNode
 	^false
 !
@@ -152,10 +156,6 @@ scope: aLexicalScope
 
 isBlockNode
 	^true
-!
-
-subtreeNeedsAliasing
-    ^self shouldBeAliased or: [ self shouldBeInlined ]
 ! !
 
 !BlockNode methodsFor: 'visiting'!
@@ -544,3 +544,9 @@ accept: aVisitor
 	^ aVisitor visitClassReferenceNode: self
 ! !
 
+!Object methodsFor: '*Compiler-AST'!
+
+isNode
+	^ false
+! !
+

+ 1 - 1
st/Compiler-Core.st

@@ -149,7 +149,7 @@ visit: aNode
 !
 
 visitAll: aCollection
-	^ aCollection do: [ :each | self visit: each ]
+	^ aCollection collect: [ :each | self visit: each ]
 !
 
 visitAssignmentNode: aNode

+ 105 - 41
st/Compiler-Interpreter.st

@@ -68,7 +68,7 @@ fromMethodContext: aMethodContext
 ! !
 
 NodeVisitor subclass: #ASTInterpreter
-	instanceVariableNames: 'currentNode context shouldReturn'
+	instanceVariableNames: 'currentNode context shouldReturn currentValue'
 	package: 'Compiler-Interpreter'!
 
 !ASTInterpreter methodsFor: 'accessing'!
@@ -79,6 +79,10 @@ context
 
 context: anAIContext
 	context := anAIContext
+!
+
+currentValue
+	^ currentValue
 ! !
 
 !ASTInterpreter methodsFor: 'initialization'!
@@ -96,6 +100,10 @@ assign: aNode to: anObject
       	ifFalse: [ self context localAt: aNode value put: anObject ]
 !
 
+continue: anObject
+	currentValue := anObject
+!
+
 eval: aString
 	"Evaluate aString as JS source inside an JS function. 
     aString is not sandboxed."
@@ -119,84 +127,140 @@ eval: aString
 
 interpret: aNode
 	shouldReturn := false.
-    ^ self interpretNode: aNode
+    self interpret: aNode continue: [ :value |
+    	currentValue := value ]
 !
 
-interpretNode: aNode
-	currentNode := aNode.
-    ^ self visit: aNode
+interpret: aNode continue: aBlock
+
+	shouldReturn ifTrue: [ ^ self ].
+
+	aNode isNode 
+    	ifTrue: [ self visit: aNode ]
+        ifFalse: [ currentValue := aNode ].
+	aBlock value: self currentValue
 !
 
-messageFromSendNode: aSendNode
-	^ Message new
-    	selector: aSendNode selector;
-        arguments: (aSendNode arguments collect: [ :each |
-        	self interpretNode: each ]);
-        yourself
+interpretAll: aCollection continue: aBlock
+	self 
+    	interpretAll: aCollection 
+        continue: aBlock 
+        result: OrderedCollection new
+!
+
+interpretAll: nodes continue: aBlock result: aCollection
+	nodes isEmpty 
+    	ifTrue: [ aBlock value: aCollection ]
+    	ifFalse: [
+    		self interpret: nodes first continue: [:value |
+    			self 
+                	interpretAll: nodes allButFirst 
+                    continue: aBlock
+  					result: aCollection, { value } ] ]
+!
+
+messageFromSendNode: aSendNode do: aBlock
+	self interpretAll: aSendNode arguments continue: [ :args |
+    	aBlock value: (Message new
+    		selector: aSendNode selector;
+        	arguments: args;
+        	yourself) ]
 ! !
 
 !ASTInterpreter methodsFor: 'visiting'!
 
 visitAssignmentNode: aNode
-	^ self assign: aNode left to: (self interpretNode: aNode right)
+	self interpret: aNode right continue: [ :value |
+    	self continue: (self assign: aNode left to: value) ]
 !
 
 visitBlockNode: aNode
-    ^ [ self interpretNode: aNode nodes first ]
+	"TODO: Context should be set"
+    self continue: [ self interpret: aNode nodes first; currentValue ]
 !
 
 visitCascadeNode: aNode
 	"TODO: Handle super sends"
-	| receiver |
-    
-    receiver := self interpretNode: aNode receiver.
+	
+    self interpret: aNode receiver continue: [ :receiver |
+		"Only interpret the receiver once"
+        aNode nodes do: [ :each | each receiver: receiver ].
+  
+    	self 
+        	interpretAll: aNode nodes allButLast
+    		continue: [
+              	self 
+                	interpret: aNode nodes last
+                	continue: [ :val | self continue: val ] ] ]
+!
+
+visitClassReferenceNode: aNode
+	self continue: (Smalltalk current at: aNode value)
+!
 
-    aNode nodes allButLast
-    	do: [ :each | 
-        	(self messageFromSendNode: each)
-            	sendTo: receiver ].
+visitDynamicArrayNode: aNode
 
-    ^ (self messageFromSendNode: aNode nodes last)
-            	sendTo: receiver
+	self interpretAll: aNode nodes continue: [ :array |
+    	self continue: array ]
 !
 
-visitClassReferenceNode: aNode
-	^ Smalltalk current at: aNode value
+visitDynamicDictionaryNode: aNode
+	
+    self interpretAll: aNode nodes continue: [ :array | | hashedCollection |
+    	hashedCollection := HashedCollection new.
+        array do: [ :each | hashedCollection add: each ].
+        self continue: hashedCollection ]
 !
 
 visitJSStatementNode: aNode
 	shouldReturn := true.
-	^ self eval: aNode source
+	self continue: (self eval: aNode source)
 !
 
 visitReturnNode: aNode
-	shouldReturn := true.
-    ^ self interpretNode: aNode nodes first
+    self interpret: aNode nodes first continue: [ :value |
+    	shouldReturn := true.
+		self continue: value ]
 !
 
 visitSendNode: aNode
 	"TODO: Handle super sends"
     
-    ^ (self messageFromSendNode: aNode)
-    	sendTo: (self interpretNode: aNode receiver)
+    self interpret: aNode receiver continue: [ :receiver |
+    	self messageFromSendNode: aNode do: [ :message |
+        	self context pc: self context pc + 1.
+        	self continue: (message sendTo: receiver) ] ]
 !
 
 visitSequenceNode: aNode
-
-	aNode nodes allButLast do: [ :each | | value |
-        value := self interpretNode: each.
-		shouldReturn ifTrue: [ ^ value ] ].
-        
-    ^ self interpretNode: aNode nodes last
+	self interpretAll: aNode nodes continue: [ :array |
+    	self continue: array last ]
 !
 
 visitValueNode: aNode
-	^ aNode value
-!
+	self continue: aNode value
+! !
+
+ASTInterpreter subclass: #ASTDebugger
+	instanceVariableNames: 'continuation'
+	package: 'Compiler-Interpreter'!
+
+!ASTDebugger methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+    continuation := [  ]
+! !
+
+!ASTDebugger methodsFor: 'interpreting'!
+
+interpret: aNode continue: aBlock
+	continuation := [ super interpret: aNode continue: aBlock ]
+! !
+
+!ASTDebugger methodsFor: 'stepping'!
 
-visitVariableNode: aNode
-	^ aNode binding isInstanceVar
-    	ifTrue: [ self context receiver instVarAt: aNode value ]
-      	ifFalse: [ self context localAt: aNode value ]
+stepOver
+	continuation value
 ! !
 

+ 14 - 1
st/Compiler-Tests.st

@@ -29,7 +29,8 @@ interpret: aString receiver: anObject withArguments: aDictionary
     ^ self interpreter
     	context: ctx;
     	interpret: (self parse: aString forClass: anObject class) 
-        	nodes first
+        	nodes first;
+        currentValue
 !
 
 interpret: aString withArguments: aDictionary
@@ -67,6 +68,14 @@ 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.
     
@@ -86,6 +95,10 @@ testInstVarAssignment
         equals: 2
 !
 
+testNonlocalReturn
+	self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ]. ^2') equals: 1
+!
+
 testTempAssignment
 	self assert: (self interpret: 'foo | a | a := 2. ^ a') equals: 2
 ! !