Переглянути джерело

allow expression for MAP in FOREACH

Vladislav Folts 10 роки тому
батько
коміт
404b40b25a

BIN
bin/compiled.zip


+ 11 - 14
src/context.js

@@ -147,7 +147,6 @@ var ChainedContext = Class.extend({
     handleLiteral: function(s){this.__parent.handleLiteral(s);},
     handleLiteral: function(s){this.__parent.handleLiteral(s);},
     handleConst: function(type, value, code){this.__parent.handleConst(type, value, code);},
     handleConst: function(type, value, code){this.__parent.handleConst(type, value, code);},
     genTypeName: function(){return this.__parent.genTypeName();},
     genTypeName: function(){return this.__parent.genTypeName();},
-    genVarName: function(id){return this.__parent.genVarName(id);},
     qualifyScope: function(scope){return this.__parent.qualifyScope(scope);}
     qualifyScope: function(scope){return this.__parent.qualifyScope(scope);}
 });
 });
 
 
@@ -1332,9 +1331,10 @@ exports.Case = ChainedContext.extend({
         ChainedContext.prototype.init.call(this, context);
         ChainedContext.prototype.init.call(this, context);
         this.__type = undefined;
         this.__type = undefined;
         this.__firstCase = true;
         this.__firstCase = true;
-        this.genVarName("$c");
-        this.codeGenerator().write("$c = ");
+        this.__var = this.currentScope().generateTempVar("case");
+        this.codeGenerator().write("var " + this.__var + " = ");
     },
     },
+    caseVar: function(){return this.__var;},
     handleExpression: function(e){
     handleExpression: function(e){
         var type = e.type();
         var type = e.type();
         var gen = this.codeGenerator();
         var gen = this.codeGenerator();
@@ -1372,12 +1372,14 @@ exports.CaseLabelList = ChainedContext.extend({
     },
     },
     handleLabelType: function(type){this.parent().handleLabelType(type);},
     handleLabelType: function(type){this.parent().handleLabelType(type);},
     handleRange: function(from, to){
     handleRange: function(from, to){
+        var parent = this.parent();
         if (!this.__glue)
         if (!this.__glue)
-            this.parent().caseLabelBegin();
+            parent.caseLabelBegin();
 
 
+        var v = parent.parent().caseVar();
         var cond = to === undefined
         var cond = to === undefined
-            ? "$c === " + from.value
-            : "($c >= " + from.value + " && $c <= " + to.value + ")";
+            ? v + " === " + from.value
+            : "(" + v + " >= " + from.value + " && " + v + " <= " + to.value + ")";
         this.codeGenerator().write(this.__glue + cond);
         this.codeGenerator().write(this.__glue + cond);
         this.__glue = " || ";
         this.__glue = " || ";
     },
     },
@@ -1990,19 +1992,12 @@ exports.Context = Class.extend({
         this.__language = language;
         this.__language = language;
         this.__scopes = [];
         this.__scopes = [];
         this.__gen = 0;
         this.__gen = 0;
-        this.__vars = [];
     },
     },
     language: function(){return this.__language;},
     language: function(){return this.__language;},
     genTypeName: function(){
     genTypeName: function(){
         ++this.__gen;
         ++this.__gen;
         return "anonymous$" + this.__gen;
         return "anonymous$" + this.__gen;
     },
     },
-    genVarName: function(id){
-        if (this.__vars.indexOf(id) === -1) {
-            this.codeGenerator().write("var " + id + ";\n");
-            this.__vars.push(id);
-        }
-    },
     findSymbol: function(ident){
     findSymbol: function(ident){
         for(var i = this.__scopes.length; i--;){
         for(var i = this.__scopes.length; i--;){
             var scope = this.__scopes[i];
             var scope = this.__scopes[i];
@@ -2013,7 +2008,9 @@ exports.Context = Class.extend({
         }
         }
         return undefined;
         return undefined;
     },
     },
-    currentScope: function(){return this.__scopes[this.__scopes.length - 1];},
+    currentScope: function(){
+        return this.__scopes[this.__scopes.length - 1];
+    },
     pushScope: function(scope){this.__scopes.push(scope);},
     pushScope: function(scope){this.__scopes.push(scope);},
     popScope: function(){
     popScope: function(){
         var scope = this.__scopes.pop();
         var scope = this.__scopes.pop();

+ 4 - 0
src/eberon/EberonScope.ob

@@ -30,6 +30,10 @@ BEGIN
     SUPER(s, exported);
     SUPER(s, exported);
 END Operator.addSymbol;
 END Operator.addSymbol;
 
 
+PROCEDURE Operator.generateTempVar(pattern: STRING): STRING;
+    RETURN SELF.parent.generateTempVar(pattern);
+END;
+
 PROCEDURE makeOperator*(parent: Scope.PType; stdSymbols: JsMap.Type): Scope.PType;
 PROCEDURE makeOperator*(parent: Scope.PType; stdSymbols: JsMap.Type): Scope.PType;
 BEGIN
 BEGIN
     result <- NEW Operator(stdSymbols);
     result <- NEW Operator(stdSymbols);

+ 18 - 16
src/eberon/eberon_context.js

@@ -1210,6 +1210,8 @@ var ForEach = Context.Chained.extend({
         Context.Chained.prototype.init.call(this, context);
         Context.Chained.prototype.init.call(this, context);
         this.__valueId = undefined;
         this.__valueId = undefined;
         this.__keyId = undefined;
         this.__keyId = undefined;
+        this.__scopeWasCreated = false;
+        this.__codeGenerator = CodeGenerator.nullGenerator();
     },
     },
     handleIdent: function(id){
     handleIdent: function(id){
         if (!this.__valueId)
         if (!this.__valueId)
@@ -1217,34 +1219,34 @@ var ForEach = Context.Chained.extend({
         else
         else
             this.__keyId = id;
             this.__keyId = id;
     },
     },
-    handleQIdent: function(q){
-        var found = Context.getQIdSymbolAndScope(this, q);
-        var s = found.symbol();
-        var info = s.info();
-
-        var type;
-        if (info instanceof Type.Variable)
-            type = info.type();            
-        if (!type || !(type instanceof EberonMap.Type))
-            throw new Errors.Error("variable of type MAP is expected in FOREACH, got '" 
-                                 + (type ? type.description() : info.idType()) + "'");
+    codeGenerator: function(){return this.__codeGenerator;},
+    handleExpression: function(e){
+        var type = e.type();
+        if (!(type instanceof EberonMap.Type))
+            throw new Errors.Error("expression of type MAP is expected in FOREACH, got '" 
+                                 + type.description() + "'");
 
 
         var scope = EberonScope.makeOperator(
         var scope = EberonScope.makeOperator(
             this.parent().currentScope(),
             this.parent().currentScope(),
             this.language().stdSymbols);
             this.language().stdSymbols);
         this.pushScope(scope);
         this.pushScope(scope);
+        this.__scopeWasCreated = true;
 
 
-        var code = this.codeGenerator();
-        code.write("for(var " + this.__keyId + " in " + q.code + ")");
+        var code = this.parent().codeGenerator();
+        var mapVar = this.currentScope().generateTempVar("map");
+        code.write("var " + mapVar + " = " + e.code() + ";\n");
+        code.write("for(var " + this.__keyId + " in " + mapVar + ")");
         code.openScope();
         code.openScope();
-        code.write("var " + this.__valueId + " = " + q.code + "[" + this.__keyId + "];\n");
+        code.write("var " + this.__valueId + " = " + mapVar + "[" + this.__keyId + "];\n");
+        this.__codeGenerator = code;
 
 
         this.__makeVariable(this.__keyId, EberonString.string(), scope);
         this.__makeVariable(this.__keyId, EberonString.string(), scope);
         this.__makeVariable(this.__valueId, type.valueType, scope);
         this.__makeVariable(this.__valueId, type.valueType, scope);
     },
     },
     endParse: function(){
     endParse: function(){
-        this.codeGenerator().closeScope("");
-        this.popScope();
+        this.__codeGenerator.closeScope("");
+        if (this.__scopeWasCreated)
+            this.popScope();
     },
     },
     __makeVariable: function(id, type, scope){
     __makeVariable: function(id, type, scope){
         var v = new ForEachVariable(type);
         var v = new ForEachVariable(type);

+ 2 - 2
src/eberon/eberon_grammar.js

@@ -21,9 +21,9 @@ function makeStrucType(base, type){
     return or(base, mapType);
     return or(base, mapType);
 }
 }
 
 
-function makeStatement(base, statementSequence, ident, qualident){
+function makeStatement(base, statementSequence, ident, expression){
     return or(base, 
     return or(base, 
-              context(and("FOREACH", ident, ",", ident, "IN", qualident, "DO", 
+              context(and("FOREACH", ident, ",", ident, "IN", expression, "DO", 
                           statementSequence, required("END", "END expected (FOREACH)")), 
                           statementSequence, required("END", "END expected (FOREACH)")), 
                       EbContext.ForEach));
                       EbContext.ForEach));
 }
 }

+ 1 - 1
src/grammar.js

@@ -162,7 +162,7 @@ var statement = optional(
                       forStatement), 
                       forStatement), 
                   statementSequence,
                   statementSequence,
                   ident,
                   ident,
-                  qualident));
+                  expression));
 
 
 var fieldList = makeFieldList(
 var fieldList = makeFieldList(
         identdef,
         identdef,

+ 19 - 1
src/ob/Scope.ob

@@ -5,6 +5,7 @@ IMPORT
     Object, 
     Object, 
     Procedures := Procedure, 
     Procedures := Procedure, 
     ScopeBase,
     ScopeBase,
+    String,
     Symbols, 
     Symbols, 
     Types;
     Types;
 TYPE
 TYPE
@@ -23,6 +24,7 @@ TYPE
         PROCEDURE addSymbol*(s: Symbols.PSymbol; exported: BOOLEAN);
         PROCEDURE addSymbol*(s: Symbols.PSymbol; exported: BOOLEAN);
         PROCEDURE findSymbol*(id: STRING): Symbols.PFoundSymbol;
         PROCEDURE findSymbol*(id: STRING): Symbols.PFoundSymbol;
         PROCEDURE close();
         PROCEDURE close();
+        PROCEDURE generateTempVar*(pattern: STRING): STRING;
 
 
         stdSymbols: JsMap.Type;
         stdSymbols: JsMap.Type;
         symbols: JsMap.Type;
         symbols: JsMap.Type;
@@ -32,6 +34,7 @@ TYPE
     PType* = POINTER TO Type;
     PType* = POINTER TO Type;
 
 
     Procedure* = RECORD(Type)
     Procedure* = RECORD(Type)
+        tempVarCounter: INTEGER;
     END;
     END;
 
 
     CompiledModule = RECORD(Types.Module)
     CompiledModule = RECORD(Types.Module)
@@ -46,7 +49,8 @@ TYPE
         PROCEDURE Module*(name: STRING; stdSymbols: JsMap.Type);
         PROCEDURE Module*(name: STRING; stdSymbols: JsMap.Type);
 
 
         symbol: Symbols.PSymbol;
         symbol: Symbols.PSymbol;
-        exports: JsMap.Type
+        exports: JsMap.Type;
+        tempVarCounter: INTEGER;
     END;
     END;
     PModule = POINTER TO Module;
     PModule = POINTER TO Module;
 
 
@@ -199,6 +203,20 @@ BEGIN
     SUPER(s, exported);
     SUPER(s, exported);
 END Procedure.addSymbol;
 END Procedure.addSymbol;
 
 
+PROCEDURE generateTempVar(pattern: STRING; VAR counter: INTEGER): STRING;
+BEGIN
+    counter := counter + 1;
+    RETURN "$" + pattern + String.fromInt(counter);
+END;
+
+PROCEDURE Procedure.generateTempVar(pattern: STRING): STRING;
+    RETURN generateTempVar(pattern, SELF.tempVarCounter);
+END;
+
+PROCEDURE Module.generateTempVar(pattern: STRING): STRING;
+    RETURN generateTempVar(pattern, SELF.tempVarCounter);
+END;
+
 PROCEDURE makeProcedure*(stdSymbols: JsMap.Type): PType;
 PROCEDURE makeProcedure*(stdSymbols: JsMap.Type): PType;
     RETURN NEW Procedure(stdSymbols);
     RETURN NEW Procedure(stdSymbols);
 END;
 END;

+ 28 - 29
test/expected/case.js

@@ -6,70 +6,69 @@ var b1 = false;
 var i1 = 0;
 var i1 = 0;
 var byte = 0;
 var byte = 0;
 var c = 0;
 var c = 0;
-var $c;
-$c = i1;
-$c = i1;
-$c = i1;
-if ($c === 1){
+var $case1 = i1;
+var $case2 = i1;
+var $case3 = i1;
+if ($case3 === 1){
 	b1 = false;
 	b1 = false;
 }
 }
-$c = 123;
-if ($c === 1){
+var $case4 = 123;
+if ($case4 === 1){
 	b1 = true;
 	b1 = true;
 }
 }
-$c = i1;
-if ($c === 1){
+var $case5 = i1;
+if ($case5 === 1){
 	i = 2;
 	i = 2;
 }
 }
-else if ($c === 2){
+else if ($case5 === 2){
 	i = 3;
 	i = 3;
 	b1 = false;
 	b1 = false;
 }
 }
-$c = i1;
-if ($c === 1){
+var $case6 = i1;
+if ($case6 === 1){
 	i = 2;
 	i = 2;
 }
 }
-else if ($c === 2){
+else if ($case6 === 2){
 	i = 3;
 	i = 3;
 	b1 = false;
 	b1 = false;
 }
 }
-$c = i1;
-if ($c === 1 || $c === 2 || $c === 3){
+var $case7 = i1;
+if ($case7 === 1 || $case7 === 2 || $case7 === 3){
 	i = 4;
 	i = 4;
 }
 }
-else if ($c === 12){
+else if ($case7 === 12){
 	i = constI;
 	i = constI;
 }
 }
-else if (($c >= 4 && $c <= 5)){
+else if (($case7 >= 4 && $case7 <= 5)){
 	i = 5;
 	i = 5;
 }
 }
-else if ($c === 6 || ($c >= 7 && $c <= 10)){
+else if ($case7 === 6 || ($case7 >= 7 && $case7 <= 10)){
 	b1 = true;
 	b1 = true;
 }
 }
-$c = byte;
-if ($c === 1){
+var $case8 = byte;
+if ($case8 === 1){
 	i = 2;
 	i = 2;
 }
 }
-else if ($c === 257){
+else if ($case8 === 257){
 	i = 3;
 	i = 3;
 }
 }
-else if (($c >= 4 && $c <= 12)){
+else if (($case8 >= 4 && $case8 <= 12)){
 	i = 5;
 	i = 5;
 }
 }
-$c = c;
-if ($c === 65){
+var $case9 = c;
+if ($case9 === 65){
 	i = 1;
 	i = 1;
 }
 }
-else if ($c === 97){
+else if ($case9 === 97){
 	i = 2;
 	i = 2;
 }
 }
-else if ($c === 66 || $c === 67){
+else if ($case9 === 66 || $case9 === 67){
 	i = 2;
 	i = 2;
 }
 }
-else if (($c >= 68 && $c <= 70) || $c === 73 || $c === 74){
+else if (($case9 >= 68 && $case9 <= 70) || $case9 === 73 || $case9 === 74){
 	i = 3;
 	i = 3;
 }
 }
-else if (($c >= 75 && $c <= 90)){
+else if (($case9 >= 75 && $case9 <= 90)){
 	b1 = true;
 	b1 = true;
 }
 }
-}();
+}();

+ 55 - 2
test/expected/eberon/map.js

@@ -65,16 +65,61 @@ var RTL$ = {
     }
     }
 };
 };
 var test = function (){
 var test = function (){
+var m = {};
 
 
 function ForEach(){
 function ForEach(){
 	var m = {};
 	var m = {};
-	for(var k in m){
-		var v = m[k];
+	var $map1 = m;
+	for(var k in $map1){
+		var v = $map1[k];
 		RTL$.assert(v == 0);
 		RTL$.assert(v == 0);
 		RTL$.assert(k != "");
 		RTL$.assert(k != "");
 	}
 	}
 }
 }
 
 
+function makeMap(){
+	var m = {};
+	return m;
+}
+
+function ForEachWithExpression(){
+	var $map1 = makeMap();
+	for(var k in $map1){
+		var v = $map1[k];
+	}
+}
+
+function NestedForEach(){
+	var m = {};
+	
+	function inner(){
+		var $map1 = m;
+		for(var k in $map1){
+			var v = $map1[k];
+			var $map2 = m;
+			for(var k2 in $map2){
+				var v2 = $map2[k2];
+			}
+		}
+	}
+	var $map1 = m;
+	for(var k in $map1){
+		var v = $map1[k];
+		var $map2 = m;
+		for(var k2 in $map2){
+			var v2 = $map2[k2];
+		}
+	}
+	var $map3 = m;
+	for(var k3 in $map3){
+		var v3 = $map3[k3];
+		var $map4 = m;
+		for(var k in $map4){
+			var v = $map4[k];
+		}
+	}
+}
+
 function put(){
 function put(){
 	function T(){
 	function T(){
 		this.field = 0;
 		this.field = 0;
@@ -113,4 +158,12 @@ function remove(){
 	var m = {};
 	var m = {};
 	delete m["abc"];
 	delete m["abc"];
 }
 }
+var $map1 = m;
+for(var k in $map1){
+	var v = $map1[k];
+	var $map2 = m;
+	for(var k2 in $map2){
+		var v2 = $map2[k2];
+	}
+}
 }();
 }();

+ 42 - 0
test/input/eberon/map.ob

@@ -1,6 +1,8 @@
 MODULE test;
 MODULE test;
 TYPE
 TYPE
     MapOfInteger = MAP OF INTEGER;
     MapOfInteger = MAP OF INTEGER;
+VAR
+    m: MapOfInteger;
 
 
 PROCEDURE ForEach();
 PROCEDURE ForEach();
 VAR
 VAR
@@ -12,6 +14,41 @@ BEGIN
     END;
     END;
 END;
 END;
 
 
+PROCEDURE makeMap(): MapOfInteger;
+VAR
+    m: MapOfInteger;
+BEGIN
+    RETURN m;
+END;
+
+PROCEDURE ForEachWithExpression();
+BEGIN
+    FOREACH v, k IN makeMap() DO
+    END;
+END;
+
+PROCEDURE NestedForEach();
+VAR
+    m: MapOfInteger;
+
+    PROCEDURE inner();
+    BEGIN
+        FOREACH v, k IN m DO
+            FOREACH v2, k2 IN m DO
+            END;
+        END;
+    END;
+BEGIN
+    FOREACH v, k IN m DO
+        FOREACH v2, k2 IN m DO
+        END;
+    END;
+    FOREACH v3, k3 IN m DO
+        FOREACH v, k IN m DO
+        END;
+    END;
+END;
+
 PROCEDURE put();
 PROCEDURE put();
 TYPE
 TYPE
     T = RECORD 
     T = RECORD 
@@ -60,4 +97,9 @@ BEGIN
     m.remove("abc");
     m.remove("abc");
 END;
 END;
 
 
+BEGIN
+    FOREACH v, k IN m DO
+        FOREACH v2, k2 IN m DO
+        END;
+    END;
 END test.
 END test.

+ 2 - 2
test/test_unit_eberon.js

@@ -1393,8 +1393,8 @@ exports.suite = {
              ["FOREACH v, m IN m DO END", "'m' already declared in module scope"],
              ["FOREACH v, m IN m DO END", "'m' already declared in module scope"],
              ["FOREACH v, k IN m DO k := \"\"; END", "cannot assign to FOREACH variable"],
              ["FOREACH v, k IN m DO k := \"\"; END", "cannot assign to FOREACH variable"],
              ["FOREACH v, k IN m DO v := 0; END", "cannot assign to FOREACH variable"],
              ["FOREACH v, k IN m DO v := 0; END", "cannot assign to FOREACH variable"],
-             ["FOREACH v, k IN r DO END", "variable of type MAP is expected in FOREACH, got 'T'"],
-             ["FOREACH v, k IN T DO END", "variable of type MAP is expected in FOREACH, got 'type'"]
+             ["FOREACH v, k IN r DO END", "expression of type MAP is expected in FOREACH, got 'T'"],
+             ["FOREACH v, k IN T DO END", "expression of type MAP is expected in FOREACH, got 'type T'"]
             )
             )
         ),
         ),
     "FOREACH scope": testWithContext(
     "FOREACH scope": testWithContext(