Преглед на файлове

More changes to ASTDebugger

Nicolas Petton преди 11 години
родител
ревизия
582bd3dc4e
променени са 6 файла, в които са добавени 592 реда и са изтрити 40 реда
  1. 183 11
      js/Compiler-Interpreter.deploy.js
  2. 258 21
      js/Compiler-Interpreter.js
  3. 20 0
      js/Compiler-Tests.deploy.js
  4. 25 0
      js/Compiler-Tests.js
  5. 89 8
      st/Compiler-Interpreter.st
  6. 17 0
      st/Compiler-Tests.st

+ 183 - 11
js/Compiler-Interpreter.deploy.js

@@ -207,6 +207,22 @@ smalltalk.AIContext.klass);
 
 
 smalltalk.addClass('ASTDebugger', smalltalk.Object, ['interpreter', 'context'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+"_buildAST",
+smalltalk.method({
+selector: "buildAST",
+fn: function (){
+var self=this;
+var ast;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+ast=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._parse_(_st(_st(self)._method())._source());
+_st(_st((smalltalk.SemanticAnalyzer || SemanticAnalyzer))._on_(_st(_st(_st(self)._context())._receiver())._class()))._visit_(ast);
+$1=ast;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"buildAST",{ast:ast}, smalltalk.ASTDebugger)})}
+}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 "_context_",
 smalltalk.method({
@@ -219,15 +235,26 @@ return self}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext}, s
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
-"_defaultInterpreter",
+"_defaultInterpreterClass",
 smalltalk.method({
-selector: "defaultInterpreter",
+selector: "defaultInterpreterClass",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st((smalltalk.ASTSteppingInterpreter || ASTSteppingInterpreter))._new();
+$1=(smalltalk.ASTSteppingInterpreter || ASTSteppingInterpreter);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"defaultInterpreter",{}, smalltalk.ASTDebugger)})}
+}, function($ctx1) {$ctx1.fill(self,"defaultInterpreterClass",{}, smalltalk.ASTDebugger)})}
+}),
+smalltalk.ASTDebugger);
+
+smalltalk.addMethod(
+"_initializeInterpreter",
+smalltalk.method({
+selector: "initializeInterpreter",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(_st(self)._interpreter())._interpret_(_st(_st(_st(self)._buildAST())._nodes())._first());
+return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{}, smalltalk.ASTDebugger)})}
 }),
 smalltalk.ASTDebugger);
 
@@ -238,6 +265,7 @@ selector: "initializeWithContext:",
 fn: function (aMethodContext){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st(self)._context_fromMethodContext_((smalltalk.IAContext || IAContext),aMethodContext);
+_st(self)._initializeInterpreter();
 return self}, function($ctx1) {$ctx1.fill(self,"initializeWithContext:",{aMethodContext:aMethodContext}, smalltalk.ASTDebugger)})}
 }),
 smalltalk.ASTDebugger);
@@ -251,7 +279,7 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $2,$1;
 $2=self["@interpreter"];
 if(($receiver = $2) == nil || $receiver == undefined){
-self["@interpreter"]=_st(self)._defaultInterpreter();
+self["@interpreter"]=_st(_st(self)._defaultInterpreterClass())._new();
 $1=self["@interpreter"];
 } else {
 $1=$2;
@@ -307,6 +335,26 @@ return self}, function($ctx1) {$ctx1.fill(self,"resume",{}, smalltalk.ASTDebugge
 }),
 smalltalk.ASTDebugger);
 
+smalltalk.addMethod(
+"_step",
+smalltalk.method({
+selector: "step",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st((function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(_st(_st(self)._interpreter())._nextNode())._notNil())._and_((function(){
+return smalltalk.withContext(function($ctx3) {
return _st(_st(_st(self)._interpreter())._nextNode())._stopOnStepping();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})})))._or_((function(){
+return smalltalk.withContext(function($ctx3) {
return _st(_st(_st(self)._interpreter())._atEnd())._not();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
_st(_st(self)._interpreter())._step();
+return _st(self)._step();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"step",{}, smalltalk.ASTDebugger)})}
+}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 "_stepInto",
 smalltalk.method({
@@ -324,7 +372,7 @@ smalltalk.method({
 selector: "stepOver",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._shouldBeImplemented();
+return smalltalk.withContext(function($ctx1) { 
_st(self)._step();
 return self}, function($ctx1) {$ctx1.fill(self,"stepOver",{}, smalltalk.ASTDebugger)})}
 }),
 smalltalk.ASTDebugger);
@@ -778,9 +826,42 @@ return $1;
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_shouldReturn",
+smalltalk.method({
+selector: "shouldReturn",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@shouldReturn"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=false;
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"shouldReturn",{}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
 
 
-smalltalk.addClass('ASTSteppingInterpreter', smalltalk.ASTInterpreter, ['continuation'], 'Compiler-Interpreter');
+smalltalk.addClass('ASTSteppingInterpreter', smalltalk.ASTInterpreter, ['continuation', 'nextNode'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+"_atEnd",
+smalltalk.method({
+selector: "atEnd",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._shouldReturn())._or_((function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(self)._nextNode()).__eq_eq(_st(self)._currentNode());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"atEnd",{}, smalltalk.ASTSteppingInterpreter)})}
+}),
+smalltalk.ASTSteppingInterpreter);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -790,7 +871,7 @@ var self=this;
 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)})}
+return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.ASTSteppingInterpreter)})}
 }),
 smalltalk.ASTSteppingInterpreter);
 
@@ -800,10 +881,24 @@ smalltalk.method({
 selector: "interpret:continue:",
 fn: function (aNode,aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
self["@continuation"]=(function(){
+return smalltalk.withContext(function($ctx1) { 
self["@nextNode"]=aNode;
+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)})}
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock}, smalltalk.ASTSteppingInterpreter)})}
+}),
+smalltalk.ASTSteppingInterpreter);
+
+smalltalk.addMethod(
+"_nextNode",
+smalltalk.method({
+selector: "nextNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@nextNode"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{}, smalltalk.ASTSteppingInterpreter)})}
 }),
 smalltalk.ASTSteppingInterpreter);
 
@@ -814,7 +909,7 @@ selector: "step",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st(self["@continuation"])._value();
-return self}, function($ctx1) {$ctx1.fill(self,"step",{}, smalltalk.ASTDebugger)})}
+return self}, function($ctx1) {$ctx1.fill(self,"step",{}, smalltalk.ASTSteppingInterpreter)})}
 }),
 smalltalk.ASTSteppingInterpreter);
 
@@ -833,6 +928,17 @@ return $1;
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return false;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.Node)})}
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -846,6 +952,17 @@ return $1;
 }),
 smalltalk.AssignmentNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.AssignmentNode)})}
+}),
+smalltalk.AssignmentNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -859,6 +976,17 @@ return $1;
 }),
 smalltalk.BlockNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.BlockNode)})}
+}),
+smalltalk.BlockNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -885,6 +1013,17 @@ return $1;
 }),
 smalltalk.DynamicArrayNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.DynamicArrayNode)})}
+}),
+smalltalk.DynamicArrayNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -898,6 +1037,17 @@ return $1;
 }),
 smalltalk.DynamicDictionaryNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.DynamicDictionaryNode)})}
+}),
+smalltalk.DynamicDictionaryNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -911,6 +1061,17 @@ return $1;
 }),
 smalltalk.JSStatementNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.JSStatementNode)})}
+}),
+smalltalk.JSStatementNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -950,6 +1111,17 @@ return $1;
 }),
 smalltalk.SendNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.SendNode)})}
+}),
+smalltalk.SendNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({

+ 258 - 21
js/Compiler-Interpreter.js

@@ -279,6 +279,27 @@ smalltalk.AIContext.klass);
 
 smalltalk.addClass('ASTDebugger', smalltalk.Object, ['interpreter', 'context'], 'Compiler-Interpreter');
 smalltalk.ASTDebugger.comment="ASTDebugger is a debugger to Amber.\x0aIt uses an AST interpreter to step through the code.\x0a\x0aASTDebugger instances are created from a `MethodContext` with `ASTDebugger class >> context:`.\x0aThey hold an `AIContext` instance internally, recursive copy of the `MethodContext`.\x0a\x0aUse the methods of the 'stepping' protocol to do stepping."
+smalltalk.addMethod(
+"_buildAST",
+smalltalk.method({
+selector: "buildAST",
+category: 'initialization',
+fn: function (){
+var self=this;
+var ast;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+ast=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._parse_(_st(_st(self)._method())._source());
+_st(_st((smalltalk.SemanticAnalyzer || SemanticAnalyzer))._on_(_st(_st(_st(self)._context())._receiver())._class()))._visit_(ast);
+$1=ast;
+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    The AST is annotated with a SemanticAnalyzer, \x0a    to know the semantics and bindings of each node needed for later debugging\x22\x0a    \x0a    | ast |\x0a    \x0a    ast := Smalltalk current parse: self method source.\x0a    (SemanticAnalyzer on: self context receiver class)\x0a    \x09visit: ast.    \x0a    \x0a    ^ ast",
+messageSends: ["parse:", "source", "method", "current", "visit:", "on:", "class", "receiver", "context"],
+referencedClasses: ["Smalltalk", "SemanticAnalyzer"]
+}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 "_context_",
 smalltalk.method({
@@ -296,23 +317,39 @@ referencedClasses: ["AIContext"]
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
-"_defaultInterpreter",
+"_defaultInterpreterClass",
 smalltalk.method({
-selector: "defaultInterpreter",
+selector: "defaultInterpreterClass",
 category: 'defaults',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st((smalltalk.ASTSteppingInterpreter || ASTSteppingInterpreter))._new();
+$1=(smalltalk.ASTSteppingInterpreter || ASTSteppingInterpreter);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"defaultInterpreter",{}, smalltalk.ASTDebugger)})},
+}, function($ctx1) {$ctx1.fill(self,"defaultInterpreterClass",{}, smalltalk.ASTDebugger)})},
 args: [],
-source: "defaultInterpreter\x0a\x09^ ASTSteppingInterpreter new",
-messageSends: ["new"],
+source: "defaultInterpreterClass\x0a\x09^ ASTSteppingInterpreter",
+messageSends: [],
 referencedClasses: ["ASTSteppingInterpreter"]
 }),
 smalltalk.ASTDebugger);
 
+smalltalk.addMethod(
+"_initializeInterpreter",
+smalltalk.method({
+selector: "initializeInterpreter",
+category: 'initialization',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(_st(self)._interpreter())._interpret_(_st(_st(_st(self)._buildAST())._nodes())._first());
+return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{}, smalltalk.ASTDebugger)})},
+args: [],
+source: "initializeInterpreter\x0a\x09self interpreter interpret: self buildAST nodes first",
+messageSends: ["interpret:", "first", "nodes", "buildAST", "interpreter"],
+referencedClasses: []
+}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 "_initializeWithContext_",
 smalltalk.method({
@@ -321,10 +358,11 @@ category: 'initialization',
 fn: function (aMethodContext){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st(self)._context_fromMethodContext_((smalltalk.IAContext || IAContext),aMethodContext);
+_st(self)._initializeInterpreter();
 return self}, function($ctx1) {$ctx1.fill(self,"initializeWithContext:",{aMethodContext:aMethodContext}, smalltalk.ASTDebugger)})},
 args: ["aMethodContext"],
-source: "initializeWithContext: aMethodContext\x0a\x09\x22TODO: do we need to handle block contexts?\x22\x0a    self context: IAContext fromMethodContext: aMethodContext",
-messageSends: ["context:fromMethodContext:"],
+source: "initializeWithContext: aMethodContext\x0a\x09\x22TODO: do we need to handle block contexts?\x22\x0a    \x0a    self context: IAContext fromMethodContext: aMethodContext.\x0a    self initializeInterpreter",
+messageSends: ["context:fromMethodContext:", "initializeInterpreter"],
 referencedClasses: ["IAContext"]
 }),
 smalltalk.ASTDebugger);
@@ -339,7 +377,7 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $2,$1;
 $2=self["@interpreter"];
 if(($receiver = $2) == nil || $receiver == undefined){
-self["@interpreter"]=_st(self)._defaultInterpreter();
+self["@interpreter"]=_st(_st(self)._defaultInterpreterClass())._new();
 $1=self["@interpreter"];
 } else {
 $1=$2;
@@ -347,8 +385,8 @@ $1=$2;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"interpreter",{}, smalltalk.ASTDebugger)})},
 args: [],
-source: "interpreter\x0a\x09^ interpreter ifNil: [ interpreter := self defaultInterpreter ]",
-messageSends: ["ifNil:", "defaultInterpreter"],
+source: "interpreter\x0a\x09^ interpreter ifNil: [ interpreter := self defaultInterpreterClass new ]",
+messageSends: ["ifNil:", "new", "defaultInterpreterClass"],
 referencedClasses: []
 }),
 smalltalk.ASTDebugger);
@@ -419,6 +457,31 @@ referencedClasses: []
 }),
 smalltalk.ASTDebugger);
 
+smalltalk.addMethod(
+"_step",
+smalltalk.method({
+selector: "step",
+category: 'stepping',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st((function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(_st(_st(self)._interpreter())._nextNode())._notNil())._and_((function(){
+return smalltalk.withContext(function($ctx3) {
return _st(_st(_st(self)._interpreter())._nextNode())._stopOnStepping();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})})))._or_((function(){
+return smalltalk.withContext(function($ctx3) {
return _st(_st(_st(self)._interpreter())._atEnd())._not();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
_st(_st(self)._interpreter())._step();
+return _st(self)._step();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"step",{}, smalltalk.ASTDebugger)})},
+args: [],
+source: "step\x0a\x09\x22The ASTSteppingInterpreter stops at each node interpretation. \x0a    One step will interpret nodes until:\x0a    - we get at the end\x0a    - the next node is a stepping node (send, assignment, etc.)\x22\x0a    \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                self step ]",
+messageSends: ["whileFalse:", "step", "interpreter", "or:", "not", "atEnd", "and:", "stopOnStepping", "nextNode", "notNil"],
+referencedClasses: []
+}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 "_stepInto",
 smalltalk.method({
@@ -442,11 +505,11 @@ selector: "stepOver",
 category: 'stepping',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._shouldBeImplemented();
+return smalltalk.withContext(function($ctx1) { 
_st(self)._step();
 return self}, function($ctx1) {$ctx1.fill(self,"stepOver",{}, smalltalk.ASTDebugger)})},
 args: [],
-source: "stepOver\x0a\x09self shouldBeImplemented",
-messageSends: ["shouldBeImplemented"],
+source: "stepOver\x0a\x09self step",
+messageSends: ["step"],
 referencedClasses: []
 }),
 smalltalk.ASTDebugger);
@@ -669,7 +732,7 @@ _st(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    \x09ifTrue: [ \x09\x0a        \x09currentNode := aNode.\x0a            self interpretNode: aNode continue: [ :value |\x0a  \x09\x09\x09\x09self continue: aBlock value: value] ]\x0a        ifFalse: [ self continue: aBlock value: aNode ]",
+source: "interpret: aNode continue: aBlock\x0a\x09shouldReturn ifTrue: [ ^ self ].\x0a\x0a\x09aNode isNode \x0a    \x09ifTrue: [ \x09\x0a        \x09currentNode := aNode.\x0a            self interpretNode: aNode continue: [ :value |\x0a  \x09\x09\x09\x09self continue: aBlock value: value ] ]\x0a        ifFalse: [ self continue: aBlock value: aNode ]",
 messageSends: ["ifTrue:", "ifTrue:ifFalse:", "interpretNode:continue:", "continue:value:", "isNode"],
 referencedClasses: []
 }),
@@ -1046,10 +1109,53 @@ referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_shouldReturn",
+smalltalk.method({
+selector: "shouldReturn",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@shouldReturn"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=false;
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"shouldReturn",{}, smalltalk.ASTInterpreter)})},
+args: [],
+source: "shouldReturn\x0a\x09^ shouldReturn ifNil: [ false ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
 
 
-smalltalk.addClass('ASTSteppingInterpreter', smalltalk.ASTInterpreter, ['continuation'], 'Compiler-Interpreter');
+
+smalltalk.addClass('ASTSteppingInterpreter', smalltalk.ASTInterpreter, ['continuation', 'nextNode'], 'Compiler-Interpreter');
 smalltalk.ASTSteppingInterpreter.comment="ASTSteppingInterpreter is an interpreter with stepping capabilities.\x0aUse `#step` to actually interpret the next node.\x0a\x0aUsage example:\x0a\x0a    | ast interpreter |\x0a    ast := Smalltalk current parse: 'foo 1+2+4'.\x0a    (SemanticAnalyzer on: Object) visit: ast.\x0a\x0a    interpreter := ASTSteppingInterpreter new\x0a        interpret: ast nodes first;\x0a        yourself.\x0a        \x0a    debugger step; step.\x0a    debugger step; step.\x0a    debugger result.\x22Answers 1\x22\x0a    debugger step.\x0a    debugger result. \x22Answers 3\x22\x0a    debugger step.\x0a    debugger result. \x22Answers 7\x22\x0a    "
+smalltalk.addMethod(
+"_atEnd",
+smalltalk.method({
+selector: "atEnd",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._shouldReturn())._or_((function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(self)._nextNode()).__eq_eq(_st(self)._currentNode());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+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"],
+referencedClasses: []
+}),
+smalltalk.ASTSteppingInterpreter);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -1060,7 +1166,7 @@ var self=this;
 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)})},
+return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.ASTSteppingInterpreter)})},
 args: [],
 source: "initialize\x0a\x09super initialize.\x0a    continuation := [  ]",
 messageSends: ["initialize"],
@@ -1075,17 +1181,36 @@ selector: "interpret:continue:",
 category: 'interpreting',
 fn: function (aNode,aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
self["@continuation"]=(function(){
+return smalltalk.withContext(function($ctx1) { 
self["@nextNode"]=aNode;
+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)})},
+return self}, function($ctx1) {$ctx1.fill(self,"interpret:continue:",{aNode:aNode,aBlock:aBlock}, smalltalk.ASTSteppingInterpreter)})},
 args: ["aNode", "aBlock"],
-source: "interpret: aNode continue: aBlock\x0a\x09continuation := [ super interpret: aNode continue: aBlock ]",
+source: "interpret: aNode continue: aBlock\x0a\x09nextNode := aNode.\x0a\x09continuation := [ \x0a    \x09super interpret: aNode continue: aBlock ]",
 messageSends: ["interpret:continue:"],
 referencedClasses: []
 }),
 smalltalk.ASTSteppingInterpreter);
 
+smalltalk.addMethod(
+"_nextNode",
+smalltalk.method({
+selector: "nextNode",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@nextNode"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{}, smalltalk.ASTSteppingInterpreter)})},
+args: [],
+source: "nextNode\x0a\x09^ nextNode",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTSteppingInterpreter);
+
 smalltalk.addMethod(
 "_step",
 smalltalk.method({
@@ -1094,7 +1219,7 @@ category: 'stepping',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st(self["@continuation"])._value();
-return self}, function($ctx1) {$ctx1.fill(self,"step",{}, smalltalk.ASTDebugger)})},
+return self}, function($ctx1) {$ctx1.fill(self,"step",{}, smalltalk.ASTSteppingInterpreter)})},
 args: [],
 source: "step\x0a\x09continuation value",
 messageSends: ["value"],
@@ -1122,6 +1247,22 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return false;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.Node)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -1140,6 +1281,22 @@ referencedClasses: []
 }),
 smalltalk.AssignmentNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.AssignmentNode)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.AssignmentNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -1158,6 +1315,22 @@ referencedClasses: []
 }),
 smalltalk.BlockNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.BlockNode)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.BlockNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -1194,6 +1367,22 @@ referencedClasses: []
 }),
 smalltalk.DynamicArrayNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.DynamicArrayNode)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.DynamicArrayNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -1212,6 +1401,22 @@ referencedClasses: []
 }),
 smalltalk.DynamicDictionaryNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.DynamicDictionaryNode)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.DynamicDictionaryNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -1230,6 +1435,22 @@ referencedClasses: []
 }),
 smalltalk.JSStatementNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.JSStatementNode)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.JSStatementNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({
@@ -1284,6 +1505,22 @@ referencedClasses: []
 }),
 smalltalk.SendNode);
 
+smalltalk.addMethod(
+"_isSteppingNode",
+smalltalk.method({
+selector: "isSteppingNode",
+category: '*Compiler-Interpreter',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
return true;
+}, function($ctx1) {$ctx1.fill(self,"isSteppingNode",{}, smalltalk.SendNode)})},
+args: [],
+source: "isSteppingNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.SendNode);
+
 smalltalk.addMethod(
 "_interpreter_continue_",
 smalltalk.method({

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

@@ -264,6 +264,26 @@ return $1;
 }),
 smalltalk.ASTSteppingInterpreterTest);
 
+smalltalk.addMethod(
+"_testAtEnd",
+smalltalk.method({
+selector: "testAtEnd",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_("foo 1 + 2");
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._assert_(_st(_st(self)._interpreter())._atEnd());
+return self}, function($ctx1) {$ctx1.fill(self,"testAtEnd",{}, smalltalk.ASTSteppingInterpreterTest)})}
+}),
+smalltalk.ASTSteppingInterpreterTest);
+
 smalltalk.addMethod(
 "_testMessageSend",
 smalltalk.method({

+ 25 - 0
js/Compiler-Tests.js

@@ -364,6 +364,31 @@ referencedClasses: ["ASTSteppingInterpreter"]
 }),
 smalltalk.ASTSteppingInterpreterTest);
 
+smalltalk.addMethod(
+"_testAtEnd",
+smalltalk.method({
+selector: "testAtEnd",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._interpret_("foo 1 + 2");
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._deny_(_st(_st(self)._interpreter())._atEnd());
+_st(_st(self)._interpreter())._step();
+_st(self)._assert_(_st(_st(self)._interpreter())._atEnd());
+return self}, function($ctx1) {$ctx1.fill(self,"testAtEnd",{}, smalltalk.ASTSteppingInterpreterTest)})},
+args: [],
+source: "testAtEnd\x0a\x09self interpret: 'foo 1 + 2'.\x0a    self deny: self interpreter atEnd.\x0a\x0a    self interpreter step.\x0a    self deny: self interpreter atEnd.\x0a    \x0a    self interpreter step.\x0a    self deny: self interpreter atEnd.\x0a    \x0a    self interpreter step.\x0a    self deny: self interpreter atEnd.\x0a    \x0a    self interpreter step.\x0a    self assert: self interpreter atEnd",
+messageSends: ["interpret:", "deny:", "atEnd", "interpreter", "step", "assert:"],
+referencedClasses: []
+}),
+smalltalk.ASTSteppingInterpreterTest);
+
 smalltalk.addMethod(
 "_testMessageSend",
 smalltalk.method({

+ 89 - 8
st/Compiler-Interpreter.st

@@ -98,7 +98,7 @@ context: aContext
 !
 
 interpreter
-	^ interpreter ifNil: [ interpreter := self defaultInterpreter ]
+	^ interpreter ifNil: [ interpreter := self defaultInterpreterClass new ]
 !
 
 interpreter: anInterpreter
@@ -111,15 +111,35 @@ method
 
 !ASTDebugger methodsFor: 'defaults'!
 
-defaultInterpreter
-	^ ASTSteppingInterpreter new
+defaultInterpreterClass
+	^ ASTSteppingInterpreter
 ! !
 
 !ASTDebugger methodsFor: 'initialization'!
 
+buildAST
+	"Build the AST tree from the method source code.
+    The AST is annotated with a SemanticAnalyzer, 
+    to know the semantics and bindings of each node needed for later debugging"
+    
+    | ast |
+    
+    ast := Smalltalk current parse: self method source.
+    (SemanticAnalyzer on: self context receiver class)
+    	visit: ast.    
+    
+    ^ ast
+!
+
+initializeInterpreter
+	self interpreter interpret: self buildAST nodes first
+!
+
 initializeWithContext: aMethodContext
 	"TODO: do we need to handle block contexts?"
-    self context: IAContext fromMethodContext: aMethodContext
+    
+    self context: IAContext fromMethodContext: aMethodContext.
+    self initializeInterpreter
 ! !
 
 !ASTDebugger methodsFor: 'stepping'!
@@ -132,12 +152,25 @@ resume
 	self shouldBeImplemented
 !
 
+step
+	"The ASTSteppingInterpreter stops at each node interpretation. 
+    One step will interpret nodes until:
+    - we get at the end
+    - the next node is a stepping node (send, assignment, etc.)"
+    
+	[ (self interpreter nextNode notNil and: [ self interpreter nextNode stopOnStepping ])
+		or: [ self interpreter atEnd not ] ] 
+ 			whileFalse: [
+				self interpreter step. 
+                self step ]
+!
+
 stepInto
 	self shouldBeImplemented
 !
 
 stepOver
-	self shouldBeImplemented
+	self step
 ! !
 
 !ASTDebugger class methodsFor: 'instance creation'!
@@ -205,7 +238,7 @@ interpret: aNode continue: aBlock
     	ifTrue: [ 	
         	currentNode := aNode.
             self interpretNode: aNode continue: [ :value |
-  				self continue: aBlock value: value] ]
+  				self continue: aBlock value: value ] ]
         ifFalse: [ self continue: aBlock value: aNode ]
 !
 
@@ -376,8 +409,14 @@ messageFromSendNode: aSendNode arguments: aCollection do: aBlock
         	yourself)
 ! !
 
+!ASTInterpreter methodsFor: 'testing'!
+
+shouldReturn
+	^ shouldReturn ifNil: [ false ]
+! !
+
 ASTInterpreter subclass: #ASTSteppingInterpreter
-	instanceVariableNames: 'continuation'
+	instanceVariableNames: 'continuation nextNode'
 	package: 'Compiler-Interpreter'!
 !ASTSteppingInterpreter commentStamp!
 ASTSteppingInterpreter is an interpreter with stepping capabilities.
@@ -401,6 +440,12 @@ Usage example:
     debugger step.
     debugger result. "Answers 7"!
 
+!ASTSteppingInterpreter methodsFor: 'accessing'!
+
+nextNode
+	^ nextNode
+! !
+
 !ASTSteppingInterpreter methodsFor: 'initialization'!
 
 initialize
@@ -411,7 +456,9 @@ initialize
 !ASTSteppingInterpreter methodsFor: 'interpreting'!
 
 interpret: aNode continue: aBlock
-	continuation := [ super interpret: aNode continue: aBlock ]
+	nextNode := aNode.
+	continuation := [ 
+    	super interpret: aNode continue: aBlock ]
 ! !
 
 !ASTSteppingInterpreter methodsFor: 'stepping'!
@@ -420,22 +467,40 @@ step
 	continuation value
 ! !
 
+!ASTSteppingInterpreter methodsFor: 'testing'!
+
+atEnd
+	^ self shouldReturn or: [ self nextNode == self currentNode ]
+! !
+
 !Node methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ false
 ! !
 
 !AssignmentNode methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretAssignmentNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ true
 ! !
 
 !BlockNode methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretBlockNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ true
 ! !
 
 !CascadeNode methodsFor: '*Compiler-Interpreter'!
@@ -448,18 +513,30 @@ interpreter: anInterpreter continue: aBlock
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretDynamicArrayNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ true
 ! !
 
 !DynamicDictionaryNode methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretDynamicDictionaryNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ true
 ! !
 
 !JSStatementNode methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretJSStatementNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ true
 ! !
 
 !MethodNode methodsFor: '*Compiler-Interpreter'!
@@ -478,6 +555,10 @@ interpreter: anInterpreter continue: aBlock
 
 interpreter: anInterpreter continue: aBlock
 	^ anInterpreter interpretSendNode: self continue: aBlock
+!
+
+isSteppingNode
+	^ true
 ! !
 
 !SequenceNode methodsFor: '*Compiler-Interpreter'!

+ 17 - 0
st/Compiler-Tests.st

@@ -147,6 +147,23 @@ interpreter
 
 !ASTSteppingInterpreterTest methodsFor: 'tests'!
 
+testAtEnd
+	self interpret: 'foo 1 + 2'.
+    self deny: self interpreter atEnd.
+
+    self interpreter step.
+    self deny: self interpreter atEnd.
+    
+    self interpreter step.
+    self deny: self interpreter atEnd.
+    
+    self interpreter step.
+    self deny: self interpreter atEnd.
+    
+    self interpreter step.
+    self assert: self interpreter atEnd
+!
+
 testMessageSend
 	self interpret: 'foo 1 + 2'.