Przeglądaj źródła

- adds one interpreter test for block evaluation
- adds shouldBeInlined flags to blocks in the semantic analyzer
- fixes PC retrieval

Nicolas Petton 10 lat temu
rodzic
commit
4aa03d49f3

+ 49 - 16
js/Compiler-Interpreter.deploy.js

@@ -1,5 +1,6 @@
 (function(smalltalk,nil,_st){
 smalltalk.addPackage('Compiler-Interpreter');
+
 smalltalk.addClass('AIBlockClosure', smalltalk.BlockClosure, ['node', 'outerContext'], 'Compiler-Interpreter');
 smalltalk.addMethod(
 smalltalk.method({
@@ -1697,14 +1698,25 @@ selector: "visitBlockNode:",
 fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self._blockIndex()).__gt_eq(_st(self._context())._index());
-if(! smalltalk.assert($1)){
+var $1,$3,$4,$2,$5;
+$1=_st(aNode)._shouldBeInlined();
+$2=(function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(self._blockIndex()).__gt_eq(_st(self._context())._index());
+$4=(function(){
+return smalltalk.withContext(function($ctx3) {
 self._increaseBlockIndex();
-smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
-};
+return smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})});
+return _st($3)._ifFalse_($4);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})});
+$5=(function(){
+return smalltalk.withContext(function($ctx2) {
+return smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)})});
+_st($1)._ifFalse_ifTrue_($2,$5);
 return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
-messageSends: ["ifFalse:", ">=", "blockIndex", "index", "context", "increaseBlockIndex", "visitBlockNode:"]}),
+messageSends: ["ifFalse:ifTrue:", "shouldBeInlined", "ifFalse:", ">=", "blockIndex", "index", "context", "increaseBlockIndex", "visitBlockNode:"]}),
 smalltalk.ASTPCNodeVisitor);
 
 smalltalk.addMethod(
@@ -1724,20 +1736,26 @@ selector: "visitSendNode:",
 fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2,$3;
+var $1,$3,$5,$6,$4,$2;
 smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
 $1=_st(self._pc()).__eq(_st(self._context())._pc());
-if(! smalltalk.assert($1)){
-$2=_st(aNode)._shouldBeInlined();
-if(! smalltalk.assert($2)){
-$3=_st(self._blockIndex()).__eq(_st(self._context())._index());
-if(smalltalk.assert($3)){
+$2=(function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(aNode)._shouldBeInlined();
+$4=(function(){
+return smalltalk.withContext(function($ctx3) {
+$5=_st(self._blockIndex()).__eq(_st(self._context())._index());
+$6=(function(){
+return smalltalk.withContext(function($ctx4) {
 self._pc_(_st(self._pc()).__plus((1)));
 self["@currentNode"]=aNode;
-self["@currentNode"];
-};
-};
-};
+return self["@currentNode"];
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)})});
+return _st($5)._ifTrue_($6);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})});
+return _st($3)._ifFalse_($4);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})});
+_st($1)._ifFalse_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
 messageSends: ["visitSendNode:", "ifFalse:", "=", "pc", "context", "shouldBeInlined", "ifTrue:", "blockIndex", "index", "pc:", "+"]}),
 smalltalk.ASTPCNodeVisitor);
@@ -2348,6 +2366,21 @@ return self}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode
 messageSends: ["ifTrue:", "isUnknownVar", "binding", "push:", "at:ifAbsent:", "value", "error:", "ifTrue:ifFalse:", "isInstanceVar", "instVarAt:", "receiver", "context", "localAt:"]}),
 smalltalk.Interpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "xxxDoIt",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._step();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"xxxDoIt",{},smalltalk.Interpreter)})},
+messageSends: ["value", "step"]}),
+smalltalk.Interpreter);
 
 
 smalltalk.addMethod(

+ 55 - 17
js/Compiler-Interpreter.js

@@ -1,5 +1,6 @@
 (function(smalltalk,nil,_st){
 smalltalk.addPackage('Compiler-Interpreter');
+
 smalltalk.addClass('AIBlockClosure', smalltalk.BlockClosure, ['node', 'outerContext'], 'Compiler-Interpreter');
 smalltalk.AIBlockClosure.comment="I am a special `BlockClosure` subclass used by an interpreter to interpret a block node.\x0a\x0aWhile I am polymorphic with `BlockClosure`, some methods such as `#new` will raise interpretation errors. Unlike a `BlockClosure`, my instance are not JavaScript functions.\x0a\x0aEvaluating an instance will result in interpreting the `node` instance variable (instance of `BlockNode`).";
 smalltalk.addMethod(
@@ -2240,16 +2241,27 @@ category: 'visiting',
 fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self._blockIndex()).__gt_eq(_st(self._context())._index());
-if(! smalltalk.assert($1)){
+var $1,$3,$4,$2,$5;
+$1=_st(aNode)._shouldBeInlined();
+$2=(function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(self._blockIndex()).__gt_eq(_st(self._context())._index());
+$4=(function(){
+return smalltalk.withContext(function($ctx3) {
 self._increaseBlockIndex();
-smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
-};
+return smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})});
+return _st($3)._ifFalse_($4);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})});
+$5=(function(){
+return smalltalk.withContext(function($ctx2) {
+return smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitBlockNode_.apply(_st(self), [aNode]);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)})});
+_st($1)._ifFalse_ifTrue_($2,$5);
 return self}, function($ctx1) {$ctx1.fill(self,"visitBlockNode:",{aNode:aNode},smalltalk.ASTPCNodeVisitor)})},
 args: ["aNode"],
-source: "visitBlockNode: aNode\x0a\x09self blockIndex >= self context index ifFalse: [\x0a\x09\x09self increaseBlockIndex.\x0a\x09\x09super visitBlockNode: aNode ]",
-messageSends: ["ifFalse:", ">=", "blockIndex", "index", "context", "increaseBlockIndex", "visitBlockNode:"],
+source: "visitBlockNode: aNode\x0a\x09aNode shouldBeInlined \x0a\x09\x09ifFalse: [\x0a\x09\x09\x09self blockIndex >= self context index ifFalse: [\x0a\x09\x09\x09\x09self increaseBlockIndex.\x0a\x09\x09\x09\x09super visitBlockNode: aNode ] ]\x0a\x09\x09ifTrue: [ super visitBlockNode: aNode ]",
+messageSends: ["ifFalse:ifTrue:", "shouldBeInlined", "ifFalse:", ">=", "blockIndex", "index", "context", "increaseBlockIndex", "visitBlockNode:"],
 referencedClasses: []
 }),
 smalltalk.ASTPCNodeVisitor);
@@ -2277,20 +2289,26 @@ category: 'visiting',
 fn: function (aNode){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2,$3;
+var $1,$3,$5,$6,$4,$2;
 smalltalk.ASTPCNodeVisitor.superclass.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
 $1=_st(self._pc()).__eq(_st(self._context())._pc());
-if(! smalltalk.assert($1)){
-$2=_st(aNode)._shouldBeInlined();
-if(! smalltalk.assert($2)){
-$3=_st(self._blockIndex()).__eq(_st(self._context())._index());
-if(smalltalk.assert($3)){
+$2=(function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(aNode)._shouldBeInlined();
+$4=(function(){
+return smalltalk.withContext(function($ctx3) {
+$5=_st(self._blockIndex()).__eq(_st(self._context())._index());
+$6=(function(){
+return smalltalk.withContext(function($ctx4) {
 self._pc_(_st(self._pc()).__plus((1)));
 self["@currentNode"]=aNode;
-self["@currentNode"];
-};
-};
-};
+return self["@currentNode"];
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)})});
+return _st($5)._ifTrue_($6);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})});
+return _st($3)._ifFalse_($4);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})});
+_st($1)._ifFalse_($2);
 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 blockIndex = self context index ifTrue: [\x0a\x09\x09\x09\x09self pc: self pc + 1.\x0a\x09\x09\x09\x09currentNode := aNode ] ] ]",
@@ -3095,6 +3113,26 @@ referencedClasses: []
 }),
 smalltalk.Interpreter);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "xxxDoIt",
+category: 'xxxDoIt',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._step();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"xxxDoIt",{},smalltalk.Interpreter)})},
+args: [],
+source: "xxxDoIt ^[self step] value",
+messageSends: ["value", "step"],
+referencedClasses: []
+}),
+smalltalk.Interpreter);
 
 
 smalltalk.addMethod(

+ 10 - 3
js/Compiler-Semantic.deploy.js

@@ -1304,7 +1304,7 @@ var self=this;
 function $Set(){return smalltalk.Set||(typeof Set=="undefined"?nil:Set)}
 function $IRSendInliner(){return smalltalk.IRSendInliner||(typeof IRSendInliner=="undefined"?nil:IRSendInliner)}
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2;
+var $1,$2,$3;
 $1=_st(_st(_st(aNode)._receiver())._value()).__eq("super");
 if(smalltalk.assert($1)){
 _st(aNode)._superSend_(true);
@@ -1318,18 +1318,25 @@ _st(_st(self._superSends())._at_(_st(aNode)._selector()))._add_(aNode);
 $2=_st(_st($IRSendInliner())._inlinedSelectors())._includes_(_st(aNode)._selector());
 if(smalltalk.assert($2)){
 _st(aNode)._shouldBeInlined_(true);
+_st(_st(aNode)._arguments())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(each)._isBlockNode();
+if(smalltalk.assert($3)){
+return _st(each)._shouldBeInlined_(true);
+};
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,5)})}));
 _st(_st(aNode)._receiver())._shouldBeAliased_(true);
 };
 };
 _st(self._messageSends())._at_ifAbsentPut_(_st(aNode)._selector(),(function(){
 return smalltalk.withContext(function($ctx2) {
 return _st($Set())._new();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,5)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,7)})}));
 _st(_st(self._messageSends())._at_(_st(aNode)._selector()))._add_(aNode);
 _st(aNode)._index_(_st(_st(self._messageSends())._at_(_st(aNode)._selector()))._size());
 smalltalk.SemanticAnalyzer.superclass.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
-messageSends: ["ifTrue:ifFalse:", "=", "value", "receiver", "superSend:", "value:", "at:ifAbsentPut:", "superSends", "selector", "new", "add:", "at:", "ifTrue:", "includes:", "inlinedSelectors", "shouldBeInlined:", "shouldBeAliased:", "messageSends", "index:", "size", "visitSendNode:"]}),
+messageSends: ["ifTrue:ifFalse:", "=", "value", "receiver", "superSend:", "value:", "at:ifAbsentPut:", "superSends", "selector", "new", "add:", "at:", "ifTrue:", "includes:", "inlinedSelectors", "shouldBeInlined:", "do:", "arguments", "isBlockNode", "shouldBeAliased:", "messageSends", "index:", "size", "visitSendNode:"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(

+ 11 - 4
js/Compiler-Semantic.js

@@ -1736,7 +1736,7 @@ var self=this;
 function $Set(){return smalltalk.Set||(typeof Set=="undefined"?nil:Set)}
 function $IRSendInliner(){return smalltalk.IRSendInliner||(typeof IRSendInliner=="undefined"?nil:IRSendInliner)}
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2;
+var $1,$2,$3;
 $1=_st(_st(_st(aNode)._receiver())._value()).__eq("super");
 if(smalltalk.assert($1)){
 _st(aNode)._superSend_(true);
@@ -1750,20 +1750,27 @@ _st(_st(self._superSends())._at_(_st(aNode)._selector()))._add_(aNode);
 $2=_st(_st($IRSendInliner())._inlinedSelectors())._includes_(_st(aNode)._selector());
 if(smalltalk.assert($2)){
 _st(aNode)._shouldBeInlined_(true);
+_st(_st(aNode)._arguments())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(each)._isBlockNode();
+if(smalltalk.assert($3)){
+return _st(each)._shouldBeInlined_(true);
+};
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,5)})}));
 _st(_st(aNode)._receiver())._shouldBeAliased_(true);
 };
 };
 _st(self._messageSends())._at_ifAbsentPut_(_st(aNode)._selector(),(function(){
 return smalltalk.withContext(function($ctx2) {
 return _st($Set())._new();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,5)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,7)})}));
 _st(_st(self._messageSends())._at_(_st(aNode)._selector()))._add_(aNode);
 _st(aNode)._index_(_st(_st(self._messageSends())._at_(_st(aNode)._selector()))._size());
 smalltalk.SemanticAnalyzer.superclass.fn.prototype._visitSendNode_.apply(_st(self), [aNode]);
 return self}, function($ctx1) {$ctx1.fill(self,"visitSendNode:",{aNode:aNode},smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
-source: "visitSendNode: aNode\x0a\x0a\x09aNode receiver value = 'super'\x0a\x09\x09ifTrue: [\x0a\x09\x09\x09aNode superSend: true.\x0a\x09\x09\x09aNode receiver value: 'self'.\x0a\x09\x09\x09self superSends at: aNode selector ifAbsentPut: [ Set new ].\x0a\x09\x09\x09(self superSends at: aNode selector) add: aNode ]\x0a\x09\x09\x0a\x09\x09ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [\x0a\x09\x09\x09aNode shouldBeInlined: true.\x0a\x09\x09\x09aNode receiver shouldBeAliased: true ] ].\x0a\x0a\x09self messageSends at: aNode selector ifAbsentPut: [ Set new ].\x0a\x09(self messageSends at: aNode selector) add: aNode.\x0a\x0a\x09aNode index: (self messageSends at: aNode selector) size.\x0a\x0a\x09super visitSendNode: aNode",
-messageSends: ["ifTrue:ifFalse:", "=", "value", "receiver", "superSend:", "value:", "at:ifAbsentPut:", "superSends", "selector", "new", "add:", "at:", "ifTrue:", "includes:", "inlinedSelectors", "shouldBeInlined:", "shouldBeAliased:", "messageSends", "index:", "size", "visitSendNode:"],
+source: "visitSendNode: aNode\x0a\x0a\x09aNode receiver value = 'super'\x0a\x09\x09ifTrue: [\x0a\x09\x09\x09aNode superSend: true.\x0a\x09\x09\x09aNode receiver value: 'self'.\x0a\x09\x09\x09self superSends at: aNode selector ifAbsentPut: [ Set new ].\x0a\x09\x09\x09(self superSends at: aNode selector) add: aNode ]\x0a\x09\x09\x0a\x09\x09ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [\x0a\x09\x09\x09aNode shouldBeInlined: true.\x0a\x09\x09\x09aNode arguments do: [ :each |\x0a\x09\x09\x09\x09each isBlockNode ifTrue: [ each shouldBeInlined: true ] ].\x0a\x09\x09\x09aNode receiver shouldBeAliased: true ] ].\x0a\x0a\x09self messageSends at: aNode selector ifAbsentPut: [ Set new ].\x0a\x09(self messageSends at: aNode selector) add: aNode.\x0a\x0a\x09aNode index: (self messageSends at: aNode selector) size.\x0a\x0a\x09super visitSendNode: aNode",
+messageSends: ["ifTrue:ifFalse:", "=", "value", "receiver", "superSend:", "value:", "at:ifAbsentPut:", "superSends", "selector", "new", "add:", "at:", "ifTrue:", "includes:", "inlinedSelectors", "shouldBeInlined:", "do:", "arguments", "isBlockNode", "shouldBeAliased:", "messageSends", "index:", "size", "visitSendNode:"],
 referencedClasses: ["Set", "IRSendInliner"]
 }),
 smalltalk.SemanticAnalyzer);

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

@@ -549,6 +549,18 @@ return self}, function($ctx1) {$ctx1.fill(self,"testBinarySend",{},smalltalk.Int
 messageSends: ["assert:equals:", "interpret:"]}),
 smalltalk.InterpreterTest);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testBlockEvaluation",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ true ifTrue: [ 1 asString, '2' ]"),"12");
+self._assert_equals_(self._interpret_("foo ^ #(1 2 3) collect: [ :each | each + 3 ]"),[(4), (5), (6)]);
+return self}, function($ctx1) {$ctx1.fill(self,"testBlockEvaluation",{},smalltalk.InterpreterTest)})},
+messageSends: ["assert:equals:", "interpret:"]}),
+smalltalk.InterpreterTest);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "testBlockLiteral",

+ 17 - 0
js/Compiler-Tests.js

@@ -724,6 +724,23 @@ referencedClasses: []
 }),
 smalltalk.InterpreterTest);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testBlockEvaluation",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._assert_equals_(self._interpret_("foo ^ true ifTrue: [ 1 asString, '2' ]"),"12");
+self._assert_equals_(self._interpret_("foo ^ #(1 2 3) collect: [ :each | each + 3 ]"),[(4), (5), (6)]);
+return self}, function($ctx1) {$ctx1.fill(self,"testBlockEvaluation",{},smalltalk.InterpreterTest)})},
+args: [],
+source: "testBlockEvaluation\x0a\x09self assert: (self interpret: 'foo ^ true ifTrue: [ 1 asString, ''2'' ]') equals: '12'.\x0a\x09self assert: (self interpret: 'foo ^ #(1 2 3) collect: [ :each | each + 3 ]') equals: #(4 5 6).",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.InterpreterTest);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "testBlockLiteral",

+ 11 - 3
st/Compiler-Interpreter.st

@@ -814,9 +814,12 @@ useInlinings: aBoolean
 !ASTPCNodeVisitor methodsFor: 'visiting'!
 
 visitBlockNode: aNode
-	self blockIndex >= self context index ifFalse: [
-		self increaseBlockIndex.
-		super visitBlockNode: aNode ]
+	aNode shouldBeInlined 
+		ifFalse: [
+			self blockIndex >= self context index ifFalse: [
+				self increaseBlockIndex.
+				super visitBlockNode: aNode ] ]
+		ifTrue: [ super visitBlockNode: aNode ]
 !
 
 visitJSStatementNode: aNode
@@ -1109,6 +1112,11 @@ visitVariableNode: aNode
 		ifFalse: [ self context localAt: aNode value ])
 ! !
 
+!Interpreter methodsFor: 'xxxDoIt'!
+
+xxxDoIt ^[self step] value
+! !
+
 !Node methodsFor: '*Compiler-Interpreter'!
 
 interpreter: anInterpreter continue: aBlock

+ 2 - 0
st/Compiler-Semantic.st

@@ -566,6 +566,8 @@ visitSendNode: aNode
 		
 		ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [
 			aNode shouldBeInlined: true.
+			aNode arguments do: [ :each |
+				each isBlockNode ifTrue: [ each shouldBeInlined: true ] ].
 			aNode receiver shouldBeAliased: true ] ].
 
 	self messageSends at: aNode selector ifAbsentPut: [ Set new ].

+ 5 - 0
st/Compiler-Tests.st

@@ -331,6 +331,11 @@ testBinarySend
 	self assert: (self interpret: 'foo ^ 2+3+4') equals: 9
 !
 
+testBlockEvaluation
+	self assert: (self interpret: 'foo ^ true ifTrue: [ 1 asString, ''2'' ]') equals: '12'.
+	self assert: (self interpret: 'foo ^ #(1 2 3) collect: [ :each | each + 3 ]') equals: #(4 5 6).
+!
+
 testBlockLiteral
 	self assert: (self interpret: 'foo ^ true ifTrue: [ 1 ] ifFalse: [ 2 ]') equals: 1.
 	self assert: (self interpret: 'foo true ifTrue: [ ^ 1 ] ifFalse: [ 2 ]') equals: 1.