浏览代码

fix code generation for logical expressions with "~"
more precise diagnostic for IF/THEN syntax errors
one more fix in logical expressions and "~"
tune up error diagnostic in case of referencing unknown fields

Vladislav Folts 11 年之前
父节点
当前提交
ac91f14e1a
共有 5 个文件被更改,包括 43 次插入16 次删除
  1. 23 9
      src/context.js
  2. 2 2
      src/grammar.js
  3. 4 0
      test/expected/logical.js
  4. 5 1
      test/input/logical.ob
  5. 9 4
      test/test_unit.js

+ 23 - 9
src/context.js

@@ -293,9 +293,11 @@ exports.Designator = ChainedContext.extend({
     },
     setIdent: function(id){
         var t = this.__currentType;
+        var pointerType;
         var isReadOnly = this.__info instanceof Type.Variable 
                       && this.__info.isReadOnly();
         if (t instanceof Type.Pointer){
+            pointerType = t;
             this.__handleDeref();
             isReadOnly = false;
         }
@@ -304,7 +306,7 @@ exports.Designator = ChainedContext.extend({
                 || t instanceof Module.AnyType))
             throw new Errors.Error("cannot designate '" + t.description() + "'");
 
-        this.__denote(id);
+        this.__denote(id, pointerType);
         this.__info = new Type.Variable(this.__currentType, isReadOnly);
         this.__scope = undefined;
     },
@@ -378,11 +380,15 @@ exports.Designator = ChainedContext.extend({
 
         this.__currentType = type;
     },
-    __denote: function(id){
+    __denote: function(id, pointerType){
         var t = this.__currentType;
         var fieldType = t.findSymbol(id);
-        if (!fieldType)
-            throw new Errors.Error("Type '" + t.name() + "' has no '" + id + "' field");
+        if (!fieldType){
+            var typeDesc = !t.name() && pointerType && pointerType.name()
+                ? pointerType.name()
+                : t.description();
+            throw new Errors.Error("type '" + typeDesc + "' has no '" + id + "' field");
+        }
         this.__derefCode = this.__code.result();
         this.__propCode = "\"" + id + "\"";
         this.__code.write("." + id);
@@ -892,6 +898,7 @@ exports.MulOperator = ChainedContext.extend({
 exports.Term = ChainedContext.extend({
     init: function TermContext(context){
         ChainedContext.prototype.init.call(this, context);
+        this.__logicalNot = false;
         this.__operator = undefined;
         this.__expression = undefined;
     },
@@ -904,6 +911,10 @@ exports.Term = ChainedContext.extend({
         this.handleExpression(
             new Code.Expression(d.code(), d.type(), d, value));
     },
+    handleLogicalNot: function(){
+        this.__logicalNot = !this.__logicalNot;
+        this.setType(basicTypes.bool);
+    },
     handleOperator: function(o){this.__operator = o;},
     handleConst: function(type, value, code){
         this.handleExpression(new Code.Expression(
@@ -918,6 +929,10 @@ exports.Term = ChainedContext.extend({
     endParse: function(){this.parent().handleTerm(this.__expression);},
     handleExpression: function(e){
         promoteExpressionType(this, this.__expression, e);
+        if (this.__logicalNot){
+            e = op.not(e);
+            this.__logicalNot = false;
+        }
         if (this.__operator)
             e = this.__expression ? this.__operator(this.__expression, e)
                                   : this.__operator(e);
@@ -938,12 +953,11 @@ exports.Factor = ChainedContext.extend({
             parent.handleConst(basicTypes.bool, true, "true");
         else if (s == "FALSE")
             parent.handleConst(basicTypes.bool, false, "false");
-        else if (s == "~"){
-            parent.setType(basicTypes.bool);
-            parent.handleOperator(op.not);
-        }
+        else if (s == "~")
+            parent.handleLogicalNot();
     },
-    handleFactor: function(e){this.parent().handleFactor(e);}
+    handleFactor: function(e){this.parent().handleFactor(e);},
+    handleLogicalNot: function(){this.parent().handleLogicalNot();}
 });
 
 exports.Set = ChainedContext.extend({

+ 2 - 2
src/grammar.js

@@ -103,8 +103,8 @@ var statement = optional(or(
                  ));
 var statementSequence = and(statement, repeat(and(";", statement)));
 
-var ifStatement = and("IF", context(expression, Context.If), "THEN", statementSequence
-                    , repeat(and("ELSIF", context(expression, Context.ElseIf), "THEN", statementSequence))
+var ifStatement = and("IF", context(expression, Context.If), required("THEN", "THEN expected"), statementSequence
+                    , repeat(and("ELSIF", context(expression, Context.ElseIf), required("THEN", "THEN expected"), statementSequence))
                     , optional(and("ELSE", context(statementSequence, Context.Else)))
                     , emit("END", Context.emitIfEnd));
 

+ 4 - 0
test/expected/logical.js

@@ -5,4 +5,8 @@ b2 = b1 || b1;
 b1 = b1 && b2;
 b1 = !b2;
 b1 = b1 && b2 || !b1;
+b1 = b2 && !b1;
+b1 = b2 || b1;
+b1 = b2 || !!(b1 && b2);
+b1 = !b1 && !b2;
 }();

+ 5 - 1
test/input/logical.ob

@@ -8,5 +8,9 @@ BEGIN
 	b2 := b1 OR b1;
 	b1 := b1 & b2;
 	b1 := ~b2;
-	b1 := b1 & b2 OR ~b1 
+	b1 := b1 & b2 OR ~b1;
+    b1 := b2 & ~b1;
+    b1 := b2 OR ~~b1;
+    b1 := b2 OR ~(~(b1 & b2));
+    b1 := ~b1 & ~b2;
 END m.

+ 9 - 4
test/test_unit.js

@@ -364,11 +364,14 @@ var testSuite = {
     ),
 "POINTER dereference": testWithContext(
     context(Grammar.statement,
-            "VAR p: POINTER TO RECORD field: INTEGER END; i: INTEGER; r: RECORD END;"),
+            "TYPE PT = POINTER TO RECORD END;"
+            + "VAR pt: PT; p: POINTER TO RECORD field: INTEGER END; i: INTEGER; r: RECORD END;"),
     pass("p^.field := 1",
          "p.field := 0"),
     fail(["i^", "POINTER TO type expected, got 'INTEGER'"],
-         ["r^", "POINTER TO type expected, got 'anonymous RECORD'"])
+         ["r^", "POINTER TO type expected, got 'anonymous RECORD'"],
+         ["p.unknown := 0", "type 'anonymous RECORD' has no 'unknown' field"],
+         ["pt.unknown := 0", "type 'PT' has no 'unknown' field"])
     ),
 "POINTER assignment": testWithContext(
     context(Grammar.statement,
@@ -751,7 +754,9 @@ var testSuite = {
          ["IF b1 THEN i1 := 0 ELSIF i1 THEN i1 := 2 END",
           "'BOOLEAN' expression expected, got 'INTEGER'"],
          ["IF p THEN i1 := 0 END",
-          "'BOOLEAN' expression expected, got 'POINTER TO anonymous RECORD'"])
+          "'BOOLEAN' expression expected, got 'POINTER TO anonymous RECORD'"],
+         ["IF b1 (*THEN*) i1 := 0 END", "THEN expected"],
+         ["IF b1 THEN i1 := 0 ELSIF ~b1 (*THEN*) i1 := 0 END", "THEN expected"])
     ),
 "CASE statement": testWithContext(
     context(Grammar.statement,
@@ -940,7 +945,7 @@ var testSuite = {
          ["TYPE T = RECORD field: INTEGER END; VAR v: T; BEGIN v := 1 END",
           "type mismatch: 'v' is 'T' and cannot be assigned to 'INTEGER' expression"],
          ["TYPE T = RECORD field: INTEGER END; VAR v: T; BEGIN v.unknown := 1 END",
-          "Type 'T' has no 'unknown' field"],
+          "type 'T' has no 'unknown' field"],
          ["TYPE T1 = RECORD field1: INTEGER END; T2 = RECORD (T1) field1: INTEGER END; END",
           "base record already has field: 'field1'"])
     ),