Browse Source

new interpreter based on a stack

Nicolas Petton 11 years ago
parent
commit
1acbf3bf5f

+ 48 - 8
js/Compiler-AST.deploy.js

@@ -151,6 +151,24 @@ return false;
 messageSends: []}),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextChild",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=_st(self._nodes())._isEmpty();
+if(smalltalk.assert($2)){
+$1=self;
+} else {
+$1=_st(_st(self._nodes())._first())._nextChild();
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextChild",{},smalltalk.Node)})},
+messageSends: ["ifTrue:ifFalse:", "nextChild", "first", "nodes", "isEmpty"]}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nextNode",
@@ -176,15 +194,22 @@ smalltalk.method({
 selector: "nextNode:",
 fn: function (aNode){
 var self=this;
+var next;
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self._nodes())._at_ifAbsent_(_st(_st(self._nodes())._indexOf_(aNode)).__plus((1)),(function(){
+var $1,$2;
+var $early={};
+try {
+next=_st(self._nodes())._at_ifAbsent_(_st(_st(self._nodes())._indexOf_(aNode)).__plus((1)),(function(){
 return smalltalk.withContext(function($ctx2) {
-return nil;
+$1=self;
+throw $early=[$1];
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.Node)})},
-messageSends: ["at:ifAbsent:", "+", "indexOf:", "nodes"]}),
+$2=_st(next)._nextChild();
+return $2;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode,next:next},smalltalk.Node)})},
+messageSends: ["at:ifAbsent:", "+", "indexOf:", "nodes", "nextChild"]}),
 smalltalk.Node);
 
 smalltalk.addMethod(
@@ -423,8 +448,9 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@left"]=aNode;
+_st(aNode)._parent_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"left:",{aNode:aNode},smalltalk.AssignmentNode)})},
-messageSends: []}),
+messageSends: ["parent:"]}),
 smalltalk.AssignmentNode);
 
 smalltalk.addMethod(
@@ -461,8 +487,9 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@right"]=aNode;
+_st(aNode)._parent_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"right:",{aNode:aNode},smalltalk.AssignmentNode)})},
-messageSends: []}),
+messageSends: ["parent:"]}),
 smalltalk.AssignmentNode);
 
 
@@ -492,6 +519,19 @@ return true;
 messageSends: []}),
 smalltalk.BlockNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.BlockNode)})},
+messageSends: []}),
+smalltalk.BlockNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "parameters",

+ 61 - 11
js/Compiler-AST.js

@@ -212,6 +212,29 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextChild",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=_st(self._nodes())._isEmpty();
+if(smalltalk.assert($2)){
+$1=self;
+} else {
+$1=_st(_st(self._nodes())._first())._nextChild();
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextChild",{},smalltalk.Node)})},
+args: [],
+source: "nextChild\x0a\x09\x22Answer the next node after aNode.\x0a\x09Recurse into the possible children of the receiver to answer the next node to be evaluated\x22\x0a\x09\x0a\x09^ self nodes isEmpty\x0a\x09\x09ifTrue: [ self ]\x0a\x09\x09ifFalse: [ self nodes first nextChild ]",
+messageSends: ["ifTrue:ifFalse:", "nextChild", "first", "nodes", "isEmpty"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nextNode",
@@ -243,17 +266,24 @@ selector: "nextNode:",
 category: 'accessing',
 fn: function (aNode){
 var self=this;
+var next;
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self._nodes())._at_ifAbsent_(_st(_st(self._nodes())._indexOf_(aNode)).__plus((1)),(function(){
+var $1,$2;
+var $early={};
+try {
+next=_st(self._nodes())._at_ifAbsent_(_st(_st(self._nodes())._indexOf_(aNode)).__plus((1)),(function(){
 return smalltalk.withContext(function($ctx2) {
-return nil;
+$1=self;
+throw $early=[$1];
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.Node)})},
+$2=_st(next)._nextChild();
+return $2;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode,next:next},smalltalk.Node)})},
 args: ["aNode"],
-source: "nextNode: aNode\x0a\x09\x22Answer the next node after aNode\x22\x0a\x09\x0a\x09^ self nodes \x0a\x09\x09at: (self nodes indexOf: aNode) + 1\x0a\x09\x09ifAbsent: [ nil ]",
-messageSends: ["at:ifAbsent:", "+", "indexOf:", "nodes"],
+source: "nextNode: aNode\x0a\x09\x22Answer the next node after aNode.\x0a\x09Recurse into the possible children of the next node to answer the next node to be evaluated\x22\x0a\x09\x0a\x09| next |\x0a\x09\x0a\x09next := self nodes \x0a\x09\x09at: (self nodes indexOf: aNode) + 1\x0a\x09\x09ifAbsent: [ ^ self ].\x0a\x09\x0a\x09^ next nextChild",
+messageSends: ["at:ifAbsent:", "+", "indexOf:", "nodes", "nextChild"],
 referencedClasses: []
 }),
 smalltalk.Node);
@@ -571,10 +601,11 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@left"]=aNode;
+_st(aNode)._parent_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"left:",{aNode:aNode},smalltalk.AssignmentNode)})},
 args: ["aNode"],
-source: "left: aNode\x0a\x09left := aNode",
-messageSends: [],
+source: "left: aNode\x0a\x09left := aNode.\x0a\x09aNode parent: self",
+messageSends: ["parent:"],
 referencedClasses: []
 }),
 smalltalk.AssignmentNode);
@@ -624,10 +655,11 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@right"]=aNode;
+_st(aNode)._parent_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"right:",{aNode:aNode},smalltalk.AssignmentNode)})},
 args: ["aNode"],
-source: "right: aNode\x0a\x09right := aNode",
-messageSends: [],
+source: "right: aNode\x0a\x09right := aNode.\x0a\x09aNode parent: self",
+messageSends: ["parent:"],
 referencedClasses: []
 }),
 smalltalk.AssignmentNode);
@@ -670,6 +702,24 @@ referencedClasses: []
 }),
 smalltalk.BlockNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode:",
+category: 'accessing',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.BlockNode)})},
+args: ["aNode"],
+source: "nextNode: aNode\x0a\x09\x22Answer the receiver as we want to avoid eager evaluation\x22\x0a\x09\x0a\x09^ self",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.BlockNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "parameters",

+ 651 - 3
js/Compiler-Interpreter.deploy.js

@@ -1,5 +1,21 @@
 smalltalk.addPackage('Compiler-Interpreter');
-smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['methodContext', 'outerContext', 'pc', 'locals', 'method'], 'Compiler-Interpreter');
+smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['outerContext', 'innerContext', 'pc', 'locals', 'method', 'ast', 'interpreter'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "arguments",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self._ast())._arguments())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return self._localAt_(each);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"arguments",{},smalltalk.AIContext)})},
+messageSends: ["collect:", "localAt:", "arguments", "ast"]}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "asString",
@@ -13,6 +29,25 @@ return $1;
 messageSends: ["asString"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "ast",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self["@ast"];
+if(($receiver = $1) == nil || $receiver == undefined){
+self._initializeAST();
+} else {
+$1;
+};
+$2=self["@ast"];
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"ast",{},smalltalk.AIContext)})},
+messageSends: ["ifNil:", "initializeAST"]}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "home",
@@ -31,6 +66,19 @@ return $1;
 messageSends: ["ifTrue:ifFalse:", "methodContext", "outerContext", "isBlockContext"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initializeAST",
+fn: function (){
+var self=this;
+function $SemanticAnalyzer(){return smalltalk.SemanticAnalyzer||(typeof SemanticAnalyzer=="undefined"?nil:SemanticAnalyzer)}
+return smalltalk.withContext(function($ctx1) { 
+self["@ast"]=_st(self._method())._ast();
+_st(_st($SemanticAnalyzer())._on_(_st(self._method())._methodClass()))._visit_(self["@ast"]);
+return self}, function($ctx1) {$ctx1.fill(self,"initializeAST",{},smalltalk.AIContext)})},
+messageSends: ["ast", "method", "visit:", "on:", "methodClass"]}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "initializeFromMethodContext:",
@@ -63,6 +111,27 @@ return self}, function($ctx1) {$ctx1.fill(self,"initializeFromMethodContext:",{a
 messageSends: ["pc:", "pc", "receiver:", "receiver", "method:", "method", "ifNotNil:", "outerContext:", "fromMethodContext:", "outerContext", "class", "methodContext", "keysAndValuesDo:", "at:put:", "locals"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initializeInterpreter",
+fn: function (){
+var self=this;
+function $Interpreter(){return smalltalk.Interpreter||(typeof Interpreter=="undefined"?nil:Interpreter)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3;
+$1=_st($Interpreter())._new();
+_st($1)._context_(self);
+_st($1)._node_(self._retrieveNode());
+$2=_st($1)._yourself();
+self["@interpreter"]=$2;
+$3=_st(self._innerContext())._isBlockContext();
+if(! smalltalk.assert($3)){
+self._setupInterpreter_(self["@interpreter"]);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{},smalltalk.AIContext)})},
+messageSends: ["context:", "new", "node:", "retrieveNode", "yourself", "ifFalse:", "setupInterpreter:", "isBlockContext", "innerContext"]}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "initializeLocals",
@@ -76,6 +145,49 @@ return self}, function($ctx1) {$ctx1.fill(self,"initializeLocals",{},smalltalk.A
 messageSends: ["new", "at:put:"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "innerContext",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@innerContext"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"innerContext",{},smalltalk.AIContext)})},
+messageSends: []}),
+smalltalk.AIContext);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "innerContext:",
+fn: function (anAIContext){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@innerContext"]=anAIContext;
+return self}, function($ctx1) {$ctx1.fill(self,"innerContext:",{anAIContext:anAIContext},smalltalk.AIContext)})},
+messageSends: []}),
+smalltalk.AIContext);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpreter",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self["@interpreter"];
+if(($receiver = $1) == nil || $receiver == undefined){
+self._initializeInterpreter();
+} else {
+$1;
+};
+$2=self["@interpreter"];
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"interpreter",{},smalltalk.AIContext)})},
+messageSends: ["ifNil:", "initializeInterpreter"]}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isBlockContext",
@@ -179,8 +291,9 @@ fn: function (anAIContext){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@outerContext"]=anAIContext;
+_st(self["@outerContext"])._innerContext_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"outerContext:",{anAIContext:anAIContext},smalltalk.AIContext)})},
-messageSends: []}),
+messageSends: ["innerContext:"]}),
 smalltalk.AIContext);
 
 smalltalk.addMethod(
@@ -237,6 +350,24 @@ return self}, function($ctx1) {$ctx1.fill(self,"receiver:",{anObject:anObject},s
 messageSends: ["localAt:put:"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "retrieveNode",
+fn: function (){
+var self=this;
+function $ASTPCNodeVisitor(){return smalltalk.ASTPCNodeVisitor||(typeof ASTPCNodeVisitor=="undefined"?nil:ASTPCNodeVisitor)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+$2=_st($ASTPCNodeVisitor())._new();
+_st($2)._context_(self);
+_st($2)._visit_(self._ast());
+$3=_st($2)._currentNode();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"retrieveNode",{},smalltalk.AIContext)})},
+messageSends: ["context:", "new", "visit:", "ast", "currentNode"]}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selector",
@@ -255,6 +386,21 @@ return $1;
 messageSends: ["ifNotNil:", "selector", "method"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "setupInterpreter:",
+fn: function (anInterpreter){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(_st(self._innerContext())._arguments())._reversed())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(anInterpreter)._push_(each);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+_st(anInterpreter)._push_(_st(self._innerContext())._receiver());
+return self}, function($ctx1) {$ctx1.fill(self,"setupInterpreter:",{anInterpreter:anInterpreter},smalltalk.AIContext)})},
+messageSends: ["do:", "push:", "reversed", "arguments", "innerContext", "receiver"]}),
+smalltalk.AIContext);
+
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -517,7 +663,7 @@ messageSends: ["initializeWithContext:", "new", "yourself"]}),
 smalltalk.ASTDebugger.klass);
 
 
-smalltalk.addClass('ASTInterpreter', smalltalk.Object, ['currentNode', 'context', 'shouldReturn', 'result'], 'Compiler-Interpreter');
+smalltalk.addClass('ASTInterpreter', smalltalk.Object, ['currentNode', 'nextNode', 'context', 'shouldReturn', 'result'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "assign:to:",
@@ -966,6 +1112,35 @@ return self}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:arguments:do
 messageSends: ["continue:value:", "selector:", "selector", "new", "arguments:", "yourself"]}),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@nextNode"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=self._currentNode();
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{},smalltalk.ASTInterpreter)})},
+messageSends: ["ifNil:", "currentNode"]}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@nextNode"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.ASTInterpreter)})},
+messageSends: []}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "result",
@@ -1257,6 +1432,479 @@ smalltalk.ASTPCNodeVisitor);
 
 
 
+smalltalk.addClass('Interpreter', smalltalk.NodeVisitor, ['node', 'context', 'stack', 'returnValue'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "assign:to:",
+fn: function (aNode,anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($1)){
+_st(_st(self._context())._receiver())._instVarAt_put_(_st(aNode)._value(),anObject);
+} else {
+_st(self._context())._localAt_put_(_st(aNode)._value(),anObject);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"assign:to:",{aNode:aNode,anObject:anObject},smalltalk.Interpreter)})},
+messageSends: ["ifTrue:ifFalse:", "instVarAt:put:", "value", "receiver", "context", "localAt:put:", "isInstanceVar", "binding"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "atEnd",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._shouldReturn())._or_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(self._node())._isNil();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"atEnd",{},smalltalk.Interpreter)})},
+messageSends: ["or:", "isNil", "node", "shouldReturn"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "context",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@context"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"context",{},smalltalk.Interpreter)})},
+messageSends: []}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "context:",
+fn: function (aContext){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@context"]=aContext;
+return self}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext},smalltalk.Interpreter)})},
+messageSends: []}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "eval:",
+fn: function (aString){
+var self=this;
+var source,function_;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+function $Compiler(){return smalltalk.Compiler||(typeof Compiler=="undefined"?nil:Compiler)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3;
+source=_st($String())._streamContents_((function(str){
+return smalltalk.withContext(function($ctx2) {
+_st(str)._nextPutAll_("(function(");
+_st(_st(_st(self._context())._locals())._keys())._do_separatedBy_((function(each){
+return smalltalk.withContext(function($ctx3) {
+return _st(str)._nextPutAll_(each);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx2)})}),(function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(str)._nextPutAll_(",");
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+$1=str;
+_st($1)._nextPutAll_("){ return (function() {");
+_st($1)._nextPutAll_(aString);
+$2=_st($1)._nextPutAll_("})() })");
+return $2;
+}, function($ctx2) {$ctx2.fillBlock({str:str},$ctx1)})}));
+function_=_st(_st($Compiler())._new())._eval_(source);
+$3=_st(function_)._valueWithPossibleArguments_(_st(_st(self._context())._locals())._values());
+return $3;
+}, function($ctx1) {$ctx1.fill(self,"eval:",{aString:aString,source:source,function_:function_},smalltalk.Interpreter)})},
+messageSends: ["streamContents:", "nextPutAll:", "do:separatedBy:", "keys", "locals", "context", "eval:", "new", "valueWithPossibleArguments:", "values"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpret",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._visit_(self._node());
+return self}, function($ctx1) {$ctx1.fill(self,"interpret",{},smalltalk.Interpreter)})},
+messageSends: ["visit:", "node"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "messageFromSendNode:arguments:",
+fn: function (aSendNode,aCollection){
+var self=this;
+function $Message(){return smalltalk.Message||(typeof Message=="undefined"?nil:Message)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+$2=_st($Message())._new();
+_st($2)._selector_(_st(aSendNode)._selector());
+_st($2)._arguments_(aCollection);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:arguments:",{aSendNode:aSendNode,aCollection:aCollection},smalltalk.Interpreter)})},
+messageSends: ["selector:", "selector", "new", "arguments:", "yourself"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "messageNotUnderstood:receiver:",
+fn: function (aMessage,anObject){
+var self=this;
+function $MessageNotUnderstood(){return smalltalk.MessageNotUnderstood||(typeof MessageNotUnderstood=="undefined"?nil:MessageNotUnderstood)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st($MessageNotUnderstood())._new();
+_st($1)._meesage_(aMessage);
+_st($1)._receiver_(anObject);
+$2=_st($1)._signal();
+return self}, function($ctx1) {$ctx1.fill(self,"messageNotUnderstood:receiver:",{aMessage:aMessage,anObject:anObject},smalltalk.Interpreter)})},
+messageSends: ["meesage:", "new", "receiver:", "signal"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "next",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._node_(_st(self._node())._nextNode());
+return self}, function($ctx1) {$ctx1.fill(self,"next",{},smalltalk.Interpreter)})},
+messageSends: ["node:", "nextNode", "node"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "node",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@node"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"node",{},smalltalk.Interpreter)})},
+messageSends: []}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "node:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@node"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"node:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: []}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "peek",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._stack())._last();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"peek",{},smalltalk.Interpreter)})},
+messageSends: ["last", "stack"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "pop",
+fn: function (){
+var self=this;
+var value;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+value=self._peek();
+_st(self._stack())._removeLast();
+$1=value;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"pop",{value:value},smalltalk.Interpreter)})},
+messageSends: ["peek", "removeLast", "stack"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "proceed",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(self._node())._notNil();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileTrue_((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._step();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"proceed",{},smalltalk.Interpreter)})},
+messageSends: ["whileTrue:", "step", "notNil", "node"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "push:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._stack())._add_(anObject);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"push:",{anObject:anObject},smalltalk.Interpreter)})},
+messageSends: ["add:", "stack"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "returnValue",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@returnValue"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=_st(self._context())._receiver();
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"returnValue",{},smalltalk.Interpreter)})},
+messageSends: ["ifNil:", "receiver", "context"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "returnValue:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@returnValue"]=anObject;
+return self}, function($ctx1) {$ctx1.fill(self,"returnValue:",{anObject:anObject},smalltalk.Interpreter)})},
+messageSends: []}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "sendMessage:to:superSend:",
+fn: function (aMessage,anObject,aBoolean){
+var self=this;
+var method;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4,$5,$6;
+var $early={};
+try {
+$1=aBoolean;
+if(! smalltalk.assert($1)){
+$2=_st(aMessage)._sendTo_(anObject);
+return $2;
+};
+$3=_st(_st(anObject)._class())._superclass();
+if(($receiver = $3) == nil || $receiver == undefined){
+$4=self._messageNotUnderstood_receiver_(aMessage,anObject);
+return $4;
+} else {
+$3;
+};
+method=_st(_st(_st(_st(anObject)._class())._superclass())._methodDictionary())._at_ifAbsent_(_st(aMessage)._selector(),(function(){
+return smalltalk.withContext(function($ctx2) {
+$5=self._messageNotUnderstood_receiver_(aMessage,anObject);
+throw $early=[$5];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$6=_st(method)._sendTo_arguments_(anObject,_st(aMessage)._arguments());
+return $6;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"sendMessage:to:superSend:",{aMessage:aMessage,anObject:anObject,aBoolean:aBoolean,method:method},smalltalk.Interpreter)})},
+messageSends: ["ifFalse:", "sendTo:", "ifNil:", "messageNotUnderstood:receiver:", "superclass", "class", "at:ifAbsent:", "selector", "methodDictionary", "sendTo:arguments:", "arguments"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "shouldReturn",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self["@returnValue"])._notNil();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"shouldReturn",{},smalltalk.Interpreter)})},
+messageSends: ["notNil"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "skip",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._next();
+return self}, function($ctx1) {$ctx1.fill(self,"skip",{},smalltalk.Interpreter)})},
+messageSends: ["next"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "stack",
+fn: function (){
+var self=this;
+function $OrderedCollection(){return smalltalk.OrderedCollection||(typeof OrderedCollection=="undefined"?nil:OrderedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@stack"];
+if(($receiver = $2) == nil || $receiver == undefined){
+self["@stack"]=_st($OrderedCollection())._new();
+$1=self["@stack"];
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"stack",{},smalltalk.Interpreter)})},
+messageSends: ["ifNil:", "new"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "step",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self;
+_st($1)._interpret();
+$2=_st($1)._next();
+return self}, function($ctx1) {$ctx1.fill(self,"step",{},smalltalk.Interpreter)})},
+messageSends: ["interpret", "next"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visit:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self._shouldReturn();
+if(! smalltalk.assert($1)){
+smalltalk.NodeVisitor.fn.prototype._visit_.apply(_st(self), [aNode]);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"visit:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["ifFalse:", "visit:", "shouldReturn"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitAssignmentNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assign_to_(_st(aNode)._left(),self._peek());
+return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["assign:to:", "left", "peek"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitClassReferenceNode:",
+fn: function (aNode){
+var self=this;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+self._push_(_st(_st($Smalltalk())._current())._at_(_st(aNode)._value()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["push:", "at:", "value", "current"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitJSStatementNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._returnValue_(self._eval_(_st(aNode)._source()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["returnValue:", "eval:", "source"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"visitNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: []}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitReturnNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._returnValue_(self._pop());
+return self}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["returnValue:", "pop"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitSendNode:",
+fn: function (aNode){
+var self=this;
+var receiver,args,message,result;
+return smalltalk.withContext(function($ctx1) { 
+args=_st(_st(aNode)._arguments())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return self._pop();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+receiver=self._pop();
+message=self._messageFromSendNode_arguments_(aNode,args);
+result=self._sendMessage_to_superSend_(message,receiver,_st(aNode)._superSend());
+self._push_(result);
+return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode,receiver:receiver,args:args,message:message,result:result},smalltalk.Interpreter)})},
+messageSends: ["collect:", "pop", "arguments", "messageFromSendNode:arguments:", "sendMessage:to:superSend:", "superSend", "push:"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitValueNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._push_(_st(aNode)._value());
+return self}, function($ctx1) {$ctx1.fill(self,"visitValueNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["push:", "value"]}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitVariableNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$2;
+$1=self;
+$3=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($3)){
+$2=_st(_st(self._context())._receiver())._instVarAt_(_st(aNode)._value());
+} else {
+$2=_st(self._context())._localAt_(_st(aNode)._value());
+};
+_st($1)._push_($2);
+return self}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode},smalltalk.Interpreter)})},
+messageSends: ["push:", "ifTrue:ifFalse:", "instVarAt:", "value", "receiver", "context", "localAt:", "isInstanceVar", "binding"]}),
+smalltalk.Interpreter);
+
+
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpreter:continue:",

+ 867 - 9
js/Compiler-Interpreter.js

@@ -1,6 +1,27 @@
 smalltalk.addPackage('Compiler-Interpreter');
-smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['methodContext', 'outerContext', 'pc', 'locals', 'method'], 'Compiler-Interpreter');
+smalltalk.addClass('AIContext', smalltalk.NodeVisitor, ['outerContext', 'innerContext', 'pc', 'locals', 'method', 'ast', 'interpreter'], 'Compiler-Interpreter');
 smalltalk.AIContext.comment="I am like a `MethodContext`, used by the `ASTInterpreter`.\x0aUnlike a `MethodContext`, my instances are not read-only.\x0a\x0aWhen debugging, my instances are created by copying the current `MethodContext` (thisContext)";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "arguments",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self._ast())._arguments())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return self._localAt_(each);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"arguments",{},smalltalk.AIContext)})},
+args: [],
+source: "arguments\x0a\x09^ self ast arguments collect: [ :each |\x0a\x09\x09self localAt: each ]",
+messageSends: ["collect:", "localAt:", "arguments", "ast"],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "asString",
@@ -19,6 +40,30 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "ast",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self["@ast"];
+if(($receiver = $1) == nil || $receiver == undefined){
+self._initializeAST();
+} else {
+$1;
+};
+$2=self["@ast"];
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"ast",{},smalltalk.AIContext)})},
+args: [],
+source: "ast\x0a\x09ast ifNil: [ self initializeAST ].\x0a\x09^ ast",
+messageSends: ["ifNil:", "initializeAST"],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "home",
@@ -42,6 +87,24 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initializeAST",
+category: 'initialization',
+fn: function (){
+var self=this;
+function $SemanticAnalyzer(){return smalltalk.SemanticAnalyzer||(typeof SemanticAnalyzer=="undefined"?nil:SemanticAnalyzer)}
+return smalltalk.withContext(function($ctx1) { 
+self["@ast"]=_st(self._method())._ast();
+_st(_st($SemanticAnalyzer())._on_(_st(self._method())._methodClass()))._visit_(self["@ast"]);
+return self}, function($ctx1) {$ctx1.fill(self,"initializeAST",{},smalltalk.AIContext)})},
+args: [],
+source: "initializeAST\x0a\x09ast := self method ast.\x0a\x09(SemanticAnalyzer on: self method methodClass)\x0a\x09\x09visit: ast",
+messageSends: ["ast", "method", "visit:", "on:", "methodClass"],
+referencedClasses: ["SemanticAnalyzer"]
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "initializeFromMethodContext:",
@@ -79,6 +142,32 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initializeInterpreter",
+category: 'initialization',
+fn: function (){
+var self=this;
+function $Interpreter(){return smalltalk.Interpreter||(typeof Interpreter=="undefined"?nil:Interpreter)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3;
+$1=_st($Interpreter())._new();
+_st($1)._context_(self);
+_st($1)._node_(self._retrieveNode());
+$2=_st($1)._yourself();
+self["@interpreter"]=$2;
+$3=_st(self._innerContext())._isBlockContext();
+if(! smalltalk.assert($3)){
+self._setupInterpreter_(self["@interpreter"]);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{},smalltalk.AIContext)})},
+args: [],
+source: "initializeInterpreter\x0a\x09interpreter := Interpreter new\x0a\x09\x09context: self;\x0a\x09\x09node: self retrieveNode;\x0a\x09\x09yourself.\x0a\x09self innerContext isBlockContext ifFalse: [\x0a\x09\x09self setupInterpreter: interpreter ]",
+messageSends: ["context:", "new", "node:", "retrieveNode", "yourself", "ifFalse:", "setupInterpreter:", "isBlockContext", "innerContext"],
+referencedClasses: ["Interpreter"]
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "initializeLocals",
@@ -97,6 +186,64 @@ referencedClasses: ["Dictionary"]
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "innerContext",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@innerContext"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"innerContext",{},smalltalk.AIContext)})},
+args: [],
+source: "innerContext\x0a\x09^ innerContext",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "innerContext:",
+category: 'accessing',
+fn: function (anAIContext){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@innerContext"]=anAIContext;
+return self}, function($ctx1) {$ctx1.fill(self,"innerContext:",{anAIContext:anAIContext},smalltalk.AIContext)})},
+args: ["anAIContext"],
+source: "innerContext: anAIContext\x0a\x09innerContext := anAIContext",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpreter",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self["@interpreter"];
+if(($receiver = $1) == nil || $receiver == undefined){
+self._initializeInterpreter();
+} else {
+$1;
+};
+$2=self["@interpreter"];
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"interpreter",{},smalltalk.AIContext)})},
+args: [],
+source: "interpreter\x0a\x09interpreter ifNil: [ self initializeInterpreter ].\x0a\x09^ interpreter",
+messageSends: ["ifNil:", "initializeInterpreter"],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isBlockContext",
@@ -236,10 +383,11 @@ fn: function (anAIContext){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@outerContext"]=anAIContext;
+_st(self["@outerContext"])._innerContext_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"outerContext:",{anAIContext:anAIContext},smalltalk.AIContext)})},
 args: ["anAIContext"],
-source: "outerContext: anAIContext\x0a\x09outerContext := anAIContext",
-messageSends: [],
+source: "outerContext: anAIContext\x0a\x09outerContext := anAIContext.\x0a\x09outerContext innerContext: self",
+messageSends: ["innerContext:"],
 referencedClasses: []
 }),
 smalltalk.AIContext);
@@ -247,7 +395,7 @@ smalltalk.AIContext);
 smalltalk.addMethod(
 smalltalk.method({
 selector: "pc",
-category: 'accessing',
+category: 'interpreting',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
@@ -271,7 +419,7 @@ smalltalk.AIContext);
 smalltalk.addMethod(
 smalltalk.method({
 selector: "pc:",
-category: 'accessing',
+category: 'interpreting',
 fn: function (anInteger){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
@@ -287,7 +435,7 @@ smalltalk.AIContext);
 smalltalk.addMethod(
 smalltalk.method({
 selector: "receiver",
-category: 'accessing',
+category: 'interpreting',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
@@ -305,7 +453,7 @@ smalltalk.AIContext);
 smalltalk.addMethod(
 smalltalk.method({
 selector: "receiver:",
-category: 'accessing',
+category: 'interpreting',
 fn: function (anObject){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
@@ -318,6 +466,29 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "retrieveNode",
+category: 'interpreting',
+fn: function (){
+var self=this;
+function $ASTPCNodeVisitor(){return smalltalk.ASTPCNodeVisitor||(typeof ASTPCNodeVisitor=="undefined"?nil:ASTPCNodeVisitor)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+$2=_st($ASTPCNodeVisitor())._new();
+_st($2)._context_(self);
+_st($2)._visit_(self._ast());
+$3=_st($2)._currentNode();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"retrieveNode",{},smalltalk.AIContext)})},
+args: [],
+source: "retrieveNode\x0a\x09^ ASTPCNodeVisitor new\x0a\x09\x09context: self;\x0a\x09\x09visit: self ast;\x0a\x09\x09currentNode",
+messageSends: ["context:", "new", "visit:", "ast", "currentNode"],
+referencedClasses: ["ASTPCNodeVisitor"]
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selector",
@@ -335,12 +506,32 @@ $1=_st(self._method())._selector();
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"selector",{},smalltalk.AIContext)})},
 args: [],
-source: "selector\x0a\x09^ self method\x0a\x09\x09ifNotNil: [ self method selector ]",
+source: "selector\x0a\x09^ self method ifNotNil: [ \x0a\x09\x09self method selector ]",
 messageSends: ["ifNotNil:", "selector", "method"],
 referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "setupInterpreter:",
+category: 'interpreting',
+fn: function (anInterpreter){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(_st(self._innerContext())._arguments())._reversed())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(anInterpreter)._push_(each);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+_st(anInterpreter)._push_(_st(self._innerContext())._receiver());
+return self}, function($ctx1) {$ctx1.fill(self,"setupInterpreter:",{anInterpreter:anInterpreter},smalltalk.AIContext)})},
+args: ["anInterpreter"],
+source: "setupInterpreter: anInterpreter\x0a\x09\x22Push the send args and receiver to the interpreter stack\x22\x0a\x09\x0a\x09self innerContext arguments reversed do: [ :each | \x0a\x09\x09anInterpreter push: each ].\x0a\x09\x09\x0a\x09anInterpreter push: (self innerContext receiver)",
+messageSends: ["do:", "push:", "reversed", "arguments", "innerContext", "receiver"],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -694,7 +885,7 @@ referencedClasses: []
 smalltalk.ASTDebugger.klass);
 
 
-smalltalk.addClass('ASTInterpreter', smalltalk.Object, ['currentNode', 'context', 'shouldReturn', 'result'], 'Compiler-Interpreter');
+smalltalk.addClass('ASTInterpreter', smalltalk.Object, ['currentNode', 'nextNode', 'context', 'shouldReturn', 'result'], 'Compiler-Interpreter');
 smalltalk.ASTInterpreter.comment="I am like a `NodeVisitor`, interpreting nodes one after each other.\x0aI am built using Continuation Passing Style for stepping purposes.\x0a\x0a## Usage example:\x0a\x0a\x09| ast interpreter |\x0a\x09ast := Smalltalk current parse: 'foo 1+2+4'.\x0a\x09(SemanticAnalyzer on: Object) visit: ast.\x0a\x0a\x09ASTInterpreter new\x0a\x09\x09interpret: ast nodes first;\x0a\x09\x09result \x22Answers 7\x22";
 smalltalk.addMethod(
 smalltalk.method({
@@ -1279,6 +1470,45 @@ referencedClasses: ["Message"]
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@nextNode"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=self._currentNode();
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{},smalltalk.ASTInterpreter)})},
+args: [],
+source: "nextNode\x0a\x09^ nextNode ifNil: [ self currentNode ]",
+messageSends: ["ifNil:", "currentNode"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode:",
+category: 'accessing',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@nextNode"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.ASTInterpreter)})},
+args: ["aNode"],
+source: "nextNode: aNode\x0a\x09nextNode := aNode",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "result",
@@ -1662,6 +1892,634 @@ smalltalk.ASTPCNodeVisitor);
 
 
 
+smalltalk.addClass('Interpreter', smalltalk.NodeVisitor, ['node', 'context', 'stack', 'returnValue'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "assign:to:",
+category: 'private',
+fn: function (aNode,anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($1)){
+_st(_st(self._context())._receiver())._instVarAt_put_(_st(aNode)._value(),anObject);
+} else {
+_st(self._context())._localAt_put_(_st(aNode)._value(),anObject);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"assign:to:",{aNode:aNode,anObject:anObject},smalltalk.Interpreter)})},
+args: ["aNode", "anObject"],
+source: "assign: aNode to: anObject\x0a\x09aNode binding isInstanceVar\x0a\x09\x09ifTrue: [ self context receiver instVarAt: aNode value put: anObject ]\x0a\x09\x09ifFalse: [ self context localAt: aNode value put: anObject ]",
+messageSends: ["ifTrue:ifFalse:", "instVarAt:put:", "value", "receiver", "context", "localAt:put:", "isInstanceVar", "binding"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "atEnd",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._shouldReturn())._or_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(self._node())._isNil();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"atEnd",{},smalltalk.Interpreter)})},
+args: [],
+source: "atEnd\x0a\x09^ self shouldReturn or: [ self node isNil ]",
+messageSends: ["or:", "isNil", "node", "shouldReturn"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "context",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@context"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"context",{},smalltalk.Interpreter)})},
+args: [],
+source: "context\x0a\x09^ context",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "context:",
+category: 'accessing',
+fn: function (aContext){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@context"]=aContext;
+return self}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext},smalltalk.Interpreter)})},
+args: ["aContext"],
+source: "context: aContext\x0a\x09context := aContext",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "eval:",
+category: 'private',
+fn: function (aString){
+var self=this;
+var source,function_;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+function $Compiler(){return smalltalk.Compiler||(typeof Compiler=="undefined"?nil:Compiler)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3;
+source=_st($String())._streamContents_((function(str){
+return smalltalk.withContext(function($ctx2) {
+_st(str)._nextPutAll_("(function(");
+_st(_st(_st(self._context())._locals())._keys())._do_separatedBy_((function(each){
+return smalltalk.withContext(function($ctx3) {
+return _st(str)._nextPutAll_(each);
+}, function($ctx3) {$ctx3.fillBlock({each:each},$ctx2)})}),(function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(str)._nextPutAll_(",");
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+$1=str;
+_st($1)._nextPutAll_("){ return (function() {");
+_st($1)._nextPutAll_(aString);
+$2=_st($1)._nextPutAll_("})() })");
+return $2;
+}, function($ctx2) {$ctx2.fillBlock({str:str},$ctx1)})}));
+function_=_st(_st($Compiler())._new())._eval_(source);
+$3=_st(function_)._valueWithPossibleArguments_(_st(_st(self._context())._locals())._values());
+return $3;
+}, function($ctx1) {$ctx1.fill(self,"eval:",{aString:aString,source:source,function_:function_},smalltalk.Interpreter)})},
+args: ["aString"],
+source: "eval: aString\x0a\x09\x22Evaluate aString as JS source inside an JS function.\x0a\x09aString is not sandboxed.\x22\x0a\x09\x0a\x09| source function |\x0a\x09\x0a\x09source := String streamContents: [ :str |\x0a\x09\x09str nextPutAll: '(function('.\x0a\x09\x09self context locals keys\x0a\x09\x09\x09do: [ :each | str nextPutAll: each ]\x0a\x09\x09\x09separatedBy: [ str nextPutAll: ',' ].\x0a\x09\x09str\x0a\x09\x09\x09nextPutAll: '){ return (function() {';\x0a\x09\x09\x09nextPutAll: aString;\x0a\x09\x09\x09nextPutAll: '})() })' ].\x0a\x09\x09\x09\x0a\x09function := Compiler new eval: source.\x0a\x09\x0a\x09^ function valueWithPossibleArguments: self context locals values",
+messageSends: ["streamContents:", "nextPutAll:", "do:separatedBy:", "keys", "locals", "context", "eval:", "new", "valueWithPossibleArguments:", "values"],
+referencedClasses: ["String", "Compiler"]
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpret",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._visit_(self._node());
+return self}, function($ctx1) {$ctx1.fill(self,"interpret",{},smalltalk.Interpreter)})},
+args: [],
+source: "interpret\x0a\x09\x22Interpret the next node to be evaluated\x22\x0a\x09\x0a\x09self visit: self node",
+messageSends: ["visit:", "node"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "messageFromSendNode:arguments:",
+category: 'private',
+fn: function (aSendNode,aCollection){
+var self=this;
+function $Message(){return smalltalk.Message||(typeof Message=="undefined"?nil:Message)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+$2=_st($Message())._new();
+_st($2)._selector_(_st(aSendNode)._selector());
+_st($2)._arguments_(aCollection);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"messageFromSendNode:arguments:",{aSendNode:aSendNode,aCollection:aCollection},smalltalk.Interpreter)})},
+args: ["aSendNode", "aCollection"],
+source: "messageFromSendNode: aSendNode arguments: aCollection\x0a\x09^ Message new\x0a\x09\x09selector: aSendNode selector;\x0a\x09\x09arguments: aCollection;\x0a\x09\x09yourself",
+messageSends: ["selector:", "selector", "new", "arguments:", "yourself"],
+referencedClasses: ["Message"]
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "messageNotUnderstood:receiver:",
+category: 'private',
+fn: function (aMessage,anObject){
+var self=this;
+function $MessageNotUnderstood(){return smalltalk.MessageNotUnderstood||(typeof MessageNotUnderstood=="undefined"?nil:MessageNotUnderstood)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st($MessageNotUnderstood())._new();
+_st($1)._meesage_(aMessage);
+_st($1)._receiver_(anObject);
+$2=_st($1)._signal();
+return self}, function($ctx1) {$ctx1.fill(self,"messageNotUnderstood:receiver:",{aMessage:aMessage,anObject:anObject},smalltalk.Interpreter)})},
+args: ["aMessage", "anObject"],
+source: "messageNotUnderstood: aMessage receiver: anObject\x0a\x09MessageNotUnderstood new\x0a\x09\x09meesage: aMessage;\x0a\x09\x09receiver: anObject;\x0a\x09\x09signal",
+messageSends: ["meesage:", "new", "receiver:", "signal"],
+referencedClasses: ["MessageNotUnderstood"]
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "next",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._node_(_st(self._node())._nextNode());
+return self}, function($ctx1) {$ctx1.fill(self,"next",{},smalltalk.Interpreter)})},
+args: [],
+source: "next\x0a\x09self node: self node nextNode",
+messageSends: ["node:", "nextNode", "node"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "node",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@node"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"node",{},smalltalk.Interpreter)})},
+args: [],
+source: "node\x0a\x09\x22Answer the next node, ie the node to be evaluated in the next step\x22\x0a\x09\x0a\x09^ node",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "node:",
+category: 'accessing',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@node"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"node:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "node: aNode\x0a\x09node := aNode",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "peek",
+category: 'stack',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._stack())._last();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"peek",{},smalltalk.Interpreter)})},
+args: [],
+source: "peek\x0a\x09\x22Peek the top object of the context stack\x22\x0a\x09\x0a\x09^ self stack last",
+messageSends: ["last", "stack"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "pop",
+category: 'stack',
+fn: function (){
+var self=this;
+var value;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+value=self._peek();
+_st(self._stack())._removeLast();
+$1=value;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"pop",{value:value},smalltalk.Interpreter)})},
+args: [],
+source: "pop\x0a\x09\x22Pop an object from the context stack\x22\x0a\x09\x0a\x09| value |\x0a\x09\x0a\x09value := self peek.\x0a\x09self stack removeLast.\x0a\x09^ value",
+messageSends: ["peek", "removeLast", "stack"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "proceed",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(self._node())._notNil();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileTrue_((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._step();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"proceed",{},smalltalk.Interpreter)})},
+args: [],
+source: "proceed\x0a\x09\x22Eagerly evaluate the ast\x22\x0a\x09\x0a\x09[ self node notNil ] whileTrue: [ \x0a\x09\x09self step ]",
+messageSends: ["whileTrue:", "step", "notNil", "node"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "push:",
+category: 'stack',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._stack())._add_(anObject);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"push:",{anObject:anObject},smalltalk.Interpreter)})},
+args: ["anObject"],
+source: "push: anObject\x0a\x09\x22Push an object to the context stack\x22\x0a\x09\x0a\x09^ self stack add: anObject",
+messageSends: ["add:", "stack"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "returnValue",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@returnValue"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=_st(self._context())._receiver();
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"returnValue",{},smalltalk.Interpreter)})},
+args: [],
+source: "returnValue\x0a\x09^ returnValue ifNil: [ self context receiver ]",
+messageSends: ["ifNil:", "receiver", "context"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "returnValue:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@returnValue"]=anObject;
+return self}, function($ctx1) {$ctx1.fill(self,"returnValue:",{anObject:anObject},smalltalk.Interpreter)})},
+args: ["anObject"],
+source: "returnValue: anObject\x0a\x09returnValue := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "sendMessage:to:superSend:",
+category: 'private',
+fn: function (aMessage,anObject,aBoolean){
+var self=this;
+var method;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4,$5,$6;
+var $early={};
+try {
+$1=aBoolean;
+if(! smalltalk.assert($1)){
+$2=_st(aMessage)._sendTo_(anObject);
+return $2;
+};
+$3=_st(_st(anObject)._class())._superclass();
+if(($receiver = $3) == nil || $receiver == undefined){
+$4=self._messageNotUnderstood_receiver_(aMessage,anObject);
+return $4;
+} else {
+$3;
+};
+method=_st(_st(_st(_st(anObject)._class())._superclass())._methodDictionary())._at_ifAbsent_(_st(aMessage)._selector(),(function(){
+return smalltalk.withContext(function($ctx2) {
+$5=self._messageNotUnderstood_receiver_(aMessage,anObject);
+throw $early=[$5];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$6=_st(method)._sendTo_arguments_(anObject,_st(aMessage)._arguments());
+return $6;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"sendMessage:to:superSend:",{aMessage:aMessage,anObject:anObject,aBoolean:aBoolean,method:method},smalltalk.Interpreter)})},
+args: ["aMessage", "anObject", "aBoolean"],
+source: "sendMessage: aMessage to: anObject superSend: aBoolean\x0a\x09| method |\x0a\x09\x0a\x09aBoolean ifFalse: [ ^ aMessage sendTo: anObject ].\x0a\x09anObject class superclass ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x0a\x09method := anObject class superclass methodDictionary\x0a\x09\x09at: aMessage selector\x0a\x09\x09ifAbsent: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].\x0a\x09\x09\x0a\x09^ method sendTo: anObject arguments: aMessage arguments",
+messageSends: ["ifFalse:", "sendTo:", "ifNil:", "messageNotUnderstood:receiver:", "superclass", "class", "at:ifAbsent:", "selector", "methodDictionary", "sendTo:arguments:", "arguments"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "shouldReturn",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self["@returnValue"])._notNil();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"shouldReturn",{},smalltalk.Interpreter)})},
+args: [],
+source: "shouldReturn\x0a\x09^ returnValue notNil",
+messageSends: ["notNil"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "skip",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._next();
+return self}, function($ctx1) {$ctx1.fill(self,"skip",{},smalltalk.Interpreter)})},
+args: [],
+source: "skip\x0a\x09self next",
+messageSends: ["next"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "stack",
+category: 'accessing',
+fn: function (){
+var self=this;
+function $OrderedCollection(){return smalltalk.OrderedCollection||(typeof OrderedCollection=="undefined"?nil:OrderedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@stack"];
+if(($receiver = $2) == nil || $receiver == undefined){
+self["@stack"]=_st($OrderedCollection())._new();
+$1=self["@stack"];
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"stack",{},smalltalk.Interpreter)})},
+args: [],
+source: "stack\x0a\x09^ stack ifNil: [ stack := OrderedCollection new ]",
+messageSends: ["ifNil:", "new"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "step",
+category: 'interpreting',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self;
+_st($1)._interpret();
+$2=_st($1)._next();
+return self}, function($ctx1) {$ctx1.fill(self,"step",{},smalltalk.Interpreter)})},
+args: [],
+source: "step\x0a\x09self \x0a\x09\x09interpret; \x0a\x09\x09next",
+messageSends: ["interpret", "next"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visit:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self._shouldReturn();
+if(! smalltalk.assert($1)){
+smalltalk.NodeVisitor.fn.prototype._visit_.apply(_st(self), [aNode]);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"visit:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visit: aNode\x0a\x09self shouldReturn ifFalse: [ super visit: aNode ]",
+messageSends: ["ifFalse:", "visit:", "shouldReturn"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitAssignmentNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assign_to_(_st(aNode)._left(),self._peek());
+return self}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitAssignmentNode: aNode\x0a\x09\x22Do not pop as the pushed value would be the right node\x22\x0a\x09\x0a\x09self assign: aNode left to: self peek",
+messageSends: ["assign:to:", "left", "peek"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitClassReferenceNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+self._push_(_st(_st($Smalltalk())._current())._at_(_st(aNode)._value()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitClassReferenceNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitClassReferenceNode: aNode\x0a\x09self push: (Smalltalk current at: aNode value)",
+messageSends: ["push:", "at:", "value", "current"],
+referencedClasses: ["Smalltalk"]
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitJSStatementNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._returnValue_(self._eval_(_st(aNode)._source()));
+return self}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitJSStatementNode: aNode\x0a\x09self returnValue: (self eval: aNode source)",
+messageSends: ["returnValue:", "eval:", "source"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"visitNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitNode: aNode\x0a\x09\x22Do nothing by default. Especially, do not visit children recursively.\x22",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitReturnNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._returnValue_(self._pop());
+return self}, function($ctx1) {$ctx1.fill(self,"visitReturnNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitReturnNode: aNode\x0a\x09self returnValue: self pop",
+messageSends: ["returnValue:", "pop"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitSendNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+var receiver,args,message,result;
+return smalltalk.withContext(function($ctx1) { 
+args=_st(_st(aNode)._arguments())._collect_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return self._pop();
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+receiver=self._pop();
+message=self._messageFromSendNode_arguments_(aNode,args);
+result=self._sendMessage_to_superSend_(message,receiver,_st(aNode)._superSend());
+self._push_(result);
+return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode,receiver:receiver,args:args,message:message,result:result},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitSendNode: aNode\x0a\x09| receiver args message result |\x0a\x09\x0a\x09args := aNode arguments collect: [ :each |\x0a\x09\x09self pop ].\x0a\x09receiver := self pop.\x0a\x09\x0a\x09message := self\x0a\x09\x09messageFromSendNode: aNode\x0a\x09\x09arguments: args.\x0a\x09\x0a\x09result := self sendMessage: message to: receiver superSend: aNode superSend.\x0a\x09\x0a\x09self push: result",
+messageSends: ["collect:", "pop", "arguments", "messageFromSendNode:arguments:", "sendMessage:to:superSend:", "superSend", "push:"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitValueNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._push_(_st(aNode)._value());
+return self}, function($ctx1) {$ctx1.fill(self,"visitValueNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitValueNode: aNode\x0a\x09self push: aNode value",
+messageSends: ["push:", "value"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitVariableNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$2;
+$1=self;
+$3=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($3)){
+$2=_st(_st(self._context())._receiver())._instVarAt_(_st(aNode)._value());
+} else {
+$2=_st(self._context())._localAt_(_st(aNode)._value());
+};
+_st($1)._push_($2);
+return self}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode},smalltalk.Interpreter)})},
+args: ["aNode"],
+source: "visitVariableNode: aNode\x0a\x09\x22TODO: what about other vars (JS globals for instance)?\x22\x0a\x09\x0a\x09self push: (aNode binding isInstanceVar\x0a\x09\x09ifTrue: [ self context receiver instVarAt: aNode value ]\x0a\x09\x09ifFalse: [ self context localAt: aNode value ])",
+messageSends: ["push:", "ifTrue:ifFalse:", "instVarAt:", "value", "receiver", "context", "localAt:", "isInstanceVar", "binding"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
+
+
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpreter:continue:",

+ 13 - 0
js/Kernel-Methods.deploy.js

@@ -576,6 +576,19 @@ return self}, function($ctx1) {$ctx1.fill(self,"selector:",{aString:aString},sma
 messageSends: ["basicAt:put:"]}),
 smalltalk.CompiledMethod);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "sendTo:arguments:",
+fn: function (anObject,aCollection){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._fn())._applyTo_arguments_(anObject,aCollection);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"sendTo:arguments:",{anObject:anObject,aCollection:aCollection},smalltalk.CompiledMethod)})},
+messageSends: ["applyTo:arguments:", "fn"]}),
+smalltalk.CompiledMethod);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "source",

+ 18 - 0
js/Kernel-Methods.js

@@ -788,6 +788,24 @@ referencedClasses: []
 }),
 smalltalk.CompiledMethod);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "sendTo:arguments:",
+category: 'evaluating',
+fn: function (anObject,aCollection){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._fn())._applyTo_arguments_(anObject,aCollection);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"sendTo:arguments:",{anObject:anObject,aCollection:aCollection},smalltalk.CompiledMethod)})},
+args: ["anObject", "aCollection"],
+source: "sendTo: anObject arguments: aCollection\x0a\x09^ self fn applyTo: anObject arguments: aCollection",
+messageSends: ["applyTo:arguments:", "fn"],
+referencedClasses: []
+}),
+smalltalk.CompiledMethod);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "source",

+ 27 - 5
st/Compiler-AST.st

@@ -24,17 +24,31 @@ extent
 		ifNotNil: [ :node | node position ]
 !
 
+nextChild
+	"Answer the next node after aNode.
+	Recurse into the possible children of the receiver to answer the next node to be evaluated"
+	
+	^ self nodes isEmpty
+		ifTrue: [ self ]
+		ifFalse: [ self nodes first nextChild ]
+!
+
 nextNode
 	^ self parent ifNotNil: [ :node |
 		node nextNode: self ]
 !
 
 nextNode: aNode
-	"Answer the next node after aNode"
+	"Answer the next node after aNode.
+	Recurse into the possible children of the next node to answer the next node to be evaluated"
 	
-	^ self nodes 
+	| next |
+	
+	next := self nodes 
 		at: (self nodes indexOf: aNode) + 1
-		ifAbsent: [ nil ]
+		ifAbsent: [ ^ self ].
+	
+	^ next nextChild
 !
 
 nodes
@@ -149,7 +163,8 @@ left
 !
 
 left: aNode
-	left := aNode
+	left := aNode.
+	aNode parent: self
 !
 
 nodes
@@ -161,7 +176,8 @@ right
 !
 
 right: aNode
-	right := aNode
+	right := aNode.
+	aNode parent: self
 ! !
 
 !AssignmentNode methodsFor: 'testing'!
@@ -184,6 +200,12 @@ I represent an block closure node.!
 
 !BlockNode methodsFor: 'accessing'!
 
+nextNode: aNode
+	"Answer the receiver as we want to avoid eager evaluation"
+	
+	^ self
+!
+
 parameters
 	^parameters ifNil: [parameters := Array new]
 !

+ 297 - 21
st/Compiler-Interpreter.st

@@ -1,6 +1,6 @@
 Smalltalk current createPackage: 'Compiler-Interpreter'!
 NodeVisitor subclass: #AIContext
-	instanceVariableNames: 'methodContext outerContext pc locals method'
+	instanceVariableNames: 'outerContext innerContext pc locals method ast interpreter'
 	package: 'Compiler-Interpreter'!
 !AIContext commentStamp!
 I am like a `MethodContext`, used by the `ASTInterpreter`.
@@ -16,6 +16,14 @@ home
 		ifFalse: [ self ]
 !
 
+innerContext
+	^ innerContext
+!
+
+innerContext: anAIContext
+	innerContext := anAIContext
+!
+
 localAt: aString
 	^ self locals at: aString ifAbsent: [ nil ]
 !
@@ -43,28 +51,13 @@ outerContext
 !
 
 outerContext: anAIContext
-	outerContext := anAIContext
-!
-
-pc
-	^ pc ifNil: [ pc := 0 ]
-!
-
-pc: anInteger
-	pc := anInteger
-!
-
-receiver
-	^ self localAt: 'self'
-!
-
-receiver: anObject
-	self localAt: 'self' put: anObject
+	outerContext := anAIContext.
+	outerContext innerContext: self
 !
 
 selector
-	^ self method
-		ifNotNil: [ self method selector ]
+	^ self method ifNotNil: [ 
+		self method selector ]
 ! !
 
 !AIContext methodsFor: 'converting'!
@@ -75,6 +68,12 @@ asString
 
 !AIContext methodsFor: 'initialization'!
 
+initializeAST
+	ast := self method ast.
+	(SemanticAnalyzer on: self method methodClass)
+		visit: ast
+!
+
 initializeFromMethodContext: aMethodContext
 	methodContext := aMethodContext.
 	
@@ -89,11 +88,69 @@ initializeFromMethodContext: aMethodContext
 				self locals at: key put: value ] ]
 !
 
+initializeInterpreter
+	interpreter := Interpreter new
+		context: self;
+		node: self retrieveNode;
+		yourself.
+	self innerContext isBlockContext ifFalse: [
+		self setupInterpreter: interpreter ]
+!
+
 initializeLocals
 	locals := Dictionary new.
 	locals at: 'thisContext' put: self.
 ! !
 
+!AIContext methodsFor: 'interpreting'!
+
+arguments
+	^ self ast arguments collect: [ :each |
+		self localAt: each ]
+!
+
+ast
+	ast ifNil: [ self initializeAST ].
+	^ ast
+!
+
+interpreter
+	interpreter ifNil: [ self initializeInterpreter ].
+	^ interpreter
+!
+
+pc
+	^ pc ifNil: [ pc := 0 ]
+!
+
+pc: anInteger
+	pc := anInteger
+!
+
+receiver
+	^ self localAt: 'self'
+!
+
+receiver: anObject
+	self localAt: 'self' put: anObject
+!
+
+retrieveNode
+	^ ASTPCNodeVisitor new
+		context: self;
+		visit: self ast;
+		currentNode
+!
+
+setupInterpreter: anInterpreter
+	"Push the send args and receiver to the interpreter stack"
+	
+	self innerContext arguments reversed do: [ :each | 
+		anInterpreter push: each ].
+		
+	anInterpreter push: (self innerContext receiver)
+! !
+
 !AIContext methodsFor: 'testing'!
 
 isBlockContext
@@ -233,7 +290,7 @@ context: aContext
 ! !
 
 Object subclass: #ASTInterpreter
-	instanceVariableNames: 'currentNode context shouldReturn result'
+	instanceVariableNames: 'currentNode nextNode context shouldReturn result'
 	package: 'Compiler-Interpreter'!
 !ASTInterpreter commentStamp!
 I am like a `NodeVisitor`, interpreting nodes one after each other.
@@ -263,6 +320,14 @@ currentNode
 	^ currentNode
 !
 
+nextNode
+	^ nextNode ifNil: [ self currentNode ]
+!
+
+nextNode: aNode
+	nextNode := aNode
+!
+
 result
 	^ result
 ! !
@@ -614,6 +679,217 @@ visitSendNode: aNode
 			currentNode := aNode ] ]
 ! !
 
+NodeVisitor subclass: #Interpreter
+	instanceVariableNames: 'node context stack returnValue'
+	package: 'Compiler-Interpreter'!
+
+!Interpreter methodsFor: 'accessing'!
+
+context
+	^ context
+!
+
+context: aContext
+	context := aContext
+!
+
+node
+	"Answer the next node, ie the node to be evaluated in the next step"
+	
+	^ node
+!
+
+node: aNode
+	node := aNode
+!
+
+returnValue
+	^ returnValue ifNil: [ self context receiver ]
+!
+
+returnValue: anObject
+	returnValue := anObject
+!
+
+stack
+	^ stack ifNil: [ stack := OrderedCollection new ]
+! !
+
+!Interpreter methodsFor: 'interpreting'!
+
+interpret
+	"Interpret the next node to be evaluated"
+	
+	self visit: self node
+!
+
+next
+	self node: self node nextNode
+!
+
+proceed
+	"Eagerly evaluate the ast"
+	
+	[ self node notNil ] whileTrue: [ 
+		self step ]
+!
+
+skip
+	self next
+!
+
+step
+	self 
+		interpret; 
+		next
+! !
+
+!Interpreter methodsFor: 'private'!
+
+assign: aNode to: anObject
+	aNode binding isInstanceVar
+		ifTrue: [ self context receiver instVarAt: aNode value put: anObject ]
+		ifFalse: [ self context localAt: aNode value put: anObject ]
+!
+
+eval: aString
+	"Evaluate aString as JS source inside an JS function.
+	aString is not sandboxed."
+	
+	| source function |
+	
+	source := String streamContents: [ :str |
+		str nextPutAll: '(function('.
+		self context locals keys
+			do: [ :each | str nextPutAll: each ]
+			separatedBy: [ str nextPutAll: ',' ].
+		str
+			nextPutAll: '){ return (function() {';
+			nextPutAll: aString;
+			nextPutAll: '})() })' ].
+			
+	function := Compiler new eval: source.
+	
+	^ function valueWithPossibleArguments: self context locals values
+!
+
+messageFromSendNode: aSendNode arguments: aCollection
+	^ Message new
+		selector: aSendNode selector;
+		arguments: aCollection;
+		yourself
+!
+
+messageNotUnderstood: aMessage receiver: anObject
+	MessageNotUnderstood new
+		meesage: aMessage;
+		receiver: anObject;
+		signal
+!
+
+sendMessage: aMessage to: anObject superSend: aBoolean
+	| method |
+	
+	aBoolean ifFalse: [ ^ aMessage sendTo: anObject ].
+	anObject class superclass ifNil: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].
+	
+	method := anObject class superclass methodDictionary
+		at: aMessage selector
+		ifAbsent: [ ^ self messageNotUnderstood: aMessage receiver: anObject ].
+		
+	^ method sendTo: anObject arguments: aMessage arguments
+! !
+
+!Interpreter methodsFor: 'stack'!
+
+peek
+	"Peek the top object of the context stack"
+	
+	^ self stack last
+!
+
+pop
+	"Pop an object from the context stack"
+	
+	| value |
+	
+	value := self peek.
+	self stack removeLast.
+	^ value
+!
+
+push: anObject
+	"Push an object to the context stack"
+	
+	^ self stack add: anObject
+! !
+
+!Interpreter methodsFor: 'testing'!
+
+atEnd
+	^ self shouldReturn or: [ self node isNil ]
+!
+
+shouldReturn
+	^ returnValue notNil
+! !
+
+!Interpreter methodsFor: 'visiting'!
+
+visit: aNode
+	self shouldReturn ifFalse: [ super visit: aNode ]
+!
+
+visitAssignmentNode: aNode
+	"Do not pop as the pushed value would be the right node"
+	
+	self assign: aNode left to: self peek
+!
+
+visitClassReferenceNode: aNode
+	self push: (Smalltalk current at: aNode value)
+!
+
+visitJSStatementNode: aNode
+	self returnValue: (self eval: aNode source)
+!
+
+visitNode: aNode
+	"Do nothing by default. Especially, do not visit children recursively."
+!
+
+visitReturnNode: aNode
+	self returnValue: self pop
+!
+
+visitSendNode: aNode
+	| receiver args message result |
+	
+	args := aNode arguments collect: [ :each |
+		self pop ].
+	receiver := self pop.
+	
+	message := self
+		messageFromSendNode: aNode
+		arguments: args.
+	
+	result := self sendMessage: message to: receiver superSend: aNode superSend.
+	
+	self push: result
+!
+
+visitValueNode: aNode
+	self push: aNode value
+!
+
+visitVariableNode: aNode
+	"TODO: what about other vars (JS globals for instance)?"
+	
+	self push: (aNode binding isInstanceVar
+		ifTrue: [ self context receiver instVarAt: aNode value ]
+		ifFalse: [ self context localAt: aNode value ])
+! !
+
 !Node methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock

+ 6 - 0
st/Kernel-Methods.st

@@ -298,6 +298,12 @@ defaultCategory
 	^ 'as yet unclassified'
 ! !
 
+!CompiledMethod methodsFor: 'evaluating'!
+
+sendTo: anObject arguments: aCollection
+	^ self fn applyTo: anObject arguments: aCollection
+! !
+
 !CompiledMethod methodsFor: 'testing'!
 
 isCompiledMethod