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

js -> eberon transition
fix diagnostic when operator applies to the result of proper procedure call

Vladislav Folts преди 10 години
родител
ревизия
186cbbea7e
променени са 7 файла, в които са добавени 148 реда и са изтрити 85 реда
  1. BIN
      bin/compiled.zip
  2. 6 79
      src/context.js
  3. 2 2
      src/eberon/eberon_context.js
  4. 128 1
      src/ob/ContextExpression.ob
  5. 8 1
      src/ob/ContextHierarchy.ob
  6. 1 1
      src/oberon/oberon_grammar.js
  7. 3 1
      test/test_unit.js

BIN
bin/compiled.zip


+ 6 - 79
src/context.js

@@ -4,6 +4,7 @@ var Cast = require("js/Cast.js");
 var Code = require("js/Code.js");
 var Code = require("js/Code.js");
 var CodeGenerator = require("js/CodeGenerator.js");
 var CodeGenerator = require("js/CodeGenerator.js");
 var ConstValue = require("js/ConstValue.js");
 var ConstValue = require("js/ConstValue.js");
+var ContextExpression = require("js/ContextExpression.js");
 var ContextHierarchy = require("js/ContextHierarchy.js");
 var ContextHierarchy = require("js/ContextHierarchy.js");
 var Errors = require("js/Errors.js");
 var Errors = require("js/Errors.js");
 var Module = require("js/Module.js");
 var Module = require("js/Module.js");
@@ -613,49 +614,6 @@ exports.ArrayDimensions = ChainedContext.extend({
     }
     }
 });
 });
 
 
-var numericOpTypeCheck = {
-    expect: "numeric type",
-    check: function(t){return Type.numeric().indexOf(t) != -1;}
-};
-
-var numericOrSetOpTypeCheck = {
-    expect: numericOpTypeCheck.expect + " or SET",
-    check: function(t){return numericOpTypeCheck.check(t) || t == basicTypes.set;}
-};
-
-var intOpTypeCheck = {
-    expect: Type.intsDescription(),
-    check: Type.isInt
-};
-
-function throwOperatorTypeMismatch(op, expect, type){
-    throw new Errors.Error(
-        "operator '" + op +
-        "' type mismatch: " + expect + " expected, got '" +
-        type.description() + "'");
-}
-
-function assertOpType(type, check, literal){
-    if (!check.check(type))
-        throwOperatorTypeMismatch(literal, check.expect, type);
-}
-
-function assertNumericOp(type, literal, op, intOp){
-    assertOpType(type, numericOpTypeCheck, literal);
-    return (intOp && Type.isInt(type))
-           ? intOp : op;
-}
-
-function assertNumericOrSetOp(type, literal, op, intOp, setOp){
-    assertOpType(type, numericOrSetOpTypeCheck, literal);
-    return Type.isInt(type) ? intOp : type == basicTypes.set ? setOp : op;
-}
-
-function assertIntOp(type, literal, op){
-    assertOpType(type, intOpTypeCheck, literal);
-    return op;
-}
-
 function useIntOrderOp(t){
 function useIntOrderOp(t){
     return Type.isInt(t) || t == basicTypes.ch;
     return Type.isInt(t) || t == basicTypes.ch;
 }
 }
@@ -795,7 +753,7 @@ function relationOp(leftType, rightType, literal, ops, context){
             break;
             break;
         }
         }
     if (mismatch)
     if (mismatch)
-        throwOperatorTypeMismatch(literal, mismatch, type);
+        ContextExpression.throwOperatorTypeMismatch(literal, mismatch, type);
     return o;
     return o;
 }
 }
 
 
@@ -816,10 +774,10 @@ exports.AddOperator = ChainedContext.extend({
             case "+":
             case "+":
                 result = this._matchPlusOperator(type);
                 result = this._matchPlusOperator(type);
                 if (!result)
                 if (!result)
-                    throwOperatorTypeMismatch(s, this._expectPlusOperator(), type);
+                    ContextExpression.throwOperatorTypeMismatch(s, this._expectPlusOperator(), type);
                 break;
                 break;
             case "-":
             case "-":
-                return assertNumericOrSetOp(type, s, op.subReal, op.subInt, op.setDiff);
+                return ContextExpression.assertNumericOrSetOp(type, s, op.subReal, op.subInt, op.setDiff);
             case "OR":
             case "OR":
                 if (type != basicTypes.bool)
                 if (type != basicTypes.bool)
                     throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
                     throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
@@ -840,37 +798,6 @@ exports.AddOperator = ChainedContext.extend({
     _expectPlusOperator: function(){return "numeric type or SET";}
     _expectPlusOperator: function(){return "numeric type or SET";}
 });
 });
 
 
-exports.MulOperator = ChainedContext.extend({
-    init: function MulOperatorContext(context){
-        ChainedContext.prototype.init.call(this, context);
-    },
-    handleLiteral: function(s){
-        var parent = this.parent();
-        var type = parent.type();
-        var o;
-        if (s == "*")
-            o = assertNumericOrSetOp(type, s, op.mulReal, op.mulInt, op.setIntersection);
-        else if (s == "/"){
-            if (Type.isInt(type))
-                throw new Errors.Error("operator DIV expected for integer division");
-            o = assertNumericOrSetOp(type, s, op.divReal, undefined, op.setSymmetricDiff);
-        }
-        else if (s == "DIV")
-            o = assertIntOp(type, s, op.divInt);
-        else if (s == "MOD")
-            o = assertIntOp(type, s, op.mod);
-        else if (s == "&"){
-            if (type != basicTypes.bool)
-                throw new Errors.Error("BOOLEAN expected as operand of '&', got '"
-                                     + type.description() + "'");
-            o = op.and;
-        }
-
-        if (o)
-            parent.handleOperator(o);
-    }
-});
-
 function designatorAsExpression(d){
 function designatorAsExpression(d){
     var info = d.info();
     var info = d.info();
     if (info instanceof Type.ProcedureId){
     if (info instanceof Type.ProcedureId){
@@ -968,10 +895,10 @@ exports.SimpleExpression = ChainedContext.extend({
         var o;
         var o;
         switch(this.__unaryOperator){
         switch(this.__unaryOperator){
             case "-":
             case "-":
-                o = assertNumericOrSetOp(type, this.__unaryOperator, op.negateReal, op.negateInt, op.setComplement);
+                o = ContextExpression.assertNumericOrSetOp(type, this.__unaryOperator, op.negateReal, op.negateInt, op.setComplement);
                 break;
                 break;
             case "+":
             case "+":
-                o = assertNumericOp(type, this.__unaryOperator, op.unaryPlus);
+                o = ContextExpression.assertNumericOp(type, this.__unaryOperator, op.unaryPlus);
                 break;
                 break;
             }
             }
         if (o){
         if (o){

+ 2 - 2
src/eberon/eberon_context.js

@@ -846,9 +846,9 @@ var AddOperator = Context.AddOperator.extend({
     }
     }
 });
 });
 
 
-var MulOperator = Context.MulOperator.extend({
+var MulOperator = Class.extend.call(ContextExpression.MulOperator, {
     init: function EberonContext$MulOperator(context){
     init: function EberonContext$MulOperator(context){
-        Context.MulOperator.prototype.init.call(this, context);
+        ContextExpression.MulOperator.call(this, context);
     },
     },
     endParse: function(s){
     endParse: function(s){
         this.parent().handleLogicalAnd();
         this.parent().handleLogicalAnd();

+ 128 - 1
src/ob/ContextExpression.ob

@@ -1,6 +1,6 @@
 MODULE ContextExpression;
 MODULE ContextExpression;
 IMPORT 
 IMPORT 
-    Chars, Code, ConstValue, ContextHierarchy, Operator, String, Types;
+    Chars, Code, ConstValue, ContextHierarchy, Errors, Operator, String, Types;
 TYPE
 TYPE
     ExpressionHandler = RECORD(ContextHierarchy.Node)
     ExpressionHandler = RECORD(ContextHierarchy.Node)
         PROCEDURE handleExpression(e: Code.PExpression);
         PROCEDURE handleExpression(e: Code.PExpression);
@@ -38,6 +38,11 @@ TYPE
         simpleExpression: PSimpleExpression;
         simpleExpression: PSimpleExpression;
         operator: BinaryOperator;
         operator: BinaryOperator;
     END;
     END;
+    PTerm = POINTER TO Term;
+
+    MulOperator* = RECORD(ContextHierarchy.Node)
+        PROCEDURE handleLiteral(s: STRING);
+    END;
 
 
     Const = RECORD(ContextHierarchy.Node)
     Const = RECORD(ContextHierarchy.Node)
         PROCEDURE Const(factor: PFactor);
         PROCEDURE Const(factor: PFactor);
@@ -57,6 +62,25 @@ TYPE
         PROCEDURE handleStr*(s: STRING);
         PROCEDURE handleStr*(s: STRING);
     END;
     END;
 
 
+    OpTypeCheck = RECORD
+        PROCEDURE expect(): STRING;
+        PROCEDURE check(t: Types.PType): BOOLEAN;
+    END;
+
+    IntOpTypeCheck = RECORD(OpTypeCheck)
+    END;
+
+    NumericOpTypeCheck = RECORD(OpTypeCheck)
+    END;
+
+    NumericOrSetOpTypeCheck = RECORD(NumericOpTypeCheck)
+    END;
+
+VAR
+    intOpTypeCheck: IntOpTypeCheck;
+    numericOpTypeCheck: NumericOpTypeCheck;
+    numericOrSetOpTypeCheck: NumericOrSetOpTypeCheck;
+
 PROCEDURE Factor.Factor(parent: PFactor)
 PROCEDURE Factor.Factor(parent: PFactor)
     | SUPER(parent),
     | SUPER(parent),
       factorParent(parent);
       factorParent(parent);
@@ -150,6 +174,85 @@ BEGIN
     SELF.simpleExpression.handleTerm(e);
     SELF.simpleExpression.handleTerm(e);
 END;
 END;
 
 
+PROCEDURE throwOperatorTypeMismatch*(op, expect: STRING; type: Types.PType);
+BEGIN
+    Errors.raise(
+        "operator '" + op +
+        "' type mismatch: " + expect + " expected, got '" +
+        type.description() + "'");
+END;
+
+PROCEDURE assertOpType(type: Types.PType; check: OpTypeCheck; literal: STRING);
+BEGIN
+    IF ~check.check(type) THEN
+        throwOperatorTypeMismatch(literal, check.expect(), type);
+    END;
+END;
+
+PROCEDURE assertNumericOp*(type: Types.PType; literal: STRING; op, intOp: BinaryOperator): BinaryOperator;
+VAR
+    result: BinaryOperator;
+BEGIN
+    assertOpType(type, numericOpTypeCheck, literal);
+    IF (intOp # NIL) & Types.isInt(type) THEN
+        result := intOp;
+    ELSE
+        result := op;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE assertNumericOrSetOp*(type: Types.PType; literal: STRING; op: BinaryOperator; intOp, setOp: BinaryOperator): BinaryOperator;
+VAR
+    result: BinaryOperator;
+BEGIN
+    assertOpType(type, numericOrSetOpTypeCheck, literal);
+    IF Types.isInt(type) THEN
+        result := intOp;
+    ELSIF type = Types.basic.set THEN
+        result := setOp;
+    ELSE
+        result := op;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE assertIntOp(type: Types.PType; literal: STRING; op: BinaryOperator): BinaryOperator;
+BEGIN
+    assertOpType(type, intOpTypeCheck, literal);
+    RETURN op;
+END;
+
+PROCEDURE MulOperator.handleLiteral(s: STRING);
+VAR
+    o: BinaryOperator;
+BEGIN
+    parent <- SELF.parent()(PTerm);
+    type <- parent.type();
+    IF s = "*" THEN
+        o := assertNumericOrSetOp(type, s, Operator.mulReal, Operator.mulInt, Operator.setIntersection);
+    ELSIF s = "/" THEN
+        IF Types.isInt(type) THEN
+            Errors.raise("operator DIV expected for integer division");
+        END;
+        o := assertNumericOrSetOp(type, s, Operator.divReal, NIL, Operator.setSymmetricDiff);
+    ELSIF s = "DIV" THEN
+        o := assertIntOp(type, s, Operator.divInt);
+    ELSIF s = "MOD" THEN
+        o := assertIntOp(type, s, Operator.mod);
+    ELSIF s = "&" THEN
+        IF type # Types.basic.bool THEN
+            Errors.raise("BOOLEAN expected as operand of '&', got '"
+                                 + type.description() + "'");
+        END;
+        o := Operator.and;
+    ELSE
+        ASSERT(FALSE);
+    END;
+
+    parent.handleOperator(o);
+END;
+
 PROCEDURE Const.Const(factor: PFactor)
 PROCEDURE Const.Const(factor: PFactor)
     | SUPER(factor),
     | SUPER(factor),
       factor(factor);
       factor(factor);
@@ -213,4 +316,28 @@ BEGIN
         escapeString(s));
         escapeString(s));
 END;
 END;
 
 
+PROCEDURE IntOpTypeCheck.expect(): STRING;
+    RETURN Types.intsDescription();
+END;
+
+PROCEDURE IntOpTypeCheck.check(t: Types.PType): BOOLEAN;
+    RETURN Types.isInt(t);
+END;
+
+PROCEDURE NumericOpTypeCheck.expect(): STRING;
+    RETURN "numeric type";
+END;
+
+PROCEDURE NumericOpTypeCheck.check(t: Types.PType): BOOLEAN;
+    RETURN Types.numeric.indexOf(t) # -1;
+END;
+
+PROCEDURE NumericOrSetOpTypeCheck.expect(): STRING;
+    RETURN SUPER() + " or SET";
+END;
+
+PROCEDURE NumericOrSetOpTypeCheck.check(t: Types.PType): BOOLEAN;
+    RETURN SUPER(t) OR (t = Types.basic.set);
+END;
+
 END ContextExpression.
 END ContextExpression.

+ 8 - 1
src/ob/ContextHierarchy.ob

@@ -202,9 +202,16 @@ PROCEDURE unwrapType*(id: Types.PId): Types.PType;
 END;
 END;
 
 
 PROCEDURE throwTypeMismatch(from, to: Types.PType);
 PROCEDURE throwTypeMismatch(from, to: Types.PType);
+VAR
+    fromDescription: STRING;
 BEGIN
 BEGIN
+    IF from # NIL THEN
+        fromDescription := "'" + from.description() + "'";
+    ELSE
+        fromDescription := "no type (proper procedure call)";
+    END;
     Errors.raise("type mismatch: expected '" + to.description() 
     Errors.raise("type mismatch: expected '" + to.description() 
-               + "', got '" + from.description() + "'");
+               + "', got " + fromDescription);
 END;
 END;
 
 
 PROCEDURE checkTypeMatch*(from, to: Types.PType);
 PROCEDURE checkTypeMatch*(from, to: Types.PType);

+ 1 - 1
src/oberon/oberon_grammar.js

@@ -115,7 +115,7 @@ exports.language = {
             FormalType:         Context.FormalType,
             FormalType:         Context.FormalType,
             Term:               ContextExpression.Term,
             Term:               ContextExpression.Term,
             AddOperator:        Context.AddOperator,
             AddOperator:        Context.AddOperator,
-            MulOperator:        Context.MulOperator,
+            MulOperator:        ContextExpression.MulOperator,
             SimpleExpression:   Context.SimpleExpression, 
             SimpleExpression:   Context.SimpleExpression, 
             Expression:         Context.Expression,
             Expression:         Context.Expression,
             For:                Context.For,
             For:                Context.For,

+ 3 - 1
test/test_unit.js

@@ -848,7 +848,9 @@ return {
          ["p2(TRUE, 1)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
          ["p2(TRUE, 1)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
          ["p2(1, 1)", "type mismatch for argument 2: 'INTEGER' cannot be converted to 'BOOLEAN'"],
          ["p2(1, 1)", "type mismatch for argument 2: 'INTEGER' cannot be converted to 'BOOLEAN'"],
          ["p3", "procedure returning a result cannot be used as a statement"],
          ["p3", "procedure returning a result cannot be used as a statement"],
-         ["p3()", "procedure returning a result cannot be used as a statement"]
+         ["p3()", "procedure returning a result cannot be used as a statement"],
+         ["IF p() THEN END", "procedure returning no result cannot be used in an expression"],
+         ["IF ~p() THEN END", "type mismatch: expected 'BOOLEAN', got no type (proper procedure call)"]
          )
          )
 ),
 ),
 "procedure assignment": testWithContext(
 "procedure assignment": testWithContext(