// Do type equalisation on the expression node and report an error if the // two operands types are mismatched (eg. string and integer) BinaryOpParseNode TypeEqualise(BinaryOpParseNode node) { // Don't check a bogus parse tree. if (node.Left == null || node.Right == null) { return node; } switch (node.ID) { case ParseID.ADD: case ParseID.SUB: case ParseID.MULT: case ParseID.DIVIDE: case ParseID.EXP: return ArithmeticEqualise(node); case ParseID.CONCAT: return StringEqualise(node); case ParseID.OR: case ParseID.AND: case ParseID.LE: case ParseID.LT: case ParseID.GE: case ParseID.GT: case ParseID.EQ: case ParseID.NE: case ParseID.EQV: case ParseID.NEQV: case ParseID.EQUOP: return LogicalEqualise(node); } Debug.Assert(false, "Unhandled expression node token"); return null; }
// Verify that both operands of a string operator are strings. This // is the only type that is permitted. BinaryOpParseNode StringEqualise(BinaryOpParseNode node) { SymType type1 = node.Left.Type; SymType type2 = node.Right.Type; if (type1 == SymType.CHAR && type2 == SymType.CHAR) { node.Type = SymType.CHAR; return node; } if (type1 == SymType.FIXEDCHAR || type2 == SymType.FIXEDCHAR) { if (Symbol.IsCharType(type2)) { node.Type = SymType.FIXEDCHAR; return node; } } _messages.Error(MessageCode.TYPEMISMATCH, "Character operands expected"); return node; }
/// OPERATION OPERATOR ORDER OF PRECEDENCE /// exponentiate ** 8 /// multiply * 7 /// divide / 7 /// add + 6 /// subtract - 6 /// less than .LT. 5 /// less than or equal to .LE. 5 /// equal to .EQ. 5 /// not equal to .NE. 5 /// greater than or equal to .GE. 5 /// greater than .GT. 5 /// not .NOT. 4 /// and .AND. 3 /// or .OR. 2 /// xor .XOR. 1 /// neqv .NEQV. 1 /// eqv .EQV. 1 /// Parse an expression. ParseNode ParseExpression(int level) { ParseNode op1 = Operand(); bool done = false; while (!done) { SimpleToken token = _ls.GetToken(); bool isRTL = false; ParseID parseID; int preced; switch (token.ID) { case TokenID.KEQV: parseID = ParseID.EQV; preced = 1; break; case TokenID.KNEQV: parseID = ParseID.NEQV; preced = 1; break; case TokenID.KXOR: parseID = ParseID.XOR; preced = 1; break; case TokenID.KOR: parseID = ParseID.OR; preced = 2; break; case TokenID.KAND: parseID = ParseID.AND; preced = 3; break; case TokenID.KGT: parseID = ParseID.GT; preced = 5; break; case TokenID.KGE: parseID = ParseID.GE; preced = 5; break; case TokenID.KLE: parseID = ParseID.LE; preced = 5; break; case TokenID.KEQ: parseID = ParseID.EQ; preced = 5; break; case TokenID.KNE: parseID = ParseID.NE; preced = 5; break; case TokenID.EQUOP: parseID = ParseID.EQ; preced = 5; break; case TokenID.KLT: parseID = ParseID.LT; preced = 5; break; case TokenID.PLUS: parseID = ParseID.ADD; preced = 6; break; case TokenID.MINUS: parseID = ParseID.SUB; preced = 6; break; case TokenID.STAR: parseID = ParseID.MULT; preced = 7; break; case TokenID.DIVIDE: parseID = ParseID.DIVIDE; preced = 7; break; case TokenID.CONCAT: parseID = ParseID.CONCAT; preced = 8; break; case TokenID.EXP: parseID = ParseID.EXP; preced = 10; isRTL = true; break; default: _ls.BackToken(); done = true; continue; } if (level >= preced) { _ls.BackToken(); done = true; } else { // For operators that evaluate right to left (such as EXP), drop the // precedence so that further occurrences of the same operator to // the right are grouped together. if (isRTL) { --preced; } BinaryOpParseNode op = new BinaryOpParseNode(parseID); op.Left = op1; op.Right = ParseExpression(preced); op1 = TypeEqualise(op); } } return op1; }
// Optimise an exponentation expression where both nodes are literal // values. Substitute the node with the result of the exponentation. ParseNode OptimiseExponentation(ParseNode node) { BinaryOpParseNode tokenNode = (BinaryOpParseNode)node; tokenNode.Left = OptimiseExpressionTree(tokenNode.Left); tokenNode.Right = OptimiseExpressionTree(tokenNode.Right); if (tokenNode.IsNumber) { NumberParseNode op1 = (NumberParseNode)tokenNode.Left; NumberParseNode op2 = (NumberParseNode)tokenNode.Right; node = new NumberParseNode(op1.Value.Pow(op2.Value)); } // x raised to the powers of -1, 0 and 1 all yield constant expressions // so we can simplify that right now. if (tokenNode.Right.IsNumber) { Variant rightValue = tokenNode.Right.Value; if (rightValue.Compare(-1)) { BinaryOpParseNode divNode = new BinaryOpParseNode(ParseID.DIVIDE); divNode.Left = new NumberParseNode(new Variant(1)); divNode.Right = tokenNode.Left; divNode.Type = tokenNode.Left.Type; return divNode; } if (rightValue.IsZero) { return new NumberParseNode(1); } if (rightValue.Compare(1)) { return tokenNode.Left; } } return node; }
// Do type equalisation for arithmetic expressions. In this instance, // the type of the result is cast to the largest type that can // accommodate the two operands. Anything that evaluates to a non- // arithmetic value yields a type mismatch. BinaryOpParseNode ArithmeticEqualise(BinaryOpParseNode node) { SymType type1 = node.Left.Type; SymType type2 = node.Right.Type; if (type1 == SymType.INTEGER) { switch (type2) { case SymType.DOUBLE: node.Type = SymType.DOUBLE; return node; case SymType.FLOAT: node.Type = SymType.FLOAT; return node; case SymType.COMPLEX: node.Type = SymType.COMPLEX; return node; case SymType.INTEGER: node.Type = SymType.INTEGER; return node; } } if (type1 == SymType.FLOAT) { switch (type2) { case SymType.DOUBLE: node.Type = SymType.DOUBLE; return node; case SymType.COMPLEX: node.Type = SymType.COMPLEX; return node; case SymType.FLOAT: case SymType.INTEGER: node.Type = SymType.FLOAT; return node; } } if (type1 == SymType.DOUBLE) { switch (type2) { case SymType.DOUBLE: case SymType.FLOAT: case SymType.INTEGER: node.Type = SymType.DOUBLE; return node; } } if (type1 == SymType.COMPLEX) { switch (type2) { case SymType.INTEGER: case SymType.FLOAT: case SymType.COMPLEX: node.Type = SymType.COMPLEX; return node; } } if (type1 == SymType.FIXEDCHAR) { switch (type2) { case SymType.FIXEDCHAR: node.Type = SymType.FIXEDCHAR; return node; } } _messages.Error(MessageCode.TYPEMISMATCH, "Type mismatch in expression"); return node; }
// Do type equalisation for logical expressions. In this instance, // the two operands must match (numeric vs. numeric, char vs. char // or logical vs. logical). The result is always logical. BinaryOpParseNode LogicalEqualise(BinaryOpParseNode node) { SymType type1 = node.Left.Type; SymType type2 = node.Right.Type; if (type1 == SymType.INTEGER || type1 == SymType.FLOAT || type1 == SymType.DOUBLE) { switch (type2) { case SymType.DOUBLE: case SymType.FLOAT: case SymType.INTEGER: node.Type = SymType.BOOLEAN; return node; } } if (Symbol.IsCharType(type1) && Symbol.IsCharType(type2)) { node.Type = SymType.BOOLEAN; return node; } if (Symbol.IsLogicalType(type1) && Symbol.IsLogicalType(type2)) { node.Type = SymType.BOOLEAN; return node; } _messages.Error(MessageCode.TYPEMISMATCH, "Type mismatch in expression"); return node; }