瀏覽代碼

inverted type promotion in expressions

Vladislav Folts 11 年之前
父節點
當前提交
c9cb1a5778
共有 6 個文件被更改,包括 149 次插入57 次删除
  1. 二進制
      bin/compiled.zip
  2. 71 19
      src/eberon/eberon_context.js
  3. 1 0
      src/eberon/eberon_grammar.js
  4. 1 1
      src/grammar.js
  5. 1 0
      src/oberon/oberon_grammar.js
  6. 75 37
      test/test_unit_eberon.js

二進制
bin/compiled.zip


+ 71 - 19
src/eberon/eberon_context.js

@@ -115,17 +115,18 @@ var TempVariable = Type.Variable.extend({
     init: function TempVariable(type){
         this.__originalType = type;
         this.__type = type;
-        this.__negate = false;
+        this.__invert = false;
     },
     type: function(){
-        return this.__negate ? this.__originalType : this.__type;
+        return this.__invert ? this.__originalType : this.__type;
     },
     isReadOnly: function(){return true;},
     idType: function(){return "temporary variable";},
     promoteType: function(t){
         this.__type = t;
     },
-    negateType: function(){this.__negate = !this.__negate;},
+    invertType: function(){this.__invert = !this.__invert;},
+    inverted: function(){return this.__invert;},
     resetPromotion: function(){
         this.__type = this.__originalType;
     }
@@ -611,7 +612,7 @@ var Factor = Context.Factor.extend({
     init: function EberonContext$Factor(context){
         Context.Factor.prototype.init.call(this, context);
         this.__typePromotion = new TypePromotionHandler();
-        this.__negate = false;
+        this.__invert = false;
     },
     handleMessage: function(msg){
         if (this.__typePromotion.handleMessage(msg))
@@ -620,12 +621,12 @@ var Factor = Context.Factor.extend({
     },
     handleLiteral: function(s){
         if (s == "~")
-            this.__negate = !this.__negate;
+            this.__invert = !this.__invert;
         Context.Factor.prototype.handleLiteral.call(this, s);
     },
     endParse: function(){
-        if (this.__negate)
-            this.__typePromotion.negate();
+        if (this.__invert)
+            this.__typePromotion.invert();
         this.__typePromotion.transferTo(this.parent());
     }
 });
@@ -641,7 +642,7 @@ var AddOperator = Context.AddOperator.extend({
     },
     _expectPlusOperator: function(){return "numeric type or SET or STRING";},
     endParse: function(){
-        this.handleMessage(resetTypePromotionMsg);
+        this.parent().handleLogicalOr();
     }
 });
 
@@ -649,10 +650,8 @@ var MulOperator = Context.MulOperator.extend({
     init: function EberonContext$MulOperator(context){
         Context.MulOperator.prototype.init.call(this, context);
     },
-    handleLiteral: function(s){
-        if (s != "&")
-            this.handleMessage(resetTypePromotionMsg);
-        return Context.MulOperator.prototype.handleLiteral.call(this, s);
+    endParse: function(s){
+        this.parent().handleLogicalAnd();
     }
 });
 
@@ -662,6 +661,8 @@ function PromoteTypeMsg(info, type){
 }
 
 function resetTypePromotionMsg(){}
+function resetInvertedPromotedTypesMsg(){}
+function invertTypePromotionMsg(){}
 
 function TransferPromotedTypesMsg(types){
     this.types = types;
@@ -715,6 +716,38 @@ var RelationOps = Context.RelationOps.extend({
     }
 });
 
+var Term = Context.Term.extend({
+    init: function EberonContext$Term(context){
+        Context.Term.prototype.init.call(this, context);
+        this.__invertedTypePromotionReset = false;
+    },
+    handleLogicalAnd: function(){
+        if (!this.__invertedTypePromotionReset){
+            this.handleMessage(resetInvertedPromotedTypesMsg);
+            this.__invertedTypePromotionReset = true;
+        }
+    }
+});
+
+var SimpleExpression = Context.SimpleExpression.extend({
+    init: function EberonContext$SimpleExpression(context){
+        Context.SimpleExpression.prototype.init.call(this, context);
+        this.__typePromotionInverted = false;
+    },
+    handleLogicalOr: function(){
+        if (!this.__typePromotionInverted){
+            this.handleMessage(invertTypePromotionMsg);
+            this.handleMessage(resetInvertedPromotedTypesMsg);
+            this.__typePromotionInverted = true;
+        }
+    },
+    endParse: function(){
+        if (this.__typePromotionInverted)
+            this.handleMessage(invertTypePromotionMsg);
+        Context.SimpleExpression.prototype.endParse.call(this);
+    }
+});
+
 var relationOps = new RelationOps();
 
 var Expression = Context.Expression.extend({
@@ -767,21 +800,39 @@ var TypePromotionHandler = Class.extend({
             Array.prototype.push.apply(this.__promotedTypes, msg.types);
             return true;
         }
-        if (msg == resetTypePromotionMsg){
-            this.reset();
-            return true;
+        switch (msg){
+            case resetTypePromotionMsg:
+                this.reset();
+                return true;
+            case invertTypePromotionMsg:
+                this.invert();
+                return true;
+            case resetInvertedPromotedTypesMsg:
+                this.resetInverted();
+                return true;
         }
     return false;
     },
-    negate: function(){
+    invert: function(){
         for(var i = 0; i < this.__promotedTypes.length; ++i)
-            this.__promotedTypes[i].negateType();
+            this.__promotedTypes[i].invertType();
     },
     reset: function(){
         for(var i = 0; i < this.__promotedTypes.length; ++i)
             this.__promotedTypes[i].resetPromotion();
         this.__promotedTypes = [];
     },
+    resetInverted: function(inverted){
+        var left = [];
+        for(var i = 0; i < this.__promotedTypes.length; ++i){
+            var t = this.__promotedTypes[i];
+            if (t.inverted())
+                t.resetPromotion();
+            else
+                left.push(t);
+        }
+        this.__promotedTypes = left;
+    },
     transferTo: function(context){
         if (this.__promotedTypes.length)
             context.handleMessage(new TransferPromotedTypesMsg(this.__promotedTypes));
@@ -804,7 +855,7 @@ var If = Context.If.extend({
     handleLiteral: function(s){
         Context.If.prototype.handleLiteral.call(this, s);
         if (s == "ELSIF" || s == "ELSE"){
-            this.__typePromotion.negate();
+            this.__typePromotion.invert();
             this.__newScope();
         }
     },
@@ -886,8 +937,9 @@ exports.ProcOrMethodId = ProcOrMethodId;
 exports.ProcOrMethodDecl = ProcOrMethodDecl;
 exports.RecordDecl = RecordDecl;
 exports.Repeat = Repeat;
+exports.SimpleExpression = SimpleExpression;
 exports.TemplValueInit = TemplValueInit;
-exports.Term = Context.Term;
+exports.Term = Term;
 exports.TypeDeclaration = TypeDeclaration;
 exports.VariableDeclaration = VariableDeclaration;
 exports.While = While;

+ 1 - 0
src/eberon/eberon_grammar.js

@@ -81,6 +81,7 @@ exports.language = {
             Term:               EbContext.Term,
             AddOperator:        EbContext.AddOperator,
             MulOperator:        EbContext.MulOperator,
+            SimpleExpression:   EbContext.SimpleExpression, 
             Expression:         EbContext.Expression,
             For:                EbContext.For,
             While:              EbContext.While,

+ 1 - 1
src/grammar.js

@@ -95,7 +95,7 @@ var simpleExpression = context(
         and(optional(or("+", "-"))
           , term
           , repeat(and(addOperator, term)))
-      , Context.SimpleExpression);
+      , contexts.SimpleExpression);
 var relation = or("=", "#", "<=", "<", ">=", ">", "IN", "IS");
 var expression = context(and(simpleExpression, optional(and(relation, simpleExpression)))
                        , contexts.Expression);

+ 1 - 0
src/oberon/oberon_grammar.js

@@ -68,6 +68,7 @@ exports.language = {
             Term:               Context.Term,
             AddOperator:        Context.AddOperator,
             MulOperator:        Context.MulOperator,
+            SimpleExpression:   Context.SimpleExpression, 
             Expression:         Context.Expression,
             For:                Context.For,
             While:              Context.While,

+ 75 - 37
test/test_unit_eberon.js

@@ -21,6 +21,31 @@ function testWithGrammar(parser, pass, faile){
     return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
 }
 
+var typePromotion = {
+    context: context(
+        grammar.declarationSequence,
+        "TYPE Base = RECORD END;"
+        + "Derived = RECORD (Base) flag: BOOLEAN END;"
+        + "PDerived = POINTER TO Derived;"
+        + "VAR pBase: POINTER TO Base; bVar: BOOLEAN;"
+        + "PROCEDURE proc(b: BOOLEAN): BOOLEAN; RETURN b END proc;"),
+    expression: function(e){
+        return "PROCEDURE p(); BEGIN b <- pBase; b2 <- pBase; ASSERT(" + e + "); END p;";
+    },
+    passExpressions: function(){
+        var result = [];
+        for(var i = 0; i < arguments.length; ++i)
+            result.push(this.expression(arguments[i]));
+        return pass.apply(this, result);
+    },
+    failExpressions: function(){
+        var result = [];
+        for(var i = 0; i < arguments.length; ++i)
+            result.push([this.expression(arguments[i]), "type 'Base' has no 'flag' field"]);
+        return fail.apply(this, result);
+    }
+};
+
 exports.suite = {
 "arithmetic operators": testWithContext(
     context(grammar.statement, "VAR b1: BOOLEAN;"),
@@ -392,54 +417,65 @@ exports.suite = {
             )
         ),
     "type promotion in expression": testWithContext(
-        context(grammar.declarationSequence,
-                "TYPE Base = RECORD END;"
-                + "Derived = RECORD (Base) flag: BOOLEAN END;"
-                + "PDerived = POINTER TO Derived;"
-                + "VAR pBase: POINTER TO Base; bVar: BOOLEAN;"
-                + "PROCEDURE proc(b: BOOLEAN): BOOLEAN; RETURN b END proc;"
-               ),
-        pass("PROCEDURE p(); BEGIN b <- pBase; ASSERT((b IS PDerived) & b.flag); END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; ASSERT((b IS PDerived) & bVar & b.flag); END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; ASSERT((b IS PDerived) & (bVar OR b.flag)); END p;",
-             "PROCEDURE p(); BEGIN b1 <- pBase; b2 <- pBase; ASSERT((b1 IS PDerived) & (b2 IS PDerived) & b1.flag & b2.flag); END p;",
-             "PROCEDURE p(); BEGIN b1 <- pBase; b2 <- pBase; ASSERT((b1 IS PDerived) & proc(TRUE) & b1.flag); END p;",
-             "PROCEDURE p(); BEGIN b1 <- pBase; b2 <- pBase; ASSERT((b1 IS PDerived) & ~proc(TRUE) & b1.flag); END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; ASSERT(~(~(b IS PDerived)) & b.flag); END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; ASSERT(~~(b IS PDerived) & b.flag); END p;"
-             //TODO: "PROCEDURE p(); BEGIN b <- pBase; ASSERT(((b IS PDerived) = TRUE) & b.flag); END p;",
+        typePromotion.context,
+        typePromotion.passExpressions(
+             "(b IS PDerived) & b.flag",
+             "(b IS PDerived) & bVar & b.flag",
+             "(b IS PDerived) & (bVar OR b.flag)",
+             "(b IS PDerived) & (b2 IS PDerived) & b.flag & b2.flag",
+             "(b IS PDerived) & proc(TRUE) & b.flag",
+             "(b IS PDerived) & ~proc(TRUE) & b.flag",
+             "~(~(b IS PDerived)) & b.flag",
+             "~~(b IS PDerived) & b.flag"
+             //TODO: "((b IS PDerived) = TRUE) & b.flag); END p;",
              ),
-        fail(["PROCEDURE p(); BEGIN b <- pBase; ASSERT((b IS PDerived) OR b.flag); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; ASSERT((b IS PDerived) OR bVar & b.flag); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; ASSERT(~(b IS PDerived) & b.flag); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b1 <- pBase; b2 <- pBase; ASSERT(((b1 IS PDerived) & (b2 IS PDerived) OR bVar) & b1.flag); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; ASSERT(proc(b IS PDerived) & proc(b.flag)); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; ASSERT(ORD(b IS PDerived) * ORD(b.flag) = 0); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; ASSERT(((b IS PDerived) = FALSE) & b.flag); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; ASSERT(b IS PDerived); ASSERT(b.flag); END p;",
-              "type 'Base' has no 'flag' field"],
-             ["PROCEDURE p(); BEGIN b <- pBase; bVar := b IS PDerived; ASSERT(b.flag); END p;",
-              "type 'Base' has no 'flag' field"]
+        typePromotion.failExpressions(
+            "(b IS PDerived) OR b.flag",
+             "(b IS PDerived) OR bVar & b.flag",
+             "~(b IS PDerived) & b.flag",
+             "((b IS PDerived) & (b2 IS PDerived) OR bVar) & b.flag",
+             "proc(b IS PDerived) & proc(b.flag)",
+             "ORD(b IS PDerived) * ORD(b.flag) = 0",
+             "((b IS PDerived) = FALSE) & b.flag",
+             "b IS PDerived); ASSERT(b.flag"
+             // TODO: move to statements test "bVar := b IS PDerived; ASSERT(b.flag)",
              )
         ),
+    "invert type promotion in expression": testWithContext(
+        typePromotion.context,
+        typePromotion.passExpressions(
+             "~(b IS PDerived) OR b.flag",
+             "~(b IS PDerived) OR b.flag OR bVar",
+             "~(b IS PDerived) OR b.flag & bVar",
+             "~(b IS PDerived) OR bVar & b.flag",
+             "~(b IS PDerived) OR (bVar & b.flag)",
+             "~(b IS PDerived) OR bVar OR b.flag",
+             "~(b IS PDerived) OR (bVar = b.flag)",
+             "~(~(b IS PDerived) OR bVar) & b.flag",
+             "~(~(b IS PDerived) OR b.flag) & b.flag"
+             ),
+        typePromotion.failExpressions(
+             "(~(b IS PDerived) OR bVar) & b.flag",
+             "(ORD(~(b IS PDerived)) + ORD(b.flag)",
+             "~(~(b IS PDerived) OR bVar) OR b.flag",
+             "~(~(b IS PDerived) & bVar) & b.flag",
+             "~(b IS PDerived) OR b.flag = b.flag"
+            )
+        ),
     "type promotion in condition": testWithContext(
         context(grammar.declarationSequence,
                 "TYPE Base = RECORD END;"
                 + "Derived = RECORD (Base) flag: BOOLEAN END;"
+                + "Derived2 = RECORD (Derived) flag2: BOOLEAN END;"
                 + "PDerived = POINTER TO Derived;"
+                + "PDerived2 = POINTER TO Derived2;"
                 + "VAR pBase: POINTER TO Base; bVar: BOOLEAN;"
                 + "PROCEDURE proc(b: BOOLEAN): BOOLEAN; RETURN b END proc;"
                ),
         pass("PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN b.flag := FALSE; END; END p;",
              "PROCEDURE p(); BEGIN b <- pBase; IF (b IS PDerived) & bVar THEN b.flag := FALSE; END; END p;",
-             "PROCEDURE p(); BEGIN b <- pBase; IF FALSE THEN ELSIF b IS PDerived THEN b.flag := FALSE; END; END p;"
+             "PROCEDURE p(); BEGIN b <- pBase; IF FALSE THEN ELSIF b IS PDerived THEN b.flag := FALSE; END; END p;",
+             "PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN bVar := (b IS PDerived2) & b.flag2; b.flag := FALSE; END; END p;"
              ),
         fail(["PROCEDURE p(); BEGIN b <- pBase; IF (b IS PDerived) OR bVar THEN b.flag := FALSE; END; END p;",
               "type 'Base' has no 'flag' field"],
@@ -448,10 +484,12 @@ exports.suite = {
              ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN ELSE b.flag := FALSE; END; END p;",
               "type 'Base' has no 'flag' field"],
              ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN ELSIF TRUE THEN b.flag := FALSE; END; END p;",
-              "type 'Base' has no 'flag' field"]
+              "type 'Base' has no 'flag' field"],
+             ["PROCEDURE p(); BEGIN b <- pBase; IF b IS PDerived THEN bVar := b IS PDerived; b.flag := FALSE; END; END p;",
+              "invalid type test: 'Derived' is not an extension of 'Derived'"]
              )
         ),
-    "negate type promotion in condition": testWithContext(
+    "invert type promotion in condition": testWithContext(
         context(grammar.declarationSequence,
                 "TYPE Base = RECORD END;"
                 + "Derived = RECORD (Base) flag: BOOLEAN END;"