Explorar o código

- fix for block interpretation

Nicolas Petton %!s(int64=10) %!d(string=hai) anos
pai
achega
c021bd5719

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

@@ -127,10 +127,14 @@ smalltalk.method({
 selector: "valueWithPossibleArguments:",
 fn: function (aCollection){
 var self=this;
-var context;
+var context,sequenceNode;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2,$3,$4;
+var $1,$2,$3,$4,$5;
 context=_st(self["@outerContext"])._newBlockContext();
+$1=_st(_st(_st(self["@node"])._nodes())._first())._copy();
+_st($1)._parent_(nil);
+$2=_st($1)._yourself();
+sequenceNode=$2;
 _st(_st(self["@node"])._parameters())._withIndexDo_((function(each,index){
 return smalltalk.withContext(function($ctx2) {
 return _st(context)._localAt_put_(each,_st(aCollection)._at_ifAbsent_(index,(function(){
@@ -138,14 +142,15 @@ return smalltalk.withContext(function($ctx3) {
 return nil;
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})})));
 }, function($ctx2) {$ctx2.fillBlock({each:each,index:index},$ctx1,1)})}));
-$1=_st(context)._interpreter();
-_st($1)._node_(_st(_st(_st(self["@node"])._nodes())._first())._nextChild());
-$2=_st($1)._proceed();
-$3=_st(self["@outerContext"])._interpreter();
-_st($3)._push_(_st(_st(context)._interpreter())._value());
-$4=_st($3)._returnValue_(_st(context)._returnValue());
-return self}, function($ctx1) {$ctx1.fill(self,"valueWithPossibleArguments:",{aCollection:aCollection,context:context},smalltalk.AIBlockClosure)})},
-messageSends: ["newBlockContext", "withIndexDo:", "parameters", "localAt:put:", "at:ifAbsent:", "node:", "interpreter", "nextChild", "first", "nodes", "proceed", "push:", "value", "returnValue:", "returnValue"]}),
+$3=_st(context)._interpreter();
+_st($3)._node_(_st(sequenceNode)._nextChild());
+$4=_st($3)._proceed();
+_st(_st(self["@outerContext"])._interpreter())._returnValue_(_st(_st(context)._interpreter())._returnValue());
+_st(console)._log_(_st(_st(context)._interpreter())._returnValue());
+$5=_st(_st(context)._interpreter())._pop();
+return $5;
+}, function($ctx1) {$ctx1.fill(self,"valueWithPossibleArguments:",{aCollection:aCollection,context:context,sequenceNode:sequenceNode},smalltalk.AIBlockClosure)})},
+messageSends: ["newBlockContext", "parent:", "copy", "first", "nodes", "yourself", "withIndexDo:", "parameters", "localAt:put:", "at:ifAbsent:", "node:", "interpreter", "nextChild", "proceed", "returnValue:", "returnValue", "log:", "pop"]}),
 smalltalk.AIBlockClosure);
 
 
@@ -385,6 +390,17 @@ return $2;
 messageSends: ["ifNil:", "initializeInterpreter"]}),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpreter:",
+fn: function (anInterpreter){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@interpreter"]=anInterpreter;
+return self}, function($ctx1) {$ctx1.fill(self,"interpreter:",{anInterpreter:anInterpreter},smalltalk.AIContext)})},
+messageSends: []}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isBlockContext",
@@ -1727,7 +1743,7 @@ smalltalk.ASTPCNodeVisitor);
 
 
 
-smalltalk.addClass('Interpreter', smalltalk.NodeVisitor, ['node', 'context', 'stack', 'value', 'returnValue'], 'Compiler-Interpreter');
+smalltalk.addClass('Interpreter', smalltalk.NodeVisitor, ['node', 'context', 'stack', 'returnValue'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "assign:to:",
@@ -1917,10 +1933,18 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
+var $early={};
+try {
+_st(self._stack())._ifEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
+throw $early=[nil];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 $1=_st(self._stack())._last();
 return $1;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
 }, function($ctx1) {$ctx1.fill(self,"peek",{},smalltalk.Interpreter)})},
-messageSends: ["last", "stack"]}),
+messageSends: ["ifEmpty:", "stack", "last"]}),
 smalltalk.Interpreter);
 
 smalltalk.addMethod(
@@ -2168,12 +2192,12 @@ smalltalk.method({
 selector: "visitBlockNode:",
 fn: function (aNode){
 var self=this;
-var blockContext,block;
+var block;
 function $AIBlockClosure(){return smalltalk.AIBlockClosure||(typeof AIBlockClosure=="undefined"?nil:AIBlockClosure)}
 return smalltalk.withContext(function($ctx1) { 
 block=_st($AIBlockClosure())._forContext_node_(self._context(),aNode);
 self._push_(block);
-return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode,blockContext:blockContext,block:block},smalltalk.Interpreter)})},
+return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode,block:block},smalltalk.Interpreter)})},
 messageSends: ["forContext:node:", "context", "push:"]}),
 smalltalk.Interpreter);
 

+ 46 - 17
js/Compiler-Interpreter.js

@@ -179,10 +179,14 @@ selector: "valueWithPossibleArguments:",
 category: 'evaluating',
 fn: function (aCollection){
 var self=this;
-var context;
+var context,sequenceNode;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2,$3,$4;
+var $1,$2,$3,$4,$5;
 context=_st(self["@outerContext"])._newBlockContext();
+$1=_st(_st(_st(self["@node"])._nodes())._first())._copy();
+_st($1)._parent_(nil);
+$2=_st($1)._yourself();
+sequenceNode=$2;
 _st(_st(self["@node"])._parameters())._withIndexDo_((function(each,index){
 return smalltalk.withContext(function($ctx2) {
 return _st(context)._localAt_put_(each,_st(aCollection)._at_ifAbsent_(index,(function(){
@@ -190,16 +194,17 @@ return smalltalk.withContext(function($ctx3) {
 return nil;
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})})));
 }, function($ctx2) {$ctx2.fillBlock({each:each,index:index},$ctx1,1)})}));
-$1=_st(context)._interpreter();
-_st($1)._node_(_st(_st(_st(self["@node"])._nodes())._first())._nextChild());
-$2=_st($1)._proceed();
-$3=_st(self["@outerContext"])._interpreter();
-_st($3)._push_(_st(_st(context)._interpreter())._value());
-$4=_st($3)._returnValue_(_st(context)._returnValue());
-return self}, function($ctx1) {$ctx1.fill(self,"valueWithPossibleArguments:",{aCollection:aCollection,context:context},smalltalk.AIBlockClosure)})},
+$3=_st(context)._interpreter();
+_st($3)._node_(_st(sequenceNode)._nextChild());
+$4=_st($3)._proceed();
+_st(_st(self["@outerContext"])._interpreter())._returnValue_(_st(_st(context)._interpreter())._returnValue());
+_st(console)._log_(_st(_st(context)._interpreter())._returnValue());
+$5=_st(_st(context)._interpreter())._pop();
+return $5;
+}, function($ctx1) {$ctx1.fill(self,"valueWithPossibleArguments:",{aCollection:aCollection,context:context,sequenceNode:sequenceNode},smalltalk.AIBlockClosure)})},
 args: ["aCollection"],
-source: "valueWithPossibleArguments: aCollection\x0a\x09| context |\x0a\x09context := outerContext newBlockContext.\x0a\x0a\x09\x22Populate the arguments into the context locals\x22\x09\x0a\x09node parameters withIndexDo: [ :each :index |\x0a\x09\x09context localAt: each put: (aCollection at: index ifAbsent: [ nil ]) ].\x0a\x0a\x09\x22Interpret the first node of the BlockSequenceNode\x22\x0a\x09context interpreter\x0a\x09\x09node: node nodes first nextChild;\x0a\x09\x09proceed.\x0a\x09\x09\x0a\x09outerContext interpreter \x0a\x09\x09push: context interpreter value;\x0a\x09\x09returnValue: context returnValue",
-messageSends: ["newBlockContext", "withIndexDo:", "parameters", "localAt:put:", "at:ifAbsent:", "node:", "interpreter", "nextChild", "first", "nodes", "proceed", "push:", "value", "returnValue:", "returnValue"],
+source: "valueWithPossibleArguments: aCollection\x0a\x09| context sequenceNode |\x0a\x09context := outerContext newBlockContext.\x0a\x0a\x09\x22Interpret a copy of the sequence node to avoid creating a new AIBlockClosure\x22\x0a\x09sequenceNode := node nodes first copy\x0a\x09\x09parent: nil;\x0a\x09\x09yourself.\x0a\x0a\x09\x22Populate the arguments into the context locals\x22\x09\x0a\x09node parameters withIndexDo: [ :each :index |\x0a\x09\x09context localAt: each put: (aCollection at: index ifAbsent: [ nil ]) ].\x0a\x0a\x09\x22Interpret the first node of the BlockSequenceNode\x22\x0a\x09context interpreter\x0a\x09\x09node: sequenceNode nextChild;\x0a\x09\x09proceed.\x0a\x09\x09\x0a\x09outerContext interpreter\x0a\x09\x09returnValue: context interpreter returnValue.\x0a\x09\x09\x0a\x09console log: context interpreter returnValue.\x0a\x09\x09\x0a\x09^ context interpreter pop",
+messageSends: ["newBlockContext", "parent:", "copy", "first", "nodes", "yourself", "withIndexDo:", "parameters", "localAt:put:", "at:ifAbsent:", "node:", "interpreter", "nextChild", "proceed", "returnValue:", "returnValue", "log:", "pop"],
 referencedClasses: []
 }),
 smalltalk.AIBlockClosure);
@@ -507,6 +512,22 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "interpreter:",
+category: 'interpreting',
+fn: function (anInterpreter){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@interpreter"]=anInterpreter;
+return self}, function($ctx1) {$ctx1.fill(self,"interpreter:",{anInterpreter:anInterpreter},smalltalk.AIContext)})},
+args: ["anInterpreter"],
+source: "interpreter: anInterpreter\x0a\x09interpreter := anInterpreter",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isBlockContext",
@@ -2279,7 +2300,7 @@ smalltalk.ASTPCNodeVisitor);
 
 
 
-smalltalk.addClass('Interpreter', smalltalk.NodeVisitor, ['node', 'context', 'stack', 'value', 'returnValue'], 'Compiler-Interpreter');
+smalltalk.addClass('Interpreter', smalltalk.NodeVisitor, ['node', 'context', 'stack', 'returnValue'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "assign:to:",
@@ -2530,12 +2551,20 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1;
+var $early={};
+try {
+_st(self._stack())._ifEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
+throw $early=[nil];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 $1=_st(self._stack())._last();
 return $1;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
 }, 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"],
+source: "peek\x0a\x09\x22Peek the top object of the context stack\x22\x0a\x09\x0a\x09self stack ifEmpty: [ ^ nil ].\x0a\x09\x0a\x09^ self stack last",
+messageSends: ["ifEmpty:", "stack", "last"],
 referencedClasses: []
 }),
 smalltalk.Interpreter);
@@ -2861,14 +2890,14 @@ selector: "visitBlockNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-var blockContext,block;
+var block;
 function $AIBlockClosure(){return smalltalk.AIBlockClosure||(typeof AIBlockClosure=="undefined"?nil:AIBlockClosure)}
 return smalltalk.withContext(function($ctx1) { 
 block=_st($AIBlockClosure())._forContext_node_(self._context(),aNode);
 self._push_(block);
-return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode,blockContext:blockContext,block:block},smalltalk.Interpreter)})},
+return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode,block:block},smalltalk.Interpreter)})},
 args: ["aNode"],
-source: "visitBlockNode: aNode\x0a\x09\x22Do not evaluate the block node.\x0a\x09Instead, put all instructions into a block that we push to the stack for later evaluation\x22\x0a\x09\x0a\x09| blockContext block |\x0a\x09\x0a\x09block := AIBlockClosure forContext: self context node: aNode.\x0a\x09\x0a\x09self push: block",
+source: "visitBlockNode: aNode\x0a\x09\x22Do not evaluate the block node.\x0a\x09Instead, put all instructions into a block that we push to the stack for later evaluation\x22\x0a\x09\x0a\x09| block |\x0a\x09\x0a\x09block := AIBlockClosure forContext: self context node: aNode.\x0a\x09\x0a\x09self push: block",
 messageSends: ["forContext:node:", "context", "push:"],
 referencedClasses: ["AIBlockClosure"]
 }),

+ 17 - 13
js/Compiler-Tests.deploy.js

@@ -497,25 +497,29 @@ smalltalk.method({
 selector: "interpret:receiver:withArguments:",
 fn: function (aString,anObject,aDictionary){
 var self=this;
-var ctx;
+var ctx,interpreter;
 function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-ctx=_st($AIContext())._new();
-_st(ctx)._receiver_(anObject);
+var $1,$2,$4,$5,$3;
+interpreter=self._interpreter();
+$1=_st($AIContext())._new();
+_st($1)._receiver_(anObject);
+_st($1)._interpreter_(interpreter);
+$2=_st($1)._yourself();
+ctx=$2;
 _st(aDictionary)._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return _st(ctx)._localAt_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
-$2=self._interpreter();
-_st($2)._context_(ctx);
-_st($2)._interpret_(_st(self._parse_forClass_(aString,_st(anObject)._class()))._nextChild());
-_st($2)._proceed();
-$3=_st($2)._result();
-$1=$3;
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx},smalltalk.InterpreterTest)})},
-messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "nextChild", "parse:forClass:", "class", "proceed", "result"]}),
+$4=interpreter;
+_st($4)._context_(ctx);
+_st($4)._interpret_(_st(self._parse_forClass_(aString,_st(anObject)._class()))._nextChild());
+_st($4)._proceed();
+$5=_st($4)._result();
+$3=$5;
+return $3;
+}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx,interpreter:interpreter},smalltalk.InterpreterTest)})},
+messageSends: ["interpreter", "receiver:", "new", "interpreter:", "yourself", "keysAndValuesDo:", "localAt:put:", "context:", "interpret:", "nextChild", "parse:forClass:", "class", "proceed", "result"]}),
 smalltalk.InterpreterTest);
 
 smalltalk.addMethod(

+ 18 - 14
js/Compiler-Tests.js

@@ -658,27 +658,31 @@ selector: "interpret:receiver:withArguments:",
 category: 'interpreting',
 fn: function (aString,anObject,aDictionary){
 var self=this;
-var ctx;
+var ctx,interpreter;
 function $AIContext(){return smalltalk.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-ctx=_st($AIContext())._new();
-_st(ctx)._receiver_(anObject);
+var $1,$2,$4,$5,$3;
+interpreter=self._interpreter();
+$1=_st($AIContext())._new();
+_st($1)._receiver_(anObject);
+_st($1)._interpreter_(interpreter);
+$2=_st($1)._yourself();
+ctx=$2;
 _st(aDictionary)._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return _st(ctx)._localAt_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
-$2=self._interpreter();
-_st($2)._context_(ctx);
-_st($2)._interpret_(_st(self._parse_forClass_(aString,_st(anObject)._class()))._nextChild());
-_st($2)._proceed();
-$3=_st($2)._result();
-$1=$3;
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx},smalltalk.InterpreterTest)})},
+$4=interpreter;
+_st($4)._context_(ctx);
+_st($4)._interpret_(_st(self._parse_forClass_(aString,_st(anObject)._class()))._nextChild());
+_st($4)._proceed();
+$5=_st($4)._result();
+$3=$5;
+return $3;
+}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx,interpreter:interpreter},smalltalk.InterpreterTest)})},
 args: ["aString", "anObject", "aDictionary"],
-source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx |\x0a\x09\x0a\x09ctx := AIContext new.\x0a\x09ctx receiver: anObject.\x0a\x09aDictionary keysAndValuesDo: [ :key :value |\x0a\x09\x09ctx localAt: key put: value ].\x0a\x09\x0a\x09^ self interpreter\x0a\x09\x09context: ctx;\x0a\x09\x09interpret: (self parse: aString forClass: anObject class) nextChild;\x0a\x09\x09proceed;\x0a\x09\x09result",
-messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "nextChild", "parse:forClass:", "class", "proceed", "result"],
+source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a\x09\x0a\x09| ctx interpreter |\x0a\x09\x0a\x09interpreter := self interpreter.\x0a\x09\x0a\x09ctx := AIContext new\x0a\x09\x09receiver: anObject;\x0a\x09\x09interpreter: interpreter;\x0a\x09\x09yourself.\x0a\x09aDictionary keysAndValuesDo: [ :key :value |\x0a\x09\x09ctx localAt: key put: value ].\x0a\x09\x0a\x09^ interpreter\x0a\x09\x09context: ctx;\x0a\x09\x09interpret: (self parse: aString forClass: anObject class) nextChild;\x0a\x09\x09proceed;\x0a\x09\x09result",
+messageSends: ["interpreter", "receiver:", "new", "interpreter:", "yourself", "keysAndValuesDo:", "localAt:put:", "context:", "interpret:", "nextChild", "parse:forClass:", "class", "proceed", "result"],
 referencedClasses: ["AIContext"]
 }),
 smalltalk.InterpreterTest);

+ 21 - 7
st/Compiler-Interpreter.st

@@ -56,21 +56,29 @@ value: firstArgument value: secondArgument value: thirdArgument
 !
 
 valueWithPossibleArguments: aCollection
-	| context |
+	| context sequenceNode |
 	context := outerContext newBlockContext.
 
+	"Interpret a copy of the sequence node to avoid creating a new AIBlockClosure"
+	sequenceNode := node nodes first copy
+		parent: nil;
+		yourself.
+
 	"Populate the arguments into the context locals"	
 	node parameters withIndexDo: [ :each :index |
 		context localAt: each put: (aCollection at: index ifAbsent: [ nil ]) ].
 
 	"Interpret the first node of the BlockSequenceNode"
 	context interpreter
-		node: node nodes first nextChild;
+		node: sequenceNode nextChild;
 		proceed.
 		
-	outerContext interpreter 
-		push: context interpreter value;
-		returnValue: context returnValue
+	outerContext interpreter
+		returnValue: context interpreter returnValue.
+		
+	console log: context interpreter returnValue.
+		
+	^ context interpreter pop
 ! !
 
 !AIBlockClosure methodsFor: 'initialization'!
@@ -238,6 +246,10 @@ interpreter
 	^ interpreter
 !
 
+interpreter: anInterpreter
+	interpreter := anInterpreter
+!
+
 pc
 	^ pc ifNil: [ pc := 0 ]
 !
@@ -822,7 +834,7 @@ visitSendNode: aNode
 ! !
 
 NodeVisitor subclass: #Interpreter
-	instanceVariableNames: 'node context stack value returnValue'
+	instanceVariableNames: 'node context stack returnValue'
 	package: 'Compiler-Interpreter'!
 
 !Interpreter methodsFor: 'accessing'!
@@ -967,6 +979,8 @@ sendMessage: aMessage to: anObject superSend: aBoolean
 peek
 	"Peek the top object of the context stack"
 	
+	self stack ifEmpty: [ ^ nil ].
+	
 	^ self stack last
 !
 
@@ -1019,7 +1033,7 @@ visitBlockNode: aNode
 	"Do not evaluate the block node.
 	Instead, put all instructions into a block that we push to the stack for later evaluation"
 	
-	| blockContext block |
+	| block |
 	
 	block := AIBlockClosure forContext: self context node: aNode.
 	

+ 8 - 4
st/Compiler-Tests.st

@@ -307,14 +307,18 @@ interpreter
 interpret: aString receiver: anObject withArguments: aDictionary
 	"The food is a methodNode. Interpret the sequenceNode only"
 	
-	| ctx |
+	| ctx interpreter |
 	
-	ctx := AIContext new.
-	ctx receiver: anObject.
+	interpreter := self interpreter.
+	
+	ctx := AIContext new
+		receiver: anObject;
+		interpreter: interpreter;
+		yourself.
 	aDictionary keysAndValuesDo: [ :key :value |
 		ctx localAt: key put: value ].
 	
-	^ self interpreter
+	^ interpreter
 		context: ctx;
 		interpret: (self parse: aString forClass: anObject class) nextChild;
 		proceed;