瀏覽代碼

ternary operator

Vladislav Folts 9 年之前
父節點
當前提交
d4489a7b52

二進制
bin/compiled.zip


+ 105 - 20
src/eberon/EberonContextExpression.ob

@@ -1,27 +1,28 @@
 MODULE EberonContextExpression;
 IMPORT
+    Cast,
     Context, ContextExpression, ContextHierarchy, 
     EberonContextDesignator, EberonContextProcedure, 
     EberonMap, EberonOperator, EberonString, EberonTypePromotion, 
-    Expression, LanguageContext,
+    Errors, Expression, LanguageContext,
     JS,
-    Object, Types;
+    Object, Record, Types;
 TYPE
-    ExpressionNode* = RECORD(ContextExpression.ExpressionNode)
-        PROCEDURE ExpressionNode(parent: ContextExpression.PExpressionHandler);
+    ExpressionNode* = RECORD(ContextExpression.ExpressionHandler)
+        condition, first, second: Expression.PType;
+    END;
+
+    RelationExpression* = RECORD(ContextExpression.ExpressionNode)
+        PROCEDURE RelationExpression(parent: ContextExpression.PExpressionHandler);
 
         PROCEDURE handleTypePromotion(promotion: EberonTypePromotion.PCombined);
 
         currentTypePromotion: EberonTypePromotion.PCombined;
     END;
-    PExpressionNode = POINTER TO ExpressionNode;
 
     SimpleExpression* = RECORD(ContextExpression.SimpleExpression)
-        PROCEDURE SimpleExpression(parent: PExpressionNode);
-
         PROCEDURE handleLogicalOr();
 
-        parentExppression: PExpressionNode;
         typePromotion: EberonTypePromotion.PCombined;
         currentPromotion: EberonTypePromotion.PMaybe;
         orHandled: BOOLEAN;
@@ -59,11 +60,100 @@ TYPE
 VAR
     relationOps: POINTER TO RelationOps;
 
-PROCEDURE ExpressionNode.ExpressionNode(parent: ContextExpression.PExpressionHandler)
+PROCEDURE hierarchyDepth(t: Record.Type): INTEGER;
+BEGIN
+    result <- 0;
+    base <- t.base;
+    WHILE base # NIL DO
+        INC(result);
+        base := base.base;
+    END;
+    RETURN result;
+END;
+
+PROCEDURE getNthBase(t: Record.PType; n: INTEGER): Record.PType;
+BEGIN
+    result <- t;
+    i <- n;
+    WHILE i # 0 DO
+        result := result.base;
+        DEC(i);
+    END;
+    RETURN result;
+END;
+
+PROCEDURE findCommonBase(t1, t2: Record.PType): Record.PType;
+VAR
+    result: Record.PType;
+BEGIN
+    depth1 <- hierarchyDepth(t1^);
+    depth2 <- hierarchyDepth(t2^);
+    commonBase1 <- t1;
+    commonBase2 <- t2;
+    IF depth1 > depth2 THEN
+        commonBase1 := getNthBase(commonBase1, depth1 - depth2);
+    ELSE
+        commonBase2 := getNthBase(commonBase2, depth2 - depth1);
+    END;
+
+    WHILE commonBase1 # commonBase2 DO
+        commonBase1 := commonBase1.base;
+        commonBase2 := commonBase2.base;
+    END;
+
+    RETURN commonBase1;
+END;
+
+PROCEDURE ExpressionNode.handleExpression(e: Expression.PType);
+BEGIN
+    IF SELF.condition = NIL THEN
+        SELF.condition := e;
+    ELSIF SELF.first = NIL THEN
+        SELF.first := e;
+    ELSE
+        SELF.second := e;
+    END;
+END;
+
+PROCEDURE ExpressionNode.endParse(): BOOLEAN;
+VAR
+    resultType: Types.PType;
+    op: LanguageContext.PCastOp;
+BEGIN
+    result <- SELF.first;
+    IF result = NIL THEN
+        result := SELF.condition;
+    ELSE
+        firstType <- SELF.first.type();
+        secondType <- SELF.second.type();
+        IF (firstType IS Record.PType) & (secondType IS Record.PType) THEN
+            resultType := findCommonBase(firstType, secondType);
+        ELSIF (firstType IS Record.PPointer) & (secondType IS Record.PPointer) THEN
+            resultType := findCommonBase(Record.pointerBase(firstType^), Record.pointerBase(secondType^));
+        END;
+
+        IF resultType = NIL THEN 
+            IF SELF.root().language().types.implicitCast(firstType, secondType, FALSE, op) # Cast.errNo THEN
+                Errors.raise("incompatible types in ternary operator: '" 
+                             + firstType.description() + "' and '" + secondType.description() + "'");
+            END;
+            resultType := firstType;
+        END;
+
+        result := Expression.makeSimple(SELF.condition.code() + " ? " 
+                                        + SELF.first.code() + " : "
+                                        + SELF.second.code(),
+                                        resultType);
+    END;
+    SELF.parent()(ContextExpression.PExpressionHandler).handleExpression(result);
+    RETURN TRUE;
+END;
+
+PROCEDURE RelationExpression.RelationExpression(parent: ContextExpression.PExpressionHandler)
     | SUPER(parent, relationOps);
 END;
 
-PROCEDURE ExpressionNode.handleMessage(VAR msg: ContextHierarchy.Message): Object.PType;
+PROCEDURE RelationExpression.handleMessage(VAR msg: ContextHierarchy.Message): Object.PType;
 VAR
     result: Object.PType;
 BEGIN
@@ -74,12 +164,12 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE ExpressionNode.handleTypePromotion(promotion: EberonTypePromotion.PCombined);
+PROCEDURE RelationExpression.handleTypePromotion(promotion: EberonTypePromotion.PCombined);
 BEGIN
     SELF.currentTypePromotion := promotion;
 END;
 
-PROCEDURE ExpressionNode.handleLiteral(s: STRING);
+PROCEDURE RelationExpression.handleLiteral(s: STRING);
 BEGIN
     IF SELF.currentTypePromotion # NIL THEN
         SELF.currentTypePromotion.clear();
@@ -87,7 +177,7 @@ BEGIN
     SUPER(s);
 END;
 
-PROCEDURE ExpressionNode.doRelationOperation(left, right: Expression.PType; relation: STRING): ContextExpression.BinaryOperatorCx;
+PROCEDURE RelationExpression.doRelationOperation(left, right: Expression.PType; relation: STRING): ContextExpression.BinaryOperatorCx;
 VAR
     result: ContextExpression.BinaryOperatorCx;
 BEGIN
@@ -100,7 +190,7 @@ BEGIN
     RETURN result;
 END;
 
-PROCEDURE ExpressionNode.endParse(): BOOLEAN;
+PROCEDURE RelationExpression.endParse(): BOOLEAN;
 BEGIN
     IF SELF.currentTypePromotion # NIL THEN
         void <- SELF.parent().handleMessage(
@@ -109,11 +199,6 @@ BEGIN
     RETURN SUPER();
 END;
 
-PROCEDURE SimpleExpression.SimpleExpression(parent: PExpressionNode)
-    | SUPER(parent),
-      parentExppression(parent);
-END;
-
 PROCEDURE SimpleExpression.handleLogicalOr();
 BEGIN
     IF SELF.typePromotion # NIL THEN
@@ -157,7 +242,7 @@ END;
 PROCEDURE SimpleExpression.endParse(): BOOLEAN;
 BEGIN
     IF SELF.typePromotion # NIL THEN
-        SELF.parentExppression.handleTypePromotion(SELF.typePromotion);
+        SELF.parent()^(RelationExpression).handleTypePromotion(SELF.typePromotion);
     END;
     RETURN SUPER();
 END;

+ 12 - 1
src/eberon/eberon_grammar.js

@@ -78,6 +78,17 @@ function makeDesignator(ident, qualident, selector, actualParameters){
     };
 }
 
+function makeExpression(base){
+    var relExp = context(base, EberonContextExpression.RelationExpression);
+    var expression = context(
+        and(relExp, optional(and("?", relExp, 
+                                 required(":", "expected \":\" after \"?\" in ternary operator"), 
+                                 required(function(){ return expression.apply(this, arguments);}, 
+                                          "expression is expected after \":\" in ternary operator")))),
+        EberonContextExpression.ExpressionNode);
+    return expression;
+}
+
 function makeProcedureDeclaration(ident, procedureHeading, procedureBody){
     return context(and(procedureHeading, ";",
                        procedureBody,
@@ -136,6 +147,7 @@ exports.language = {
     grammar: Grammar.make(
         makeIdentdef,
         makeDesignator,
+        makeExpression,
         makeStrucType,
         makeStatement,
         makeProcedureHeading,
@@ -160,7 +172,6 @@ exports.language = {
             AddOperator:        EberonContextExpression.AddOperator,
             MulOperator:        EberonContextExpression.MulOperator,
             SimpleExpression:   EberonContextExpression.SimpleExpression, 
-            Expression:         EberonContextExpression.ExpressionNode,
             For:                EberonContextLoop.For,
             While:              EberonContextLoop.While,
             If:                 EberonContextIf.Type,

+ 2 - 2
src/grammar.js

@@ -28,6 +28,7 @@ var reservedWords = "ARRAY IMPORT THEN BEGIN IN TO BY IS TRUE CASE MOD TYPE CONS
 
 function make(makeIdentdef,
               makeDesignator,
+              makeExpression,
               makeStrucType,
               makeStatement,
               makeProcedureHeading, 
@@ -100,8 +101,7 @@ var simpleExpression = context(
           , repeat(and(addOperator, term)))
       , contexts.SimpleExpression);
 var relation = or("=", "#", "<=", "<", ">=", ">", "IN", "IS");
-var expression = context(and(simpleExpression, optional(and(relation, simpleExpression)))
-                       , contexts.Expression);
+var expression = makeExpression(and(simpleExpression, optional(and(relation, simpleExpression))));
 var constExpression = expression;
 
 var element = context(and(expression, optional(and("..", expression))), ContextExpression.SetElement);

+ 5 - 1
src/oberon/oberon_grammar.js

@@ -66,6 +66,10 @@ function makeDesignator(ident, qualident, selector, actualParameters){
     };
 }
 
+function makeExpression(base){
+    return context(base, ContextExpression.ExpressionNode);
+}
+
 function makeProcedureDeclaration(ident, procedureHeading, procedureBody){
     return context(and(procedureHeading, ";",
                        procedureBody,
@@ -104,6 +108,7 @@ exports.language = {
     grammar: Grammar.make(
         makeIdentdef,
         makeDesignator,
+        makeExpression,
         makeStrucType,
         makeStatement,
         makeProcedureHeading,
@@ -128,7 +133,6 @@ exports.language = {
             AddOperator:        ContextExpression.AddOperator,
             MulOperator:        ContextExpression.MulOperator,
             SimpleExpression:   ContextExpression.SimpleExpression, 
-            Expression:         ContextExpression.ExpressionNode,
             For:                ContextLoop.For,
             While:              ContextLoop.While,
             If:                 ContextIf.Type,

+ 10 - 0
test/expected/eberon/ternary_operator.js

@@ -0,0 +1,10 @@
+var test = function (){
+
+function integer(b/*BOOLEAN*/, i1/*INTEGER*/, i2/*INTEGER*/){
+	return b ? i1 : i2;
+}
+
+function integer2(b1/*BOOLEAN*/, b2/*BOOLEAN*/, i1/*INTEGER*/, i2/*INTEGER*/, i3/*INTEGER*/){
+	return b1 ? i1 : b2 ? i2 : i3;
+}
+}();

+ 11 - 0
test/input/eberon/ternary_operator.ob

@@ -0,0 +1,11 @@
+MODULE test;
+
+PROCEDURE integer(b: BOOLEAN; i1, i2: INTEGER): INTEGER;
+    RETURN b ? i1 : i2;
+END;
+
+PROCEDURE integer2(b1, b2: BOOLEAN; i1, i2, i3: INTEGER): INTEGER;
+    RETURN b1 ? i1 : b2 ? i2 : i3;
+END;
+
+END test.

+ 35 - 1
test/test_unit_eberon.js

@@ -1499,5 +1499,39 @@ exports.suite = {
                 "TYPE M = MAP OF INTEGER;"),
         pass("PROCEDURE p(): M; VAR m: M; BEGIN; RETURN m; END;")
         )
-    }
+    },
+"ternary operator": testWithContext(
+        context(grammar.expression,
+                "TYPE Base = RECORD END; Derived = RECORD(Base) END;"
+                    + "Derived2 = RECORD(Base) END; Derived3 = RECORD(Base) END;" 
+                + "VAR b: BOOLEAN; i1, i2: INTEGER; s: STRING; byte: BYTE;"
+                + "rb: Base; rd: Derived; rd2: Derived2; rd3: Derived3;"
+                + "pb: POINTER TO Base; pd: POINTER TO Derived; pd2: POINTER TO Derived2; pd3: POINTER TO Derived3;"
+                + "PROCEDURE passBase(b: Base): BOOLEAN; RETURN TRUE END;"
+                + "PROCEDURE passDerived(d: Derived): BOOLEAN; RETURN TRUE END;"
+                ),
+        pass("b ? i1 : i2",
+             "(b ? i1 : i2) # 0",
+             "FLT(b ? i1 : i2)",
+             "b ? i1 : byte",
+             "b ? byte : i1",
+             "b ? pb : pd",
+             "b ? pd : pb",
+             "passBase(b ? pb : pd)",
+             "passBase(b ? pd : pb)",
+             "passBase(b ? pd2 : pd3)",
+             "passBase(b ? rb : rd)",
+             "passBase(b ? rd : rb)",
+             "passBase(b ? rd2 : rd3)",
+             "b ? i1 : b ? i1 : i2"
+             ),
+        fail(["b ?", "not parsed"],
+             ["b ? i1", "expected \":\" after \"?\" in ternary operator"],
+             ["b ? i1 :", "expression is expected after \":\" in ternary operator"],
+             ["b ? i1 : s", "incompatible types in ternary operator: 'INTEGER' and 'STRING'"],
+             ["passDerived(b ? pb : pd)", "type mismatch for argument 1: 'Base' cannot be converted to 'Derived'"],
+             ["passDerived(b ? pd : pb)", "type mismatch for argument 1: 'Base' cannot be converted to 'Derived'"],
+             ["b ? b ? i1 : i2 : i1", "expected \":\" after \"?\" in ternary operator"]
+             )
+    )
 };