Browse Source

Compiler changes:
- AST nodes Keep a reference to their parent
- New ASTPCNodeVisitor used to determine from a context the current evaluated node

Nicolas Petton 11 years ago
parent
commit
f48b4b375a

+ 155 - 11
js/Compiler-AST.deploy.js

@@ -1,5 +1,5 @@
 smalltalk.addPackage('Compiler-AST');
-smalltalk.addClass('Node', smalltalk.Object, ['position', 'nodes', 'shouldBeInlined', 'shouldBeAliased'], 'Compiler-AST');
+smalltalk.addClass('Node', smalltalk.Object, ['parent', 'position', 'nodes', 'shouldBeInlined', 'shouldBeAliased'], 'Compiler-AST');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "accept:",
@@ -20,8 +20,36 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 _st(_st(self)._nodes())._add_(aNode);
+_st(aNode)._parent_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addNode:",{aNode:aNode},smalltalk.Node)})},
-messageSends: ["add:", "nodes"]}),
+messageSends: ["add:", "nodes", "parent:"]}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "extent",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+$2=_st(self)._nextNode();
+if(($receiver = $2) == nil || $receiver == undefined){
+$3=_st(self)._parent();
+if(($receiver = $3) == nil || $receiver == undefined){
+$1=$3;
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._extent();
+};
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._position();
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"extent",{},smalltalk.Node)})},
+messageSends: ["ifNil:ifNotNil:", "ifNotNil:", "extent", "parent", "position", "nextNode"]}),
 smalltalk.Node);
 
 smalltalk.addMethod(
@@ -68,6 +96,17 @@ return false;
 messageSends: []}),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isJSStatementNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isJSStatementNode",{},smalltalk.Node)})},
+messageSends: []}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isNode",
@@ -112,6 +151,42 @@ return false;
 messageSends: []}),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=_st(self)._parent();
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=$2;
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._nextNode_(self);
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{},smalltalk.Node)})},
+messageSends: ["ifNotNil:", "nextNode:", "parent"]}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._nodes())._at_ifAbsent_(_st(_st(_st(self)._nodes())._indexOf_(aNode)).__plus((1)),(function(){
+return smalltalk.withContext(function($ctx2) {
+return nil;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},smalltalk.Node)})},
+messageSends: ["at:ifAbsent:", "+", "indexOf:", "nodes"]}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nodes",
@@ -139,7 +214,35 @@ fn: function (aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@nodes"]=aCollection;
+_st(aCollection)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._parent_(self);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"nodes:",{aCollection:aCollection},smalltalk.Node)})},
+messageSends: ["do:", "parent:"]}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parent",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@parent"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"parent",{},smalltalk.Node)})},
+messageSends: []}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parent:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@parent"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"parent:",{aNode:aNode},smalltalk.Node)})},
 messageSends: []}),
 smalltalk.Node);
 
@@ -149,17 +252,23 @@ selector: "position",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $2,$1;
+var $2,$3,$1;
 $2=self["@position"];
 if(($receiver = $2) == nil || $receiver == undefined){
-self["@position"]=_st((0)).__at((0));
-$1=self["@position"];
+$3=_st(self)._parent();
+if(($receiver = $3) == nil || $receiver == undefined){
+$1=$3;
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._position();
+};
 } else {
 $1=$2;
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"position",{},smalltalk.Node)})},
-messageSends: ["ifNil:", "@"]}),
+messageSends: ["ifNil:", "ifNotNil:", "position", "parent"]}),
 smalltalk.Node);
 
 smalltalk.addMethod(
@@ -542,6 +651,17 @@ return $1;
 messageSends: ["visitJSStatementNode:"]}),
 smalltalk.JSStatementNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isJSStatementNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isJSStatementNode",{},smalltalk.JSStatementNode)})},
+messageSends: []}),
+smalltalk.JSStatementNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "source",
@@ -640,6 +760,19 @@ return self}, function($ctx1) {$ctx1.fill(self,"classReferences:",{aCollection:a
 messageSends: []}),
 smalltalk.MethodNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "extent",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(_st(self)._source())._lines())._size()).__at(_st(_st(_st(_st(_st(self)._source())._lines())._last())._size()).__plus((1)));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"extent",{},smalltalk.MethodNode)})},
+messageSends: ["@", "+", "size", "last", "lines", "source"]}),
+smalltalk.MethodNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "messageSends",
@@ -866,8 +999,12 @@ fn: function (aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@arguments"]=aCollection;
+_st(aCollection)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._parent_(self);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"arguments:",{aCollection:aCollection},smalltalk.SendNode)})},
-messageSends: []}),
+messageSends: ["do:", "parent:"]}),
 smalltalk.SendNode);
 
 smalltalk.addMethod(
@@ -945,7 +1082,7 @@ $3=_st($2)._yourself();
 $1=$3;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"nodes",{},smalltalk.SendNode)})},
-messageSends: ["add:", "receiver", "withAll:", "arguments", "yourself"]}),
+messageSends: ["add:", "withAll:", "arguments", "receiver", "yourself"]}),
 smalltalk.SendNode);
 
 smalltalk.addMethod(
@@ -967,9 +1104,14 @@ selector: "receiver:",
 fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+var $1;
 self["@receiver"]=aNode;
+$1=_st(aNode)._isNode();
+if(smalltalk.assert($1)){
+_st(aNode)._parent_(self);
+};
 return self}, function($ctx1) {$ctx1.fill(self,"receiver:",{aNode:aNode},smalltalk.SendNode)})},
-messageSends: []}),
+messageSends: ["ifTrue:", "parent:", "isNode"]}),
 smalltalk.SendNode);
 
 smalltalk.addMethod(
@@ -1045,6 +1187,7 @@ function $SendNode(){return smalltalk.SendNode||(typeof SendNode=="undefined"?ni
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$5,$4,$6,$1;
 $2=_st($SendNode())._new();
+_st($2)._position_(_st(self)._position());
 $3=$2;
 $5=_st(self)._receiver();
 if(($receiver = $5) == nil || $receiver == undefined){
@@ -1059,7 +1202,7 @@ $6=_st($2)._yourself();
 $1=$6;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"valueForReceiver:",{anObject:anObject},smalltalk.SendNode)})},
-messageSends: ["receiver:", "ifNil:ifNotNil:", "valueForReceiver:", "receiver", "new", "selector:", "selector", "arguments:", "arguments", "yourself"]}),
+messageSends: ["position:", "position", "new", "receiver:", "ifNil:ifNotNil:", "valueForReceiver:", "receiver", "selector:", "selector", "arguments:", "arguments", "yourself"]}),
 smalltalk.SendNode);
 
 
@@ -1087,13 +1230,14 @@ function $BlockSequenceNode(){return smalltalk.BlockSequenceNode||(typeof BlockS
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
 $2=_st($BlockSequenceNode())._new();
+_st($2)._position_(_st(self)._position());
 _st($2)._nodes_(_st(self)._nodes());
 _st($2)._temps_(_st(self)._temps());
 $3=_st($2)._yourself();
 $1=$3;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"asBlockSequenceNode",{},smalltalk.SequenceNode)})},
-messageSends: ["nodes:", "nodes", "new", "temps:", "temps", "yourself"]}),
+messageSends: ["position:", "position", "new", "nodes:", "nodes", "temps:", "temps", "yourself"]}),
 smalltalk.SequenceNode);
 
 smalltalk.addMethod(

+ 202 - 18
js/Compiler-AST.js

@@ -1,5 +1,5 @@
 smalltalk.addPackage('Compiler-AST');
-smalltalk.addClass('Node', smalltalk.Object, ['position', 'nodes', 'shouldBeInlined', 'shouldBeAliased'], 'Compiler-AST');
+smalltalk.addClass('Node', smalltalk.Object, ['parent', 'position', 'nodes', 'shouldBeInlined', 'shouldBeAliased'], 'Compiler-AST');
 smalltalk.Node.comment="I am the abstract root class of the abstract syntax tree.\x0a\x0aConcrete classes should implement `#accept:` to allow visiting.\x0a\x0a`position` holds a point containing line and column number of the symbol location in the original source file."
 smalltalk.addMethod(
 smalltalk.method({
@@ -27,10 +27,43 @@ fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 _st(_st(self)._nodes())._add_(aNode);
+_st(aNode)._parent_(self);
 return self}, function($ctx1) {$ctx1.fill(self,"addNode:",{aNode:aNode},smalltalk.Node)})},
 args: ["aNode"],
-source: "addNode: aNode\x0a\x09self nodes add: aNode",
-messageSends: ["add:", "nodes"],
+source: "addNode: aNode\x0a\x09self nodes add: aNode.\x0a\x09aNode parent: self",
+messageSends: ["add:", "nodes", "parent:"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "extent",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+$2=_st(self)._nextNode();
+if(($receiver = $2) == nil || $receiver == undefined){
+$3=_st(self)._parent();
+if(($receiver = $3) == nil || $receiver == undefined){
+$1=$3;
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._extent();
+};
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._position();
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"extent",{},smalltalk.Node)})},
+args: [],
+source: "extent\x0a\x09\x22Answer the line and column of the end position of the receiver in the source code\x22\x0a\x09\x0a\x09^ self nextNode \x0a\x09\x09ifNil: [ self parent ifNotNil: [ :node | node extent ] ]\x0a\x09\x09ifNotNil: [ :node | node position ]",
+messageSends: ["ifNil:ifNotNil:", "ifNotNil:", "extent", "parent", "position", "nextNode"],
 referencedClasses: []
 }),
 smalltalk.Node);
@@ -99,6 +132,22 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isJSStatementNode",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"isJSStatementNode",{},smalltalk.Node)})},
+args: [],
+source: "isJSStatementNode\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isNode",
@@ -163,6 +212,52 @@ referencedClasses: []
 }),
 smalltalk.Node);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=_st(self)._parent();
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=$2;
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._nextNode_(self);
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{},smalltalk.Node)})},
+args: [],
+source: "nextNode\x0a\x09^ self parent ifNotNil: [ :node |\x0a\x09\x09node nextNode: self ]",
+messageSends: ["ifNotNil:", "nextNode:", "parent"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode:",
+category: 'accessing',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._nodes())._at_ifAbsent_(_st(_st(_st(self)._nodes())._indexOf_(aNode)).__plus((1)),(function(){
+return smalltalk.withContext(function($ctx2) {
+return nil;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode:",{aNode:aNode},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"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "nodes",
@@ -196,9 +291,47 @@ fn: function (aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@nodes"]=aCollection;
+_st(aCollection)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._parent_(self);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"nodes:",{aCollection:aCollection},smalltalk.Node)})},
 args: ["aCollection"],
-source: "nodes: aCollection\x0a\x09nodes := aCollection",
+source: "nodes: aCollection\x0a\x09nodes := aCollection.\x0a\x09aCollection do: [ :each | each parent: self ]",
+messageSends: ["do:", "parent:"],
+referencedClasses: []
+}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parent",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@parent"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"parent",{},smalltalk.Node)})},
+args: [],
+source: "parent\x0a\x09^ parent",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Node);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parent:",
+category: 'accessing',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@parent"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"parent:",{aNode:aNode},smalltalk.Node)})},
+args: ["aNode"],
+source: "parent: aNode\x0a\x09parent := aNode",
 messageSends: [],
 referencedClasses: []
 }),
@@ -211,19 +344,25 @@ category: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $2,$1;
+var $2,$3,$1;
 $2=self["@position"];
 if(($receiver = $2) == nil || $receiver == undefined){
-self["@position"]=_st((0)).__at((0));
-$1=self["@position"];
+$3=_st(self)._parent();
+if(($receiver = $3) == nil || $receiver == undefined){
+$1=$3;
+} else {
+var node;
+node=$receiver;
+$1=_st(node)._position();
+};
 } else {
 $1=$2;
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"position",{},smalltalk.Node)})},
 args: [],
-source: "position\x0a\x09^position ifNil: [position := 0@0]",
-messageSends: ["ifNil:", "@"],
+source: "position\x0a\x09\x22answer the line and column of the receiver in the source code\x22\x0a\x09\x0a\x09^ position ifNil: [ \x0a\x09\x09self parent ifNotNil: [ :node | node position ] ]",
+messageSends: ["ifNil:", "ifNotNil:", "position", "parent"],
 referencedClasses: []
 }),
 smalltalk.Node);
@@ -749,6 +888,22 @@ referencedClasses: []
 }),
 smalltalk.JSStatementNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "isJSStatementNode",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"isJSStatementNode",{},smalltalk.JSStatementNode)})},
+args: [],
+source: "isJSStatementNode\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.JSStatementNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "source",
@@ -883,6 +1038,24 @@ referencedClasses: []
 }),
 smalltalk.MethodNode);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "extent",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(_st(_st(self)._source())._lines())._size()).__at(_st(_st(_st(_st(_st(self)._source())._lines())._last())._size()).__plus((1)));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"extent",{},smalltalk.MethodNode)})},
+args: [],
+source: "extent\x0a\x09^ self source lines size @ (self source lines last size + 1)",
+messageSends: ["@", "+", "size", "last", "lines", "source"],
+referencedClasses: []
+}),
+smalltalk.MethodNode);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "messageSends",
@@ -1197,10 +1370,14 @@ fn: function (aCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@arguments"]=aCollection;
+_st(aCollection)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(each)._parent_(self);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"arguments:",{aCollection:aCollection},smalltalk.SendNode)})},
 args: ["aCollection"],
-source: "arguments: aCollection\x0a\x09arguments := aCollection",
-messageSends: [],
+source: "arguments: aCollection\x0a\x09arguments := aCollection.\x0a\x09aCollection do: [ :each | each parent: self ]",
+messageSends: ["do:", "parent:"],
 referencedClasses: []
 }),
 smalltalk.SendNode);
@@ -1303,7 +1480,7 @@ return $1;
 }, function($ctx1) {$ctx1.fill(self,"nodes",{},smalltalk.SendNode)})},
 args: [],
 source: "nodes\x0a\x09^ (Array withAll: self arguments)\x0a\x09\x09add: self receiver;\x0a\x09\x09yourself",
-messageSends: ["add:", "receiver", "withAll:", "arguments", "yourself"],
+messageSends: ["add:", "withAll:", "arguments", "receiver", "yourself"],
 referencedClasses: ["Array"]
 }),
 smalltalk.SendNode);
@@ -1333,11 +1510,16 @@ category: 'accessing',
 fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+var $1;
 self["@receiver"]=aNode;
+$1=_st(aNode)._isNode();
+if(smalltalk.assert($1)){
+_st(aNode)._parent_(self);
+};
 return self}, function($ctx1) {$ctx1.fill(self,"receiver:",{aNode:aNode},smalltalk.SendNode)})},
 args: ["aNode"],
-source: "receiver: aNode\x0a\x09receiver := aNode",
-messageSends: [],
+source: "receiver: aNode\x0a\x09receiver := aNode.\x0a\x09aNode isNode ifTrue: [\x0a\x09\x09aNode parent: self ]",
+messageSends: ["ifTrue:", "parent:", "isNode"],
 referencedClasses: []
 }),
 smalltalk.SendNode);
@@ -1441,6 +1623,7 @@ function $SendNode(){return smalltalk.SendNode||(typeof SendNode=="undefined"?ni
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$5,$4,$6,$1;
 $2=_st($SendNode())._new();
+_st($2)._position_(_st(self)._position());
 $3=$2;
 $5=_st(self)._receiver();
 if(($receiver = $5) == nil || $receiver == undefined){
@@ -1456,8 +1639,8 @@ $1=$6;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"valueForReceiver:",{anObject:anObject},smalltalk.SendNode)})},
 args: ["anObject"],
-source: "valueForReceiver: anObject\x0a\x09^SendNode new\x0a\x09\x09receiver: (self receiver\x0a\x09\x09ifNil: [anObject]\x0a\x09\x09ifNotNil: [self receiver valueForReceiver: anObject]);\x0a\x09\x09selector: self selector;\x0a\x09\x09arguments: self arguments;\x0a\x09\x09yourself",
-messageSends: ["receiver:", "ifNil:ifNotNil:", "valueForReceiver:", "receiver", "new", "selector:", "selector", "arguments:", "arguments", "yourself"],
+source: "valueForReceiver: anObject\x0a\x09^SendNode new\x0a\x09\x09position: self position;\x0a\x09\x09receiver: (self receiver\x0a\x09\x09ifNil: [anObject]\x0a\x09\x09ifNotNil: [self receiver valueForReceiver: anObject]);\x0a\x09\x09selector: self selector;\x0a\x09\x09arguments: self arguments;\x0a\x09\x09yourself",
+messageSends: ["position:", "position", "new", "receiver:", "ifNil:ifNotNil:", "valueForReceiver:", "receiver", "selector:", "selector", "arguments:", "arguments", "yourself"],
 referencedClasses: ["SendNode"]
 }),
 smalltalk.SendNode);
@@ -1494,6 +1677,7 @@ function $BlockSequenceNode(){return smalltalk.BlockSequenceNode||(typeof BlockS
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
 $2=_st($BlockSequenceNode())._new();
+_st($2)._position_(_st(self)._position());
 _st($2)._nodes_(_st(self)._nodes());
 _st($2)._temps_(_st(self)._temps());
 $3=_st($2)._yourself();
@@ -1501,8 +1685,8 @@ $1=$3;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"asBlockSequenceNode",{},smalltalk.SequenceNode)})},
 args: [],
-source: "asBlockSequenceNode\x0a\x09^BlockSequenceNode new\x0a\x09\x09nodes: self nodes;\x0a\x09\x09temps: self temps;\x0a\x09\x09yourself",
-messageSends: ["nodes:", "nodes", "new", "temps:", "temps", "yourself"],
+source: "asBlockSequenceNode\x0a\x09^BlockSequenceNode new\x0a\x09\x09position: self position;\x0a\x09\x09nodes: self nodes;\x0a\x09\x09temps: self temps;\x0a\x09\x09yourself",
+messageSends: ["position:", "position", "new", "nodes:", "nodes", "temps:", "temps", "yourself"],
 referencedClasses: ["BlockSequenceNode"]
 }),
 smalltalk.SequenceNode);

+ 164 - 14
js/Compiler-Interpreter.deploy.js

@@ -316,11 +316,10 @@ smalltalk.method({
 selector: "context:",
 fn: function (aContext){
 var self=this;
-function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
 return smalltalk.withContext(function($ctx1) { 
-self["@context"]=_st($AIContext())._new();
+self["@context"]=aContext;
 return self}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext},smalltalk.ASTDebugger)})},
-messageSends: ["new"]}),
+messageSends: []}),
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
@@ -342,23 +341,31 @@ smalltalk.method({
 selector: "initializeInterpreter",
 fn: function (){
 var self=this;
+var ast,next;
+function $ASTPCNodeVisitor(){return smalltalk.ASTPCNodeVisitor||(typeof ASTPCNodeVisitor=="undefined"?nil:ASTPCNodeVisitor)}
 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)})},
-messageSends: ["interpret:", "first", "nodes", "buildAST", "interpreter"]}),
+var $1,$2;
+ast=_st(self)._buildAST();
+$1=_st($ASTPCNodeVisitor())._new();
+_st($1)._context_(_st(self)._context());
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+next=$2;
+_st(_st(self)._interpreter())._interpret_(next);
+return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{ast:ast,next:next},smalltalk.ASTDebugger)})},
+messageSends: ["buildAST", "context:", "context", "new", "visit:", "currentNode", "interpret:", "interpreter"]}),
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
 smalltalk.method({
 selector: "initializeWithContext:",
-fn: function (aMethodContext){
+fn: function (aContext){
 var self=this;
-function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
 return smalltalk.withContext(function($ctx1) { 
-_st(self)._context_(_st($AIContext())._fromMethodContext_(aMethodContext));
+_st(self)._context_(aContext);
 _st(self)._initializeInterpreter();
-return self}, function($ctx1) {$ctx1.fill(self,"initializeWithContext:",{aMethodContext:aMethodContext},smalltalk.ASTDebugger)})},
-messageSends: ["context:", "fromMethodContext:", "initializeInterpreter"]}),
+return self}, function($ctx1) {$ctx1.fill(self,"initializeWithContext:",{aContext:aContext},smalltalk.ASTDebugger)})},
+messageSends: ["context:", "initializeInterpreter"]}),
 smalltalk.ASTDebugger);
 
 smalltalk.addMethod(
@@ -404,6 +411,19 @@ return $1;
 messageSends: ["method", "context"]}),
 smalltalk.ASTDebugger);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._interpreter())._nextNode();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{},smalltalk.ASTDebugger)})},
+messageSends: ["nextNode", "interpreter"]}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "proceed",
@@ -476,16 +496,16 @@ smalltalk.ASTDebugger);
 smalltalk.addMethod(
 smalltalk.method({
 selector: "context:",
-fn: function (aMethodContext){
+fn: function (aContext){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
 $2=_st(self)._new();
-_st($2)._initializeWithContext_(aMethodContext);
+_st($2)._initializeWithContext_(aContext);
 $3=_st($2)._yourself();
 $1=$3;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"context:",{aMethodContext:aMethodContext},smalltalk.ASTDebugger.klass)})},
+}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext},smalltalk.ASTDebugger.klass)})},
 messageSends: ["initializeWithContext:", "new", "yourself"]}),
 smalltalk.ASTDebugger.klass);
 
@@ -1100,6 +1120,136 @@ smalltalk.ASTSteppingInterpreter);
 
 
 
+smalltalk.addClass('ASTPCNodeVisitor', smalltalk.NodeVisitor, ['useInlinings', 'pc', 'context', 'currentNode'], 'Compiler-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.ASTPCNodeVisitor)})},
+messageSends: []}),
+smalltalk.ASTPCNodeVisitor);
+
+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.ASTPCNodeVisitor)})},
+messageSends: []}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "currentNode",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@currentNode"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"currentNode",{},smalltalk.ASTPCNodeVisitor)})},
+messageSends: []}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "pc",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@pc"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(0);
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"pc",{},smalltalk.ASTPCNodeGetter)})},
+messageSends: ["ifNil:"]}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "pc:",
+fn: function (anInteger){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@pc"]=anInteger;
+return self}, function($ctx1) {$ctx1.fill(self,"pc:",{anInteger:anInteger},smalltalk.ASTPCNodeGetter)})},
+messageSends: []}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "useInlinings",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@useInlinings"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=true;
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"useInlinings",{},smalltalk.ASTPCNodeGetter)})},
+messageSends: ["ifNil:"]}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "useInlinings:",
+fn: function (aBoolean){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@useInlinings"]=aBoolean;
+return self}, function($ctx1) {$ctx1.fill(self,"useInlinings:",{aBoolean:aBoolean},smalltalk.ASTPCNodeGetter)})},
+messageSends: []}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitJSStatementNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@currentNode"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
+messageSends: []}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitSendNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+smalltalk.NodeVisitor.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
+$1=_st(_st(self)._pc()).__eq(_st(_st(self)._context())._pc());
+if(! smalltalk.assert($1)){
+$2=_st(aNode)._shouldBeInlined();
+if(! smalltalk.assert($2)){
+_st(self)._pc_(_st(_st(self)._pc()).__plus((1)));
+self["@currentNode"]=aNode;
+self["@currentNode"];
+};
+};
+return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
+messageSends: ["visitSendNode:", "ifFalse:", "pc:", "+", "pc", "shouldBeInlined", "=", "context"]}),
+smalltalk.ASTPCNodeVisitor);
+
+
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpreter:continue:",

+ 224 - 23
js/Compiler-Interpreter.js

@@ -424,14 +424,13 @@ selector: "context:",
 category: 'accessing',
 fn: function (aContext){
 var self=this;
-function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
 return smalltalk.withContext(function($ctx1) { 
-self["@context"]=_st($AIContext())._new();
+self["@context"]=aContext;
 return self}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext},smalltalk.ASTDebugger)})},
 args: ["aContext"],
-source: "context: aContext\x0a\x09context := AIContext new.",
-messageSends: ["new"],
-referencedClasses: ["AIContext"]
+source: "context: aContext\x0a\x09context := aContext",
+messageSends: [],
+referencedClasses: []
 }),
 smalltalk.ASTDebugger);
 
@@ -460,13 +459,22 @@ selector: "initializeInterpreter",
 category: 'initialization',
 fn: function (){
 var self=this;
+var ast,next;
+function $ASTPCNodeVisitor(){return smalltalk.ASTPCNodeVisitor||(typeof ASTPCNodeVisitor=="undefined"?nil:ASTPCNodeVisitor)}
 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)})},
+var $1,$2;
+ast=_st(self)._buildAST();
+$1=_st($ASTPCNodeVisitor())._new();
+_st($1)._context_(_st(self)._context());
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+next=$2;
+_st(_st(self)._interpreter())._interpret_(next);
+return self}, function($ctx1) {$ctx1.fill(self,"initializeInterpreter",{ast:ast,next:next},smalltalk.ASTDebugger)})},
 args: [],
-source: "initializeInterpreter\x0a\x09self interpreter interpret: self buildAST nodes first",
-messageSends: ["interpret:", "first", "nodes", "buildAST", "interpreter"],
-referencedClasses: []
+source: "initializeInterpreter\x0a\x09| ast next |\x0a\x09ast := self buildAST.\x0a\x09next := ASTPCNodeVisitor new\x0a\x09\x09context: self context;\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode.\x0a\x09self interpreter interpret: next",
+messageSends: ["buildAST", "context:", "context", "new", "visit:", "currentNode", "interpret:", "interpreter"],
+referencedClasses: ["ASTPCNodeVisitor"]
 }),
 smalltalk.ASTDebugger);
 
@@ -474,17 +482,16 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "initializeWithContext:",
 category: 'initialization',
-fn: function (aMethodContext){
+fn: function (aContext){
 var self=this;
-function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
 return smalltalk.withContext(function($ctx1) { 
-_st(self)._context_(_st($AIContext())._fromMethodContext_(aMethodContext));
+_st(self)._context_(aContext);
 _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\x09\x0a\x09self context: (AIContext fromMethodContext: aMethodContext).\x0a\x09self initializeInterpreter",
-messageSends: ["context:", "fromMethodContext:", "initializeInterpreter"],
-referencedClasses: ["AIContext"]
+return self}, function($ctx1) {$ctx1.fill(self,"initializeWithContext:",{aContext:aContext},smalltalk.ASTDebugger)})},
+args: ["aContext"],
+source: "initializeWithContext: aContext\x0a\x09\x22TODO: do we need to handle block contexts?\x22\x0a\x09\x0a\x09self context: aContext.\x0a\x09self initializeInterpreter",
+messageSends: ["context:", "initializeInterpreter"],
+referencedClasses: []
 }),
 smalltalk.ASTDebugger);
 
@@ -546,6 +553,24 @@ referencedClasses: []
 }),
 smalltalk.ASTDebugger);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "nextNode",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(self)._interpreter())._nextNode();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"nextNode",{},smalltalk.ASTDebugger)})},
+args: [],
+source: "nextNode\x0a\x09^ self interpreter nextNode",
+messageSends: ["nextNode", "interpreter"],
+referencedClasses: []
+}),
+smalltalk.ASTDebugger);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "proceed",
@@ -644,18 +669,18 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "context:",
 category: 'instance creation',
-fn: function (aMethodContext){
+fn: function (aContext){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
 $2=_st(self)._new();
-_st($2)._initializeWithContext_(aMethodContext);
+_st($2)._initializeWithContext_(aContext);
 $3=_st($2)._yourself();
 $1=$3;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"context:",{aMethodContext:aMethodContext},smalltalk.ASTDebugger.klass)})},
-args: ["aMethodContext"],
-source: "context: aMethodContext\x0a\x09^ self new\x0a\x09\x09initializeWithContext: aMethodContext;\x0a\x09\x09yourself",
+}, function($ctx1) {$ctx1.fill(self,"context:",{aContext:aContext},smalltalk.ASTDebugger.klass)})},
+args: ["aContext"],
+source: "context: aContext\x0a\x09^ self new\x0a\x09\x09initializeWithContext: aContext;\x0a\x09\x09yourself",
 messageSends: ["initializeWithContext:", "new", "yourself"],
 referencedClasses: []
 }),
@@ -1454,6 +1479,182 @@ smalltalk.ASTSteppingInterpreter);
 
 
 
+smalltalk.addClass('ASTPCNodeVisitor', smalltalk.NodeVisitor, ['useInlinings', 'pc', 'context', 'currentNode'], 'Compiler-Interpreter');
+smalltalk.ASTPCNodeVisitor.comment="I visit an AST until I get to the current pc node and answer it.\x0a\x0a## API\x0a\x0aMy instances must be filled with a context object using `#context:`.\x0a\x0aAfter visiting the AST the current node corresponding to the `pc` is answered by `#currentNode`"
+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.ASTPCNodeVisitor)})},
+args: [],
+source: "context\x0a\x09^ context",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+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.ASTPCNodeVisitor)})},
+args: ["aContext"],
+source: "context: aContext\x0a\x09context := aContext",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "currentNode",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@currentNode"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"currentNode",{},smalltalk.ASTPCNodeVisitor)})},
+args: [],
+source: "currentNode\x0a\x09^ currentNode",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "pc",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@pc"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(0);
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"pc",{},smalltalk.ASTPCNodeGetter)})},
+args: [],
+source: "pc\x0a\x09^ pc ifNil: [ 0 ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "pc:",
+category: 'accessing',
+fn: function (anInteger){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@pc"]=anInteger;
+return self}, function($ctx1) {$ctx1.fill(self,"pc:",{anInteger:anInteger},smalltalk.ASTPCNodeGetter)})},
+args: ["anInteger"],
+source: "pc: anInteger\x0a\x09pc := anInteger",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "useInlinings",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@useInlinings"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=true;
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"useInlinings",{},smalltalk.ASTPCNodeGetter)})},
+args: [],
+source: "useInlinings\x0a\x09^ useInlinings ifNil: [ true ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "useInlinings:",
+category: 'accessing',
+fn: function (aBoolean){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@useInlinings"]=aBoolean;
+return self}, function($ctx1) {$ctx1.fill(self,"useInlinings:",{aBoolean:aBoolean},smalltalk.ASTPCNodeGetter)})},
+args: ["aBoolean"],
+source: "useInlinings: aBoolean\x0a\x09useInlinings := aBoolean",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitJSStatementNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@currentNode"]=aNode;
+return self}, function($ctx1) {$ctx1.fill(self,"visitJSStatementNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
+args: ["aNode"],
+source: "visitJSStatementNode: aNode\x0a\x09currentNode := aNode",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "visitSendNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+smalltalk.NodeVisitor.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
+$1=_st(_st(self)._pc()).__eq(_st(_st(self)._context())._pc());
+if(! smalltalk.assert($1)){
+$2=_st(aNode)._shouldBeInlined();
+if(! smalltalk.assert($2)){
+_st(self)._pc_(_st(_st(self)._pc()).__plus((1)));
+self["@currentNode"]=aNode;
+self["@currentNode"];
+};
+};
+return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
+args: ["aNode"],
+source: "visitSendNode: aNode\x0a\x09super visitSendNode: aNode.\x0a\x09\x0a\x09self pc = self context pc ifFalse: [\x0a\x09\x09aNode shouldBeInlined ifFalse: [ \x0a\x09\x09\x09self pc: self pc + 1.\x0a\x09\x09\x09currentNode := aNode ] ]",
+messageSends: ["visitSendNode:", "ifFalse:", "pc:", "+", "pc", "shouldBeInlined", "=", "context"],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitor);
+
+
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpreter:continue:",

+ 166 - 30
js/Compiler-Tests.deploy.js

@@ -1,5 +1,5 @@
 smalltalk.addPackage('Compiler-Tests');
-smalltalk.addClass('AbstractASTInterpreterTest', smalltalk.TestCase, [], 'Compiler-Tests');
+smalltalk.addClass('ASTVisitorTest', smalltalk.TestCase, [], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "analyze:forClass:",
@@ -11,10 +11,173 @@ var $1;
 _st(_st($SemanticAnalyzer())._on_(aClass))._visit_(aNode);
 $1=aNode;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"analyze:forClass:",{aNode:aNode,aClass:aClass},smalltalk.AbstractASTInterpreterTest)})},
+}, function($ctx1) {$ctx1.fill(self,"analyze:forClass:",{aNode:aNode,aClass:aClass},smalltalk.ASTVisitorTest)})},
 messageSends: ["visit:", "on:"]}),
-smalltalk.AbstractASTInterpreterTest);
+smalltalk.ASTVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parse:",
+fn: function (aString){
+var self=this;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st($Smalltalk())._current())._parse_(aString);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"parse:",{aString:aString},smalltalk.ASTVisitorTest)})},
+messageSends: ["parse:", "current"]}),
+smalltalk.ASTVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parse:forClass:",
+fn: function (aString,aClass){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._analyze_forClass_(_st(self)._parse_(aString),aClass);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"parse:forClass:",{aString:aString,aClass:aClass},smalltalk.ASTVisitorTest)})},
+messageSends: ["analyze:forClass:", "parse:"]}),
+smalltalk.ASTVisitorTest);
+
+
+
+smalltalk.addClass('ASTPCNodeVisitorTest', smalltalk.ASTVisitorTest, [], 'Compiler-Tests');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "astPCNodeVisitor",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._astPCNodeVisitorForPC_((0));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"astPCNodeVisitor",{},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["astPCNodeVisitorForPC:"]}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "astPCNodeVisitorForPC:",
+fn: function (anInteger){
+var self=this;
+function $ASTPCNodeVisitor(){return smalltalk.ASTPCNodeVisitor||(typeof ASTPCNodeVisitor=="undefined"?nil:ASTPCNodeVisitor)}
+function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$4,$5,$1;
+$2=_st($ASTPCNodeVisitor())._new();
+_st($2)._pc_((0));
+$3=_st($AIContext())._new();
+_st($3)._pc_(anInteger);
+$4=_st($3)._yourself();
+_st($2)._context_($4);
+$5=_st($2)._yourself();
+$1=$5;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"astPCNodeVisitorForPC:",{anInteger:anInteger},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["pc:", "new", "context:", "yourself"]}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testJSStatementNode",
+fn: function (){
+var self=this;
+var ast,visitor;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo <consolee.log(1)>",$Object());
+$1=_st(self)._astPCNodeVisitor();
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_(_st($2)._isJSStatementNode());
+return self}, function($ctx1) {$ctx1.fill(self,"testJSStatementNode",{ast:ast,visitor:visitor},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["parse:forClass:", "assert:", "isJSStatementNode", "visit:", "astPCNodeVisitor", "currentNode"]}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testMessageSend",
+fn: function (){
+var self=this;
+var ast;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo self asString yourself. ^ self asBoolean",$Object());
+$1=_st(self)._astPCNodeVisitorForPC_((2));
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_equals_(_st($2)._selector(),"yourself");
+return self}, function($ctx1) {$ctx1.fill(self,"testMessageSend",{ast:ast},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["parse:forClass:", "assert:equals:", "selector", "visit:", "astPCNodeVisitorForPC:", "currentNode"]}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testMessageSendWithInlining",
+fn: function (){
+var self=this;
+var ast;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4;
+ast=_st(self)._parse_forClass_("foo true ifTrue: [ self asString yourself ]. ^ self asBoolean",$Object());
+$1=_st(self)._astPCNodeVisitorForPC_((2));
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_equals_(_st($2)._selector(),"yourself");
+ast=_st(self)._parse_forClass_("foo true ifTrue: [ self asString yourself ]. ^ self asBoolean",$Object());
+$3=_st(self)._astPCNodeVisitorForPC_((3));
+_st($3)._visit_(ast);
+$4=_st($3)._currentNode();
+_st(self)._assert_equals_(_st($4)._selector(),"asBoolean");
+return self}, function($ctx1) {$ctx1.fill(self,"testMessageSendWithInlining",{ast:ast},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["parse:forClass:", "assert:equals:", "selector", "visit:", "astPCNodeVisitorForPC:", "currentNode"]}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testNoMessageSend",
+fn: function (){
+var self=this;
+var ast;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo ^ self",$Object());
+$1=_st(self)._astPCNodeVisitor();
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_(_st($2)._isNil());
+return self}, function($ctx1) {$ctx1.fill(self,"testNoMessageSend",{ast:ast},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["parse:forClass:", "assert:", "isNil", "visit:", "astPCNodeVisitor", "currentNode"]}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testPC",
+fn: function (){
+var self=this;
+var ast,visitor;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo <console.log(1)>",$Object());
+$1=_st(self)._astPCNodeVisitor();
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_(_st($2)._isJSStatementNode());
+return self}, function($ctx1) {$ctx1.fill(self,"testPC",{ast:ast,visitor:visitor},smalltalk.ASTPCNodeVisitorTest)})},
+messageSends: ["parse:forClass:", "assert:", "isJSStatementNode", "visit:", "astPCNodeVisitor", "currentNode"]}),
+smalltalk.ASTPCNodeVisitorTest);
 
+
+
+smalltalk.addClass('AbstractASTInterpreterTest', smalltalk.ASTVisitorTest, [], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpret:",
@@ -81,33 +244,6 @@ return $1;
 messageSends: ["subclassResponsibility"]}),
 smalltalk.AbstractASTInterpreterTest);
 
-smalltalk.addMethod(
-smalltalk.method({
-selector: "parse:",
-fn: function (aString){
-var self=this;
-function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(_st($Smalltalk())._current())._parse_(aString);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"parse:",{aString:aString},smalltalk.AbstractASTInterpreterTest)})},
-messageSends: ["parse:", "current"]}),
-smalltalk.AbstractASTInterpreterTest);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "parse:forClass:",
-fn: function (aString,aClass){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self)._analyze_forClass_(_st(self)._parse_(aString),aClass);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"parse:forClass:",{aString:aString,aClass:aClass},smalltalk.AbstractASTInterpreterTest)})},
-messageSends: ["analyze:forClass:", "parse:"]}),
-smalltalk.AbstractASTInterpreterTest);
-
 
 
 smalltalk.addClass('ASTInterpreterTest', smalltalk.AbstractASTInterpreterTest, [], 'Compiler-Tests');

+ 212 - 41
js/Compiler-Tests.js

@@ -1,9 +1,9 @@
 smalltalk.addPackage('Compiler-Tests');
-smalltalk.addClass('AbstractASTInterpreterTest', smalltalk.TestCase, [], 'Compiler-Tests');
+smalltalk.addClass('ASTVisitorTest', smalltalk.TestCase, [], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "analyze:forClass:",
-category: 'interpreting',
+category: 'convenience',
 fn: function (aNode,aClass){
 var self=this;
 function $SemanticAnalyzer(){return smalltalk.SemanticAnalyzer||(typeof SemanticAnalyzer=="undefined"?nil:SemanticAnalyzer)}
@@ -12,14 +12,222 @@ var $1;
 _st(_st($SemanticAnalyzer())._on_(aClass))._visit_(aNode);
 $1=aNode;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"analyze:forClass:",{aNode:aNode,aClass:aClass},smalltalk.AbstractASTInterpreterTest)})},
+}, function($ctx1) {$ctx1.fill(self,"analyze:forClass:",{aNode:aNode,aClass:aClass},smalltalk.ASTVisitorTest)})},
 args: ["aNode", "aClass"],
 source: "analyze: aNode forClass: aClass\x0a\x09(SemanticAnalyzer on: aClass) visit: aNode.\x0a\x09^ aNode",
 messageSends: ["visit:", "on:"],
 referencedClasses: ["SemanticAnalyzer"]
 }),
-smalltalk.AbstractASTInterpreterTest);
+smalltalk.ASTVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parse:",
+category: 'parsing',
+fn: function (aString){
+var self=this;
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st($Smalltalk())._current())._parse_(aString);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"parse:",{aString:aString},smalltalk.ASTVisitorTest)})},
+args: ["aString"],
+source: "parse: aString\x0a\x09^ Smalltalk current parse: aString",
+messageSends: ["parse:", "current"],
+referencedClasses: ["Smalltalk"]
+}),
+smalltalk.ASTVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "parse:forClass:",
+category: 'parsing',
+fn: function (aString,aClass){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._analyze_forClass_(_st(self)._parse_(aString),aClass);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"parse:forClass:",{aString:aString,aClass:aClass},smalltalk.ASTVisitorTest)})},
+args: ["aString", "aClass"],
+source: "parse: aString forClass: aClass\x0a\x09^ self analyze: (self parse: aString) forClass: aClass",
+messageSends: ["analyze:forClass:", "parse:"],
+referencedClasses: []
+}),
+smalltalk.ASTVisitorTest);
+
+
+
+smalltalk.addClass('ASTPCNodeVisitorTest', smalltalk.ASTVisitorTest, [], 'Compiler-Tests');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "astPCNodeVisitor",
+category: 'factory',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self)._astPCNodeVisitorForPC_((0));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"astPCNodeVisitor",{},smalltalk.ASTPCNodeVisitorTest)})},
+args: [],
+source: "astPCNodeVisitor\x0a\x09^ self astPCNodeVisitorForPC: 0",
+messageSends: ["astPCNodeVisitorForPC:"],
+referencedClasses: []
+}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "astPCNodeVisitorForPC:",
+category: 'factory',
+fn: function (anInteger){
+var self=this;
+function $ASTPCNodeVisitor(){return smalltalk.ASTPCNodeVisitor||(typeof ASTPCNodeVisitor=="undefined"?nil:ASTPCNodeVisitor)}
+function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$4,$5,$1;
+$2=_st($ASTPCNodeVisitor())._new();
+_st($2)._pc_((0));
+$3=_st($AIContext())._new();
+_st($3)._pc_(anInteger);
+$4=_st($3)._yourself();
+_st($2)._context_($4);
+$5=_st($2)._yourself();
+$1=$5;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"astPCNodeVisitorForPC:",{anInteger:anInteger},smalltalk.ASTPCNodeVisitorTest)})},
+args: ["anInteger"],
+source: "astPCNodeVisitorForPC: anInteger\x0a\x09^ ASTPCNodeVisitor new\x0a\x09\x09pc: 0;\x0a\x09\x09context: (AIContext new \x0a\x09\x09\x09pc: anInteger; \x0a\x09\x09\x09yourself);\x0a\x09\x09yourself",
+messageSends: ["pc:", "new", "context:", "yourself"],
+referencedClasses: ["ASTPCNodeVisitor", "AIContext"]
+}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testJSStatementNode",
+category: 'tests',
+fn: function (){
+var self=this;
+var ast,visitor;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo <consolee.log(1)>",$Object());
+$1=_st(self)._astPCNodeVisitor();
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_(_st($2)._isJSStatementNode());
+return self}, function($ctx1) {$ctx1.fill(self,"testJSStatementNode",{ast:ast,visitor:visitor},smalltalk.ASTPCNodeVisitorTest)})},
+args: [],
+source: "testJSStatementNode\x0a\x09| ast visitor |\x0a\x09\x0a\x09ast := self parse: 'foo <consolee.log(1)>' forClass: Object.\x0a\x09self assert: (self astPCNodeVisitor\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode) isJSStatementNode",
+messageSends: ["parse:forClass:", "assert:", "isJSStatementNode", "visit:", "astPCNodeVisitor", "currentNode"],
+referencedClasses: ["Object"]
+}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testMessageSend",
+category: 'tests',
+fn: function (){
+var self=this;
+var ast;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo self asString yourself. ^ self asBoolean",$Object());
+$1=_st(self)._astPCNodeVisitorForPC_((2));
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_equals_(_st($2)._selector(),"yourself");
+return self}, function($ctx1) {$ctx1.fill(self,"testMessageSend",{ast:ast},smalltalk.ASTPCNodeVisitorTest)})},
+args: [],
+source: "testMessageSend\x0a\x09| ast |\x0a\x09\x0a\x09ast := self parse: 'foo self asString yourself. ^ self asBoolean' forClass: Object.\x0a\x09self assert: ((self astPCNodeVisitorForPC: 2)\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode) selector equals: 'yourself'",
+messageSends: ["parse:forClass:", "assert:equals:", "selector", "visit:", "astPCNodeVisitorForPC:", "currentNode"],
+referencedClasses: ["Object"]
+}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testMessageSendWithInlining",
+category: 'tests',
+fn: function (){
+var self=this;
+var ast;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4;
+ast=_st(self)._parse_forClass_("foo true ifTrue: [ self asString yourself ]. ^ self asBoolean",$Object());
+$1=_st(self)._astPCNodeVisitorForPC_((2));
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_equals_(_st($2)._selector(),"yourself");
+ast=_st(self)._parse_forClass_("foo true ifTrue: [ self asString yourself ]. ^ self asBoolean",$Object());
+$3=_st(self)._astPCNodeVisitorForPC_((3));
+_st($3)._visit_(ast);
+$4=_st($3)._currentNode();
+_st(self)._assert_equals_(_st($4)._selector(),"asBoolean");
+return self}, function($ctx1) {$ctx1.fill(self,"testMessageSendWithInlining",{ast:ast},smalltalk.ASTPCNodeVisitorTest)})},
+args: [],
+source: "testMessageSendWithInlining\x0a\x09| ast |\x0a\x09\x0a\x09ast := self parse: 'foo true ifTrue: [ self asString yourself ]. ^ self asBoolean' forClass: Object.\x0a\x09self assert: ((self astPCNodeVisitorForPC: 2)\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode) selector equals: 'yourself'.\x0a\x09\x09\x0a\x09ast := self parse: 'foo true ifTrue: [ self asString yourself ]. ^ self asBoolean' forClass: Object.\x0a\x09self assert: ((self astPCNodeVisitorForPC: 3)\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode) selector equals: 'asBoolean'",
+messageSends: ["parse:forClass:", "assert:equals:", "selector", "visit:", "astPCNodeVisitorForPC:", "currentNode"],
+referencedClasses: ["Object"]
+}),
+smalltalk.ASTPCNodeVisitorTest);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testNoMessageSend",
+category: 'tests',
+fn: function (){
+var self=this;
+var ast;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo ^ self",$Object());
+$1=_st(self)._astPCNodeVisitor();
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_(_st($2)._isNil());
+return self}, function($ctx1) {$ctx1.fill(self,"testNoMessageSend",{ast:ast},smalltalk.ASTPCNodeVisitorTest)})},
+args: [],
+source: "testNoMessageSend\x0a\x09| ast |\x0a\x09\x0a\x09ast := self parse: 'foo ^ self' forClass: Object.\x0a\x09self assert: (self astPCNodeVisitor\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode) isNil",
+messageSends: ["parse:forClass:", "assert:", "isNil", "visit:", "astPCNodeVisitor", "currentNode"],
+referencedClasses: ["Object"]
+}),
+smalltalk.ASTPCNodeVisitorTest);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testPC",
+category: 'tests',
+fn: function (){
+var self=this;
+var ast,visitor;
+function $Object(){return smalltalk.Object||(typeof Object=="undefined"?nil:Object)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+ast=_st(self)._parse_forClass_("foo <console.log(1)>",$Object());
+$1=_st(self)._astPCNodeVisitor();
+_st($1)._visit_(ast);
+$2=_st($1)._currentNode();
+_st(self)._assert_(_st($2)._isJSStatementNode());
+return self}, function($ctx1) {$ctx1.fill(self,"testPC",{ast:ast,visitor:visitor},smalltalk.ASTPCNodeVisitorTest)})},
+args: [],
+source: "testPC\x0a\x09| ast visitor |\x0a\x09\x0a\x09ast := self parse: 'foo <console.log(1)>' forClass: Object.\x0a\x09self assert: (self astPCNodeVisitor\x0a\x09\x09visit: ast;\x0a\x09\x09currentNode) isJSStatementNode",
+messageSends: ["parse:forClass:", "assert:", "isJSStatementNode", "visit:", "astPCNodeVisitor", "currentNode"],
+referencedClasses: ["Object"]
+}),
+smalltalk.ASTPCNodeVisitorTest);
+
+
+
+smalltalk.addClass('AbstractASTInterpreterTest', smalltalk.ASTVisitorTest, [], 'Compiler-Tests');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpret:",
@@ -106,43 +314,6 @@ referencedClasses: []
 }),
 smalltalk.AbstractASTInterpreterTest);
 
-smalltalk.addMethod(
-smalltalk.method({
-selector: "parse:",
-category: 'parsing',
-fn: function (aString){
-var self=this;
-function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(_st($Smalltalk())._current())._parse_(aString);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"parse:",{aString:aString},smalltalk.AbstractASTInterpreterTest)})},
-args: ["aString"],
-source: "parse: aString\x0a\x09^ Smalltalk current parse: aString",
-messageSends: ["parse:", "current"],
-referencedClasses: ["Smalltalk"]
-}),
-smalltalk.AbstractASTInterpreterTest);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "parse:forClass:",
-category: 'parsing',
-fn: function (aString,aClass){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self)._analyze_forClass_(_st(self)._parse_(aString),aClass);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"parse:forClass:",{aString:aString,aClass:aClass},smalltalk.AbstractASTInterpreterTest)})},
-args: ["aString", "aClass"],
-source: "parse: aString forClass: aClass\x0a\x09^ self analyze: (self parse: aString) forClass: aClass",
-messageSends: ["analyze:forClass:", "parse:"],
-referencedClasses: []
-}),
-smalltalk.AbstractASTInterpreterTest);
-
 
 
 smalltalk.addClass('ASTInterpreterTest', smalltalk.AbstractASTInterpreterTest, [], 'Compiler-Tests');

+ 59 - 6
st/Compiler-AST.st

@@ -1,6 +1,6 @@
 Smalltalk current createPackage: 'Compiler-AST'!
 Object subclass: #Node
-	instanceVariableNames: 'position nodes shouldBeInlined shouldBeAliased'
+	instanceVariableNames: 'parent position nodes shouldBeInlined shouldBeAliased'
 	package: 'Compiler-AST'!
 !Node commentStamp!
 I am the abstract root class of the abstract syntax tree.
@@ -12,15 +12,48 @@ Concrete classes should implement `#accept:` to allow visiting.
 !Node methodsFor: 'accessing'!
 
 addNode: aNode
-	self nodes add: aNode
+	self nodes add: aNode.
+	aNode parent: self
+!
+
+extent
+	"Answer the line and column of the end position of the receiver in the source code"
+	
+	^ self nextNode 
+		ifNil: [ self parent ifNotNil: [ :node | node extent ] ]
+		ifNotNil: [ :node | node position ]
+!
+
+nextNode
+	^ self parent ifNotNil: [ :node |
+		node nextNode: self ]
+!
+
+nextNode: aNode
+	"Answer the next node after aNode"
+	
+	^ self nodes 
+		at: (self nodes indexOf: aNode) + 1
+		ifAbsent: [ nil ]
 !
 
 nodes
 	^nodes ifNil: [nodes := Array new]
 !
 
+parent
+	^ parent
+!
+
+parent: aNode
+	parent := aNode
+!
+
 position
-	^position ifNil: [position := 0@0]
+	"answer the line and column of the receiver in the source code"
+	
+	^ position ifNil: [ 
+		self parent ifNotNil: [ :node | node position ] ]
 !
 
 shouldBeAliased
@@ -42,7 +75,8 @@ shouldBeInlined: aBoolean
 !Node methodsFor: 'building'!
 
 nodes: aCollection
-	nodes := aCollection
+	nodes := aCollection.
+	aCollection do: [ :each | each parent: self ]
 !
 
 position: aPosition
@@ -67,6 +101,10 @@ isImmutable
 	^false
 !
 
+isJSStatementNode
+	^ false
+!
+
 isNode
 	^ true
 !
@@ -240,6 +278,12 @@ source: aString
 	source := aString
 ! !
 
+!JSStatementNode methodsFor: 'testing'!
+
+isJSStatementNode
+	^ true
+! !
+
 !JSStatementNode methodsFor: 'visiting'!
 
 accept: aVisitor
@@ -272,6 +316,10 @@ classReferences: aCollection
 	classReferences := aCollection
 !
 
+extent
+	^ self source lines size @ (self source lines last size + 1)
+!
+
 messageSends
 	^ messageSends
 !
@@ -363,7 +411,8 @@ arguments
 !
 
 arguments: aCollection
-	arguments := aCollection
+	arguments := aCollection.
+	aCollection do: [ :each | each parent: self ]
 !
 
 cascadeNodeWithMessages: aCollection
@@ -397,7 +446,9 @@ receiver
 !
 
 receiver: aNode
-	receiver := aNode
+	receiver := aNode.
+	aNode isNode ifTrue: [
+		aNode parent: self ]
 !
 
 selector
@@ -418,6 +469,7 @@ superSend: aBoolean
 
 valueForReceiver: anObject
 	^SendNode new
+		position: self position;
 		receiver: (self receiver
 		ifNil: [anObject]
 		ifNotNil: [self receiver valueForReceiver: anObject]);
@@ -470,6 +522,7 @@ temps: aCollection
 
 asBlockSequenceNode
 	^BlockSequenceNode new
+		position: self position;
 		nodes: self nodes;
 		temps: self temps;
 		yourself

+ 73 - 6
st/Compiler-Interpreter.st

@@ -127,7 +127,7 @@ context
 !
 
 context: aContext
-	context := AIContext new.
+	context := aContext
 !
 
 interpreter
@@ -140,6 +140,10 @@ interpreter: anInterpreter
 
 method
 	^ self context method
+!
+
+nextNode
+	^ self interpreter nextNode
 ! !
 
 !ASTDebugger methodsFor: 'defaults'!
@@ -165,13 +169,19 @@ buildAST
 !
 
 initializeInterpreter
-	self interpreter interpret: self buildAST nodes first
+	| ast next |
+	ast := self buildAST.
+	next := ASTPCNodeVisitor new
+		context: self context;
+		visit: ast;
+		currentNode.
+	self interpreter interpret: next
 !
 
-initializeWithContext: aMethodContext
+initializeWithContext: aContext
 	"TODO: do we need to handle block contexts?"
 	
-	self context: (AIContext fromMethodContext: aMethodContext).
+	self context: aContext.
 	self initializeInterpreter
 ! !
 
@@ -214,9 +224,9 @@ atEnd
 
 !ASTDebugger class methodsFor: 'instance creation'!
 
-context: aMethodContext
+context: aContext
 	^ self new
-		initializeWithContext: aMethodContext;
+		initializeWithContext: aContext;
 		yourself
 ! !
 
@@ -545,6 +555,63 @@ atEnd
 	^ self shouldReturn or: [ self nextNode == self currentNode ]
 ! !
 
+NodeVisitor subclass: #ASTPCNodeVisitor
+	instanceVariableNames: 'useInlinings pc context currentNode'
+	package: 'Compiler-Interpreter'!
+!ASTPCNodeVisitor commentStamp!
+I visit an AST until I get to the current pc node and answer it.
+
+## API
+
+My instances must be filled with a context object using `#context:`.
+
+After visiting the AST the current node corresponding to the `pc` is answered by `#currentNode`!
+
+!ASTPCNodeVisitor methodsFor: 'accessing'!
+
+context
+	^ context
+!
+
+context: aContext
+	context := aContext
+!
+
+currentNode
+	^ currentNode
+!
+
+pc
+	^ pc ifNil: [ 0 ]
+!
+
+pc: anInteger
+	pc := anInteger
+!
+
+useInlinings
+	^ useInlinings ifNil: [ true ]
+!
+
+useInlinings: aBoolean
+	useInlinings := aBoolean
+! !
+
+!ASTPCNodeVisitor methodsFor: 'visiting'!
+
+visitJSStatementNode: aNode
+	currentNode := aNode
+!
+
+visitSendNode: aNode
+	super visitSendNode: aNode.
+	
+	self pc = self context pc ifFalse: [
+		aNode shouldBeInlined ifFalse: [ 
+			self pc: self pc + 1.
+			currentNode := aNode ] ]
+! !
+
 !Node methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock

+ 93 - 16
st/Compiler-Tests.st

@@ -1,5 +1,97 @@
 Smalltalk current createPackage: 'Compiler-Tests'!
-TestCase subclass: #AbstractASTInterpreterTest
+TestCase subclass: #ASTVisitorTest
+	instanceVariableNames: ''
+	package: 'Compiler-Tests'!
+
+!ASTVisitorTest methodsFor: 'convenience'!
+
+analyze: aNode forClass: aClass
+	(SemanticAnalyzer on: aClass) visit: aNode.
+	^ aNode
+! !
+
+!ASTVisitorTest methodsFor: 'parsing'!
+
+parse: aString
+	^ Smalltalk current parse: aString
+!
+
+parse: aString forClass: aClass
+	^ self analyze: (self parse: aString) forClass: aClass
+! !
+
+ASTVisitorTest subclass: #ASTPCNodeVisitorTest
+	instanceVariableNames: ''
+	package: 'Compiler-Tests'!
+
+!ASTPCNodeVisitorTest methodsFor: 'factory'!
+
+astPCNodeVisitor
+	^ self astPCNodeVisitorForPC: 0
+!
+
+astPCNodeVisitorForPC: anInteger
+	^ ASTPCNodeVisitor new
+		pc: 0;
+		context: (AIContext new 
+			pc: anInteger; 
+			yourself);
+		yourself
+! !
+
+!ASTPCNodeVisitorTest methodsFor: 'tests'!
+
+testJSStatementNode
+	| ast visitor |
+	
+	ast := self parse: 'foo <consolee.log(1)>' forClass: Object.
+	self assert: (self astPCNodeVisitor
+		visit: ast;
+		currentNode) isJSStatementNode
+!
+
+testMessageSend
+	| ast |
+	
+	ast := self parse: 'foo self asString yourself. ^ self asBoolean' forClass: Object.
+	self assert: ((self astPCNodeVisitorForPC: 2)
+		visit: ast;
+		currentNode) selector equals: 'yourself'
+!
+
+testMessageSendWithInlining
+	| ast |
+	
+	ast := self parse: 'foo true ifTrue: [ self asString yourself ]. ^ self asBoolean' forClass: Object.
+	self assert: ((self astPCNodeVisitorForPC: 2)
+		visit: ast;
+		currentNode) selector equals: 'yourself'.
+		
+	ast := self parse: 'foo true ifTrue: [ self asString yourself ]. ^ self asBoolean' forClass: Object.
+	self assert: ((self astPCNodeVisitorForPC: 3)
+		visit: ast;
+		currentNode) selector equals: 'asBoolean'
+!
+
+testNoMessageSend
+	| ast |
+	
+	ast := self parse: 'foo ^ self' forClass: Object.
+	self assert: (self astPCNodeVisitor
+		visit: ast;
+		currentNode) isNil
+!
+
+testPC
+	| ast visitor |
+	
+	ast := self parse: 'foo <console.log(1)>' forClass: Object.
+	self assert: (self astPCNodeVisitor
+		visit: ast;
+		currentNode) isJSStatementNode
+! !
+
+ASTVisitorTest subclass: #AbstractASTInterpreterTest
 	instanceVariableNames: ''
 	package: 'Compiler-Tests'!
 
@@ -11,11 +103,6 @@ interpreter
 
 !AbstractASTInterpreterTest methodsFor: 'interpreting'!
 
-analyze: aNode forClass: aClass
-	(SemanticAnalyzer on: aClass) visit: aNode.
-	^ aNode
-!
-
 interpret: aString
 	^ self
 		interpret: aString
@@ -46,16 +133,6 @@ interpret: aString withArguments: aDictionary
 		withArguments: aDictionary
 ! !
 
-!AbstractASTInterpreterTest methodsFor: 'parsing'!
-
-parse: aString
-	^ Smalltalk current parse: aString
-!
-
-parse: aString forClass: aClass
-	^ self analyze: (self parse: aString) forClass: aClass
-! !
-
 AbstractASTInterpreterTest subclass: #ASTInterpreterTest
 	instanceVariableNames: ''
 	package: 'Compiler-Tests'!