Ver Fonte

FOREACH to iterate MAP

Vladislav Folts há 10 anos atrás
pai
commit
9b80e2d72c

BIN
bin/compiled.zip


+ 4 - 4
src/eberon/EberonMap.ob

@@ -4,7 +4,7 @@ TYPE
     Type* = RECORD(Types.StorageType)
         PROCEDURE Type*(type: Types.PType);
 
-        type: Types.PType;
+        valueType: Types.PType;
     END;
 
 PROCEDURE Type.initializer(cx: Context.Type): STRING;
@@ -12,11 +12,11 @@ PROCEDURE Type.initializer(cx: Context.Type): STRING;
 END;
 
 PROCEDURE Type.description(): STRING;
-    RETURN "MAP OF " + SELF.type.description();
+    RETURN "MAP OF " + SELF.valueType.description();
 END;
 
-PROCEDURE Type.Type(type: Types.PType)
-    | type(type);
+PROCEDURE Type.Type(valueType: Types.PType)
+    | valueType(valueType);
 END;
 
 END EberonMap.

+ 57 - 0
src/eberon/eberon_context.js

@@ -152,6 +152,13 @@ var InPlaceStringLiteral = TypeNarrowVariable.extend({
     idType: function(){return "string literal";}
 });
 
+var ForEachVariable = TypeNarrowVariable.extend({
+    init: function(type){
+        TypeNarrowVariable.prototype.init.call(this, type, false, true);
+    },
+    idType: function(){return "FOREACH variable";}
+});
+
 var Identdef = Context.Identdef.extend({
     init: function(parent){
         Context.Identdef.prototype.init.call(this, parent);
@@ -1168,6 +1175,55 @@ var MapDecl = Context.Chained.extend({
     }
 });
 
+var ForEach = Context.Chained.extend({
+    init: function EberonContext$MapDecl(context){
+        Context.Chained.prototype.init.call(this, context);
+        this.__valueId = undefined;
+        this.__keyId = undefined;
+    },
+    handleIdent: function(id){
+        if (!this.__valueId)
+            this.__valueId = id;
+        else
+            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()) + "'");
+
+        var scope = EberonScope.makeOperator(
+            this.parent().currentScope(),
+            this.language().stdSymbols);
+        this.pushScope(scope);
+
+        var code = this.codeGenerator();
+        code.write("for(var " + this.__keyId + " in " + q.code + ")");
+        code.openScope();
+        code.write("var " + this.__valueId + " = " + q.code + "[" + this.__keyId + "];\n");
+
+        this.__makeVariable(this.__keyId, EberonString.string(), scope);
+        this.__makeVariable(this.__valueId, type.valueType, scope);
+    },
+    endParse: function(){
+        this.codeGenerator().closeScope("");
+        this.popScope();
+    },
+    __makeVariable: function(id, type, scope){
+        var v = new ForEachVariable(type);
+        var s = new Symbol.Symbol(id, v);
+        scope.addSymbol(s);
+        return s;
+    }
+});
+
 var ArrayDecl = Context.ArrayDecl.extend({
     init: function EberonContext$ArrayDecl(context){
         Context.ArrayDecl.prototype.init.call(this, context);
@@ -1278,6 +1334,7 @@ exports.Designator = Designator;
 exports.Expression = Expression;
 exports.ExpressionProcedureCall = ExpressionProcedureCall;
 exports.For = For;
+exports.ForEach = ForEach;
 exports.FormalParameters = FormalParameters;
 exports.FormalParametersProcDecl = FormalParametersProcDecl;
 exports.FormalType = FormalType;

+ 9 - 1
src/eberon/eberon_grammar.js

@@ -21,6 +21,13 @@ function makeStrucType(base, type){
     return or(base, mapType);
 }
 
+function makeStatement(base, statementSequence, ident, qualident){
+    return or(base, 
+              context(and("FOREACH", ident, ",", ident, "IN", qualident, "DO", 
+                          statementSequence, required("END", "END expected (FOREACH)")), 
+                      EbContext.ForEach));
+}
+
 function makeProcedureHeading(ident, identdef, formalParameters){
     return and("PROCEDURE",
                context(and(optional(and(ident, ".")), identdef), EbContext.ProcOrMethodId),
@@ -116,6 +123,7 @@ exports.language = {
         makeIdentdef,
         makeDesignator,
         makeStrucType,
+        makeStatement,
         makeProcedureHeading,
         makeProcedureDeclaration,
         makeFieldList, 
@@ -147,7 +155,7 @@ exports.language = {
             Return:             EbContext.Return,
             ModuleDeclaration:  EbContext.ModuleDeclaration
         },
-        Grammar.reservedWords + " SELF SUPER MAP"
+        Grammar.reservedWords + " SELF SUPER MAP FOREACH"
         ),
     stdSymbols: Symbols.makeStd(),
     types: {

+ 15 - 10
src/grammar.js

@@ -25,6 +25,7 @@ var reservedWords = "ARRAY IMPORT THEN BEGIN IN TO BY IS TRUE CASE MOD TYPE CONS
 function make(makeIdentdef,
               makeDesignator,
               makeStrucType,
+              makeStatement,
               makeProcedureHeading, 
               makeProcedureDeclaration,
               makeFieldList,
@@ -116,16 +117,9 @@ var actualParameters = and("(", context(optional(expList), Context.ActualParamet
 var assignment = and(context(or(":=", "="), Context.CheckAssignment),
                      required(expression, "expression expected"));
 
-var statement = optional(or(
-                   emit(designator.assignmentOrProcedureCall(assignment, expression), Context.emitEndStatement)
-                   // break recursive declaration of ifStatement/caseStatement/whileStatement/repeatStatement
-                 , function(stream, context){return ifStatement(stream, context);}
-                 , function(stream, context){return caseStatement(stream, context);}
-                 , function(stream, context){return whileStatement(stream, context);}
-                 , function(stream, context){return repeatStatement(stream, context);}
-                 , function(stream, context){return forStatement(stream, context);}
-                 ));
-var statementSequence = and(statement, repeat(and(";", statement)));
+// break recursive declaration of statement
+var forwardStatement = function(stream, context){return statement(stream, context);};
+var statementSequence = and(forwardStatement, repeat(and(";", forwardStatement)));
 
 var ifStatement = and("IF", context(and(expression, required("THEN", "THEN expected"), statementSequence,
                                         repeat(and("ELSIF", expression, required("THEN", "THEN expected"), statementSequence)),
@@ -159,6 +153,17 @@ var forStatement = and("FOR",
                                  , statementSequence, required("END", "END expected (FOR)"))
                              , contexts.For));
 
+var statement = optional(
+    makeStatement(or( emit(designator.assignmentOrProcedureCall(assignment, expression), Context.emitEndStatement),
+                      ifStatement,
+                      caseStatement,
+                      whileStatement,
+                      repeatStatement,
+                      forStatement), 
+                  statementSequence,
+                  ident,
+                  qualident));
+
 var fieldList = makeFieldList(
         identdef,
         identList,

+ 5 - 0
src/oberon/oberon_grammar.js

@@ -19,6 +19,10 @@ function makeStrucType(base){
     return base;
 }
 
+function makeStatement(base){
+    return base;
+}
+
 function makeProcedureHeading(ident, identdef, formalParameters){
     return and("PROCEDURE"
              , identdef
@@ -86,6 +90,7 @@ exports.language = {
         makeIdentdef,
         makeDesignator,
         makeStrucType,
+        makeStatement,
         makeProcedureHeading,
         makeProcedureDeclaration,
         makeFieldList,

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

@@ -1,3 +1,17 @@
-var m = function (){
-var v = {};
+var RTL$ = {
+    assert: function (condition){
+        if (!condition)
+            throw new Error("assertion failed");
+    }
+};
+var test = function (){
+var m = {};
+
+function ForEach(){
+	for(var k in m){
+		var v = m[k];
+		RTL$.assert(v == 0);
+		RTL$.assert(k != "");
+	}
+}
 }();

+ 12 - 3
test/input/eberon/map.ob

@@ -1,4 +1,13 @@
-MODULE m;
+MODULE test;
 VAR
-    v: MAP OF INTEGER;
-END m.
+    m: MAP OF INTEGER;
+
+PROCEDURE ForEach();
+BEGIN
+    FOREACH v, k IN m DO
+        ASSERT(v = 0);
+        ASSERT(k # "");
+    END;
+END;
+
+END test.

+ 14 - 0
test/input/eberon/run/map.ob

@@ -0,0 +1,14 @@
+MODULE test;
+
+PROCEDURE testEmptyForEach();
+VAR
+    m: MAP OF INTEGER;
+BEGIN
+    FOREACH v, k IN m DO
+        ASSERT(FALSE);
+    END;
+END;
+
+BEGIN
+    testEmptyForEach();
+END test.

+ 25 - 0
test/test_unit_eberon.js

@@ -1341,6 +1341,31 @@ exports.suite = {
              ["TYPE M = MAP OF Undeclared;", "undeclared identifier: 'Undeclared'"],
              ["VAR MAP: INTEGER;", "not parsed"]
             )
+        ),
+    "FOREACH": testWithContext(
+        context(grammar.statement,
+                "TYPE T = RECORD END;"
+              + "VAR m: MAP OF INTEGER; r: T;"),
+        pass("FOREACH v, k IN m DO END",
+             "FOREACH v, k IN m DO ASSERT(k # \"abc\"); END",
+             "FOREACH v, k IN m DO ASSERT(v # 123); END"
+            ),
+        fail(["FOREACH k, k IN m DO END", "'k' already declared"],
+             ["FOREACH m, k 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 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 scope": testWithContext(
+        context(grammar.declarationSequence,
+                "VAR m: MAP OF INTEGER;"),
+        pass(),
+        fail(["PROCEDURE p(); BEGIN FOREACH k, v IN m DO END; ASSERT(k # \"abc\"); END;", "undeclared identifier: 'k'"],
+             ["PROCEDURE p(); BEGIN FOREACH k, v IN m DO END; ASSERT(v # 123); END;", "undeclared identifier: 'v'"]
+             )
         )
     }
 };