Browse Source

MAP's "remove" mrthod.

Vladislav Folts 10 năm trước cách đây
mục cha
commit
f8c09c1674

BIN
bin/compiled.zip


+ 6 - 8
src/context.js

@@ -336,12 +336,10 @@ exports.Designator = ChainedContext.extend({
         }
         var field = t.denote(id);
         var currentType = field.type();
-        this.__derefCode = this.__code;
-        var codeId = currentType instanceof Type.Procedure 
-                 ? currentType.designatorCode(id)
-                 : Type.mangleField(id, currentType);
-        this.__propCode = "\"" + codeId + "\"";
-        this._advance(currentType, field.asVar(isReadOnly, this), "." + codeId);
+        var fieldCode = field.designatorCode(this.__code);
+        this.__derefCode = fieldCode.derefCode;
+        this.__propCode = fieldCode.propCode;
+        this._advance(currentType, field.asVar(isReadOnly, this), fieldCode.code, undefined, true);
         this.__scope = undefined;
     },
     _currentType: function(){return this.__currentType;},
@@ -377,10 +375,10 @@ exports.Designator = ChainedContext.extend({
                                  + (length - 1)
                                  + ", got " + value );
     },
-    _advance: function(type, info, code, lval){
+    _advance: function(type, info, code, lval, replace){
         this.__currentType = type;
         this.__info = info;
-        this.__code += code;
+        this.__code = (replace ? "" : this.__code) + code;
         this.__lval = lval;
     },
     _indexSequence: function(info, code, indexCode){

+ 5 - 36
src/eberon/EberonArray.ob

@@ -1,17 +1,10 @@
 MODULE EberonArray;
-IMPORT Code, Context, EberonTypes, Errors, LanguageContext, Procedure, Types;
+IMPORT Code, EberonTypes, Errors, LanguageContext, Procedure, Types;
 CONST
     methodNameIndexOf = "indexOf";
 TYPE
     Method* = RECORD(Procedure.Std)
     END;
-    PMethod* = POINTER TO Method;
-
-    MethodField* = RECORD(Types.Field)
-        PROCEDURE MethodField*(method: PMethod);
-
-        method: PMethod
-    END;
 
     MethodIndexOf = RECORD(Method)
         PROCEDURE MethodIndexOf(elementsType: Types.PType);
@@ -32,32 +25,12 @@ PROCEDURE Method.description(): STRING;
     RETURN "array method '" + SELF.name + "'"
 END Method.description;
 
-PROCEDURE MethodField.id(): STRING;
-    RETURN SELF.method.name
-END MethodField.id;
-
-PROCEDURE MethodField.exported(): BOOLEAN;
-    RETURN FALSE
-END MethodField.exported;
-
-PROCEDURE MethodField.type(): Types.PType;
-    RETURN SELF.method
-END MethodField.type;
-
-PROCEDURE MethodField.asVar(isReadOnly: BOOLEAN; cx: Context.Type): Types.PId;
-    RETURN EberonTypes.makeMethod(SELF.method)
-END MethodField.asVar;
-
 PROCEDURE MethodIndexOf.MethodIndexOf(elementsType: Types.PType)
     | SUPER(methodNameIndexOf, NIL),
       elementsType(elementsType);
 BEGIN
 END;
 
-PROCEDURE MethodIndexOf.designatorCode(id: STRING): STRING;
-    RETURN "indexOf"
-END MethodIndexOf.designatorCode;
-
 PROCEDURE MethodIndexOf.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
 BEGIN
     call <- NEW MethodCallIndexOf();
@@ -73,18 +46,14 @@ BEGIN
     RETURN Code.makeSimpleExpression("(" + argCode.result() + ")", Types.basic.integer)
 END MethodCallIndexOf.make;
 
-PROCEDURE denoteMethod*(id: STRING; elementsType: Types.PType): PMethod;
+PROCEDURE denoteMethod*(id: STRING; elementsType: Types.PType): Types.PField;
 VAR
-    result: PMethod;
+    result: Types.PField;
 BEGIN
     IF id = methodNameIndexOf THEN
-        result := NEW MethodIndexOf(elementsType);
+        result := NEW EberonTypes.MethodField(NEW MethodIndexOf(elementsType));
     END;
     RETURN result
-END denoteMethod;
-
-PROCEDURE MethodField.MethodField(method: PMethod)
-    | method(method);
 END;
 
 PROCEDURE denote(id: STRING; a: Types.Array): Types.PField;
@@ -95,7 +64,7 @@ BEGIN
         IF (a.elementsType IS Types.PRecord) OR (a.elementsType IS Types.PArray) THEN
             Errors.raise("'" + methodNameIndexOf + "' is not defined for array of '" + a.elementsType.description() + "'");
         END;
-        result := NEW MethodField(NEW MethodIndexOf(a.elementsType));
+        result := NEW EberonTypes.MethodField(NEW MethodIndexOf(a.elementsType));
     END;
     RETURN result
 END denote;

+ 52 - 25
src/eberon/EberonDynamicArray.ob

@@ -1,5 +1,5 @@
 MODULE EberonDynamicArray;
-IMPORT Cast, Code, Context, EberonArray, Errors, LanguageContext, Procedure, Types;
+IMPORT Cast, Code, Context, EberonArray, EberonTypes, Errors, LanguageContext, Procedure, Types;
 CONST
     methodNameAdd = "add";
     methodNameClear = "clear";
@@ -19,7 +19,7 @@ TYPE
     END;
 
     MethodAdd = RECORD(Method)
-    PROCEDURE MethodAdd(elementsType: Types.PType);
+        PROCEDURE MethodAdd(elementsType: Types.PType);
 
         elementsType: Types.PType
     END;
@@ -36,6 +36,22 @@ TYPE
     MethodCallRemove = RECORD(Procedure.StdCall)
     END;
 
+    MethodAddField = RECORD(EberonTypes.MethodField)
+        PROCEDURE MethodAddField(elementsType: Types.PType);
+    END;
+
+    MethodClearField = RECORD(EberonTypes.MethodField)
+        PROCEDURE MethodClearField();
+    END;
+
+    MethodRemoveField = RECORD(EberonTypes.MethodField)
+        PROCEDURE MethodRemoveField();
+    END;
+
+VAR
+    methodClear: POINTER TO MethodClearField;
+    methodRemove: POINTER TO MethodRemoveField;
+
 PROCEDURE arrayDimensionDescription(VAR a: Types.Array): STRING;
 VAR
     result: STRING;
@@ -63,25 +79,22 @@ END;
 
 PROCEDURE DynamicArray.denote(id: STRING): Types.PField;
 VAR
-    method: EberonArray.PMethod;
     result: Types.PField;
 BEGIN
     IF      id = methodNameAdd THEN
-        method := NEW MethodAdd(SELF.elementsType);
+        result := NEW MethodAddField(SELF.elementsType);
     ELSIF   id = methodNameClear THEN
-        method := NEW MethodClear(methodNameClear, NIL);
+        result := methodClear;
     ELSIF   id = methodNameRemove THEN
-        method := NEW MethodRemove(methodNameRemove, NIL);
-    ELSE
-        method := EberonArray.denoteMethod(id, SELF.elementsType);
-    END;
-    IF method # NIL THEN
-        result := NEW EberonArray.MethodField(method);
+        result := methodRemove;
     ELSE
-        result := SUPER(id);
+        result := EberonArray.denoteMethod(id, SELF.elementsType);
+        IF result = NIL THEN
+            result := SUPER(id);
+        END;
     END;
     RETURN result
-END DynamicArray.denote;
+END;
 
 PROCEDURE AddCallGenerator.handleArgument(e: Code.PExpression);
 BEGIN
@@ -120,9 +133,17 @@ PROCEDURE Method.description(): STRING;
     RETURN "dynamic array method '" + SELF.name + "'"
 END Method.description;
 
-PROCEDURE MethodAdd.designatorCode(id: STRING): STRING;
-    RETURN "push"
-END MethodAdd.designatorCode;
+PROCEDURE MethodAddField.designatorCode(leadCode: STRING): Types.PFieldCode;
+    RETURN NEW Types.FieldCode(leadCode + "." + "push", "", "");
+END;
+
+PROCEDURE MethodClearField.designatorCode(leadCode: STRING): Types.PFieldCode;
+    RETURN NEW Types.FieldCode(leadCode + "." + "splice", "", "");
+END;
+
+PROCEDURE MethodRemoveField.designatorCode(leadCode: STRING): Types.PFieldCode;
+    RETURN NEW Types.FieldCode(leadCode + "." + "splice", "", "");
+END;
 
 PROCEDURE MethodAdd.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
 BEGIN
@@ -132,9 +153,17 @@ BEGIN
     RETURN result
 END MethodAdd.callGenerator;
 
-PROCEDURE MethodClear.designatorCode(id: STRING): STRING;
-    RETURN "splice"
-END MethodClear.designatorCode;
+PROCEDURE MethodAddField.MethodAddField(elementsType: Types.PType)
+    | SUPER(NEW MethodAdd(elementsType));
+END;
+
+PROCEDURE MethodClearField.MethodClearField()
+    | SUPER(NEW MethodClear(methodNameClear, NIL));
+END;
+
+PROCEDURE MethodRemoveField.MethodRemoveField()
+    | SUPER(NEW MethodRemove(methodNameRemove, NIL));
+END;
 
 PROCEDURE MethodCallClear.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
 BEGIN
@@ -153,16 +182,11 @@ BEGIN
     RETURN Code.makeSimpleExpression("(" + argCode.result() + ", 1)", NIL)
 END MethodCallRemove.make;
 
-PROCEDURE MethodRemove.designatorCode(id: STRING): STRING;
-    RETURN "splice"
-END MethodRemove.designatorCode;
-
 PROCEDURE MethodClear.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
     RETURN Procedure.makeCallGenerator(NEW MethodCallClear(), cx)
 END;
 
-PROCEDURE MethodRemove.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator
-;
+PROCEDURE MethodRemove.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
 BEGIN
     call <- NEW MethodCallRemove();
     a <- NEW Types.ProcedureArgument(Types.basic.integer, FALSE);
@@ -170,4 +194,7 @@ BEGIN
     RETURN Procedure.makeCallGenerator(call, cx)
 END MethodRemove.callGenerator;
 
+BEGIN
+    NEW(methodClear);
+    NEW(methodRemove);
 END EberonDynamicArray.

+ 55 - 1
src/eberon/EberonMap.ob

@@ -1,5 +1,7 @@
 MODULE EberonMap;
-IMPORT Context, Types;
+IMPORT Code, Context, EberonString, EberonTypes, LanguageContext, Procedure, Types;
+CONST
+    removeMethodName = "remove";
 TYPE
     Type* = RECORD(Types.StorageType)
         PROCEDURE Type*(type: Types.PType);
@@ -7,6 +9,19 @@ TYPE
         valueType: Types.PType;
     END;
 
+    Method = RECORD(Procedure.Std)
+    END;
+
+    MethodRemoveField = RECORD(EberonTypes.MethodField)
+        PROCEDURE MethodRemoveField();
+    END;
+
+    MethodRemove = RECORD(Method)
+    END;
+
+    MethodCallRemove = RECORD(Procedure.StdCall)
+    END;
+
 PROCEDURE Type.initializer(cx: Context.Type): STRING;
     RETURN "{}";
 END;
@@ -19,5 +34,44 @@ PROCEDURE Type.Type(valueType: Types.PType)
     | valueType(valueType);
 END;
 
+PROCEDURE Type.denote(id: STRING): Types.PField;
+VAR
+    result: Types.PField;
+BEGIN
+    IF id = removeMethodName THEN
+        result := NEW MethodRemoveField();
+    ELSE
+        result := SUPER(id);
+    END;
+    RETURN result;
+END;
+
+PROCEDURE MethodCallRemove.make(args: ARRAY OF Code.PExpression; cx: LanguageContext.PType): Code.PExpression;
+BEGIN
+    argCode <- Procedure.makeArgumentsCode(cx);
+    arg <- Procedure.checkSingleArgument(args, SELF, cx.types, argCode);
+    RETURN Code.makeSimpleExpression("[" + argCode.result() + "]", NIL)
+END;
+
+PROCEDURE MethodRemove.description(): STRING;
+    RETURN "MAP's method 'remove'";
+END;
+
+PROCEDURE MethodRemove.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
+BEGIN
+    call <- NEW MethodCallRemove();
+    a <- NEW Types.ProcedureArgument(NEW Types.OpenArray(Types.basic.ch), FALSE);
+    call.args.add(a);
+    RETURN Procedure.makeCallGenerator(call, cx)
+END;
+
+PROCEDURE MethodRemoveField.MethodRemoveField()
+    | SUPER(NEW MethodRemove(removeMethodName, NIL));
+END;
+
+PROCEDURE MethodRemoveField.designatorCode(leadCode: STRING): Types.PFieldCode;
+    RETURN NEW Types.FieldCode("delete " + leadCode, "", "");
+END;
+
 END EberonMap.
 

+ 1 - 1
src/eberon/EberonRecord.ob

@@ -207,7 +207,7 @@ BEGIN
 END;
 
 PROCEDURE RecordFieldAsMethod.asVar(isReadOnly: BOOLEAN; cx: Context.Type): Types.PId;
-    RETURN EberonTypes.makeMethod(SELF.type()); 
+    RETURN NEW EberonTypes.MethodVariable(SELF.type()); 
 END;
 
 PROCEDURE constructor*(r: Record): Types.PDefinedProcedure;

+ 31 - 3
src/eberon/EberonTypes.ob

@@ -1,5 +1,5 @@
 MODULE EberonTypes;
-IMPORT LanguageContext, Procedure, Types;
+IMPORT Context, LanguageContext, Procedure, Types;
 
 TYPE
     CallGenerator = PROCEDURE(cx: LanguageContext.PType; type: Types.DefinedProcedure): Procedure.PCallGenerator;
@@ -17,6 +17,14 @@ TYPE
     MethodVariable* = RECORD(Types.ProcedureId)
     END;
 
+    PMethod* = POINTER TO Procedure.Std;
+
+    MethodField* = RECORD(Types.Field)
+        PROCEDURE MethodField*(method: PMethod);
+
+        method: PMethod
+    END;
+
 PROCEDURE MethodType.designatorCode(id: STRING): STRING;
     RETURN id
 END MethodType.designatorCode;
@@ -44,8 +52,28 @@ PROCEDURE MethodVariable.idType(): STRING;
     RETURN "method"
 END MethodVariable.idType;
 
-PROCEDURE makeMethod*(type: Types.PType): Types.PProcedureId;
-    RETURN NEW MethodVariable(type);
+PROCEDURE MethodField.MethodField(method: PMethod)
+    | method(method);
+END;
+
+PROCEDURE MethodField.id(): STRING;
+    RETURN SELF.method.name
+END;
+
+PROCEDURE MethodField.exported(): BOOLEAN;
+    RETURN FALSE
+END;
+
+PROCEDURE MethodField.type(): Types.PType;
+    RETURN SELF.method
+END;
+
+PROCEDURE MethodField.asVar(isReadOnly: BOOLEAN; cx: Context.Type): Types.PId;
+    RETURN NEW MethodVariable(SELF.method)
+END;
+
+PROCEDURE MethodField.designatorCode(leadCode: STRING): Types.PFieldCode;
+    RETURN NEW Types.FieldCode(leadCode + "." + Types.mangleField(SELF.method.name, SELF.method), "", "");
 END;
 
 END EberonTypes.

+ 14 - 6
src/ob/Module.ob

@@ -10,13 +10,14 @@ TYPE
         PROCEDURE AnyType();
 
         PROCEDURE callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
-        PROCEDURE denote(id: STRING): Types.PField;
         PROCEDURE designatorCode(id: STRING): STRING;
-        asField: POINTER TO AnyField;
         asVar: Types.PId
     END;
 
     AnyField = RECORD(Types.Field)
+        PROCEDURE AnyField(id: STRING);
+
+        mId: STRING;
     END;
 
     AnyTypeProc* = RECORD(Procedure.Std)
@@ -50,8 +51,8 @@ PROCEDURE AnyType.callGenerator(cx: LanguageContext.PType): Procedure.PCallGener
 END AnyType.callGenerator;
 
 PROCEDURE AnyType.denote(id: STRING): Types.PField;
-    RETURN any.asField
-END AnyType.denote;
+    RETURN NEW AnyField(id);
+END;
 
 PROCEDURE AnyType.designatorCode(id: STRING): STRING;
     RETURN id
@@ -73,6 +74,10 @@ PROCEDURE AnyField.asVar(isReadOnly: BOOLEAN; cx: Context.Type): Types.PId;
     RETURN any.asVar
 END AnyField.asVar;
 
+PROCEDURE AnyField.designatorCode(leadCode: STRING): Types.PFieldCode;
+    RETURN NEW Types.FieldCode(leadCode + "." + SELF.mId, "", "");
+END;
+
 PROCEDURE AnyTypeProc.callGenerator(cx: LanguageContext.PType): Procedure.PCallGenerator;
     RETURN makeCallGenerator(cx)
 END AnyTypeProc.callGenerator;
@@ -143,8 +148,11 @@ PROCEDURE makeJS*(): PType;
 END;
 
 PROCEDURE AnyType.AnyType()
-    | asField(NEW AnyField()),
-      asVar(NEW Types.TypeId(SELF(POINTER)));
+    | asVar(NEW Types.TypeId(SELF(POINTER)));
+END;
+
+PROCEDURE AnyField.AnyField(id: STRING)
+    | mId(id);
 END;
 
 BEGIN

+ 50 - 32
src/ob/Types.ob

@@ -1,7 +1,6 @@
 MODULE Types;
 IMPORT
     Context, Errors, JS, JsMap, Object, ScopeBase, Str := String;
-
 TYPE
     Id* = RECORD(Object.Type)
         PROCEDURE idType*(): STRING
@@ -14,11 +13,6 @@ TYPE
     END;
     PType* = POINTER TO Type;
 
-    StorageType* = RECORD(Type)    
-        PROCEDURE initializer*(cx: Context.Type): STRING
-    END;
-    PStorageType* = POINTER TO StorageType;
-
     TypeId* = RECORD(Id)
         PROCEDURE TypeId*(type: PType);
 
@@ -96,11 +90,19 @@ TYPE
 
     PString* = POINTER TO String;
 
+    FieldCode* = RECORD
+        PROCEDURE FieldCode*(code, derefCode, propCode: STRING);
+
+        code, derefCode, propCode: STRING;
+    END;
+    PFieldCode* = POINTER TO FieldCode;
+
     Field* = RECORD(Object.Type)
         PROCEDURE id*(): STRING;
         PROCEDURE exported*(): BOOLEAN;
         PROCEDURE type*(): PType;
         PROCEDURE asVar*(isReadOnly: BOOLEAN; cx: Context.Type): PId;
+        PROCEDURE designatorCode*(leadCode: STRING): PFieldCode;
     END;
     PField* = POINTER TO Field;
 
@@ -114,11 +116,15 @@ TYPE
     END;
     PRecordField* = POINTER TO RecordField;
 
+    StorageType* = RECORD(Type)    
+        PROCEDURE initializer*(cx: Context.Type): STRING;
+        PROCEDURE denote*(id: STRING): PField;
+    END;
+    PStorageType* = POINTER TO StorageType;
+
     NamedType* = RECORD(StorageType)
         PROCEDURE NamedType(name: STRING);
 
-        PROCEDURE denote*(id: STRING): PField;
-
         name*: STRING
     END;
 
@@ -586,12 +592,12 @@ PROCEDURE Array.description(): STRING;
     RETURN arrayDescription(SELF, arrayDimensionDescription)
 END Array.description;
 
-PROCEDURE NamedType.denote(id: STRING): PField;
+PROCEDURE StorageType.denote(id: STRING): PField;
 BEGIN
     Errors.raise("selector '." + id + "' cannot be applied to '" 
                + SELF.description() + "'");
     RETURN NIL
-END NamedType.denote;
+END;
 
 PROCEDURE OpenArray.initializer(cx: Context.Type): STRING;
     RETURN ""
@@ -729,6 +735,32 @@ PROCEDURE Module.Module(name: STRING)
     | name(name);
 END;
 
+PROCEDURE FieldCode.FieldCode(code, derefCode, propCode: STRING)
+    | code(code), derefCode(derefCode), propCode(propCode);
+END;
+
+PROCEDURE mangleJSProperty*(id: STRING): STRING;
+BEGIN
+    result <- id;
+    IF (id = "constructor") OR (id = "prototype") THEN
+        result := result + "$";
+    END;
+    RETURN result;
+END;
+
+PROCEDURE mangleField*(id: STRING; type: PType): STRING;
+BEGIN
+    result <- id;
+    IF isScalar(type^) 
+        OR ((type IS PArray)
+            & isScalar(arrayBaseElementsType(type^)^)) THEN
+        result := mangleJSProperty(result);
+    ELSE
+        result := "$" + result;
+    END;
+    RETURN result;
+END;
+
 PROCEDURE RecordField.id(): STRING;
     RETURN SELF.mIdentdef.id();
 END;
@@ -741,6 +773,14 @@ PROCEDURE RecordField.identdef(): Context.PIdentdefInfo;
     RETURN SELF.mIdentdef;
 END;
 
+PROCEDURE RecordField.designatorCode(leadCode: STRING): PFieldCode;
+CONST
+    kQuote = 22X;
+BEGIN
+    codeId <- mangleField(SELF.mIdentdef.id(), SELF.mType);
+    RETURN NEW FieldCode(leadCode + "." + codeId, leadCode, kQuote + codeId + kQuote);
+END;
+
 PROCEDURE RecordField.type(): PType;
     RETURN SELF.mType;
 END;
@@ -754,28 +794,6 @@ PROCEDURE RecordField.RecordField(identdef: Context.PIdentdefInfo; type: PType)
       mType(type);
 END;
 
-PROCEDURE mangleJSProperty*(id: STRING): STRING;
-BEGIN
-    result <- id;
-    IF (id = "constructor") OR (id = "prototype") THEN
-        result := result + "$";
-    END;
-    RETURN result;
-END;
-
-PROCEDURE mangleField*(id: STRING; type: PType): STRING;
-BEGIN
-    result <- id;
-    IF isScalar(type^) 
-        OR ((type IS PArray)
-            & isScalar(arrayBaseElementsType(type^)^)) THEN
-        result := mangleJSProperty(result);
-    ELSE
-        result := "$" + result;
-    END;
-    RETURN result;
-END;
-
 BEGIN
     basic.bool := NEW BasicType("BOOLEAN", "false");
     basic.ch := NEW BasicType("CHAR", "0");

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

@@ -108,4 +108,9 @@ function get(){
 	RTL$.assert(RTL$.getMappedValue(m, s) == 3);
 	RTL$.assert(RTL$.getMappedValue(m, a) == 4);
 }
+
+function remove(){
+	var m = {};
+	delete m["abc"];
+}
 }();

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

@@ -53,4 +53,11 @@ BEGIN
     ASSERT(m[a] = 4);
 END;
 
+PROCEDURE remove();
+VAR
+    m: MapOfInteger;
+BEGIN
+    m.remove("abc");
+END;
+
 END test.

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

@@ -48,9 +48,28 @@ BEGIN
     ASSERT(a IN m);
 END;
 
+PROCEDURE testRemove();
+VAR
+    m: MAP OF INTEGER;
+    a: ARRAY 3 OF CHAR;
+BEGIN
+    m["abc"] := 1;
+    m.remove("cde");
+    ASSERT("abc" IN m);
+    m.remove("abc");
+    ASSERT(~("abc" IN m));
+    
+    a := "cde";
+    m[a] := 1;
+    ASSERT("cde" IN m);
+    m.remove(a);
+    ASSERT(~("cde" IN m));
+END;
+
 BEGIN
     testEmptyForEach();
     testForEach();
     testPutGet();
     testIn();
+    testRemove();
 END test.

+ 11 - 0
test/test_unit_eberon.js

@@ -1404,6 +1404,17 @@ exports.suite = {
         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'"]
              )
+        ),
+    "remove": testWithContext(
+        context(grammar.statement,
+                "VAR m: MAP OF INTEGER; a: ARRAY * OF CHAR;"),
+        pass("m.remove(\"abc\")",
+             "m.remove(a)"),
+        fail(["m.remove(123)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'ARRAY OF CHAR'"],
+             ["m.remove()", "1 argument(s) expected, got 0"],
+             ["m.remove(\"abc\", \"abc\")", "1 argument(s) expected, got 2"],
+             ["v <- m.remove", "MAP's method 'remove' cannot be referenced"]
+            )
         )
     }
 };