Преглед на файлове

parse constructor definition

Vladislav Folts преди 10 години
родител
ревизия
f29a07cb67
променени са 2 файла, в които са добавени 94 реда и са изтрити 31 реда
  1. 70 23
      src/eberon/eberon_context.js
  2. 24 8
      test/test_unit_eberon.js

+ 70 - 23
src/eberon/eberon_context.js

@@ -28,9 +28,10 @@ function superMethodCallGenerator(context, type){
     return Procedure.makeProcCallGeneratorWithCustomArgs(context, type, args);
 }
 
-function MethodOrProcMsg(id, type){
+function MethodOrProcMsg(id, type, isConstructor){
     this.id = id;
     this.type = type;
+    this.isConstructor = isConstructor;
 }
 
 var ProcOrMethodId = Context.Chained.extend({
@@ -58,7 +59,23 @@ var ProcOrMethodId = Context.Chained.extend({
         if (this.__type && id.exported())
             throw new Errors.Error("method implementation cannot be exported: " + id.id());
         checkOrdinaryExport(id, "procedure");
-        this.handleMessage(new MethodOrProcMsg(id, this.__type));
+
+        var isConstructor = false;
+        if (!this.__type){
+            var s = this.currentScope().findSymbol(id.id());
+            if (s){
+                var info = s.symbol().info();
+                if (info instanceof Type.TypeId){
+                    var boundType = info.type();
+                    if (boundType instanceof Type.Record){
+                        this.__type = boundType;
+                        isConstructor = true;
+                    }
+                }
+            }
+        }
+
+        this.handleMessage(new MethodOrProcMsg(id, this.__type, isConstructor));
     }
 });
 
@@ -375,6 +392,7 @@ var RecordType = Class.extend.call(Type.Record, {
     init: function EberonContext$RecordType(name, cons, scope){
         Type.Record.call(this);
         Type.initRecord(this, name, cons, scope);
+        this.__customConstructor = undefined;
         this.__finalized = false;
         this.__declaredMethods = {};
         this.__definedMethods = [];
@@ -427,9 +445,21 @@ var RecordType = Class.extend.call(Type.Record, {
         if (!methodId.exported())
             this.__nonExportedMethods.push(id);
     },
+    defineConstructor: function(type){
+        if (this.__customConstructor)
+            throw new Errors.Error("constructor '" + Type.typeName(this) + "' already defined");
+        if (type.result())
+            throw new Errors.Error("constructor '" + Type.typeName(this) + "' cannot have result type specified");
+        this.__customConstructor = type;
+    },
     defineMethod: function(methodId, type){
         var base = Type.recordBase(this);
         var id = methodId.id();
+
+        if (this.__definedMethods.indexOf(id) != -1)
+            throw new Errors.Error(
+                  "method '" + Type.typeName(this) + "." + id + "' already defined");
+
         var existingField = this.findSymbol(id);
         if (!existingField || !(existingField.type() instanceof EberonTypes.MethodType)){
             throw new Errors.Error(
@@ -635,6 +665,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
         this.__methodType = undefined;
         this.__boundType = undefined;
         this.__endingId = undefined;
+        this.__isConstructor = false;
     },
     handleMessage: function(msg){
         if (msg == getMethodSelf){
@@ -656,15 +687,11 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
             if (type){
                 this.__methodId = id;
                 this.__boundType = type;
+                this.__isConstructor = msg.isConstructor;
             }
 
-            Context.ProcDecl.prototype.handleIdentdef.call(
-                this,
-                type ? new Context.IdentdefInfo(Type.typeName(type) + "." + id.id(),
-                                                id.exported()) 
-                     : id
-                );
-            return undefined;
+            Context.ProcDecl.prototype.handleIdentdef.call(this, id);
+            return;
         }
 
         if (handleTypePromotionMadeInSeparateStatement(msg))
@@ -687,29 +714,49 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
         return Context.ProcDecl.prototype._makeArgumentVariable.call(this, arg);
     },
     setType: function(type){
-        Context.ProcDecl.prototype.setType.call(this, type);
-        if (this.__methodId)
+        if (this.__methodId){
             this.__methodType = EberonTypes.makeMethodType(this.__methodId.id(), type, Procedure.makeProcCallGenerator);
+            this.__type = type;
+            }            
+        else
+            Context.ProcDecl.prototype.setType.call(this, type);
     },
     handleIdent: function(id){
-        if (this.__boundType){
-            if (!this.__endingId)
-                this.__endingId = id + ".";
-            else {
-                Context.ProcDecl.prototype.handleIdent.call(this, this.__endingId + id);
-                this.__endingId = undefined;
-            }
+        if (!this.__boundType)
+            Context.ProcDecl.prototype.handleIdent.call(this, id);
+        /*else if (!this.__endingId){
+            if (!this.__isConstructor || id != Type.typeName(this.__boundType))
+                this.__endingId = id;
         }
+        else if (this.__endingId == Type.typeName(this.__boundType)
+                && id == this.__id.id())
+            this.__endingId = undefined;
+        */
+        else if (this.__endingId)
+            this.__endingId = this.__endingId + "." + id;
         else
-            Context.ProcDecl.prototype.handleIdent.call(this, id);
+            this.__endingId = id;
     },
     endParse: function(){
         if (this.__boundType){
-            if (this.__endingId)
-                // should throw
-                Context.ProcDecl.prototype.handleIdent.call(this, this.__endingId);
+            if (this.__endingId){
+                var expected 
+                    = (this.__isConstructor 
+                             ? ""
+                             : (Type.typeName(this.__boundType) + "."))
+                    + this.__id.id();
+                if (this.__endingId != expected)
+                    throw new Errors.Error(
+                          "mismatched method names: expected '" 
+                        + expected
+                        + "' at the end (or nothing), got '" 
+                        + this.__endingId + "'");
+            }
 
-            this.__boundType.defineMethod(this.__methodId, this.__methodType);
+            if (this.__isConstructor)
+                this.__boundType.defineConstructor(this.__methodType);
+            else
+                this.__boundType.defineMethod(this.__methodId, this.__methodType);
         }
         Context.ProcDecl.prototype.endParse.call(this);
     },

+ 24 - 8
test/test_unit_eberon.js

@@ -20,7 +20,7 @@ function testWithModule(src, pass, fail){
     return TestUnitCommon.testWithModule(src, language, pass, fail);
 }
 
-function testWithGrammar(parser, pass, faile){
+function testWithGrammar(parser, pass, fail){
     return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
 }
 
@@ -86,7 +86,7 @@ exports.suite = {
     pass(),
     fail(["SELF: INTEGER", "not parsed"],
          ["SUPER: INTEGER", "not parsed"],
-         ["STRING: INTEGER", "not parsed"]
+         ["STRING: INTEGER", "'STRING' already declared"]
          )
     ),
 "abstract method declaration": testWithContext(
@@ -122,15 +122,15 @@ exports.suite = {
           "RECORD type expected in method declaration, got 'ARRAY 1 OF INTEGER'"],
          ["PROCEDURE T.p(), NEW; END;", "not parsed"],
          ["PROCEDURE T.p(); END p;",
-          "mismatched procedure names: 'T.p' at the begining and 'p.' at the end"],
+          "mismatched method names: expected 'T.p' at the end (or nothing), got 'p'"],
          ["PROCEDURE T.p(); END T2.p;",
-          "mismatched procedure names: 'T.p' at the begining and 'T2.p' at the end"],
+          "mismatched method names: expected 'T.p' at the end (or nothing), got 'T2.p'"],
          ["PROCEDURE T.p(); END T.p2;",
-          "mismatched procedure names: 'T.p' at the begining and 'T.p2' at the end"],
+          "mismatched method names: expected 'T.p' at the end (or nothing), got 'T.p2'"],
          ["PROCEDURE T.intField(); END T.intField;",
           "'T' has no declaration for method 'intField'"],
-         ["PROCEDURE T.p(); END T.p; PROCEDURE T.p(), NEW; END T.p;",
-          "'T.p' already declared"],
+         ["PROCEDURE T.p(); END T.p; PROCEDURE T.p(); END T.p;",
+          "method 'T.p' already defined"],
          ["PROCEDURE p(); TYPE T = RECORD PROCEDURE m(); PROCEDURE m() END; END p;",
           "cannot declare a new method 'm': method already was declared"],
          ["PROCEDURE p(); TYPE T = RECORD m: INTEGER; PROCEDURE m() END; END p;",
@@ -149,7 +149,7 @@ exports.suite = {
          ["PROCEDURE proc(); TYPE T = RECORD (Base) PROCEDURE p() END; END proc;",
           "cannot declare a new method 'p': method already was declared"],
          ["PROCEDURE T.p(); END T.p; PROCEDURE T.p(); END T.p;",
-          "'T.p' already declared"],
+          "method 'T.p' already defined"],
          ["PROCEDURE T.p(a: INTEGER); END T.p;",
           "overridden method 'p' signature mismatch: should be 'PROCEDURE', got 'PROCEDURE(INTEGER)'"],
          ["PROCEDURE p(); PROCEDURE T.p(); END T.p; END p;",
@@ -1039,5 +1039,21 @@ exports.suite = {
          "TYPE T = RECORD PROCEDURE method(); END; PROCEDURE T.method(); END;",
          "PROCEDURE p(): INTEGER; RETURN 0; END;"
          )
+    ),
+"constructor": testWithGrammar(
+    grammar.declarationSequence, 
+    pass("TYPE T = RECORD END; PROCEDURE T(); END;",
+         "TYPE T = RECORD i: INTEGER; END; PROCEDURE T(); BEGIN SELF.i := 0; END;",
+         "TYPE T = RECORD END; PROCEDURE T(); END T;",
+         "TYPE T = RECORD END; PROCEDURE p(); PROCEDURE T(); END; BEGIN T(); END;" /* local procedure name may match type name from outer scope*/
+         ),
+    fail(["TYPE T = RECORD END; PROCEDURE T(); END; PROCEDURE T(); END;", "constructor 'T' already defined"],
+         ["TYPE T = RECORD END; PROCEDURE T(): INTEGER; RETURN 0; END;", "constructor 'T' cannot have result type specified"],
+         ["TYPE T = ARRAY 3 OF INTEGER; PROCEDURE T(); END;", "'T' already declared"],
+         ["TYPE T = RECORD END; PROCEDURE T(); END T.T;", "mismatched method names: expected 'T' at the end (or nothing), got 'T.T'"],
+         ["TYPE T = RECORD END; PROCEDURE T.T(); END;", "'T' has no declaration for method 'T'"],
+         ["TYPE T = RECORD END; PROCEDURE T(); END T2;", "mismatched method names: expected 'T' at the end (or nothing), got 'T2'"],
+         ["TYPE T = RECORD END; PROCEDURE T(); END T.T;", "mismatched method names: expected 'T' at the end (or nothing), got 'T.T'"]
+         )
     )
 };