Browse Source

Augmented operand compiled correctly.

Herbert Vojčík 8 years ago
parent
commit
290a463312
8 changed files with 286 additions and 19 deletions
  1. 55 2
      src/Compiler-AST.js
  2. 11 0
      src/Compiler-AST.st
  3. 122 16
      src/Compiler-IR.js
  4. 22 1
      src/Compiler-IR.st
  5. 32 0
      src/Compiler-Semantic.js
  6. 5 0
      src/Compiler-Semantic.st
  7. 32 0
      src/Compiler-Tests.js
  8. 7 0
      src/Compiler-Tests.st

+ 55 - 2
src/Compiler-AST.js

@@ -1911,6 +1911,29 @@ $globals.QuasiSendNode);
 
 
 $core.addClass('BranchSendNode', $globals.QuasiSendNode, [], 'Compiler-AST');
+$core.addMethod(
+$core.method({
+selector: "accept:",
+protocol: 'visiting',
+fn: function (aVisitor){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(aVisitor)._visitBranchSendNode_(self);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"accept:",{aVisitor:aVisitor},$globals.BranchSendNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aVisitor"],
+source: "accept: aVisitor\x0a\x09^ aVisitor visitBranchSendNode: self",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["visitBranchSendNode:"]
+}),
+$globals.BranchSendNode);
+
 $core.addMethod(
 $core.method({
 selector: "valueForReceiver:",
@@ -1920,7 +1943,14 @@ var self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
+var $1,$receiver;
 self._nodes_([$recv($recv(self._nodes())._first())._valueForReceiver_(anObject)]);
+$1=self._receiver();
+if(($receiver = $1) == null || $receiver.isNil){
+self._receiver_(anObject);
+} else {
+$1;
+};
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"valueForReceiver:",{anObject:anObject},$globals.BranchSendNode)});
@@ -1928,10 +1958,10 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anObject"],
-source: "valueForReceiver: anObject\x0a\x09self nodes: {self nodes first valueForReceiver: anObject}.\x0a\x09^ self",
+source: "valueForReceiver: anObject\x0a\x09self nodes: {self nodes first valueForReceiver: anObject}.\x0a\x09self receiver ifNil: [ self receiver: anObject ].\x0a\x09^ self",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["nodes:", "valueForReceiver:", "first", "nodes"]
+messageSends: ["nodes:", "valueForReceiver:", "first", "nodes", "ifNil:", "receiver", "receiver:"]
 }),
 $globals.BranchSendNode);
 
@@ -3348,6 +3378,29 @@ messageSends: ["visitSequenceNode:"]
 }),
 $globals.NodeVisitor);
 
+$core.addMethod(
+$core.method({
+selector: "visitBranchSendNode:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return self._visitNode_(aNode);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitBranchSendNode:",{aNode:aNode},$globals.NodeVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitBranchSendNode: aNode\x0a\x09^ self visitNode: aNode",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["visitNode:"]
+}),
+$globals.NodeVisitor);
+
 $core.addMethod(
 $core.method({
 selector: "visitCascadeNode:",

+ 11 - 0
src/Compiler-AST.st

@@ -454,9 +454,16 @@ QuasiSendNode subclass: #BranchSendNode
 
 valueForReceiver: anObject
 	self nodes: {self nodes first valueForReceiver: anObject}.
+	self receiver ifNil: [ self receiver: anObject ].
 	^ self
 ! !
 
+!BranchSendNode methodsFor: 'visiting'!
+
+accept: aVisitor
+	^ aVisitor visitBranchSendNode: self
+! !
+
 QuasiSendNode subclass: #CascadeNode
 	instanceVariableNames: ''
 	package: 'Compiler-AST'!
@@ -822,6 +829,10 @@ visitBlockSequenceNode: aNode
 	^ self visitSequenceNode: aNode
 !
 
+visitBranchSendNode: aNode
+	^ self visitNode: aNode
+!
+
 visitCascadeNode: aNode
 	^ self visitNode: aNode
 !

+ 122 - 16
src/Compiler-IR.js

@@ -4,7 +4,7 @@ $core.addPackage('Compiler-IR');
 $core.packages["Compiler-IR"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Compiler-IR"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
-$core.addClass('IRASTTranslator', $globals.NodeVisitor, ['source', 'theClass', 'method', 'sequence', 'nextAlias'], 'Compiler-IR');
+$core.addClass('IRASTTranslator', $globals.NodeVisitor, ['source', 'theClass', 'method', 'sequence', 'nextAlias', 'nodeAliases'], 'Compiler-IR');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.IRASTTranslator.comment="I am the AST (abstract syntax tree) visitor responsible for building the intermediate representation graph.";
 //>>excludeEnd("ide");
@@ -21,7 +21,7 @@ function $IRAssignment(){return $globals.IRAssignment||(typeof IRAssignment=="un
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2,$3,$5,$4,$6,$7,$9,$8;
+var $1,$2,$3,$4,$6,$5,$7,$8,$10,$9,$receiver;
 $1=$recv(aNode)._isImmutable();
 if($core.assert($1)){
 $2=self._visit_(aNode);
@@ -30,33 +30,42 @@ $ctx1.sendIdx["visit:"]=1;
 //>>excludeEnd("ctx");
 return $2;
 };
-$3=$recv($IRVariable())._new();
+$3=self._nodeAliasFor_(aNode);
+if(($receiver = $3) == null || $receiver.isNil){
+$3;
+} else {
+var aliasVar;
+aliasVar=$receiver;
+return aliasVar;
+};
+$4=$recv($IRVariable())._new();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["new"]=1;
 //>>excludeEnd("ctx");
-$5=$recv($AliasVar())._new();
+$6=$recv($AliasVar())._new();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["new"]=2;
 //>>excludeEnd("ctx");
-$4=$recv($5)._name_("$".__comma(self._nextAlias()));
-$recv($3)._variable_($4);
-$6=$recv($3)._yourself();
+$5=$recv($6)._name_("$".__comma(self._nextAlias()));
+$recv($4)._variable_($5);
+$7=$recv($4)._yourself();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["yourself"]=1;
 //>>excludeEnd("ctx");
-variable=$6;
-$7=self._sequence();
-$9=$recv($IRAssignment())._new();
-$recv($9)._add_(variable);
+variable=$7;
+self._node_aliased_(aNode,variable);
+$8=self._sequence();
+$10=$recv($IRAssignment())._new();
+$recv($10)._add_(variable);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["add:"]=2;
 //>>excludeEnd("ctx");
-$recv($9)._add_(self._visit_(aNode));
+$recv($10)._add_(self._visit_(aNode));
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["add:"]=3;
 //>>excludeEnd("ctx");
-$8=$recv($9)._yourself();
-$recv($7)._add_($8);
+$9=$recv($10)._yourself();
+$recv($8)._add_($9);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.sendIdx["add:"]=1;
 //>>excludeEnd("ctx");
@@ -68,10 +77,10 @@ return variable;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aNode"],
-source: "alias: aNode\x0a\x09| variable |\x0a\x0a\x09aNode isImmutable ifTrue: [ ^ self visit: aNode ].\x0a\x0a\x09variable := IRVariable new\x0a\x09\x09variable: (AliasVar new name: '$', self nextAlias);\x0a\x09\x09yourself.\x0a\x0a\x09self sequence add: (IRAssignment new\x0a\x09\x09add: variable;\x0a\x09\x09add: (self visit: aNode);\x0a\x09\x09yourself).\x0a\x0a\x09self method internalVariables add: variable.\x0a\x0a\x09^ variable",
+source: "alias: aNode\x0a\x09| variable |\x0a\x0a\x09aNode isImmutable ifTrue: [ ^ self visit: aNode ].\x0a\x09\x0a\x09(self nodeAliasFor: aNode) ifNotNil: [ :aliasVar | ^ aliasVar ].\x0a\x0a\x09variable := IRVariable new\x0a\x09\x09variable: (AliasVar new name: '$', self nextAlias);\x0a\x09\x09yourself.\x0a\x09self node: aNode aliased: variable.\x0a\x0a\x09self sequence add: (IRAssignment new\x0a\x09\x09add: variable;\x0a\x09\x09add: (self visit: aNode);\x0a\x09\x09yourself).\x0a\x0a\x09self method internalVariables add: variable.\x0a\x0a\x09^ variable",
 referencedClasses: ["IRVariable", "AliasVar", "IRAssignment"],
 //>>excludeEnd("ide");
-messageSends: ["ifTrue:", "isImmutable", "visit:", "variable:", "new", "name:", ",", "nextAlias", "yourself", "add:", "sequence", "internalVariables", "method"]
+messageSends: ["ifTrue:", "isImmutable", "visit:", "ifNotNil:", "nodeAliasFor:", "variable:", "new", "name:", ",", "nextAlias", "yourself", "node:aliased:", "add:", "sequence", "internalVariables", "method"]
 }),
 $globals.IRASTTranslator);
 
@@ -204,6 +213,71 @@ messageSends: ["ifNil:", "+", "asString"]
 }),
 $globals.IRASTTranslator);
 
+$core.addMethod(
+$core.method({
+selector: "node:aliased:",
+protocol: 'accessing',
+fn: function (aNode,anAliasVar){
+var self=this;
+function $Dictionary(){return $globals.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$receiver;
+$1=self["@nodeAliases"];
+if(($receiver = $1) == null || $receiver.isNil){
+self["@nodeAliases"]=$recv($Dictionary())._new();
+self["@nodeAliases"];
+} else {
+$1;
+};
+$recv(self["@nodeAliases"])._at_put_(aNode,anAliasVar);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"node:aliased:",{aNode:aNode,anAliasVar:anAliasVar},$globals.IRASTTranslator)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode", "anAliasVar"],
+source: "node: aNode aliased: anAliasVar\x0a\x09nodeAliases ifNil: [ nodeAliases := Dictionary new ].\x0a\x09nodeAliases at: aNode put: anAliasVar",
+referencedClasses: ["Dictionary"],
+//>>excludeEnd("ide");
+messageSends: ["ifNil:", "new", "at:put:"]
+}),
+$globals.IRASTTranslator);
+
+$core.addMethod(
+$core.method({
+selector: "nodeAliasFor:",
+protocol: 'accessing',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$receiver;
+$1=self["@nodeAliases"];
+if(($receiver = $1) == null || $receiver.isNil){
+return nil;
+} else {
+return $recv(self["@nodeAliases"])._at_ifAbsent_(aNode,(function(){
+
+}));
+};
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"nodeAliasFor:",{aNode:aNode},$globals.IRASTTranslator)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "nodeAliasFor: aNode\x0a\x09nodeAliases\x0a\x09\x09ifNil: [ ^ nil ]\x0a\x09\x09ifNotNil: [ ^ nodeAliases at: aNode ifAbsent: [] ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifNil:ifNotNil:", "at:ifAbsent:"]
+}),
+$globals.IRASTTranslator);
+
 $core.addMethod(
 $core.method({
 selector: "sequence",
@@ -549,6 +623,38 @@ messageSends: ["withSequence:do:", "new", "ifNotEmpty:", "nodes", "do:", "allBut
 }),
 $globals.IRASTTranslator);
 
+$core.addMethod(
+$core.method({
+selector: "visitBranchSendNode:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($recv(aNode)._nodes())._do_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(self._sequence())._add_(self._visit_(each));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self._visitOrAlias_($recv(aNode)._receiver());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitBranchSendNode:",{aNode:aNode},$globals.IRASTTranslator)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitBranchSendNode: aNode\x0a\x09aNode nodes do: [ :each |\x0a\x09\x09self sequence add: (self visit: each) ].\x0a\x0a\x09^ self visitOrAlias: aNode receiver",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["do:", "nodes", "add:", "sequence", "visit:", "visitOrAlias:", "receiver"]
+}),
+$globals.IRASTTranslator);
+
 $core.addMethod(
 $core.method({
 selector: "visitCascadeNode:",

+ 22 - 1
src/Compiler-IR.st

@@ -1,6 +1,6 @@
 Smalltalk createPackage: 'Compiler-IR'!
 NodeVisitor subclass: #IRASTTranslator
-	instanceVariableNames: 'source theClass method sequence nextAlias'
+	instanceVariableNames: 'source theClass method sequence nextAlias nodeAliases'
 	package: 'Compiler-IR'!
 !IRASTTranslator commentStamp!
 I am the AST (abstract syntax tree) visitor responsible for building the intermediate representation graph.!
@@ -21,6 +21,17 @@ nextAlias
 	^ nextAlias asString
 !
 
+node: aNode aliased: anAliasVar
+	nodeAliases ifNil: [ nodeAliases := Dictionary new ].
+	nodeAliases at: aNode put: anAliasVar
+!
+
+nodeAliasFor: aNode
+	nodeAliases
+		ifNil: [ ^ nil ]
+		ifNotNil: [ ^ nodeAliases at: aNode ifAbsent: [] ]
+!
+
 sequence
 	^ sequence
 !
@@ -60,10 +71,13 @@ alias: aNode
 	| variable |
 
 	aNode isImmutable ifTrue: [ ^ self visit: aNode ].
+	
+	(self nodeAliasFor: aNode) ifNotNil: [ :aliasVar | ^ aliasVar ].
 
 	variable := IRVariable new
 		variable: (AliasVar new name: '$', self nextAlias);
 		yourself.
+	self node: aNode aliased: variable.
 
 	self sequence add: (IRAssignment new
 		add: variable;
@@ -137,6 +151,13 @@ visitBlockSequenceNode: aNode
 					ifTrue: [ self sequence add: (self visitOrAlias: aNode nodes last) ] ]]
 !
 
+visitBranchSendNode: aNode
+	aNode nodes do: [ :each |
+		self sequence add: (self visit: each) ].
+
+	^ self visitOrAlias: aNode receiver
+!
+
 visitCascadeNode: aNode
 	aNode nodes inject: aNode receiver into: [ :previous :each |
 		| receiver |

+ 32 - 0
src/Compiler-Semantic.js

@@ -2260,6 +2260,38 @@ messageSends: ["pushScope:", "newBlockScope", "scope:", "node:", "blockIndex:",
 }),
 $globals.SemanticAnalyzer);
 
+$core.addMethod(
+$core.method({
+selector: "visitBranchSendNode:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($recv(aNode)._receiver())._shouldBeAliased_(true);
+(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true, 
+//>>excludeEnd("ctx");
+($globals.SemanticAnalyzer.superclass||$boot.dnu).fn.prototype._visitBranchSendNode_.apply($recv(self), [aNode]));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitBranchSendNode:",{aNode:aNode},$globals.SemanticAnalyzer)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitBranchSendNode: aNode\x0a\x09aNode receiver shouldBeAliased: true.\x0a\x09super visitBranchSendNode: aNode",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["shouldBeAliased:", "receiver", "visitBranchSendNode:"]
+}),
+$globals.SemanticAnalyzer);
+
 $core.addMethod(
 $core.method({
 selector: "visitCascadeNode:",

+ 5 - 0
src/Compiler-Semantic.st

@@ -549,6 +549,11 @@ visitBlockNode: aNode
 	self popScope
 !
 
+visitBranchSendNode: aNode
+	aNode receiver shouldBeAliased: true.
+	super visitBranchSendNode: aNode
+!
+
 visitCascadeNode: aNode
 	aNode receiver: aNode nodes first receiver.
 	super visitCascadeNode: aNode

+ 32 - 0
src/Compiler-Tests.js

@@ -679,6 +679,38 @@ messageSends: ["should:return:"]
 }),
 $globals.CodeGeneratorTest);
 
+$core.addMethod(
+$core.method({
+selector: "testAugments",
+protocol: 'tests',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+self._should_return_("foo ^ (Array new) (add: 3) (add: 4)",[(3), (4)]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["should:return:"]=1;
+//>>excludeEnd("ctx");
+self._should_return_("foo ^ (Array new) (add: 3; add: 4)",[(3), (4)]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["should:return:"]=2;
+//>>excludeEnd("ctx");
+self._should_return_("foo ^ (Array new) (add: 3) (add: 4) size",(2));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testAugments",{},$globals.CodeGeneratorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testAugments\x0a\x09\x0a\x09self should: 'foo ^ (Array new) (add: 3) (add: 4)' return: #(3 4).\x0a\x09self should: 'foo ^ (Array new) (add: 3; add: 4)' return: #(3 4).\x0a\x09self should: 'foo ^ (Array new) (add: 3) (add: 4) size' return: 2",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["should:return:"]
+}),
+$globals.CodeGeneratorTest);
+
 $core.addMethod(
 $core.method({
 selector: "testBackslashSelectors",

+ 7 - 0
src/Compiler-Tests.st

@@ -193,6 +193,13 @@ testAssignment
 	self should: 'foo | a | ^ a := true ifTrue: [ 1 ]' return: 1
 !
 
+testAugments
+	
+	self should: 'foo ^ (Array new) (add: 3) (add: 4)' return: #(3 4).
+	self should: 'foo ^ (Array new) (add: 3; add: 4)' return: #(3 4).
+	self should: 'foo ^ (Array new) (add: 3) (add: 4) size' return: 2
+!
+
 testBackslashSelectors
 	
 	self should: '\ arg ^ 4' return: 4.