Ver código fonte

strict checking for POINTER TO undeclared (yet) RECORD

Vladislav Folts 12 anos atrás
pai
commit
a1240e6a0b
5 arquivos alterados com 79 adições e 6 exclusões
  1. 29 3
      src/context.js
  2. 3 1
      src/grammar.js
  3. 25 0
      test/expected/pointer.js
  4. 11 0
      test/input/pointer.ob
  5. 11 2
      test/test_unit.js

+ 29 - 3
src/context.js

@@ -534,11 +534,14 @@ exports.PointerDecl = ChainedContext.extend({
         if (existing)
             return existing;
 
+        var scope = this.currentScope();
+        scope.addUnresolved(id);
         var resolve = function(){return getSymbol(parent, id).info().type();};
+
         var type = new Type.ForwardRecord(resolve);
         return new FoundSymbol(
             new Symbol(id, new Type.TypeId(type)),
-            this.currentScope()
+            scope
             );
     },
     genTypeName: function(){
@@ -1487,6 +1490,17 @@ exports.TypeDeclaration = ChainedContext.extend({
     type: function(){return this.parent().type();}
 });
 
+exports.TypeSection = ChainedContext.extend({
+    init: function TypeSection(context){
+        ChainedContext.prototype.init.bind(this)(context);
+    },
+    endParse: function(){
+        var unresolved = this.currentScope().unresolved();
+        if (unresolved.length)
+            throw new Errors.Error("no declaration found for '" + unresolved.join("', '") + "'");
+    }
+});
+
 exports.TypeCast = ChainedContext.extend({
     init: function TypeCastContext(context){
         ChainedContext.prototype.init.bind(this)(context);
@@ -1571,6 +1585,7 @@ var Scope = Class.extend({
         this.__symbols = {};
         for(var p in stdSymbols)
             this.__symbols[p] = stdSymbols[p];
+        this.__unresolved = [];
     },
     id: function(){return this.__id;},
     addSymbol: function(symbol){
@@ -1578,8 +1593,19 @@ var Scope = Class.extend({
         if (this.findSymbol(id))
             throw new Errors.Error( "'" + id + "' already declared");
         this.__symbols[id] = symbol;
+
+        var i = this.__unresolved.indexOf(id);
+        if (i != -1){
+            var info = symbol.info();
+            if (!(info.type() instanceof Type.Record))
+                throw new Errors.Error(
+                    "'" + id + "' must be of RECORD type because it was used before in the declation of POINTER");
+            this.__unresolved.splice(i, 1);
+        }
     },
-    findSymbol: function(ident){return this.__symbols[ident];}
+    findSymbol: function(ident){return this.__symbols[ident];},
+    addUnresolved: function(id){this.__unresolved.push(id);},
+    unresolved: function(){return this.__unresolved;}
 });
 
 exports.Context = Class.extend({
@@ -1626,5 +1652,5 @@ exports.Context = Class.extend({
     codeGenerator: function(){return this.__code;},
     rtl: function(){
         return this.__rtl;
-    }
+    },
 });

+ 3 - 1
src/grammar.js

@@ -1,3 +1,5 @@
+"use strict";
+
 var Context = require("context.js");
 var Lexer = require("lexer.js");
 var Parser = require("parser.js");
@@ -163,7 +165,7 @@ var procedureDeclaration = context(
 var constantDeclaration = context(and(ident, "=", constExpression), Context.ConstDecl);
 
 var declarationSequence = and(optional(and("CONST", repeat(and(constantDeclaration, ";"))))
-							, optional(and("TYPE", repeat(and(typeDeclaration, ";"))))
+							, optional(and("TYPE", context(repeat(and(typeDeclaration, ";")), Context.TypeSection)))
 							, optional(and("VAR", repeat(and(variableDeclaration, ";"))))
 							, repeat(and(procedureDeclaration, ";")));
 var procedureBody = and(declarationSequence

+ 25 - 0
test/expected/pointer.js

@@ -0,0 +1,25 @@
+var RTL$ = {
+    extend: function extend(methods){
+        methods.__proto__ = this.prototype; // make instanceof work
+
+        // to see constructor name in diagnostic
+        var result = methods.init;
+        methods.constructor = result.prototype.constructor;
+
+        result.prototype = methods;
+        result.extend = extend;
+        return result;
+    }
+};
+var m = function (){
+var T = RTL$.extend({
+	init: function T(){
+		this.p = null;
+		this.i = 0;
+	}
+});
+var r = new T();
+r.p = new T();
+r.p.p = new T();
+r.p.i = 123;
+}();

+ 11 - 0
test/input/pointer.ob

@@ -0,0 +1,11 @@
+MODULE m;
+TYPE
+	T = RECORD p: POINTER TO T; i: INTEGER END;
+VAR
+	r: T;
+
+BEGIN
+	NEW(r.p);
+    NEW(r.p.p);
+    r.p.i := 123;
+END m.

+ 11 - 2
test/test_unit.js

@@ -269,13 +269,22 @@ identifier: function(){
     var test = setup(Grammar.typeDeclaration);
 
     test.parse("T = POINTER TO RECORD END");
-    test.parse("T = POINTER TO NotDeclaredYet");
-    test.parse("T = POINTER TO RECORD p: POINTER TO T END");
+    test.parse("T = RECORD p: POINTER TO T END");
     test.expectError("T = POINTER TO INTEGER"
                    , "RECORD is expected as a POINTER base type, got 'INTEGER'");
     test.expectError("T = POINTER TO POINTER TO RECORD END"
                    , "RECORD is expected as a POINTER base type, got 'POINTER TO anonymous RECORD'");
 },
+"POINTER forward declaration": testWithContext(
+    context(Grammar.module, ""),
+    pass("MODULE m; TYPE T = POINTER TO NotDeclaredYet; NotDeclaredYet = RECORD END; END m."),
+    fail(["MODULE m; TYPE T = POINTER TO NotDeclaredYet; END m.",
+          "no declaration found for 'NotDeclaredYet'"],
+         ["MODULE m; TYPE T1 = POINTER TO NotDeclaredYet1; T2 = POINTER TO NotDeclaredYet2; END m.",
+          "no declaration found for 'NotDeclaredYet1', 'NotDeclaredYet2'"],
+         ["MODULE m; TYPE T1 = POINTER TO Forward; Forward = PROCEDURE; END m.",
+          "'Forward' must be of RECORD type because it was used before in the declation of POINTER"])
+    ),
 "POINTER dereference": function(){
     var test = setupWithContext(
           Grammar.statement