Browse Source

declare MAP

Vladislav Folts 10 years ago
parent
commit
411609077e

BIN
bin/compiled.zip


+ 23 - 0
src/eberon/EberonMap.ob

@@ -0,0 +1,23 @@
+MODULE EberonMap;
+IMPORT Context, Types;
+TYPE
+    Type* = RECORD(Types.StorageType)
+        PROCEDURE Type*(from, to: Types.PType);
+
+        from, to: Types.PType;
+    END;
+
+PROCEDURE Type.initializer(cx: Context.Type): STRING;
+    RETURN "{}";
+END;
+
+PROCEDURE Type.description(): STRING;
+    RETURN "MAP " + SELF.from.description() + " TO " + SELF.to.description();
+END;
+
+PROCEDURE Type.Type(from, to: Types.PType)
+    | from(from), 
+      to(to);
+END;
+
+END EberonMap.

+ 1 - 1
src/eberon/EberonSymbols.ob

@@ -1,5 +1,5 @@
 MODULE EberonSymbols;
-IMPORT EberonString, JsMap, Procedure, Scope, Symbols, Types;
+IMPORT EberonMap, EberonString, JsMap, Procedure, Scope, Symbols, Types;
 
 PROCEDURE lenArgumentCheck(argType: Types.PType): BOOLEAN;
     RETURN Procedure.lenArgumentCheck(argType) 

+ 44 - 1
src/eberon/eberon_context.js

@@ -7,7 +7,8 @@ var CodeGenerator = require("js/CodeGenerator.js");
 var Context = require("context.js");
 var EberonConstructor= require("js/EberonConstructor.js");
 var EberonContext= require("js/EberonContext.js");
-var EberonDynamicArray= require("js/EberonDynamicArray.js");
+var EberonDynamicArray = require("js/EberonDynamicArray.js");
+var EberonMap = require("js/EberonMap.js");
 var EberonRecord = require("js/EberonRecord.js");
 var EberonScope = require("js/EberonScope.js");
 var EberonString = require("js/EberonString.js");
@@ -1146,6 +1147,47 @@ var ArrayDimensions = Context.ArrayDimensions.extend({
     }
 });
 
+function checkMapFromType(type){
+    if (Type.numeric().indexOf(type) != -1
+        || type == Type.basic().set
+        || type == EberonString.string())
+        return;
+    throw new Errors.Error("cannot use '" + type.description() + "' as a key of the map, numeric type or SET or STRING or CHAR expected");
+}
+
+var MapDecl = Context.Chained.extend({
+    init: function EberonContext$MapDecl(context){
+        Context.Chained.prototype.init.call(this, context);
+        this.__fromType = undefined;
+        this.__toType = undefined;
+    },
+    handleQIdent: function(q){
+        var s = Context.getQIdSymbolAndScope(this, q);
+        var type = Context.unwrapType(s.symbol().info());
+        
+        if (!type && !this.__fromType)
+        // This is yet-to-declare type - i.e. MAP is declared during RECORD/ARRAY/PROCEDURE declarion.
+        // None of those types are supported as map's key (but can be map's value)
+            throw new Errors.Error("cannot use '" + q.id + "' as a key of the map, numeric type or SET or STRING or CHAR expected");
+
+        this.setType(type);
+    },
+    // anonymous types can be used in map declaration
+    setType: function(type){
+        if (!this.__fromType){
+            checkMapFromType(type);
+            this.__fromType = type;
+        }
+        else
+            this.__toType = type;
+    },
+    isAnonymousDeclaration: function(){return true;},
+    typeName: function(){return undefined;},
+    endParse: function(){
+        this.parent().setType(new EberonMap.Type(this.__fromType, this.__toType));
+    }
+});
+
 var ArrayDecl = Context.ArrayDecl.extend({
     init: function EberonContext$ArrayDecl(context){
         Context.ArrayDecl.prototype.init.call(this, context);
@@ -1266,6 +1308,7 @@ exports.ModuleDeclaration = ModuleDeclaration;
 exports.MulOperator = MulOperator;
 exports.AssignmentOrProcedureCall = AssignmentOrProcedureCall;
 exports.Factor = Factor;
+exports.MapDecl = MapDecl;
 exports.ProcOrMethodId = ProcOrMethodId;
 exports.ProcOrMethodDecl = ProcOrMethodDecl;
 exports.RecordDecl = RecordDecl;

+ 7 - 1
src/eberon/eberon_grammar.js

@@ -16,6 +16,11 @@ var or = Parser.or;
 var repeat = Parser.repeat;
 var required = Parser.required;
 
+function makeStrucType(base, type){
+    var mapType = context(and("MAP", type, "TO", type), EbContext.MapDecl);
+    return or(base, mapType);
+}
+
 function makeProcedureHeading(ident, identdef, formalParameters){
     return and("PROCEDURE",
                context(and(optional(and(ident, ".")), identdef), EbContext.ProcOrMethodId),
@@ -110,6 +115,7 @@ exports.language = {
     grammar: Grammar.make(
         makeIdentdef,
         makeDesignator,
+        makeStrucType,
         makeProcedureHeading,
         makeProcedureDeclaration,
         makeFieldList, 
@@ -141,7 +147,7 @@ exports.language = {
             Return:             EbContext.Return,
             ModuleDeclaration:  EbContext.ModuleDeclaration
         },
-        Grammar.reservedWords + " SELF SUPER"
+        Grammar.reservedWords + " SELF SUPER MAP"
         ),
     stdSymbols: Symbols.makeStd(),
     types: {

+ 2 - 1
src/grammar.js

@@ -24,6 +24,7 @@ var reservedWords = "ARRAY IMPORT THEN BEGIN IN TO BY IS TRUE CASE MOD TYPE CONS
 
 function make(makeIdentdef,
               makeDesignator,
+              makeStrucType,
               makeProcedureHeading, 
               makeProcedureDeclaration,
               makeFieldList,
@@ -187,7 +188,7 @@ var formalParameters = and(
 var procedureType = and("PROCEDURE"
                       , context(optional(formalParameters), contexts.FormalParameters)
                         );
-var strucType = or(arrayType, recordType, pointerType, procedureType);
+var strucType = makeStrucType(or(arrayType, recordType, pointerType, procedureType), type);
 var typeDeclaration = context(and(identdef, "=", strucType), contexts.typeDeclaration);
 
 var constantDeclaration = context(and(identdef, "=", constExpression), contexts.constDeclaration);

+ 5 - 0
src/oberon/oberon_grammar.js

@@ -15,6 +15,10 @@ var optional = Parser.optional;
 var or = Parser.or;
 var repeat = Parser.repeat;
 
+function makeStrucType(base){
+    return base;
+}
+
 function makeProcedureHeading(ident, identdef, formalParameters){
     return and("PROCEDURE"
              , identdef
@@ -81,6 +85,7 @@ exports.language = {
     grammar: Grammar.make(
         makeIdentdef,
         makeDesignator,
+        makeStrucType,
         makeProcedureHeading,
         makeProcedureDeclaration,
         makeFieldList,

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

@@ -0,0 +1,3 @@
+var m = function (){
+var v = {};
+}();

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

@@ -0,0 +1,4 @@
+MODULE m;
+VAR
+    v: MAP STRING TO INTEGER;
+END m.

+ 32 - 1
test/test_unit_eberon.js

@@ -1321,5 +1321,36 @@ exports.suite = {
          ["NEW Abstract()", "cannot instantiate 'Abstract' because it has abstract method(s): abstract"],
          ["NEW undeclared()", "undeclared identifier: 'undeclared'"]
          )
-    )
+    ),
+"map": {
+    "declaration": testWithGrammar(
+        grammar.declarationSequence, 
+        pass("TYPE M = MAP INTEGER TO INTEGER;",
+             "TYPE M = MAP INTEGER TO PROCEDURE;",
+             "TYPE M = MAP INTEGER TO PROCEDURE();",
+             "TYPE M = MAP INTEGER TO PROCEDURE(): INTEGER;",
+             "TYPE M = MAP INTEGER TO PROCEDURE(): M;",
+             "TYPE M = MAP STRING TO RECORD END;",
+             "TYPE M = MAP STRING TO POINTER TO RECORD END;",
+             "TYPE M = MAP INTEGER TO MAP INTEGER TO INTEGER;",
+             "TYPE M = MAP INTEGER TO M;",
+             "TYPE T = RECORD field: MAP INTEGER TO T; END;",
+             "VAR v: MAP SET TO STRING;"
+            ),
+        fail(["TYPE T = RECORD END; M = MAP T TO INTEGER;", "cannot use 'T' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP RECORD END TO INTEGER;", "cannot use 'anonymous RECORD' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP ARRAY 3 OF INTEGER TO INTEGER;", "cannot use 'ARRAY 3 OF INTEGER' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP ARRAY 3 OF CHAR TO INTEGER;", "cannot use 'ARRAY 3 OF CHAR' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP ARRAY * OF INTEGER TO INTEGER;", "cannot use 'ARRAY * OF INTEGER' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP MAP INTEGER TO INTEGER TO INTEGER;", "cannot use 'MAP INTEGER TO INTEGER' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE P = POINTER TO MAP INTEGER TO INTEGER;", "RECORD is expected as a POINTER base type, got 'MAP INTEGER TO INTEGER'"],
+             ["TYPE M = MAP POINTER TO RECORD END TO BOOLEAN;", "cannot use 'POINTER TO anonymous RECORD' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP PROCEDURE TO PROCEDURE;", "cannot use 'PROCEDURE' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE M = MAP INTEGER TO Undeclared;", "undeclared identifier: 'Undeclared'"],
+             ["TYPE M = MAP M TO INTEGER;", "cannot use 'M' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["TYPE T = RECORD field: MAP T TO INTEGER; END", "cannot use 'T' as a key of the map, numeric type or SET or STRING or CHAR expected"],
+             ["VAR MAP: INTEGER;", "not parsed"]
+            )
+        )
+    }
 };