2
0
Vladislav Folts 11 жил өмнө
parent
commit
52f793ccb0

+ 1 - 0
src/cast.js

@@ -107,6 +107,7 @@ function implicitCast(from, to, ops){
     return undefined;
     return undefined;
 }
 }
 
 
+exports.areProceduresMatch = areProceduresMatch;
 exports.areTypesMatch = areTypesMatch;
 exports.areTypesMatch = areTypesMatch;
 exports.implicit = implicitCast;
 exports.implicit = implicitCast;
 exports.findPointerBaseType = findPointerBaseType;
 exports.findPointerBaseType = findPointerBaseType;

+ 64 - 6
src/eberon/eberon_context.js

@@ -1,9 +1,20 @@
 "use strict";
 "use strict";
 
 
+var Cast = require("cast.js");
 var Context = require("context.js");
 var Context = require("context.js");
 var Errors = require("js/Errors.js");
 var Errors = require("js/Errors.js");
 var Type = require("type.js");
 var Type = require("type.js");
 
 
+var MethodType = Type.Procedure.extend({
+    init: function(type){
+        Type.Procedure.prototype.init.call(this);
+        this.__type = type;
+    },
+    args: function(){return this.__type.args();},
+    result: function(){return this.__type.result();},
+    description: function(){return this.__type.description();}
+});
+
 var ProcOrMethodId = Context.Chained.extend({
 var ProcOrMethodId = Context.Chained.extend({
     init: function EberonContext$ProcOrMethodId(parent){
     init: function EberonContext$ProcOrMethodId(parent){
         Context.Chained.prototype.init.call(this, parent);
         Context.Chained.prototype.init.call(this, parent);
@@ -11,7 +22,7 @@ var ProcOrMethodId = Context.Chained.extend({
         this.__type = undefined;
         this.__type = undefined;
     },
     },
     handleIdent: function(id){this.__maybeTypeId = id;},
     handleIdent: function(id){this.__maybeTypeId = id;},
-    handleLiteral: function(){
+    handleLiteral: function(s){
         var type = Context.getTypeSymbol(this, this.__maybeTypeId);
         var type = Context.getTypeSymbol(this, this.__maybeTypeId);
         if (!(type instanceof Type.Record))
         if (!(type instanceof Type.Record))
             throw new Errors.Error(
             throw new Errors.Error(
@@ -28,15 +39,43 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
     init: function EberonContext$ProcOrMethodDecl(parent){
     init: function EberonContext$ProcOrMethodDecl(parent){
         Context.ProcDecl.prototype.init.call(this, parent);
         Context.ProcDecl.prototype.init.call(this, parent);
         this.__boundType = undefined;
         this.__boundType = undefined;
+        this.__methodId = undefined;
+        this.__methodType = undefined;
+        this.__isNew = undefined;
         this.__endingId = undefined;
         this.__endingId = undefined;
     },
     },
     handleMethodOrProc: function(id, type){
     handleMethodOrProc: function(id, type){
-        this.__boundType = type;
+        if (type){
+            this.__boundType = type;
+            this.__methodId = id;
+        }
+
         Context.ProcDecl.prototype.handleIdentdef.call(
         Context.ProcDecl.prototype.handleIdentdef.call(
             this,
             this,
-            type ? new Context.IdentdefInfo(type.name() + "." + id.id(), id.exported()) : id
+            type ? new Context.IdentdefInfo(type.name() + "." + id.id(),
+                                            id.exported()) 
+                 : id
             );
             );
     },
     },
+    setType: function(type){
+        Context.ProcDecl.prototype.setType.call(this, type);
+        this.__methodType = new MethodType(type);
+    },
+    handleLiteral: function(s){
+        if (s == "NEW"){
+            var id = this.__methodId.id();
+            var existingField = this.__boundType.findSymbol(id);
+            if (existingField)
+                throw new Errors.Error(
+                      existingField instanceof MethodType
+                    ?   "base record already has method '" + id 
+                      + "' (unwanted NEW attribute?)"
+                    : "cannot declare method, record already has field '" + id + "'");
+
+            this.__boundType.addField(this.__methodId, this.__methodType);
+            this.__isNew = true;
+        }
+    },
     handleIdent: function(id){
     handleIdent: function(id){
         if (this.__boundType){
         if (this.__boundType){
             if (!this.__endingId)
             if (!this.__endingId)
@@ -50,9 +89,28 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
             Context.ProcDecl.prototype.handleIdent.call(this, id);
             Context.ProcDecl.prototype.handleIdent.call(this, id);
     },
     },
     endParse: function(){
     endParse: function(){
-        if (this.__boundType && this.__endingId)
-            // shoulf throw
-            Context.ProcDecl.prototype.handleIdent.call(this, this.__endingId);
+        if (this.__boundType){
+            if (this.__endingId)
+                // should throw
+                Context.ProcDecl.prototype.handleIdent.call(this, this.__endingId);
+
+            if (!this.__isNew){
+                var base = this.__boundType.baseType();
+                var id = this.__methodId.id();
+                var existing = base ? base.findSymbol(id) : undefined;
+                if (!existing){
+                    throw new Errors.Error(
+                          "there is no method '" + id 
+                        + "' to override in base type(s) of '" 
+                        + this.__boundType.name() + "' (NEW attribute is missed?)");
+                }
+                if (!Cast.areProceduresMatch(existing, this.__methodType))
+                    throw new Errors.Error(
+                          "overridden method '" + id + "' signature mismatch: should be '"
+                        + existing.description() + "', got '" 
+                        + this.__methodType.description() + "'");
+            }
+        }
         Context.ProcDecl.prototype.endParse.call(this);
         Context.ProcDecl.prototype.endParse.call(this);
     }
     }
 });
 });

+ 25 - 3
test/test_unit_eberon.js

@@ -13,8 +13,11 @@ function testWithContext(context, pass, fail){
 
 
 exports.suite = {
 exports.suite = {
 "new method declaration": testWithContext(
 "new method declaration": testWithContext(
-    context(grammar.declarationSequence, "TYPE T = RECORD END; A = ARRAY 1 OF INTEGER;"),
-    pass("PROCEDURE T.p(), NEW; END T.p;"),
+    context(grammar.declarationSequence, 
+            "TYPE T = RECORD intField: INTEGER END; A = ARRAY 1 OF INTEGER;"),
+    pass("PROCEDURE T.p(), NEW; END T.p;",
+         "PROCEDURE T.p*(), NEW; END T.p;"
+         ),
     fail(["PROCEDURE TUnk.p(), NEW; END TUnk.p;", "undeclared identifier: 'TUnk'"],
     fail(["PROCEDURE TUnk.p(), NEW; END TUnk.p;", "undeclared identifier: 'TUnk'"],
          ["PROCEDURE A.p(), NEW; END A.p;",
          ["PROCEDURE A.p(), NEW; END A.p;",
           "RECORD type expected in method declaration, got 'ARRAY 1 OF INTEGER'"],
           "RECORD type expected in method declaration, got 'ARRAY 1 OF INTEGER'"],
@@ -24,7 +27,26 @@ exports.suite = {
          ["PROCEDURE T.p(), NEW; END T2.p;",
          ["PROCEDURE T.p(), NEW; END T2.p;",
           "mismatched procedure names: 'T.p' at the begining and 'T2.p' at the end"],
           "mismatched procedure names: 'T.p' at the begining and 'T2.p' at the end"],
          ["PROCEDURE T.p(), NEW; END T.p2;",
          ["PROCEDURE T.p(), NEW; END T.p2;",
-          "mismatched procedure names: 'T.p' at the begining and 'T.p2' at the end"]
+          "mismatched procedure names: 'T.p' at the begining and 'T.p2' at the end"],
+         ["PROCEDURE T.intField(), NEW; END T.intField;",
+          "cannot declare method, record already has field 'intField'"],
+         ["PROCEDURE T.p(), NEW; END T.p; PROCEDURE T.p(), NEW; END T.p;",
+          "'T.p' already declared"]
          )
          )
+    ),
+"overridden method declaration": testWithContext(
+    context(grammar.declarationSequence,
+              "TYPE Base = RECORD END; T = RECORD (Base) END;"
+            + "PROCEDURE Base.p(), NEW; END Base.p;"),
+    pass("PROCEDURE T.p(); END T.p;"),
+    fail(["PROCEDURE T.pUnk(); END T.pUnk;",
+          "there is no method 'pUnk' to override in base type(s) of 'T' (NEW attribute is missed?)"],
+         ["PROCEDURE T.p(), NEW; END T.p;",
+          "base record already has method 'p' (unwanted NEW attribute?)"],
+         ["PROCEDURE T.p(); END T.p; PROCEDURE T.p(); END T.p;",
+          "'T.p' already declared"],
+         ["PROCEDURE T.p(a: INTEGER); END T.p;",
+          "overridden method 'p' signature mismatch: should be 'PROCEDURE', got 'PROCEDURE(INTEGER)'"]
+        )
     )
     )
 };
 };