/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Evaluate operands leftOperand = leftOperand.Evaluate(scope); rightOperand = rightOperand.Evaluate(scope); // Check if both operands are of the same type if (leftOperand.returnType.ToCLRType() != rightOperand.returnType.ToCLRType()) { // If not, check if left operand can be implicitely typecasted to right operand type if (leftOperand.returnType.IsCompatible(rightOperand.returnType)) { // Create implicit cast CastExpression implicitCast = new CastExpression(rightOperand.returnType); implicitCast.operand = leftOperand; // Evaluate typecast for folding this.leftOperand = implicitCast.Evaluate(scope); // Set return type this.returnType = rightOperand.returnType; } // If not, check if right operand can be implicitely typecasted to left operand type else if (rightOperand.returnType.IsCompatible(leftOperand.returnType)) { // Create implicit cast CastExpression implicitCast = new CastExpression(leftOperand.returnType); implicitCast.operand = rightOperand; // Evaluate for folding this.rightOperand = implicitCast.Evaluate(scope); // Set return type this.returnType = leftOperand.returnType; } else { // Types are incompatible - issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Incompatible types"); this.returnType = PrimitiveType.UNSUPPORTED; return this; } } // If operands are of the same type else { // Set return type this.returnType = leftOperand.returnType; } // If operand is not arithmetic, overwrite return type if ((int)op >= 6) { this.returnType = PrimitiveType.BOOL; } // Check if operator and operands are compatible switch (op) { // Addition is accepted for strings and numeric types case BinaryOperator.Add: // If type is not string, check if it is numerical if (!leftOperand.returnType.Equals(PrimitiveType.STRING)) { goto case BinaryOperator.Leq; } break; // Other arithmetic operators and comparisons (except equals and not equals) are accepted for numeric types case BinaryOperator.Sub: case BinaryOperator.Mul: case BinaryOperator.Div: case BinaryOperator.Gt: case BinaryOperator.Lt: case BinaryOperator.Geq: case BinaryOperator.Leq: // Check if type is not integer or double if (!leftOperand.returnType.Equals(PrimitiveType.INT) && !leftOperand.returnType.Equals(PrimitiveType.DOUBLE)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Arithmetic operator can only be applied to numerical types"); } break; // Modulo operator is accepted only for integer type case BinaryOperator.Rem: // Check if type is not integer if (!leftOperand.returnType.Equals(PrimitiveType.INT)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Reminder operator can only be applied to integer type"); } break; // Equality and non equality are accepted for all types case BinaryOperator.Eq: case BinaryOperator.Neq: break; // Default case stands for logical operators default: // Check if type is not boolean if (!leftOperand.returnType.Equals(PrimitiveType.BOOL)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Logical operator can only be applied to boolean type"); } break; } // If both operands are constant expressions, perform constant folding if ((leftOperand is ConstantExpression) && (rightOperand is ConstantExpression)) { // Compile time evaluation of expressions is done based on type and operator // If type is boolean if (leftOperand.returnType.Equals(PrimitiveType.BOOL)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Cast values to bool, compare and create new constant expression return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value == (bool)(rightOperand as ConstantExpression).value); // Compute non-equality case BinaryOperator.Neq: // Same as exclusive or for boolean values goto case BinaryOperator.Xor; // Compute logical and case BinaryOperator.And: // Cast values to bool, apply operation and create constant expression return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value && (bool)(rightOperand as ConstantExpression).value); // Compute logical or case BinaryOperator.Or: // Cast values to bool, apply operation and create constant expression return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value || (bool)(rightOperand as ConstantExpression).value); // Compute logical exclusive or case BinaryOperator.Xor: // Cast values to bool, apply operation and create constant expression (xor is equivalent to a // non-equality check) return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value != (bool)(rightOperand as ConstantExpression).value); } } // If type is double else if (leftOperand.returnType.Equals(PrimitiveType.DOUBLE)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value == (double)(rightOperand as ConstantExpression).value); // Compute non-equality case BinaryOperator.Neq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value != (double)(rightOperand as ConstantExpression).value); // Compute greater than case BinaryOperator.Gt: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value > (double)(rightOperand as ConstantExpression).value); // Compute greater than or equal to case BinaryOperator.Geq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value >= (double)(rightOperand as ConstantExpression).value); // Compute less than case BinaryOperator.Lt: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value < (double)(rightOperand as ConstantExpression).value); // Compute less than or equal to case BinaryOperator.Leq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value <= (double)(rightOperand as ConstantExpression).value); // Compute addition case BinaryOperator.Add: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value + (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute subtraction case BinaryOperator.Sub: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value - (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute multiplication case BinaryOperator.Mul: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value * (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute division case BinaryOperator.Div: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value / (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); } } // If type is integer else if (leftOperand.returnType.Equals(PrimitiveType.INT)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value == (int)(rightOperand as ConstantExpression).value); // Compute non-equality case BinaryOperator.Neq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value != (int)(rightOperand as ConstantExpression).value); // Compute greater than case BinaryOperator.Gt: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value > (int)(rightOperand as ConstantExpression).value); // Compute greater than or equal to case BinaryOperator.Geq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value >= (int)(rightOperand as ConstantExpression).value); // Compute less than case BinaryOperator.Lt: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value < (int)(rightOperand as ConstantExpression).value); // Compute less than or equal to case BinaryOperator.Leq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value <= (int)(rightOperand as ConstantExpression).value); // Compute addition case BinaryOperator.Add: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value + (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute subtraction case BinaryOperator.Sub: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value - (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute multiplication case BinaryOperator.Mul: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value * (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute division case BinaryOperator.Div: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value / (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute modulo case BinaryOperator.Rem: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value % (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); } } // If type is string else if (leftOperand.returnType.Equals(PrimitiveType.STRING)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Convert values to string, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (leftOperand as ConstantExpression).value.ToString() == (rightOperand as ConstantExpression).value.ToString()); // Compute non-equality case BinaryOperator.Neq: // Convert values to string, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (leftOperand as ConstantExpression).value.ToString() != (rightOperand as ConstantExpression).value.ToString()); // Compute addition case BinaryOperator.Add: // Convert values to string, apply operation and create constant expression return new ConstantExpression(Primitive.String, (leftOperand as ConstantExpression).value.ToString() + (rightOperand as ConstantExpression).value.ToString()); } } } return this; }