Bladeren bron

IN operator for MAP

Vladislav Folts 10 jaren geleden
bovenliggende
commit
aad3e58b03

BIN
bin/compiled.zip


+ 4 - 1
src/context.js

@@ -1258,9 +1258,12 @@ exports.Expression = ChainedContext.extend({
         leftExpression = promoteTypeInExpression(leftExpression, rightExpression.type());
         rightExpression = promoteTypeInExpression(rightExpression, leftExpression.type());
 
-        var o = relationOp(leftExpression.type(), rightExpression.type(), this.__relation, this.__relOps, this);
+        var o = this._relationOperation(leftExpression.type(), rightExpression.type(), this.__relation);
         this.__expression = o(leftExpression, rightExpression, this.language().rtl);
     },
+    _relationOperation: function(left, right, relation){
+        return relationOp(left, right, relation, this.__relOps, this);
+    },
     handleLiteral: function(relation){
         this.__relation = relation;
     },

+ 6 - 1
src/eberon/EberonOperator.ob

@@ -1,5 +1,5 @@
 MODULE EberonOperator;
-IMPORT Code, CodePrecedence, OberonRtl, Operator;
+IMPORT Code, CodePrecedence, OberonRtl, Operator, Types;
 
 PROCEDURE opAddStr(left, right: Code.PConst): Code.PConst;
     RETURN Code.makeStringConst(left^(Code.StringConst).value
@@ -64,4 +64,9 @@ PROCEDURE greaterEqualStr*(left, right: Code.PExpression; rtl: OberonRtl.PType):
     RETURN Operator.relational(left, right, rtl, opGraterEqualStr, " >= ")
 END greaterEqualStr;
 
+PROCEDURE inMap*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Code.makeSimpleExpression("Object.prototype.hasOwnProperty.call(" + right.code() + ", " + left.code() + ")",
+                                     Types.basic.bool);
+END;
+
 END EberonOperator.

+ 13 - 2
src/eberon/eberon_context.js

@@ -188,6 +188,11 @@ function OperatorNewMsg(e){
     this.expression = e;
 }
 
+function checkMapKeyType(type){
+    if (type != EberonString.string() && !Type.isString(type))
+        throw new Errors.Error("invalid MAP key type: STRING or string literal or ARRAY OF CHAR expected, got '" + type.description() + "'");            
+}
+
 var Designator = Context.Designator.extend({
     init: function EberonContext$Designator(parent){
         Context.Designator.prototype.init.call(this, parent);
@@ -195,8 +200,7 @@ var Designator = Context.Designator.extend({
     },
     _checkIndexType: function(type){
         if (this._currentType() instanceof EberonMap.Type){
-            if (type != EberonString.string() && !Type.isString(type))
-                throw new Errors.Error("invalid MAP index: STRING or string literal or ARRAY OF CHAR expected, got '" + type.description() + "'");            
+            checkMapKeyType(type);
             return;
         }
         return Context.Designator.prototype._checkIndexType.call(this, type);
@@ -990,6 +994,13 @@ var Expression = Context.Expression.extend({
         if (this.__currentTypePromotion)
             this.parent().handleMessage(new TransferPromotedTypesMsg(this.__currentTypePromotion));
         return Context.Expression.prototype.endParse.call(this);
+    },
+    _relationOperation: function(left, right, relation){
+        if (relation == "IN" && right instanceof EberonMap.Type){
+            checkMapKeyType(left);
+            return eOp.inMap;            
+        }
+        return Context.Expression.prototype._relationOperation.call(this, left, right, relation);
     }
 });
 

+ 1 - 1
src/ob/Lexer.ob

@@ -12,7 +12,7 @@ CONST
         + "var void while with false true null class enum export extends "
         + "import super implements interface let package private protected "
         + "public static yield "
-        + "Math Number" (* Math and Number are used in generated code for some functions so it is 
+        + "Object Math Number" (* Object, Math and Number are used in generated code for some functions so it is 
                     reserved word from code generator standpoint *)
         ;
 

+ 0 - 0
src/ob/Object.ob → src/ob/Object$.ob


+ 5 - 0
test/expected/eberon/map.js

@@ -94,6 +94,11 @@ function put(){
 	RTL$.copyRecord(new T(), RTL$.getMappedValue(mapOfPointer, "abc"));
 }
 
+function in$(){
+	var m = {};
+	RTL$.assert(!Object.prototype.hasOwnProperty.call(m, "abc"));
+}
+
 function get(){
 	var m = {};
 	var s = '';

+ 1 - 1
test/expected/js_keyword.js

@@ -1,5 +1,5 @@
 var do$ = function (){
-var break$ = 0;var case$ = 0;var catch$ = 0;var continue$ = 0;var debugger$ = 0;var default$ = 0;var delete$ = 0;var else$ = 0;var class$ = 0;var const$ = 0;var enum$ = 0;var export$ = 0;var extends$ = 0;var import$ = 0;var super$ = 0;var true$ = 0;var false$ = 0;var null$ = 0;var implements$ = 0;var interface$ = 0;var let$ = 0;var package$ = 0;var private$ = 0;var protected$ = 0;var public$ = 0;var static$ = 0;var yield$ = 0;var finally$ = 0;var for$ = 0;var if$ = 0;var in$ = 0;var instanceof$ = 0;var new$ = 0;var return$ = 0;var switch$ = 0;var this$ = 0;var try$ = 0;var typeof$ = 0;var var$ = 0;var void$ = 0;var while$ = 0;var with$ = 0;var Math$ = 0;var Number$ = 0;
+var break$ = 0;var case$ = 0;var catch$ = 0;var continue$ = 0;var debugger$ = 0;var default$ = 0;var delete$ = 0;var else$ = 0;var class$ = 0;var const$ = 0;var enum$ = 0;var export$ = 0;var extends$ = 0;var import$ = 0;var super$ = 0;var true$ = 0;var false$ = 0;var null$ = 0;var implements$ = 0;var interface$ = 0;var let$ = 0;var package$ = 0;var private$ = 0;var protected$ = 0;var public$ = 0;var static$ = 0;var yield$ = 0;var finally$ = 0;var for$ = 0;var if$ = 0;var in$ = 0;var instanceof$ = 0;var new$ = 0;var return$ = 0;var switch$ = 0;var this$ = 0;var try$ = 0;var typeof$ = 0;var var$ = 0;var void$ = 0;var while$ = 0;var with$ = 0;var Object$ = 0;var Math$ = 0;var Number$ = 0;
 
 function function$(){
 	var i = null;

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

@@ -34,6 +34,13 @@ BEGIN
     mapOfPointer["abc"]^ := T();
 END;
 
+PROCEDURE in();
+VAR
+    m: MAP OF INTEGER;
+BEGIN
+    ASSERT(~("abc" IN m));
+END;
+
 PROCEDURE get();
 VAR
     m: MapOfInteger;

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

@@ -9,6 +9,20 @@ BEGIN
     END;
 END;
 
+PROCEDURE testForEach();
+VAR
+    m: MAP OF INTEGER;
+BEGIN
+    m["abc"] := 1;
+    count <- 0;
+    FOREACH v, k IN m DO
+        ASSERT(v = 1);
+        ASSERT(k = "abc");
+        INC(count);
+    END;
+    ASSERT(count = 1);
+END;
+
 PROCEDURE testPutGet();
 VAR
     m: MAP OF INTEGER;
@@ -22,7 +36,21 @@ BEGIN
     ASSERT(m[a] = 1);
 END;
 
+PROCEDURE testIn();
+VAR
+    m: MAP OF INTEGER;
+    a: ARRAY 3 OF CHAR;
+BEGIN
+    m["abc"] := 1;
+    ASSERT("abc" IN m);
+    ASSERT(~("cde" IN m));
+    a := "abc";
+    ASSERT(a IN m);
+END;
+
 BEGIN
     testEmptyForEach();
+    testForEach();
     testPutGet();
+    testIn();
 END test.

+ 1 - 1
test/input/js_keyword.ob

@@ -6,7 +6,7 @@ VAR
     true, false, null,
     implements, interface, let, package, private, protected, public, static, yield,
     finally, for, if, in, instanceof, new, return, switch, this, 
-    try, typeof, var, void, while, with, Math, Number
+    try, typeof, var, void, while, with, Object, Math, Number
     : INTEGER;
 PROCEDURE function();
 VAR i: throw;

+ 13 - 2
test/test_unit_eberon.js

@@ -1351,7 +1351,7 @@ exports.suite = {
              "m[sIndex] := 123",
              "m[aIndex] := 123"
             ),
-        fail(["m[123] := 123", "invalid MAP index: STRING or string literal or ARRAY OF CHAR expected, got 'INTEGER'"])
+        fail(["m[123] := 123", "invalid MAP key type: STRING or string literal or ARRAY OF CHAR expected, got 'INTEGER'"])
         ),
     "get": testWithContext(
         context(grammar.expression,
@@ -1361,7 +1361,18 @@ exports.suite = {
              "m[sIndex]",
              "m[aIndex]"
             ),
-        fail(["m[123]", "invalid MAP index: STRING or string literal or ARRAY OF CHAR expected, got 'INTEGER'"])
+        fail(["m[123]", "invalid MAP key type: STRING or string literal or ARRAY OF CHAR expected, got 'INTEGER'"])
+        ),
+    "IN": testWithContext(
+        context(grammar.expression,
+                "VAR m: MAP OF INTEGER;"
+                + "sIndex: STRING; aIndex: ARRAY 3 OF CHAR;"),
+        pass("\"abc\" IN m",
+             "(\"abc\" IN m) = FALSE",
+             "sIndex IN m",
+             "aIndex IN m"
+            ),
+        fail(["123 IN m", "invalid MAP key type: STRING or string literal or ARRAY OF CHAR expected, got 'INTEGER'"])
         ),
     "non-VAR parameter": testWithContext(
         context(grammar.declarationSequence,