Prechádzať zdrojové kódy

ASTInterpreter now handles assignments

Nicolas Petton 11 rokov pred
rodič
commit
c224b32992

+ 64 - 0
js/Compiler-Interpreter.deploy.js

@@ -23,6 +23,21 @@ return self}, function($ctx1) {$ctx1.fill(self,"initializeFromMethodContext:",{a
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+"_localAt_",
+smalltalk.method({
+selector: "localAt:",
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._locals())._at_ifAbsent_(aString,(function(){
+return smalltalk.withContext(function($ctx2) {
return nil;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"localAt:",{aString:aString}, smalltalk.AIContext)})}
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 "_localAt_put_",
 smalltalk.method({
@@ -174,6 +189,24 @@ smalltalk.AIContext.klass);
 
 
 smalltalk.addClass('ASTInterpreter', smalltalk.NodeVisitor, ['currentNode', 'context', 'shouldReturn'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+"_assign_to_",
+smalltalk.method({
+selector: "assign:to:",
+fn: function (aNode,anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(self)._context())._receiver())._instVarAt_put_(_st(aNode)._value(),anObject);
+} else {
+$1=_st(_st(self)._context())._localAt_put_(_st(aNode)._value(),anObject);
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"assign:to:",{aNode:aNode,anObject:anObject}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_context",
 smalltalk.method({
@@ -291,6 +324,19 @@ return $1;
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_visitAssignmentNode_",
+smalltalk.method({
+selector: "visitAssignmentNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self)._assign_to_(_st(aNode)._left(),_st(self)._interpretNode_(_st(aNode)._right()));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_visitBlockNode_",
 smalltalk.method({
@@ -420,5 +466,23 @@ return $1;
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_visitVariableNode_",
+smalltalk.method({
+selector: "visitVariableNode:",
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(self)._context())._receiver())._instVarAt_(_st(aNode)._value());
+} else {
+$1=_st(_st(self)._context())._localAt_(_st(aNode)._value());
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})}
+}),
+smalltalk.ASTInterpreter);
+
 
 

+ 84 - 0
js/Compiler-Interpreter.js

@@ -28,6 +28,26 @@ referencedClasses: []
 }),
 smalltalk.AIContext);
 
+smalltalk.addMethod(
+"_localAt_",
+smalltalk.method({
+selector: "localAt:",
+category: 'accessing',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._locals())._at_ifAbsent_(aString,(function(){
+return smalltalk.withContext(function($ctx2) {
return nil;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"localAt:",{aString:aString}, smalltalk.AIContext)})},
+args: ["aString"],
+source: "localAt: aString\x0a\x09^ self locals at: aString ifAbsent: [ nil ]",
+messageSends: ["at:ifAbsent:", "locals"],
+referencedClasses: []
+}),
+smalltalk.AIContext);
+
 smalltalk.addMethod(
 "_localAt_put_",
 smalltalk.method({
@@ -234,6 +254,29 @@ smalltalk.AIContext.klass);
 
 
 smalltalk.addClass('ASTInterpreter', smalltalk.NodeVisitor, ['currentNode', 'context', 'shouldReturn'], 'Compiler-Interpreter');
+smalltalk.addMethod(
+"_assign_to_",
+smalltalk.method({
+selector: "assign:to:",
+category: 'interpreting',
+fn: function (aNode,anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(self)._context())._receiver())._instVarAt_put_(_st(aNode)._value(),anObject);
+} else {
+$1=_st(_st(self)._context())._localAt_put_(_st(aNode)._value(),anObject);
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"assign:to:",{aNode:aNode,anObject:anObject}, smalltalk.ASTInterpreter)})},
+args: ["aNode", "anObject"],
+source: "assign: aNode to: anObject\x0a\x09^ aNode binding isInstanceVar \x0a    \x09ifTrue: [ self context receiver instVarAt: aNode value put: anObject ]\x0a      \x09ifFalse: [ self context localAt: aNode value put: anObject ]",
+messageSends: ["ifTrue:ifFalse:", "instVarAt:put:", "value", "receiver", "context", "localAt:put:", "isInstanceVar", "binding"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_context",
 smalltalk.method({
@@ -386,6 +429,24 @@ referencedClasses: ["Message"]
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_visitAssignmentNode_",
+smalltalk.method({
+selector: "visitAssignmentNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self)._assign_to_(_st(aNode)._left(),_st(self)._interpretNode_(_st(aNode)._right()));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"visitAssignmentNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+args: ["aNode"],
+source: "visitAssignmentNode: aNode\x0a\x09^ self assign: aNode left to: (self interpretNode: aNode right)",
+messageSends: ["assign:to:", "left", "interpretNode:", "right"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
 smalltalk.addMethod(
 "_visitBlockNode_",
 smalltalk.method({
@@ -555,5 +616,28 @@ referencedClasses: []
 }),
 smalltalk.ASTInterpreter);
 
+smalltalk.addMethod(
+"_visitVariableNode_",
+smalltalk.method({
+selector: "visitVariableNode:",
+category: 'visiting',
+fn: function (aNode){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=_st(_st(aNode)._binding())._isInstanceVar();
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(self)._context())._receiver())._instVarAt_(_st(aNode)._value());
+} else {
+$1=_st(_st(self)._context())._localAt_(_st(aNode)._value());
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode}, smalltalk.ASTInterpreter)})},
+args: ["aNode"],
+source: "visitVariableNode: aNode\x0a\x09^ aNode binding isInstanceVar\x0a    \x09ifTrue: [ self context receiver instVarAt: aNode value ]\x0a      \x09ifFalse: [ self context localAt: aNode value ]",
+messageSends: ["ifTrue:ifFalse:", "instVarAt:", "value", "receiver", "context", "localAt:", "isInstanceVar", "binding"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreter);
+
 
 

+ 55 - 6
js/Compiler-Tests.deploy.js

@@ -28,23 +28,50 @@ return $1;
 smalltalk.ASTInterpreterTest);
 
 smalltalk.addMethod(
-"_interpret_withArguments_",
+"_interpret_receiver_withArguments_",
 smalltalk.method({
-selector: "interpret:withArguments:",
-fn: function (aString,aDictionary){
+selector: "interpret:receiver:withArguments:",
+fn: function (aString,anObject,aDictionary){
 var self=this;
 var ctx;
 return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 ctx=_st((smalltalk.AIContext || AIContext))._new();
+_st(ctx)._receiver_(anObject);
 _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)})}));
-$2=_st((smalltalk.ASTInterpreter || ASTInterpreter))._new();
+$2=_st(self)._interpreter();
 _st($2)._context_(ctx);
-$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,(smalltalk.Object || Object)))._nodes())._first());
+$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,_st(anObject)._class()))._nodes())._first());
 $1=$3;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpret:withArguments:",{aString:aString,aDictionary:aDictionary,ctx:ctx}, smalltalk.ASTInterpreterTest)})}
+}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_interpret_withArguments_",
+smalltalk.method({
+selector: "interpret:withArguments:",
+fn: function (aString,aDictionary){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self)._interpret_receiver_withArguments_(aString,_st((smalltalk.Object || Object))._new(),aDictionary);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpret:withArguments:",{aString:aString,aDictionary:aDictionary}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_interpreter",
+smalltalk.method({
+selector: "interpreter",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st((smalltalk.ASTInterpreter || ASTInterpreter))._new();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpreter",{}, smalltalk.ASTInterpreterTest)})}
 }),
 smalltalk.ASTInterpreterTest);
 
@@ -121,6 +148,28 @@ return self}, function($ctx1) {$ctx1.fill(self,"testInlinedJSStatement",{}, smal
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testInstVarAssignment",
+smalltalk.method({
+selector: "testInstVarAssignment",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_receiver_withArguments_("foo: anInteger x := anInteger. ^ x",_st((smalltalk.Point || Point))._new(),smalltalk.HashedCollection._fromPairs_([_st("anInteger").__minus_gt((2))])),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAssignment",{}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_testTempAssignment",
+smalltalk.method({
+selector: "testTempAssignment",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo | a | a := 2. ^ a"),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testTempAssignment",{}, smalltalk.ASTInterpreterTest)})}
+}),
+smalltalk.ASTInterpreterTest);
+
 
 
 smalltalk.addClass('CodeGeneratorTest', smalltalk.TestCase, ['receiver'], 'Compiler-Tests');

+ 78 - 9
js/Compiler-Tests.js

@@ -38,28 +38,65 @@ referencedClasses: ["Dictionary"]
 smalltalk.ASTInterpreterTest);
 
 smalltalk.addMethod(
-"_interpret_withArguments_",
+"_interpret_receiver_withArguments_",
 smalltalk.method({
-selector: "interpret:withArguments:",
+selector: "interpret:receiver:withArguments:",
 category: 'accessing',
-fn: function (aString,aDictionary){
+fn: function (aString,anObject,aDictionary){
 var self=this;
 var ctx;
 return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 ctx=_st((smalltalk.AIContext || AIContext))._new();
+_st(ctx)._receiver_(anObject);
 _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)})}));
-$2=_st((smalltalk.ASTInterpreter || ASTInterpreter))._new();
+$2=_st(self)._interpreter();
 _st($2)._context_(ctx);
-$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,(smalltalk.Object || Object)))._nodes())._first());
+$3=_st($2)._interpret_(_st(_st(_st(self)._parse_forClass_(aString,_st(anObject)._class()))._nodes())._first());
 $1=$3;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"interpret:withArguments:",{aString:aString,aDictionary:aDictionary,ctx:ctx}, smalltalk.ASTInterpreterTest)})},
+}, function($ctx1) {$ctx1.fill(self,"interpret:receiver:withArguments:",{aString:aString,anObject:anObject,aDictionary:aDictionary,ctx:ctx}, smalltalk.ASTInterpreterTest)})},
+args: ["aString", "anObject", "aDictionary"],
+source: "interpret: aString receiver: anObject withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a    \x0a    | ctx |\x0a    \x0a    ctx := AIContext new.\x0a    ctx receiver: anObject.\x0a    aDictionary keysAndValuesDo: [ :key :value |\x0a    \x09ctx localAt: key put: value ].\x0a    \x0a    ^ self interpreter\x0a    \x09context: ctx;\x0a    \x09interpret: (self parse: aString forClass: anObject class) \x0a        \x09nodes first",
+messageSends: ["new", "receiver:", "keysAndValuesDo:", "localAt:put:", "context:", "interpreter", "interpret:", "first", "nodes", "parse:forClass:", "class"],
+referencedClasses: ["AIContext"]
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_interpret_withArguments_",
+smalltalk.method({
+selector: "interpret:withArguments:",
+category: 'accessing',
+fn: function (aString,aDictionary){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self)._interpret_receiver_withArguments_(aString,_st((smalltalk.Object || Object))._new(),aDictionary);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpret:withArguments:",{aString:aString,aDictionary:aDictionary}, smalltalk.ASTInterpreterTest)})},
 args: ["aString", "aDictionary"],
-source: "interpret: aString withArguments: aDictionary\x0a\x09\x22The food is a methodNode. Interpret the sequenceNode only\x22\x0a    \x0a    | ctx |\x0a    \x0a    ctx := AIContext new.\x0a    aDictionary keysAndValuesDo: [ :key :value |\x0a    \x09ctx localAt: key put: value ].\x0a    \x0a    ^ ASTInterpreter new\x0a    \x09context: ctx;\x0a    \x09interpret: (self parse: aString forClass: Object) \x0a        \x09nodes first",
-messageSends: ["new", "keysAndValuesDo:", "localAt:put:", "context:", "interpret:", "first", "nodes", "parse:forClass:"],
-referencedClasses: ["AIContext", "ASTInterpreter", "Object"]
+source: "interpret: aString withArguments: aDictionary\x0a\x09^ self \x0a    \x09interpret: aString \x0a        receiver: Object new\x0a        withArguments: aDictionary",
+messageSends: ["interpret:receiver:withArguments:", "new"],
+referencedClasses: ["Object"]
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_interpreter",
+smalltalk.method({
+selector: "interpreter",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st((smalltalk.ASTInterpreter || ASTInterpreter))._new();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"interpreter",{}, smalltalk.ASTInterpreterTest)})},
+args: [],
+source: "interpreter\x0a\x09^ ASTInterpreter new",
+messageSends: ["new"],
+referencedClasses: ["ASTInterpreter"]
 }),
 smalltalk.ASTInterpreterTest);
 
@@ -166,6 +203,38 @@ referencedClasses: []
 }),
 smalltalk.ASTInterpreterTest);
 
+smalltalk.addMethod(
+"_testInstVarAssignment",
+smalltalk.method({
+selector: "testInstVarAssignment",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_receiver_withArguments_("foo: anInteger x := anInteger. ^ x",_st((smalltalk.Point || Point))._new(),smalltalk.HashedCollection._fromPairs_([_st("anInteger").__minus_gt((2))])),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testInstVarAssignment",{}, smalltalk.ASTInterpreterTest)})},
+args: [],
+source: "testInstVarAssignment\x0a\x09self \x0a    \x09assert: (self \x0a    \x09\x09interpret: 'foo: anInteger x := anInteger. ^ x'\x0a        \x09receiver: Point new\x0a        \x09withArguments: #{'anInteger' -> 2})\x0a        equals: 2",
+messageSends: ["assert:equals:", "interpret:receiver:withArguments:", "new", "->"],
+referencedClasses: ["Point"]
+}),
+smalltalk.ASTInterpreterTest);
+
+smalltalk.addMethod(
+"_testTempAssignment",
+smalltalk.method({
+selector: "testTempAssignment",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_(_st(self)._interpret_("foo | a | a := 2. ^ a"),(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testTempAssignment",{}, smalltalk.ASTInterpreterTest)})},
+args: [],
+source: "testTempAssignment\x0a\x09self assert: (self interpret: 'foo | a | a := 2. ^ a') equals: 2",
+messageSends: ["assert:equals:", "interpret:"],
+referencedClasses: []
+}),
+smalltalk.ASTInterpreterTest);
+
 
 
 smalltalk.addClass('CodeGeneratorTest', smalltalk.TestCase, ['receiver'], 'Compiler-Tests');

+ 20 - 0
st/Compiler-Interpreter.st

@@ -15,6 +15,10 @@ initializeFromMethodContext: aMethodContext
     	self locals at: key put: value ]
 !
 
+localAt: aString
+	^ self locals at: aString ifAbsent: [ nil ]
+!
+
 localAt: aString put: anObject
 	self locals at: aString put: anObject
 !
@@ -86,6 +90,12 @@ initialize
 
 !ASTInterpreter methodsFor: 'interpreting'!
 
+assign: aNode to: anObject
+	^ aNode binding isInstanceVar 
+    	ifTrue: [ self context receiver instVarAt: aNode value put: anObject ]
+      	ifFalse: [ self context localAt: aNode value put: anObject ]
+!
+
 eval: aString
 	"Evaluate aString as JS source inside an JS function. 
     aString is not sandboxed."
@@ -127,6 +137,10 @@ messageFromSendNode: aSendNode
 
 !ASTInterpreter methodsFor: 'visiting'!
 
+visitAssignmentNode: aNode
+	^ self assign: aNode left to: (self interpretNode: aNode right)
+!
+
 visitBlockNode: aNode
     ^ [ self interpretNode: aNode nodes first ]
 !
@@ -178,5 +192,11 @@ visitSequenceNode: aNode
 
 visitValueNode: aNode
 	^ aNode value
+!
+
+visitVariableNode: aNode
+	^ aNode binding isInstanceVar
+    	ifTrue: [ self context receiver instVarAt: aNode value ]
+      	ifFalse: [ self context localAt: aNode value ]
 ! !
 

+ 28 - 3
st/Compiler-Tests.st

@@ -16,21 +16,33 @@ interpret: aString
         withArguments: Dictionary new
 !
 
-interpret: aString withArguments: aDictionary
+interpret: aString receiver: anObject withArguments: aDictionary
 	"The food is a methodNode. Interpret the sequenceNode only"
     
     | ctx |
     
     ctx := AIContext new.
+    ctx receiver: anObject.
     aDictionary keysAndValuesDo: [ :key :value |
     	ctx localAt: key put: value ].
     
-    ^ ASTInterpreter new
+    ^ self interpreter
     	context: ctx;
-    	interpret: (self parse: aString forClass: Object) 
+    	interpret: (self parse: aString forClass: anObject class) 
         	nodes first
 !
 
+interpret: aString withArguments: aDictionary
+	^ self 
+    	interpret: aString 
+        receiver: Object new
+        withArguments: aDictionary
+!
+
+interpreter
+	^ ASTInterpreter new
+!
+
 parse: aString
 	^ Smalltalk current parse: aString
 !
@@ -63,6 +75,19 @@ testInlinedJSStatement
     		interpret: 'foo: anInteger <return 2 + anInteger>' 
         	withArguments: #{ 'anInteger' -> 3}) 
 		equals: 5
+!
+
+testInstVarAssignment
+	self 
+    	assert: (self 
+    		interpret: 'foo: anInteger x := anInteger. ^ x'
+        	receiver: Point new
+        	withArguments: #{'anInteger' -> 2})
+        equals: 2
+!
+
+testTempAssignment
+	self assert: (self interpret: 'foo | a | a := 2. ^ a') equals: 2
 ! !
 
 TestCase subclass: #CodeGeneratorTest