Forráskód Böngészése

expression parsing refactoring

Vladislav Folts 9 éve
szülő
commit
9c0ea8928b

BIN
bin/compiled.zip


+ 81 - 66
src/eberon/EberonContextExpression.ob

@@ -4,7 +4,7 @@ IMPORT
     Context, ContextExpression, ContextHierarchy, 
     EberonContextDesignator, EberonContextProcedure, 
     EberonMap, EberonOperator, EberonString, EberonTypePromotion, 
-    Errors, Expression, LanguageContext,
+    Errors, Expression, ExpressionTree, LanguageContext,
     JS,
     Object, Record, Types;
 TYPE
@@ -14,16 +14,9 @@ TYPE
 
     RelationExpression* = RECORD(ContextExpression.ExpressionNode)
         PROCEDURE RelationExpression(parent: ContextExpression.PExpressionHandler);
-
-        PROCEDURE handleTypePromotion(promotion: EberonTypePromotion.PCombined);
-
-        currentTypePromotion: EberonTypePromotion.PCombined;
     END;
 
     SimpleExpression* = RECORD(ContextExpression.SimpleExpression)
-        typePromotion: EberonTypePromotion.PCombined;
-        currentPromotion: EberonTypePromotion.PMaybe;
-        orHandled: BOOLEAN;
     END;
 
     Term* = RECORD(ContextExpression.Term)
@@ -43,8 +36,22 @@ TYPE
     MulOperator* = RECORD(ContextExpression.MulOperator)
     END;
 
-    Ops = RECORD(ContextExpression.Ops)
+    Ops = RECORD(ExpressionTree.Ops)
+    END;
+
+    Node = RECORD(ExpressionTree.Node)
+        PROCEDURE Node();
+
+        currentTypePromotion: EberonTypePromotion.PCombined;
+    END;
+    PNode = POINTER TO Node;
+
+    SimpleList = RECORD(ExpressionTree.SimpleList)
+        typePromotion: EberonTypePromotion.PCombined;
+        currentPromotion: EberonTypePromotion.PMaybe;
+        orHandled: BOOLEAN;
     END;
+    PSimpleList = POINTER TO SimpleList;
 
     BeginTypePromotionAndMsg = RECORD(ContextHierarchy.Message)
         result: EberonTypePromotion.PCombined;
@@ -147,7 +154,7 @@ BEGIN
 END;
 
 PROCEDURE RelationExpression.RelationExpression(parent: ContextExpression.PExpressionHandler)
-    | SUPER(parent, globalOps);
+    | SUPER(parent, NEW Node());
 END;
 
 PROCEDURE RelationExpression.handleMessage(VAR msg: ContextHierarchy.Message): Object.PType;
@@ -161,22 +168,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE RelationExpression.handleTypePromotion(promotion: EberonTypePromotion.PCombined);
-BEGIN
-    SELF.currentTypePromotion := promotion;
-END;
-
-PROCEDURE RelationExpression.handleLiteral(s: STRING);
-BEGIN
-    IF SELF.currentTypePromotion # NIL THEN
-        SELF.currentTypePromotion.clear();
-    END;
-    SUPER(s);
-END;
-
-PROCEDURE Ops.in(left, right: Types.PType; cx: ContextHierarchy.Node): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.in(left, right: Types.PType; cx: ContextHierarchy.Node): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF right IS EberonMap.PType THEN
         EberonContextDesignator.checkMapKeyType(left);
@@ -189,29 +183,20 @@ END;
 
 PROCEDURE RelationExpression.endParse(): BOOLEAN;
 BEGIN
-    IF SELF.currentTypePromotion # NIL THEN
+    node <- SELF.node(PNode); 
+    IF node.currentTypePromotion # NIL THEN
         void <- SELF.parent().handleMessage(
-            NEW EberonContextDesignator.TransferPromotedTypesMsg(SELF.currentTypePromotion)^);
+            NEW EberonContextDesignator.TransferPromotedTypesMsg(node.currentTypePromotion)^);
     END;
     RETURN SUPER();
 END;
 
-PROCEDURE SimpleExpression.handleOperator(op: STRING);
-BEGIN
-    SUPER(op);
-
-    IF SELF.typePromotion # NIL THEN
-        SELF.currentPromotion := SELF.typePromotion.next();
-    ELSE
-        SELF.orHandled := TRUE;
-    END;
-END;
-
-PROCEDURE setSimpleExpressionTypePromotion(VAR e: SimpleExpression): EberonTypePromotion.PMaybe;
+PROCEDURE setSimpleExpressionTypePromotion(VAR s: SimpleExpression): EberonTypePromotion.PMaybe;
 BEGIN
+    e <- s.list(PSimpleList);
     IF e.currentPromotion = NIL THEN
         msg <- EberonContextProcedure.BeginTypePromotionOrMsg();
-        void <- e.parent().handleMessage(msg);
+        void <- s.parent().handleMessage(msg);
         e.typePromotion := msg.result;
         IF e.typePromotion # NIL THEN
             IF e.orHandled THEN
@@ -238,14 +223,6 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE SimpleExpression.endParse(): BOOLEAN;
-BEGIN
-    IF SELF.typePromotion # NIL THEN
-        SELF.parent()^(RelationExpression).handleTypePromotion(SELF.typePromotion);
-    END;
-    RETURN SUPER();
-END;
-
 PROCEDURE setTermTypePromotion(VAR term: Term): EberonTypePromotion.PMaybe;
 BEGIN
     IF term.currentPromotion = NIL THEN
@@ -306,9 +283,9 @@ BEGIN
     END;
 END;
 
-PROCEDURE Ops.plus(type: Types.PType): ContextExpression.BinaryOperator;
+PROCEDURE Ops.plus(type: Types.PType): ExpressionTree.BinaryOperator;
 VAR
-    result: ContextExpression.BinaryOperator;
+    result: ExpressionTree.BinaryOperator;
 BEGIN
     IF (type = EberonString.string) OR (type IS Types.PString) THEN
         result := EberonOperator.addStr;
@@ -328,9 +305,9 @@ BEGIN
     RETURN TRUE;
 END;
 
-PROCEDURE Ops.eq(type: Types.PType): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.eq(type: Types.PType): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF type = EberonString.string THEN
         result := EberonOperator.equalStr;
@@ -340,9 +317,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Ops.notEq(type: Types.PType): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.notEq(type: Types.PType): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF type = EberonString.string THEN
         result := EberonOperator.notEqualStr;
@@ -352,9 +329,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Ops.less(type: Types.PType): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.less(type: Types.PType): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF type = EberonString.string THEN
         result := EberonOperator.lessStr;
@@ -364,9 +341,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Ops.greater(type: Types.PType): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.greater(type: Types.PType): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF type = EberonString.string THEN
         result := EberonOperator.greaterStr;
@@ -376,9 +353,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Ops.lessEq(type: Types.PType): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.lessEq(type: Types.PType): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF type = EberonString.string THEN
         result := EberonOperator.lessEqualStr;
@@ -388,9 +365,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Ops.greaterEq(type: Types.PType): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.greaterEq(type: Types.PType): ExpressionTree.BinaryOperatorCx;
 VAR
-    result: ContextExpression.BinaryOperatorCx;
+    result: ExpressionTree.BinaryOperatorCx;
 BEGIN
     IF type = EberonString.string THEN
         result := EberonOperator.greaterEqualStr;
@@ -400,10 +377,10 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Ops.is(cx: ContextHierarchy.Node): ContextExpression.BinaryOperatorCx;
+PROCEDURE Ops.is(cx: ContextHierarchy.Node): ExpressionTree.BinaryOperatorCx;
 VAR
-    impl: ContextExpression.BinaryOperatorCx;
-    r: ContextExpression.BinaryOperatorCx;
+    impl: ExpressionTree.BinaryOperatorCx;
+    r: ExpressionTree.BinaryOperatorCx;
 
     PROCEDURE is(left, right: Expression.PType; lcx: LanguageContext.PType): Expression.PType;
     BEGIN
@@ -413,7 +390,7 @@ VAR
             IF v IS EberonTypePromotion.PVariable THEN
                 msg <- EberonContextDesignator.PromoteTypeMsg(
                             v, 
-                            ContextExpression.unwrapType(right.designator().info()));
+                            ExpressionTree.unwrapType(right.designator().info()));
                 void <- cx.handleMessage(msg);
             END;
         END;
@@ -438,6 +415,44 @@ BEGIN
     RETURN result;
 END;
 
+PROCEDURE Node.Node()
+    | SUPER(globalOps);
+END;
+
+PROCEDURE Node.makeSimple(): ExpressionTree.PSimpleList;
+    RETURN NEW SimpleList();
+END;
+
+PROCEDURE Node.addSimple(s: ExpressionTree.PSimpleList);
+BEGIN
+    typePromotion <- s(PSimpleList).typePromotion;
+    IF typePromotion # NIL THEN
+        SELF.currentTypePromotion := typePromotion;
+    END;
+
+    SUPER(s);
+END;
+
+PROCEDURE Node.addOp(op: STRING);
+BEGIN
+    IF SELF.currentTypePromotion # NIL THEN
+        SELF.currentTypePromotion.clear();
+    END;
+
+    SUPER(op);
+END;
+
+PROCEDURE SimpleList.addOp(op: STRING);
+BEGIN
+    SUPER(op);
+
+    IF SELF.typePromotion # NIL THEN
+        SELF.currentPromotion := SELF.typePromotion.next();
+    ELSE
+        SELF.orHandled := TRUE;
+    END;
+END;
+
 BEGIN
     NEW(globalOps);
 END EberonContextExpression.

+ 2 - 2
src/eberon/EberonContextProcedure.ob

@@ -5,7 +5,7 @@ IMPORT
     ContextProcedure, ContextType, 
     EberonConstructor, EberonContext, EberonContextDesignator, 
     EberonDynamicArray, EberonMap, EberonRecord, EberonTypePromotion, EberonTypes,
-    Errors, Expression, LanguageContext, Object, Procedure, Record, Types, Variable;
+    Errors, Expression, ExpressionTree, LanguageContext, Object, Procedure, Record, Types, Variable;
 TYPE
     ProcOrMethodDeclaration* = RECORD(ContextProcedure.Declaration)
         boundType: EberonRecord.PRecord;
@@ -316,7 +316,7 @@ END;
 PROCEDURE ProcOrMethodId.handleLiteral(s: STRING);
 BEGIN
     ss <- ContextHierarchy.getSymbolAndScope(SELF.root()^, SELF.maybeTypeId);
-    type <- ContextExpression.unwrapType(ss.symbol().info());
+    type <- ExpressionTree.unwrapType(ss.symbol().info());
     IF ~(type IS EberonRecord.PRecord) THEN
         Errors.raise(
               "RECORD type expected in method declaration, got '"

+ 3 - 3
src/eberon/EberonContextType.ob

@@ -1,9 +1,9 @@
 MODULE EberonContextType;
 IMPORT
     Chars,
-    Context, ContextExpression, ContextHierarchy, ContextProcedure, ContextType, 
+    Context, ContextHierarchy, ContextProcedure, ContextType, 
     EberonContext, EberonDynamicArray, EberonMap, EberonRecord, EberonTypes,
-    Errors,
+    Errors, ExpressionTree,
     Object, Procedure, R := Record, ScopeBase, Types;
 CONST
     dynamicArrayLength = -1;
@@ -206,7 +206,7 @@ END;
 PROCEDURE Map.handleQIdent(q: ContextHierarchy.QIdent);
 BEGIN
     s <- ContextHierarchy.getQIdSymbolAndScope(SELF.root()^, q);
-    type <- ContextExpression.unwrapType(s.symbol().info());
+    type <- ExpressionTree.unwrapType(s.symbol().info());
     SELF.setType(type);
 END;
 

+ 2 - 2
src/ob/ContextCase.ob

@@ -1,7 +1,7 @@
 MODULE ContextCase;
 IMPORT
     Cast, Chars, CodeGenerator, ConstValue, ContextExpression, ContextHierarchy,
-    Designator, Errors, Expression, 
+    Designator, Errors, Expression, ExpressionTree,
     Record, Scope, String, Symbols, TypeId, Types, Variable;
 TYPE
     Type* = RECORD(ContextExpression.ExpressionHandler)
@@ -187,7 +187,7 @@ BEGIN
         scope.addSymbol(NEW Symbols.Symbol(guardVar.id(), NEW GuardedVariable(guardVar, info.type())), FALSE);
     END;
 
-    SELF.codeGenerator().write(ContextExpression.typeTest(e, info, SELF).code());
+    SELF.codeGenerator().write(ExpressionTree.typeTest(e, info, SELF).code());
 END;
 
 PROCEDURE Label.endParse(): BOOLEAN;

+ 3 - 3
src/ob/ContextDesignator.ob

@@ -1,7 +1,7 @@
 MODULE ContextDesignator;
 IMPORT
     Code, ConstValue, ContextExpression, ContextHierarchy, 
-    Designator, Errors, Expression,
+    Designator, Errors, Expression, ExpressionTree,
     Record, ScopeBase, String, TypeId, Types, Variable;
 TYPE
     Index* = RECORD
@@ -97,12 +97,12 @@ PROCEDURE Type.handleTypeCast(type: Types.PType);
 BEGIN
     info <- SELF.info;
     IF info IS Types.PVariable THEN
-        ContextExpression.checkTypeCast(info, SELF.currentType, type, "type cast");
+        ExpressionTree.checkTypeCast(info, SELF.currentType, type, "type cast");
     ELSE
         Errors.raise("cannot apply type cast to " + info.idType());
     END;
 
-    code <- SELF.root().language().rtl.typeGuard(SELF.code, ContextExpression.castCode(type, SELF));
+    code <- SELF.root().language().rtl.typeGuard(SELF.code, ExpressionTree.castCode(type, SELF));
     SELF.code := code;
 
     SELF.currentType := type;

+ 31 - 704
src/ob/ContextExpression.ob

@@ -1,84 +1,28 @@
 MODULE ContextExpression;
 IMPORT 
-    Cast, Chars, CodeGenerator, ConstValue, Context, ContextHierarchy, 
-    Designator, Errors, Expression, JS, LanguageContext, Operator, 
-    Procedure, Record, Scope, String, TypeId, Types;
+    Chars, CodeGenerator, ConstValue, ContextHierarchy, 
+    Designator, Errors, Expression, ExpressionTree, Operator, 
+    Procedure, Scope, String, Types;
 TYPE
     ExpressionHandler* = RECORD(ContextHierarchy.Node)
         PROCEDURE handleExpression*(e: Expression.PType);
     END;
     PExpressionHandler* = POINTER TO ExpressionHandler;
 
-    BinaryOperator* = PROCEDURE(l, r: Expression.PType): Expression.PType;
-    BinaryOperatorCx* = PROCEDURE(l, r: Expression.PType; cx: LanguageContext.PType): Expression.PType;
-
-    Ops* = RECORD
-        PROCEDURE eq*(type: Types.PType): BinaryOperatorCx;
-        PROCEDURE notEq*(type: Types.PType): BinaryOperatorCx;
-        PROCEDURE less*(type: Types.PType): BinaryOperatorCx;
-        PROCEDURE greater*(type: Types.PType): BinaryOperatorCx;
-        PROCEDURE lessEq*(type: Types.PType): BinaryOperatorCx;
-        PROCEDURE greaterEq*(type: Types.PType): BinaryOperatorCx;
-        PROCEDURE is*(cx: ContextHierarchy.Node): BinaryOperatorCx;
-        PROCEDURE in*(left, right: Types.PType; cx: ContextHierarchy.Node): BinaryOperatorCx;
-        PROCEDURE plus*(type: Types.PType): BinaryOperator;
-
-        PROCEDURE eqExpect(): STRING;
-        PROCEDURE strongRelExpect(): STRING;
-        PROCEDURE relExpect(): STRING;
-        PROCEDURE plusExpect*(): STRING;
-
-        PROCEDURE coalesceType*(leftType, rightType: Types.PType): Types.PType;
-    END;
-    POps = POINTER TO Ops;
-
-    PSimpleItemOp = POINTER TO SimpleItemOp;
-    SimpleItem = RECORD
-        expression: Expression.PType;
-        next: PSimpleItemOp;
-    END;
-
-    SimpleItemOp = RECORD (SimpleItem)
-        PROCEDURE SimpleItemOp(op: STRING);
-
-        op: STRING;
-    END;
-
-    SimpleList = RECORD (SimpleItem)
-        PROCEDURE addExpression(e: Expression.PType);
-        PROCEDURE addOp(op: STRING);
-
-        unaryOp: STRING;
-        last: PSimpleItemOp;
-    END;
-    PSimpleList = POINTER TO SimpleList;
-
-    Item = RECORD
-        simple: PSimpleList;
-        next: POINTER TO ItemOp; 
-    END;
-
-    ItemOp = RECORD (Item)
-        PROCEDURE ItemOp(op: STRING);
-
-        op: STRING;
-    END;
-
     SimpleExpression* = RECORD(ExpressionHandler)
         PROCEDURE SimpleExpression*(parent: ContextHierarchy.PNode); 
         PROCEDURE handleOperator*(op: STRING);
 
-        list: PSimpleList;
+        list-: ExpressionTree.PSimpleList;
     END;
     PSimpleExpression = POINTER TO SimpleExpression;
 
     ExpressionNode* = RECORD(ContextHierarchy.Node)
-        PROCEDURE ExpressionNode*(parent: PExpressionHandler; ops: POps);
-        PROCEDURE handleSimple(s: PSimpleList);
+        PROCEDURE ExpressionNode*(parent: PExpressionHandler; node: ExpressionTree.PNode);
+        PROCEDURE handleSimple(s: ExpressionTree.PSimpleList);
 
-        relOps: POps;
-        list: Item;
-        last: POINTER TO ItemOp;
+        relOps: ExpressionTree.POps;
+        node-: ExpressionTree.PNode;
     END;
     PExpressionNode = POINTER TO ExpressionNode;
 
@@ -93,10 +37,10 @@ TYPE
 
     Term* = RECORD(ExpressionHandler)
         PROCEDURE type(): Types.PType;
-        PROCEDURE handleOperator(op: BinaryOperator);
+        PROCEDURE handleOperator(op: ExpressionTree.BinaryOperator);
 
         expression: Expression.PType;
-        operator: BinaryOperator;
+        operator: ExpressionTree.BinaryOperator;
     END;
     PTerm = POINTER TO Term;
 
@@ -138,601 +82,48 @@ TYPE
         expression: STRING;
     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;
-    globalOps: POps;
+    intOpTypeCheck: ExpressionTree.IntOpTypeCheck;
+    globalOps: ExpressionTree.POps;
 
-PROCEDURE throwOperatorTypeMismatch(op, expect: STRING; type: Types.PType);
+PROCEDURE assertIntOp(type: Types.PType; literal: STRING; op: ExpressionTree.BinaryOperator): ExpressionTree.BinaryOperator;
 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);
+    ExpressionTree.assertOpType(type, intOpTypeCheck, literal);
     RETURN op;
 END;
 
-PROCEDURE useIntOrderOp(t: Types.PType): BOOLEAN;
-    RETURN Types.isInt(t) OR (t = Types.basic.ch);
-END;
-
-PROCEDURE useIntEqOp(t: Types.PType): BOOLEAN;
-    RETURN Types.isInt(t)
-        OR (t = Types.basic.bool)
-        OR (t = Types.basic.ch)
-        OR (t IS Record.PPointer)
-        OR (t IS Types.PProcedure)
-        OR (t = Types.nil);
-END;
-
-PROCEDURE throwTypeNameExpected();
-BEGIN
-    Errors.raise("type name expected");
-END;
-
-PROCEDURE unwrapTypeId*(id: Types.PId): TypeId.PType;
-VAR
-    result: TypeId.PType;
-BEGIN
-    IF ~(id IS TypeId.PType) THEN
-        throwTypeNameExpected();
-    ELSE
-        result := id;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE unwrapType*(id: Types.PId): Types.PStorageType;
-    RETURN unwrapTypeId(id).type();
-END;
-
-PROCEDURE throwTypeMismatch(from, to: Types.PType);
-VAR
-    fromDescription: STRING;
-BEGIN
-    IF from # NIL THEN
-        fromDescription := "'" + from.description() + "'";
-    ELSE
-        fromDescription := "no type (proper procedure call)";
-    END;
-    Errors.raise("type mismatch: expected '" + to.description() 
-               + "', got " + fromDescription);
-END;
-
-PROCEDURE checkTypeMatch(from, to: Types.PType);
-BEGIN
-    IF ~Cast.areTypesMatch(from, to) THEN
-        throwTypeMismatch(from, to);
-    END;
-END;
-
-PROCEDURE checkImplicitCast(cx: ContextHierarchy.Root; from, to: Types.PType);
-VAR
-    op: LanguageContext.PCastOp;
-BEGIN
-    IF cx.language().types.implicitCast(from, to, FALSE, op) # Cast.errNo THEN
-        throwTypeMismatch(from, to);
-    END;
-END;
-
 PROCEDURE promoteExpressionType(cx: ContextHierarchy.Root; left, right: Expression.PType);
 BEGIN
     IF left # NIL THEN
         rightType <- right.type();
         leftType <- left.type();
         IF (leftType # NIL) & (rightType # NIL) THEN
-            checkImplicitCast(cx, rightType, leftType);
-        END;
-    END;
-END;
-
-PROCEDURE promoteTypeInExpression(e: Expression.PType; type: Types.PType): Expression.PType;
-VAR
-    v: CHAR;
-    result: Expression.PType;
-BEGIN
-    fromType <- e.type();
-    IF (type = Types.basic.ch) & (fromType IS Types.PString) & Types.stringAsChar(fromType^, v) THEN
-        result := Expression.makeSimple(String.fromInt(ORD(v)), type);
-    ELSE
-        result := e;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE checkTypeCast*(fromInfo: Types.PVariable; fromType, toType: Types.PType; msg: STRING);
-VAR
-    PROCEDURE checkCommonBase(from, to: Record.PType; prefix: STRING);
-    BEGIN
-        t <- to.base;
-        WHILE (t # NIL) & (t # from) DO
-            t := t.base;
-        END;
-        IF t = NIL THEN
-            Errors.raise(prefix + ": '" + to.description()
-                       + "' is not an extension of '" + from.description() + "'");
-        END;
-    END;
-
-BEGIN
-    prefix <- "invalid " + msg;
-
-    pointerExpected <- fromType IS Record.PPointer;
-    IF ~pointerExpected & ~(fromType IS Record.PType) THEN
-        Errors.raise(
-            prefix + ": POINTER to type or RECORD expected, got '"
-            + fromType.description() + "'");
-    END;
-
-    IF ~pointerExpected THEN
-        IF (fromInfo # NIL) & ~fromInfo.isReference() THEN
-            Errors.raise(
-                prefix + ": a value variable cannot be used");
-        ELSIF ~(toType IS Record.PType) THEN
-            Errors.raise(
-                prefix + ": RECORD type expected as an argument of RECORD " + msg + ", got '"
-              + toType.description() + "'");
-        END;
-    ELSIF ~(toType IS Record.PPointer) THEN
-        Errors.raise(
-            prefix + ": POINTER type expected as an argument of POINTER " + msg + ", got '"
-          + toType.description() + "'");
-    END;
-
-    IF pointerExpected THEN
-        checkCommonBase(Record.pointerBase(fromType(Record.PPointer)^), 
-                        Record.pointerBase(toType(Record.PPointer)^), 
-                        prefix);
-    ELSE
-        checkCommonBase(fromType(Record.PType), 
-                        toType(Record.PType), 
-                        prefix);
-    END;
-END checkTypeCast;
-
-PROCEDURE Ops.eq(type: Types.PType): BinaryOperatorCx;
-VAR
-    result: BinaryOperatorCx;
-BEGIN
-    IF useIntEqOp(type) THEN
-        result := Operator.equalInt;
-    ELSIF Types.isString(type) THEN
-        result := Operator.equalStr;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.equalReal;
-    ELSIF type = Types.basic.set THEN
-        result := Operator.equalSet;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE Ops.notEq(type: Types.PType): BinaryOperatorCx;
-VAR
-    result: BinaryOperatorCx;
-BEGIN
-    IF useIntEqOp(type) THEN
-        result := Operator.notEqualInt;
-    ELSIF Types.isString(type) THEN
-        result := Operator.notEqualStr;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.notEqualReal;
-    ELSIF type = Types.basic.set THEN
-        result := Operator.notEqualSet;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE Ops.less(type: Types.PType): BinaryOperatorCx;
-VAR
-    result: BinaryOperatorCx;
-BEGIN
-    IF useIntOrderOp(type) THEN
-        result := Operator.lessInt;
-    ELSIF Types.isString(type) THEN
-        result := Operator.lessStr;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.lessReal;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE Ops.greater(type: Types.PType): BinaryOperatorCx;
-VAR
-    result: BinaryOperatorCx;
-BEGIN
-    IF useIntOrderOp(type) THEN
-        result := Operator.greaterInt;
-    ELSIF Types.isString(type) THEN
-        result := Operator.greaterStr;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.greaterReal;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE Ops.lessEq(type: Types.PType): BinaryOperatorCx;
-VAR
-    result: BinaryOperatorCx;
-BEGIN
-    IF useIntOrderOp(type) THEN
-        result := Operator.eqLessInt;
-    ELSIF Types.isString(type) THEN
-        result := Operator.eqLessStr;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.eqLessReal;
-    ELSIF type = Types.basic.set THEN
-        result := Operator.setInclL;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE Ops.greaterEq(type: Types.PType): BinaryOperatorCx;
-VAR
-    result: BinaryOperatorCx;
-BEGIN
-    IF useIntOrderOp(type) THEN
-        result := Operator.eqGreaterInt;
-    ELSIF Types.isString(type) THEN
-        result := Operator.eqGreaterStr;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.eqGreaterReal;
-    ELSIF type = Types.basic.set THEN
-        result := Operator.setInclR;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE castCode*(type: Types.PType; cx: Context.Type): STRING;
-VAR
-    result: STRING;
-BEGIN
-    IF type IS Record.PPointer THEN
-        baseType <- Record.pointerBase(type^);
-        result := Record.constructor(cx, baseType^);
-    ELSE
-        result := Record.constructor(cx, type(Record.PType)^);
-    END;
-    RETURN result;
-END;
-
-PROCEDURE typeTest*(left: Expression.PType; right: Types.PId; cx: ContextHierarchy.Node): Expression.PType;
-VAR
-    leftVar: Types.PVariable;
-BEGIN
-    d <- left.designator();
-    IF d # NIL THEN
-        info <- d.info();
-        IF info IS Types.PVariable THEN
-            leftVar := info;
-        END;
-    END;
-
-    rightType <- unwrapType(right);
-    checkTypeCast(leftVar, left.type(), rightType, "type test");
-    RETURN Operator.is(left, Expression.makeSimple(castCode(rightType, cx), NIL));
-END;
-
-PROCEDURE Ops.is(cx: ContextHierarchy.Node): BinaryOperatorCx;
-VAR
-    r: BinaryOperatorCx;
-
-    PROCEDURE is(left, right: Expression.PType; unused: LanguageContext.PType): Expression.PType;
-    VAR
-        result: Expression.PType;
-    BEGIN
-        rightDesignator <- right.designator();
-        IF rightDesignator = NIL THEN
-            throwTypeNameExpected();
-        ELSE
-            result := typeTest(left, rightDesignator.info(), cx);
+            ExpressionTree.checkImplicitCast(cx, rightType, leftType);
         END;
-        RETURN result;
     END;
-
-BEGIN    
-    JS.do("r = is"); (*allow closure*)
-    RETURN r;
-END;
-
-PROCEDURE Ops.in(left, right: Types.PType; cx: ContextHierarchy.Node): BinaryOperatorCx;
-BEGIN
-    IF ~Types.isInt(left) THEN
-        Errors.raise(Types.intsDescription() 
-                     + " expected as an element of SET, got '" + left.description() + "'");
-    END;
-    checkImplicitCast(cx.root()^, right, Types.basic.set);
-
-    RETURN Operator.setHasBit;
-END;
-
-PROCEDURE Ops.eqExpect(): STRING;
-    RETURN "numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE";
 END;
 
-PROCEDURE Ops.strongRelExpect(): STRING;
-    RETURN "numeric type or CHAR or character array";
-END;
-
-PROCEDURE Ops.relExpect(): STRING;
-    RETURN "numeric type or SET or CHAR or character array";
-END;
-
-PROCEDURE Ops.coalesceType(leftType, rightType: Types.PType): Types.PType;
-VAR
-    result: Types.PType;
-BEGIN
-    IF (leftType IS Record.PPointer) & (rightType IS Record.PPointer) THEN
-        result := Cast.findPointerBaseType(leftType, rightType^);
-        IF result = NIL THEN
-            result := Cast.findPointerBaseType(rightType, leftType^);
-        END;
-    END;
-
-    IF result = NIL THEN
-        (*special case for strings*)
-        isStrings <- Types.isString(leftType) & Types.isString(rightType);
-        IF ~isStrings THEN
-            checkTypeMatch(rightType, leftType);
-        END;
-        result := leftType;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE ItemOp.ItemOp(op: STRING)
-    | op(op);
-END;
-
-PROCEDURE SimpleItemOp.SimpleItemOp(op: STRING)
-    | op(op);
-END;
-
-PROCEDURE ExpressionNode.ExpressionNode(parent: PExpressionHandler; ops: POps)
+PROCEDURE ExpressionNode.ExpressionNode(parent: PExpressionHandler; node: ExpressionTree.PNode)
     | SUPER(parent),
-      relOps(ops);
+      node(node);
 BEGIN
-    IF ops = NIL THEN
-        SELF.relOps := globalOps;
+    IF SELF.node = NIL THEN
+        SELF.node := NEW ExpressionTree.Node(globalOps);
     END;
 END;
 
-PROCEDURE notTypeId(e: Expression.PType);
-BEGIN
-    d <- e.designator();
-    IF d # NIL THEN
-        info <- d.info();
-        IF info IS TypeId.PType THEN
-            Errors.raise("type name '" + info.type().description() + "' cannot be used as an expression");
-        END;
-    END;
-END;
-
-PROCEDURE relationOp(left, right: Expression.PType; literal: STRING; ops: POps; context: ContextHierarchy.Node): BinaryOperatorCx;
-VAR
-    type: Types.PType;
-    o: BinaryOperatorCx;
-    mismatch: STRING;
-BEGIN
-    notTypeId(left);
-    IF literal # "IS" THEN
-        notTypeId(right);
-
-        IF literal # "IN" THEN
-            type := ops.coalesceType(left.type(), right.type());
-        END;
-    END;
-
-    IF literal = "=" THEN
-        o := ops.eq(type);
-        IF o = NIL THEN
-            mismatch := ops.eqExpect();
-        END;
-    ELSIF literal = "#" THEN
-        o := ops.notEq(type);
-        IF o = NIL THEN
-            mismatch := ops.eqExpect();
-        END;
-    ELSIF literal =  "<" THEN
-        o := ops.less(type);
-        IF o = NIL THEN
-            mismatch := ops.strongRelExpect();
-        END;
-    ELSIF literal =  ">" THEN
-        o := ops.greater(type);
-        IF o = NIL THEN
-            mismatch := ops.strongRelExpect();
-        END;
-    ELSIF literal = "<=" THEN
-        o := ops.lessEq(type);
-        IF o = NIL THEN
-            mismatch := ops.relExpect();
-        END;
-    ELSIF literal = ">=" THEN
-        o := ops.greaterEq(type);
-        IF o = NIL THEN
-            mismatch := ops.relExpect();
-        END;
-    ELSIF literal = "IS" THEN
-        o := ops.is(context);
-    ELSIF literal = "IN" THEN
-        o := ops.in(left.type(), right.type(), context);
-    END;
-
-    IF LEN(mismatch) # 0 THEN
-        throwOperatorTypeMismatch(literal, mismatch, type);
-    END;
-    RETURN o;
-END;
-
-PROCEDURE makeFirstFromSimpleList(list: SimpleList): Expression.PType;
-VAR
-    o: PROCEDURE(e: Expression.PType): Expression.PType;
-BEGIN
-    result <- list.expression;
-    IF list.unaryOp = "-" THEN
-        type <- result.type();
-        IF Types.isInt(type) THEN
-            o := Operator.negateInt;
-        ELSIF type = Types.basic.set THEN
-            o := Operator.setComplement;
-        ELSIF type = Types.basic.real THEN
-            o := Operator.negateReal;
-        ELSE
-            throwOperatorTypeMismatch(list.unaryOp, numericOrSetOpTypeCheck.expect(), type);
-        END;
-    ELSIF list.unaryOp = "+" THEN
-        assertOpType(result.type(), numericOpTypeCheck, list.unaryOp);
-        o := Operator.unaryPlus;
-    END;
-    IF o # NIL THEN
-        notTypeId(result);
-        result := o(result);
-    END;
-    RETURN result;
-END;
-
-PROCEDURE matchAddOperator(ops: Ops; s: STRING; type: Types.PType): BinaryOperator;
-VAR
-    result: BinaryOperator;
-BEGIN
-    IF s = "+" THEN
-        result := ops.plus(type);
-    ELSIF s = "-" THEN
-        result := assertNumericOrSetOp(type, s, Operator.subReal, Operator.subInt, Operator.setDiff);
-    ELSIF s = "OR" THEN
-        IF type # Types.basic.bool THEN
-            Errors.raise("BOOLEAN expected as operand of 'OR', got '"
-                         + type.description() + "'");
-        END;
-        result := Operator.or;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE makeFromSimpleList(list: SimpleList; ops: Ops; cx: ContextHierarchy.Root): Expression.PType;
-BEGIN
-    result <- makeFirstFromSimpleList(list);
-    next <- list.next;
-    WHILE next # NIL DO
-        notTypeId(result);
-        e <- next.expression;
-        notTypeId(e);
-        o <- matchAddOperator(ops, next.op, result.type());
-        checkImplicitCast(cx, e.type(), result.type());
-        result := o(result, e);
-
-        next := next.next;
-    END;
-    RETURN result;
-END;
-
-PROCEDURE makeFromList(list: Item; cx: PExpressionNode): Expression.PType;
-BEGIN
-    root <- cx.root();
-    result <- makeFromSimpleList(list.simple^, cx.relOps^, root^);
-    next <- list.next;
-    WHILE next # NIL DO
-        leftExpression <- result;
-        rightExpression <- makeFromSimpleList(next.simple^, cx.relOps^, root^);
-        leftExpression := promoteTypeInExpression(leftExpression, rightExpression.type());
-        rightExpression := promoteTypeInExpression(rightExpression, leftExpression.type());
-
-        o <- relationOp(leftExpression, rightExpression, next.op, cx.relOps, cx^);
-        result := o(leftExpression, rightExpression, ContextHierarchy.makeLanguageContext(cx));
-
-        next := next.next;
-    END;
-    notTypeId(result);
-
-    type <- result.type();
-    IF type = NIL THEN
-        Errors.raise("procedure returning no result cannot be used in an expression");
-    END;
-    RETURN result;
-END;
-
 PROCEDURE ExpressionNode.handleLiteral(s: STRING);
 BEGIN
-    next <- NEW ItemOp(s);
-    IF SELF.last = NIL THEN
-        SELF.list.next := next;
-    ELSE
-        SELF.last.next := next;
-    END;
-    SELF.last := next;
+    SELF.node.addOp(s);
 END;
 
 PROCEDURE ExpressionNode.codeGenerator(): CodeGenerator.PIGenerator;
     RETURN CodeGenerator.nullGenerator;
 END;
 
-PROCEDURE ExpressionNode.handleSimple(s: PSimpleList);
-BEGIN
-    IF SELF.list.simple = NIL THEN
-        SELF.list.simple := s;
-    ELSE
-        SELF.last.simple := s;
-    END;
-END;
-
 PROCEDURE ExpressionNode.endParse(): BOOLEAN;
 BEGIN
-    expression <- makeFromList(SELF.list, SELF(POINTER));
+    expression <- SELF.node.asExpression(SELF(POINTER));
 
     parent <- SELF.parent()(PExpressionHandler);
     parent.codeGenerator().write(expression.code());
@@ -742,7 +133,7 @@ END;
 
 PROCEDURE SimpleExpression.SimpleExpression(parent: ContextHierarchy.PNode)
     | SUPER(parent),
-      list(NEW SimpleList());
+      list(parent(PExpressionNode).node.makeSimple());
 END; 
 
 PROCEDURE SimpleExpression.handleExpression(e: Expression.PType);
@@ -750,26 +141,6 @@ BEGIN
     SELF.list.addExpression(e);
 END;
 
-PROCEDURE SimpleList.addExpression(e: Expression.PType);
-BEGIN
-    IF SELF.expression = NIL THEN
-        SELF.expression := e;
-    ELSE
-        SELF.last.expression := e;
-    END;
-END;
-
-PROCEDURE SimpleList.addOp(op: STRING);
-BEGIN
-    next <- NEW SimpleItemOp(op);
-    IF SELF.last = NIL THEN
-        SELF.next := next;
-    ELSE
-        SELF.last.next := next;
-    END;
-    SELF.last := next;
-END;
-
 PROCEDURE SimpleExpression.handleLiteral(s: STRING);
 BEGIN
     SELF.list.unaryOp := s;
@@ -782,7 +153,7 @@ END;
 
 PROCEDURE SimpleExpression.endParse(): BOOLEAN;
 BEGIN
-    SELF.parent()^(ExpressionNode).handleSimple(SELF.list);
+    SELF.parent()^(ExpressionNode).node.addSimple(SELF.list);
     RETURN TRUE;
 END;
 
@@ -816,8 +187,8 @@ END;
 PROCEDURE Factor.endParse(): BOOLEAN;
 BEGIN
     IF SELF.logicalNot THEN
-        notTypeId(SELF.expression);
-        checkTypeMatch(SELF.expression.type(), Types.basic.bool);
+        ExpressionTree.notTypeId(SELF.expression);
+        ExpressionTree.checkTypeMatch(SELF.expression.type(), Types.basic.bool);
         SELF.expression := Operator.not(SELF.expression);
     END;
     SELF.parent()^(ExpressionHandler).handleExpression(SELF.expression);
@@ -836,9 +207,9 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE Term.handleOperator(op: BinaryOperator);
+PROCEDURE Term.handleOperator(op: ExpressionTree.BinaryOperator);
 BEGIN
-    notTypeId(SELF.expression);
+    ExpressionTree.notTypeId(SELF.expression);
     SELF.operator := op;
 END;
 
@@ -848,7 +219,7 @@ BEGIN
     IF SELF.operator = NIL THEN
         SELF.expression := e;
     ELSIF SELF.expression # NIL THEN
-        notTypeId(e);
+        ExpressionTree.notTypeId(e);
         SELF.expression := SELF.operator(SELF.expression, e);
     END;
 END;
@@ -872,17 +243,17 @@ END;
 
 PROCEDURE MulOperator.handleLiteral(s: STRING);
 VAR
-    o: BinaryOperator;
+    o: ExpressionTree.BinaryOperator;
 BEGIN
     parent <- SELF.parent()(PTerm);
     type <- parent.type();
     IF s = "*" THEN
-        o := assertNumericOrSetOp(type, s, Operator.mulReal, Operator.mulInt, Operator.setIntersection);
+        o := ExpressionTree.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);
+        o := ExpressionTree.assertNumericOrSetOp(type, s, Operator.divReal, NIL, Operator.setSymmetricDiff);
     ELSIF s = "DIV" THEN
         o := assertIntOp(type, s, Operator.divInt);
     ELSIF s = "MOD" THEN
@@ -906,26 +277,6 @@ BEGIN
     parent.handleOperator(s);
 END;
 
-PROCEDURE Ops.plus(type: Types.PType): BinaryOperator;
-VAR
-    result: BinaryOperator;
-BEGIN
-    IF type = Types.basic.set THEN
-        result := Operator.setUnion;
-    ELSIF Types.isInt(type) THEN
-        result := Operator.addInt;
-    ELSIF type = Types.basic.real THEN
-        result := Operator.addReal;
-    ELSE
-        throwOperatorTypeMismatch("+", SELF.plusExpect(), type);
-    END;
-    RETURN result;
-END;
-
-PROCEDURE Ops.plusExpect(): STRING;
-    RETURN "numeric type or SET";
-END;
-
 PROCEDURE Integer.handleInt(n: INTEGER);
 BEGIN
     SELF.parent()^(ExpressionHandler).handleExpression(expressionFromConst(
@@ -1050,30 +401,6 @@ BEGIN
     RETURN TRUE;
 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;
-
 PROCEDURE designatorAsExpression*(d: Designator.PType): Expression.PType;
 VAR
     value: ConstValue.PType;

+ 2 - 2
src/ob/ContextProcedure.ob

@@ -2,7 +2,7 @@ MODULE ContextProcedure;
 IMPORT
     Cast, Chars, CodeGenerator, Context, ContextExpression, 
     ContextHierarchy, ContextType, 
-    Errors, Expression, LanguageContext,
+    Errors, Expression, ExpressionTree, LanguageContext,
     Object, Procedure, Scope, Symbols, TypeId, Types, Variable;
 TYPE
     Declaration* = RECORD(ContextType.DeclarationAndIdentHandle)
@@ -212,7 +212,7 @@ END;
 PROCEDURE FormalParameters.handleQIdent(q: ContextHierarchy.QIdent);
 BEGIN
     s <- ContextHierarchy.getQIdSymbolAndScope(SELF.root()^, q);
-    resultType <- ContextExpression.unwrapType(s.symbol().info());
+    resultType <- ExpressionTree.unwrapType(s.symbol().info());
     SELF.doCheckResultType(resultType);
     SELF.result := resultType;
 END;

+ 4 - 4
src/ob/ContextType.ob

@@ -1,7 +1,7 @@
 MODULE ContextType;
 IMPORT
     Chars, CodeGenerator, ConstValue, Context, ContextExpression, ContextHierarchy, 
-    Errors, Expression, Object, R := Record, 
+    Errors, Expression, ExpressionTree, Object, R := Record, 
     Scope, ScopeBase, String, Symbols, TypeId, Types;
 TYPE
     HandleSymbolAsType* = RECORD(ContextHierarchy.Node)
@@ -123,7 +123,7 @@ TYPE
 PROCEDURE HandleSymbolAsType.handleQIdent(q: ContextHierarchy.QIdent);
 BEGIN
     s <- ContextHierarchy.getQIdSymbolAndScope(SELF.root()^, q);
-    SELF.setType(ContextExpression.unwrapType(s.symbol().info()));
+    SELF.setType(ExpressionTree.unwrapType(s.symbol().info()));
 END;
 
 PROCEDURE FormalType.setType(type: Types.PStorageType);
@@ -424,7 +424,7 @@ END;
 PROCEDURE RecordBase.handleQIdent(q: ContextHierarchy.QIdent);
 BEGIN
     s <- ContextHierarchy.getQIdSymbolAndScope(SELF.root()^, q);
-    base <- ContextExpression.unwrapType(s.symbol().info());
+    base <- ExpressionTree.unwrapType(s.symbol().info());
     SELF.parent()^(Record).setBaseType(base);
 END;
 
@@ -500,7 +500,7 @@ BEGIN
         msg <- NEW ForwardTypeMsg(id);
         info := SELF.parent().handleMessage(msg^)(Types.PId);
     END;
-    setPointerTypeId(SELF, ContextExpression.unwrapTypeId(info));
+    setPointerTypeId(SELF, ExpressionTree.unwrapTypeId(info));
 END;
 
 PROCEDURE Pointer.setType(type: Types.PStorageType);

+ 711 - 0
src/ob/ExpressionTree.ob

@@ -0,0 +1,711 @@
+MODULE ExpressionTree;
+IMPORT
+	Cast, Context, ContextHierarchy,
+	JS, 
+	Errors, Expression, LanguageContext, Operator, Record, String, Types, TypeId;
+TYPE
+    BinaryOperator* = PROCEDURE(l, r: Expression.PType): Expression.PType;
+    BinaryOperatorCx* = PROCEDURE(l, r: Expression.PType; cx: LanguageContext.PType): Expression.PType;
+
+    Ops* = RECORD
+        PROCEDURE eq*(type: Types.PType): BinaryOperatorCx;
+        PROCEDURE notEq*(type: Types.PType): BinaryOperatorCx;
+        PROCEDURE less*(type: Types.PType): BinaryOperatorCx;
+        PROCEDURE greater*(type: Types.PType): BinaryOperatorCx;
+        PROCEDURE lessEq*(type: Types.PType): BinaryOperatorCx;
+        PROCEDURE greaterEq*(type: Types.PType): BinaryOperatorCx;
+        PROCEDURE is*(cx: ContextHierarchy.Node): BinaryOperatorCx;
+        PROCEDURE in*(left, right: Types.PType; cx: ContextHierarchy.Node): BinaryOperatorCx;
+        PROCEDURE plus*(type: Types.PType): BinaryOperator;
+
+        PROCEDURE eqExpect(): STRING;
+        PROCEDURE strongRelExpect(): STRING;
+        PROCEDURE relExpect(): STRING;
+        PROCEDURE plusExpect*(): STRING;
+
+        PROCEDURE coalesceType*(leftType, rightType: Types.PType): Types.PType;
+    END;
+    POps* = POINTER TO Ops;
+
+    PSimpleItemOp = POINTER TO SimpleItemOp;
+    SimpleItem = RECORD
+        expression: Expression.PType;
+        next: PSimpleItemOp;
+    END;
+
+    SimpleItemOp = RECORD (SimpleItem)
+        PROCEDURE SimpleItemOp(op: STRING);
+
+        op: STRING;
+    END;
+
+    SimpleList* = RECORD (SimpleItem)
+        PROCEDURE addExpression*(e: Expression.PType);
+        PROCEDURE addOp*(op: STRING);
+
+        unaryOp*: STRING;
+        last: PSimpleItemOp;
+    END;
+    PSimpleList* = POINTER TO SimpleList;
+
+    Item = RECORD
+        simple: PSimpleList;
+        next: POINTER TO ItemOp; 
+    END;
+
+    Node* = RECORD (Item)
+    	PROCEDURE Node*(ops: POps);
+
+    	PROCEDURE makeSimple*(): PSimpleList;
+    	PROCEDURE addSimple*(s: PSimpleList);
+        PROCEDURE addOp*(op: STRING);
+    	PROCEDURE asExpression*(cx: ContextHierarchy.PNode): Expression.PType;
+
+    	ops: POps;
+        last: POINTER TO ItemOp;
+    END;
+    PNode* = POINTER TO Node;
+
+    ItemOp = RECORD (Item)
+        PROCEDURE ItemOp(op: STRING);
+
+        op: STRING;
+    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
+    numericOpTypeCheck: NumericOpTypeCheck;
+    numericOrSetOpTypeCheck: NumericOrSetOpTypeCheck;
+
+PROCEDURE throwTypeNameExpected*();
+BEGIN
+    Errors.raise("type name expected");
+END;
+
+PROCEDURE castCode*(type: Types.PType; cx: Context.Type): STRING;
+VAR
+    result: STRING;
+BEGIN
+    IF type IS Record.PPointer THEN
+        baseType <- Record.pointerBase(type^);
+        result := Record.constructor(cx, baseType^);
+    ELSE
+        result := Record.constructor(cx, type(Record.PType)^);
+    END;
+    RETURN result;
+END;
+
+PROCEDURE unwrapTypeId*(id: Types.PId): TypeId.PType;
+VAR
+    result: TypeId.PType;
+BEGIN
+    IF ~(id IS TypeId.PType) THEN
+        throwTypeNameExpected();
+    ELSE
+        result := id;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE unwrapType*(id: Types.PId): Types.PStorageType;
+    RETURN unwrapTypeId(id).type();
+END;
+
+PROCEDURE checkTypeCast*(fromInfo: Types.PVariable; fromType, toType: Types.PType; msg: STRING);
+VAR
+    PROCEDURE checkCommonBase(from, to: Record.PType; prefix: STRING);
+    BEGIN
+        t <- to.base;
+        WHILE (t # NIL) & (t # from) DO
+            t := t.base;
+        END;
+        IF t = NIL THEN
+            Errors.raise(prefix + ": '" + to.description()
+                       + "' is not an extension of '" + from.description() + "'");
+        END;
+    END;
+
+BEGIN
+    prefix <- "invalid " + msg;
+
+    pointerExpected <- fromType IS Record.PPointer;
+    IF ~pointerExpected & ~(fromType IS Record.PType) THEN
+        Errors.raise(
+            prefix + ": POINTER to type or RECORD expected, got '"
+            + fromType.description() + "'");
+    END;
+
+    IF ~pointerExpected THEN
+        IF (fromInfo # NIL) & ~fromInfo.isReference() THEN
+            Errors.raise(
+                prefix + ": a value variable cannot be used");
+        ELSIF ~(toType IS Record.PType) THEN
+            Errors.raise(
+                prefix + ": RECORD type expected as an argument of RECORD " + msg + ", got '"
+              + toType.description() + "'");
+        END;
+    ELSIF ~(toType IS Record.PPointer) THEN
+        Errors.raise(
+            prefix + ": POINTER type expected as an argument of POINTER " + msg + ", got '"
+          + toType.description() + "'");
+    END;
+
+    IF pointerExpected THEN
+        checkCommonBase(Record.pointerBase(fromType(Record.PPointer)^), 
+                        Record.pointerBase(toType(Record.PPointer)^), 
+                        prefix);
+    ELSE
+        checkCommonBase(fromType(Record.PType), 
+                        toType(Record.PType), 
+                        prefix);
+    END;
+END checkTypeCast;
+
+PROCEDURE typeTest*(left: Expression.PType; right: Types.PId; cx: ContextHierarchy.Node): Expression.PType;
+VAR
+    leftVar: Types.PVariable;
+BEGIN
+    d <- left.designator();
+    IF d # NIL THEN
+        info <- d.info();
+        IF info IS Types.PVariable THEN
+            leftVar := info;
+        END;
+    END;
+
+    rightType <- unwrapType(right);
+    checkTypeCast(leftVar, left.type(), rightType, "type test");
+    RETURN Operator.is(left, Expression.makeSimple(castCode(rightType, cx), NIL));
+END;
+
+PROCEDURE throwTypeMismatch(from, to: Types.PType);
+VAR
+    fromDescription: STRING;
+BEGIN
+    IF from # NIL THEN
+        fromDescription := "'" + from.description() + "'";
+    ELSE
+        fromDescription := "no type (proper procedure call)";
+    END;
+    Errors.raise("type mismatch: expected '" + to.description() 
+               + "', got " + fromDescription);
+END;
+
+PROCEDURE throwOperatorTypeMismatch(op, expect: STRING; type: Types.PType);
+BEGIN
+    Errors.raise(
+        "operator '" + op +
+        "' type mismatch: " + expect + " expected, got '" +
+        type.description() + "'");
+END;
+
+PROCEDURE checkTypeMatch*(from, to: Types.PType);
+BEGIN
+    IF ~Cast.areTypesMatch(from, to) THEN
+        throwTypeMismatch(from, to);
+    END;
+END;
+
+PROCEDURE checkImplicitCast*(cx: ContextHierarchy.Root; from, to: Types.PType);
+VAR
+    op: LanguageContext.PCastOp;
+BEGIN
+    IF cx.language().types.implicitCast(from, to, FALSE, op) # Cast.errNo THEN
+        throwTypeMismatch(from, to);
+    END;
+END;
+
+PROCEDURE useIntOrderOp(t: Types.PType): BOOLEAN;
+    RETURN Types.isInt(t) OR (t = Types.basic.ch);
+END;
+
+PROCEDURE useIntEqOp(t: Types.PType): BOOLEAN;
+    RETURN Types.isInt(t)
+        OR (t = Types.basic.bool)
+        OR (t = Types.basic.ch)
+        OR (t IS Record.PPointer)
+        OR (t IS Types.PProcedure)
+        OR (t = Types.nil);
+END;
+
+PROCEDURE assertOpType*(type: Types.PType; check: OpTypeCheck; literal: STRING);
+BEGIN
+    IF ~check.check(type) THEN
+        throwOperatorTypeMismatch(literal, check.expect(), type);
+    END;
+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 assertNumericOp*(type: Types.PType; literal: STRING; op, intOp: ExpressionTree.BinaryOperator): ExpressionTree.BinaryOperator;
+VAR
+    result: ExpressionTree.BinaryOperator;
+BEGIN
+    assertOpType(type, numericOpTypeCheck, literal);
+    IF (intOp # NIL) & Types.isInt(type) THEN
+        result := intOp;
+    ELSE
+        result := op;
+    END;
+    RETURN result;
+END;
+*)
+PROCEDURE notTypeId*(e: Expression.PType);
+BEGIN
+    d <- e.designator();
+    IF d # NIL THEN
+        info <- d.info();
+        IF info IS TypeId.PType THEN
+            Errors.raise("type name '" + info.type().description() + "' cannot be used as an expression");
+        END;
+    END;
+END;
+
+PROCEDURE promoteTypeInExpression(e: Expression.PType; type: Types.PType): Expression.PType;
+VAR
+    v: CHAR;
+    result: Expression.PType;
+BEGIN
+    fromType <- e.type();
+    IF (type = Types.basic.ch) & (fromType IS Types.PString) & Types.stringAsChar(fromType^, v) THEN
+        result := Expression.makeSimple(String.fromInt(ORD(v)), type);
+    ELSE
+        result := e;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE relationOp(left, right: Expression.PType; literal: STRING; ops: Ops; context: ContextHierarchy.Node): BinaryOperatorCx;
+VAR
+    type: Types.PType;
+    o: BinaryOperatorCx;
+    mismatch: STRING;
+BEGIN
+    notTypeId(left);
+    IF literal # "IS" THEN
+        notTypeId(right);
+
+        IF literal # "IN" THEN
+            type := ops.coalesceType(left.type(), right.type());
+        END;
+    END;
+
+    IF literal = "=" THEN
+        o := ops.eq(type);
+        IF o = NIL THEN
+            mismatch := ops.eqExpect();
+        END;
+    ELSIF literal = "#" THEN
+        o := ops.notEq(type);
+        IF o = NIL THEN
+            mismatch := ops.eqExpect();
+        END;
+    ELSIF literal =  "<" THEN
+        o := ops.less(type);
+        IF o = NIL THEN
+            mismatch := ops.strongRelExpect();
+        END;
+    ELSIF literal =  ">" THEN
+        o := ops.greater(type);
+        IF o = NIL THEN
+            mismatch := ops.strongRelExpect();
+        END;
+    ELSIF literal = "<=" THEN
+        o := ops.lessEq(type);
+        IF o = NIL THEN
+            mismatch := ops.relExpect();
+        END;
+    ELSIF literal = ">=" THEN
+        o := ops.greaterEq(type);
+        IF o = NIL THEN
+            mismatch := ops.relExpect();
+        END;
+    ELSIF literal = "IS" THEN
+        o := ops.is(context);
+    ELSIF literal = "IN" THEN
+        o := ops.in(left.type(), right.type(), context);
+    END;
+
+    IF LEN(mismatch) # 0 THEN
+        throwOperatorTypeMismatch(literal, mismatch, type);
+    END;
+    RETURN o;
+END;
+
+PROCEDURE makeFirstFromSimpleList(list: SimpleList): Expression.PType;
+VAR
+    o: PROCEDURE(e: Expression.PType): Expression.PType;
+BEGIN
+    result <- list.expression;
+    IF list.unaryOp = "-" THEN
+        type <- result.type();
+        IF Types.isInt(type) THEN
+            o := Operator.negateInt;
+        ELSIF type = Types.basic.set THEN
+            o := Operator.setComplement;
+        ELSIF type = Types.basic.real THEN
+            o := Operator.negateReal;
+        ELSE
+            throwOperatorTypeMismatch(list.unaryOp, numericOrSetOpTypeCheck.expect(), type);
+        END;
+    ELSIF list.unaryOp = "+" THEN
+        assertOpType(result.type(), numericOpTypeCheck, list.unaryOp);
+        o := Operator.unaryPlus;
+    END;
+    IF o # NIL THEN
+        notTypeId(result);
+        result := o(result);
+    END;
+    RETURN result;
+END;
+
+PROCEDURE matchAddOperator(ops: Ops; s: STRING; type: Types.PType): BinaryOperator;
+VAR
+    result: BinaryOperator;
+BEGIN
+    IF s = "+" THEN
+        result := ops.plus(type);
+    ELSIF s = "-" THEN
+        result := assertNumericOrSetOp(type, s, Operator.subReal, Operator.subInt, Operator.setDiff);
+    ELSIF s = "OR" THEN
+        IF type # Types.basic.bool THEN
+            Errors.raise("BOOLEAN expected as operand of 'OR', got '"
+                         + type.description() + "'");
+        END;
+        result := Operator.or;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE makeFromSimpleList(list: SimpleList; ops: Ops; cx: ContextHierarchy.Root): Expression.PType;
+BEGIN
+    result <- makeFirstFromSimpleList(list);
+    next <- list.next;
+    WHILE next # NIL DO
+        notTypeId(result);
+        e <- next.expression;
+        notTypeId(e);
+        o <- matchAddOperator(ops, next.op, result.type());
+        checkImplicitCast(cx, e.type(), result.type());
+        result := o(result, e);
+
+        next := next.next;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE makeFromList(list: Item; ops: Ops; cx: ContextHierarchy.PNode): Expression.PType;
+BEGIN
+    root <- cx.root();
+    result <- makeFromSimpleList(list.simple^, ops, root^);
+    next <- list.next;
+    WHILE next # NIL DO
+        leftExpression <- result;
+        rightExpression <- makeFromSimpleList(next.simple^, ops, root^);
+        leftExpression := promoteTypeInExpression(leftExpression, rightExpression.type());
+        rightExpression := promoteTypeInExpression(rightExpression, leftExpression.type());
+
+        o <- relationOp(leftExpression, rightExpression, next.op, ops, cx^);
+        result := o(leftExpression, rightExpression, ContextHierarchy.makeLanguageContext(cx));
+
+        next := next.next;
+    END;
+    notTypeId(result);
+
+    type <- result.type();
+    IF type = NIL THEN
+        Errors.raise("procedure returning no result cannot be used in an expression");
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.is(cx: ContextHierarchy.Node): BinaryOperatorCx;
+VAR
+    r: BinaryOperatorCx;
+
+    PROCEDURE is(left, right: Expression.PType; unused: LanguageContext.PType): Expression.PType;
+    VAR
+        result: Expression.PType;
+    BEGIN
+        rightDesignator <- right.designator();
+        IF rightDesignator = NIL THEN
+            throwTypeNameExpected();
+        ELSE
+            result := typeTest(left, rightDesignator.info(), cx);
+        END;
+        RETURN result;
+    END;
+
+BEGIN    
+    JS.do("r = is"); (*allow closure*)
+    RETURN r;
+END;
+
+PROCEDURE Ops.in(left, right: Types.PType; cx: ContextHierarchy.Node): BinaryOperatorCx;
+BEGIN
+    IF ~Types.isInt(left) THEN
+        Errors.raise(Types.intsDescription() 
+                     + " expected as an element of SET, got '" + left.description() + "'");
+    END;
+    checkImplicitCast(cx.root()^, right, Types.basic.set);
+
+    RETURN Operator.setHasBit;
+END;
+
+PROCEDURE Ops.eqExpect(): STRING;
+    RETURN "numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE";
+END;
+
+PROCEDURE Ops.strongRelExpect(): STRING;
+    RETURN "numeric type or CHAR or character array";
+END;
+
+PROCEDURE Ops.relExpect(): STRING;
+    RETURN "numeric type or SET or CHAR or character array";
+END;
+
+PROCEDURE Ops.coalesceType(leftType, rightType: Types.PType): Types.PType;
+VAR
+    result: Types.PType;
+BEGIN
+    IF (leftType IS Record.PPointer) & (rightType IS Record.PPointer) THEN
+        result := Cast.findPointerBaseType(leftType, rightType^);
+        IF result = NIL THEN
+            result := Cast.findPointerBaseType(rightType, leftType^);
+        END;
+    END;
+
+    IF result = NIL THEN
+        (*special case for strings*)
+        isStrings <- Types.isString(leftType) & Types.isString(rightType);
+        IF ~isStrings THEN
+            checkTypeMatch(rightType, leftType);
+        END;
+        result := leftType;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.eq(type: Types.PType): BinaryOperatorCx;
+VAR
+    result: BinaryOperatorCx;
+BEGIN
+    IF useIntEqOp(type) THEN
+        result := Operator.equalInt;
+    ELSIF Types.isString(type) THEN
+        result := Operator.equalStr;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.equalReal;
+    ELSIF type = Types.basic.set THEN
+        result := Operator.equalSet;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.notEq(type: Types.PType): BinaryOperatorCx;
+VAR
+    result: BinaryOperatorCx;
+BEGIN
+    IF useIntEqOp(type) THEN
+        result := Operator.notEqualInt;
+    ELSIF Types.isString(type) THEN
+        result := Operator.notEqualStr;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.notEqualReal;
+    ELSIF type = Types.basic.set THEN
+        result := Operator.notEqualSet;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.less(type: Types.PType): BinaryOperatorCx;
+VAR
+    result: BinaryOperatorCx;
+BEGIN
+    IF useIntOrderOp(type) THEN
+        result := Operator.lessInt;
+    ELSIF Types.isString(type) THEN
+        result := Operator.lessStr;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.lessReal;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.greater(type: Types.PType): BinaryOperatorCx;
+VAR
+    result: BinaryOperatorCx;
+BEGIN
+    IF useIntOrderOp(type) THEN
+        result := Operator.greaterInt;
+    ELSIF Types.isString(type) THEN
+        result := Operator.greaterStr;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.greaterReal;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.lessEq(type: Types.PType): BinaryOperatorCx;
+VAR
+    result: BinaryOperatorCx;
+BEGIN
+    IF useIntOrderOp(type) THEN
+        result := Operator.eqLessInt;
+    ELSIF Types.isString(type) THEN
+        result := Operator.eqLessStr;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.eqLessReal;
+    ELSIF type = Types.basic.set THEN
+        result := Operator.setInclL;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.greaterEq(type: Types.PType): BinaryOperatorCx;
+VAR
+    result: BinaryOperatorCx;
+BEGIN
+    IF useIntOrderOp(type) THEN
+        result := Operator.eqGreaterInt;
+    ELSIF Types.isString(type) THEN
+        result := Operator.eqGreaterStr;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.eqGreaterReal;
+    ELSIF type = Types.basic.set THEN
+        result := Operator.setInclR;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.plus(type: Types.PType): BinaryOperator;
+VAR
+    result: BinaryOperator;
+BEGIN
+    IF type = Types.basic.set THEN
+        result := Operator.setUnion;
+    ELSIF Types.isInt(type) THEN
+        result := Operator.addInt;
+    ELSIF type = Types.basic.real THEN
+        result := Operator.addReal;
+    ELSE
+        throwOperatorTypeMismatch("+", SELF.plusExpect(), type);
+    END;
+    RETURN result;
+END;
+
+PROCEDURE Ops.plusExpect(): STRING;
+    RETURN "numeric type or SET";
+END;
+
+PROCEDURE ItemOp.ItemOp(op: STRING)
+    | op(op);
+END;
+
+PROCEDURE SimpleItemOp.SimpleItemOp(op: STRING)
+    | op(op);
+END;
+
+PROCEDURE SimpleList.addExpression(e: Expression.PType);
+BEGIN
+    IF SELF.expression = NIL THEN
+        SELF.expression := e;
+    ELSE
+        SELF.last.expression := e;
+    END;
+END;
+
+PROCEDURE SimpleList.addOp(op: STRING);
+BEGIN
+    next <- NEW SimpleItemOp(op);
+    IF SELF.last = NIL THEN
+        SELF.next := next;
+    ELSE
+        SELF.last.next := next;
+    END;
+    SELF.last := next;
+END;
+
+PROCEDURE Node.Node(ops: POps)
+	| ops(ops);
+END;
+
+PROCEDURE Node.makeSimple(): PSimpleList;
+	RETURN NEW SimpleList();
+END;
+
+PROCEDURE Node.addSimple(s: PSimpleList);
+BEGIN
+    IF SELF.simple = NIL THEN
+        SELF.simple := s;
+    ELSE
+        SELF.last.simple := s;
+    END;
+END;
+
+PROCEDURE Node.addOp(op: STRING);
+BEGIN
+    next <- NEW ItemOp(op);
+    IF SELF.last = NIL THEN
+        SELF.next := next;
+    ELSE
+        SELF.last.next := next;
+    END;
+    SELF.last := next;
+END;
+
+PROCEDURE Node.asExpression(cx: ContextHierarchy.PNode): Expression.PType;
+	RETURN makeFromList(SELF, SELF.ops^, cx);
+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 ExpressionTree.