1
0
Selaa lähdekoodia

Refactor: Put responsibility where it's due.

Herby Vojčík 5 vuotta sitten
vanhempi
commit
89bdb83fec
2 muutettua tiedostoa jossa 103 lisäystä ja 104 poistoa
  1. 76 72
      lang/src/Compiler-Semantic.js
  2. 27 32
      lang/src/Compiler-Semantic.st

+ 76 - 72
lang/src/Compiler-Semantic.js

@@ -1871,6 +1871,62 @@ $core.addClass("SemanticAnalyzer", $globals.NodeVisitor, ["currentScope", "block
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.SemanticAnalyzer.comment="I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.";
 //>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "bindUnscopedVariable:",
+protocol: "private",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aString"],
+source: "bindUnscopedVariable: aString\x0a\x09aString isCapitalized ifTrue: [ \x22Capital letter variables might be globals.\x22\x0a\x09\x09self classReferences add: aString.\x0a\x09\x09^ ClassRefVar new name: aString; yourself ].\x0a\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a\x09We allow all variables listed by Smalltalk>>#globalJsVariables.\x0a\x09This list includes: `window`, `document`,  `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09((Smalltalk globalJsVariables includes: aString)\x0a\x09\x09or: [ self isVariableKnown: aString inPackage: self thePackage ]) ifTrue: [\x0a\x09\x09\x09^ UnknownVar new name: aString; yourself ].\x0a\x0a\x09self errorUnknownVariable: aString",
+referencedClasses: ["ClassRefVar", "Smalltalk", "UnknownVar"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["ifTrue:", "isCapitalized", "add:", "classReferences", "name:", "new", "yourself", "or:", "includes:", "globalJsVariables", "isVariableKnown:inPackage:", "thePackage", "errorUnknownVariable:"]
+}, function ($methodClass){ return function (aString){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2,$3,$4,$5;
+$1=$recv(aString)._isCapitalized();
+if($core.assert($1)){
+$recv($self._classReferences())._add_(aString);
+$2=$recv($globals.ClassRefVar)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=1;
+//>>excludeEnd("ctx");
+$recv($2)._name_(aString);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["name:"]=1;
+//>>excludeEnd("ctx");
+$3=$recv($2)._yourself();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["yourself"]=1;
+//>>excludeEnd("ctx");
+return $3;
+}
+$4=$recv($recv($recv($globals.Smalltalk)._globalJsVariables())._includes_(aString))._or_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._isVariableKnown_inPackage_(aString,$self._thePackage());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+if($core.assert($4)){
+$5=$recv($globals.UnknownVar)._new();
+$recv($5)._name_(aString);
+return $recv($5)._yourself();
+}
+$self._errorUnknownVariable_(aString);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"bindUnscopedVariable:",{aString:aString})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.SemanticAnalyzer);
+
 $core.addMethod(
 $core.method({
 selector: "classReferences",
@@ -1933,40 +1989,24 @@ $core.method({
 selector: "errorUnknownVariable:",
 protocol: "error handling",
 //>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["aNode"],
-source: "errorUnknownVariable: aNode\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a\x09We allow all variables listed by Smalltalk>>#globalJsVariables.\x0a\x09This list includes: `window`, `document`,  `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a\x09identifier := aNode value.\x0a\x09\x0a\x09((Smalltalk globalJsVariables includes: identifier)\x0a\x09\x09or: [ self isVariableKnown: identifier inPackage: self thePackage ])\x0a\x09\x09\x09ifFalse: [\x0a\x09\x09\x09\x09UnknownVariableError new\x0a\x09\x09\x09\x09\x09variableName: aNode value;\x0a\x09\x09\x09\x09\x09signal ]",
-referencedClasses: ["Smalltalk", "UnknownVariableError"],
+args: ["aString"],
+source: "errorUnknownVariable: aString\x0a\x09UnknownVariableError new\x0a\x09\x09variableName: aString;\x0a\x09\x09signal",
+referencedClasses: ["UnknownVariableError"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["value", "ifFalse:", "or:", "includes:", "globalJsVariables", "isVariableKnown:inPackage:", "thePackage", "variableName:", "new", "signal"]
-}, function ($methodClass){ return function (aNode){
+messageSends: ["variableName:", "new", "signal"]
+}, function ($methodClass){ return function (aString){
 var self=this,$self=this;
-var identifier;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$2;
-identifier=$recv(aNode)._value();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["value"]=1;
-//>>excludeEnd("ctx");
-$1=$recv($recv($recv($globals.Smalltalk)._globalJsVariables())._includes_(identifier))._or_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $self._isVariableKnown_inPackage_(identifier,$self._thePackage());
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-if(!$core.assert($1)){
-$2=$recv($globals.UnknownVariableError)._new();
-$recv($2)._variableName_($recv(aNode)._value());
-$recv($2)._signal();
-}
+var $1;
+$1=$recv($globals.UnknownVariableError)._new();
+$recv($1)._variableName_(aString);
+$recv($1)._signal();
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"errorUnknownVariable:",{aNode:aNode,identifier:identifier})});
+}, function($ctx1) {$ctx1.fill(self,"errorUnknownVariable:",{aString:aString})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.SemanticAnalyzer);
@@ -2600,63 +2640,27 @@ selector: "visitVariableNode:",
 protocol: "visiting",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aNode"],
-source: "visitVariableNode: aNode\x0a\x09\x22Bind a ScopeVar to aNode by doing a lookup in the current scope.\x0a\x09If no ScopeVar is found, bind a UnknowVar and throw an error.\x22\x0a\x0a\x09| binding |\x0a\x09binding := currentScope lookupVariable: aNode.\x0a\x09\x0a\x09binding ifNil: [\x0a\x09\x09aNode value isCapitalized\x0a\x09\x09\x09ifTrue: [ \x22Capital letter variables might be globals.\x22\x0a\x09\x09\x09\x09binding := ClassRefVar new name: aNode value; yourself.\x0a\x09\x09\x09\x09self classReferences add: aNode value]\x0a\x09\x09\x09ifFalse: [\x0a\x09\x09\x09\x09self errorUnknownVariable: aNode.\x0a\x09\x09\x09\x09binding := UnknownVar new name: aNode value; yourself ] ].\x0a\x09\x09\x0a\x09aNode binding: binding.",
-referencedClasses: ["ClassRefVar", "UnknownVar"],
+source: "visitVariableNode: aNode\x0a\x09\x22Bind a ScopeVar to aNode by doing a lookup in the current scope.\x0a\x09If no var is found in scope, represent an externally known variable or throw an error.\x22\x0a\x0a\x09aNode binding:\x0a\x09\x09((currentScope lookupVariable: aNode) ifNil: [ self bindUnscopedVariable: aNode value ])",
+referencedClasses: [],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["lookupVariable:", "ifNil:", "ifTrue:ifFalse:", "isCapitalized", "value", "name:", "new", "yourself", "add:", "classReferences", "errorUnknownVariable:", "binding:"]
+messageSends: ["binding:", "ifNil:", "lookupVariable:", "bindUnscopedVariable:", "value"]
 }, function ($methodClass){ return function (aNode){
 var self=this,$self=this;
-var binding;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$3,$2,$4,$5,$6,$7,$8,$receiver;
-binding=$recv($self.currentScope)._lookupVariable_(aNode);
-$1=binding;
-if(($receiver = $1) == null || $receiver.a$nil){
-$3=$recv(aNode)._value();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["value"]=1;
-//>>excludeEnd("ctx");
-$2=$recv($3)._isCapitalized();
-if($core.assert($2)){
-$4=$recv($globals.ClassRefVar)._new();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["new"]=1;
-//>>excludeEnd("ctx");
-$5=$recv(aNode)._value();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["value"]=2;
-//>>excludeEnd("ctx");
-$recv($4)._name_($5);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["name:"]=1;
-//>>excludeEnd("ctx");
-binding=$recv($4)._yourself();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["yourself"]=1;
-//>>excludeEnd("ctx");
-$6=$self._classReferences();
-$7=$recv(aNode)._value();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["value"]=3;
-//>>excludeEnd("ctx");
-$recv($6)._add_($7);
-} else {
-$self._errorUnknownVariable_(aNode);
-$8=$recv($globals.UnknownVar)._new();
-$recv($8)._name_($recv(aNode)._value());
-binding=$recv($8)._yourself();
-binding;
-}
+var $2,$1,$receiver;
+$2=$recv($self.currentScope)._lookupVariable_(aNode);
+if(($receiver = $2) == null || $receiver.a$nil){
+$1=$self._bindUnscopedVariable_($recv(aNode)._value());
 } else {
-$1;
+$1=$2;
 }
-$recv(aNode)._binding_(binding);
+$recv(aNode)._binding_($1);
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode,binding:binding})});
+}, function($ctx1) {$ctx1.fill(self,"visitVariableNode:",{aNode:aNode})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.SemanticAnalyzer);

+ 27 - 32
lang/src/Compiler-Semantic.st

@@ -504,24 +504,10 @@ errorShadowingVariable: aString
 		signal
 !
 
-errorUnknownVariable: aNode
-	"Throw an error if the variable is undeclared in the global JS scope (i.e. window).
-	We allow all variables listed by Smalltalk>>#globalJsVariables.
-	This list includes: `window`, `document`,  `process` and `global`
-	for nodejs and browser environments.
-	
-	This is only to make sure compilation works on both browser-based and nodejs environments.
-	The ideal solution would be to use a pragma instead"
-
-	| identifier |
-	identifier := aNode value.
-	
-	((Smalltalk globalJsVariables includes: identifier)
-		or: [ self isVariableKnown: identifier inPackage: self thePackage ])
-			ifFalse: [
-				UnknownVariableError new
-					variableName: aNode value;
-					signal ]
+errorUnknownVariable: aString
+	UnknownVariableError new
+		variableName: aString;
+		signal
 ! !
 
 !SemanticAnalyzer methodsFor: 'factory'!
@@ -542,6 +528,26 @@ newScopeOfClass: aLexicalScopeClass
 
 !SemanticAnalyzer methodsFor: 'private'!
 
+bindUnscopedVariable: aString
+	aString isCapitalized ifTrue: [ "Capital letter variables might be globals."
+		self classReferences add: aString.
+		^ ClassRefVar new name: aString; yourself ].
+
+	"Throw an error if the variable is undeclared in the global JS scope (i.e. window).
+	We allow all variables listed by Smalltalk>>#globalJsVariables.
+	This list includes: `window`, `document`,  `process` and `global`
+	for nodejs and browser environments.
+	
+	This is only to make sure compilation works on both browser-based and nodejs environments.
+	The ideal solution would be to use a pragma instead"
+
+	((Smalltalk globalJsVariables includes: aString)
+		or: [ self isVariableKnown: aString inPackage: self thePackage ]) ifTrue: [
+			^ UnknownVar new name: aString; yourself ].
+
+	self errorUnknownVariable: aString
+!
+
 nextBlockIndex
 	blockIndex ifNil: [ blockIndex := 0 ].
 	
@@ -651,21 +657,10 @@ visitSequenceNode: aNode
 
 visitVariableNode: aNode
 	"Bind a ScopeVar to aNode by doing a lookup in the current scope.
-	If no ScopeVar is found, bind a UnknowVar and throw an error."
+	If no var is found in scope, represent an externally known variable or throw an error."
 
-	| binding |
-	binding := currentScope lookupVariable: aNode.
-	
-	binding ifNil: [
-		aNode value isCapitalized
-			ifTrue: [ "Capital letter variables might be globals."
-				binding := ClassRefVar new name: aNode value; yourself.
-				self classReferences add: aNode value]
-			ifFalse: [
-				self errorUnknownVariable: aNode.
-				binding := UnknownVar new name: aNode value; yourself ] ].
-		
-	aNode binding: binding.
+	aNode binding:
+		((currentScope lookupVariable: aNode) ifNil: [ self bindUnscopedVariable: aNode value ])
 ! !
 
 !SemanticAnalyzer class methodsFor: 'instance creation'!