/// <summary> /// Rebalances this expression based on the precendence of operators. /// </summary> internal override ExpressionBase Rebalance() { if (!Right.IsLogicalUnit) { var mathematicRight = Right as MathematicExpression; if (mathematicRight != null && !(Left is StringConstantExpression)) { // multiply and divide should happen before add or subtract. // at the same priority, they should happen left-to-right. if (GetPriority(Operation) >= GetPriority(mathematicRight.Operation)) { var newLeft = new MathematicExpression(Left, Operation, mathematicRight.Left); newLeft = newLeft.Rebalance() as MathematicExpression; return(new MathematicExpression(newLeft, mathematicRight.Operation, mathematicRight.Right)); } } var comparisonRight = Right as ComparisonExpression; if (comparisonRight != null) { return(Rebalance(comparisonRight)); } var conditionalRight = Right as ConditionalExpression; if (conditionalRight != null) { return(Rebalance(conditionalRight)); } } return(base.Rebalance()); }
/// <summary> /// Reverses application of the specified operation and amount to the provided <paramref name="value"/>. /// </summary> /// <param name="value">The value to modify.</param> /// <returns>The modified value.</returns> public int Remove(int value) { return(Apply(value, MathematicExpression.GetOppositeOperation(Operation), Amount)); }
/// <summary> /// Replaces the variables in the expression with values from <paramref name="scope" />. /// </summary> /// <param name="scope">The scope object containing variable values.</param> /// <param name="result">[out] The new expression containing the replaced variables.</param> /// <returns> /// <c>true</c> if substitution was successful, <c>false</c> if something went wrong, in which case <paramref name="result" /> will likely be a <see cref="ParseErrorExpression" />. /// </returns> public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result) { ExpressionBase left; if (!Left.ReplaceVariables(scope, out left)) { result = left; return(false); } ExpressionBase right; if (!Right.ReplaceVariables(scope, out right)) { result = right; return(false); } var integerLeft = left as IntegerConstantExpression; var integerRight = right as IntegerConstantExpression; switch (Operation) { case MathematicOperation.Add: var stringLeft = left as StringConstantExpression; var stringRight = right as StringConstantExpression; if (stringLeft != null) { if (stringRight != null) { result = new StringConstantExpression(stringLeft.Value + stringRight.Value); return(true); } if (integerRight != null) { result = new StringConstantExpression(stringLeft.Value + integerRight.Value.ToString()); return(true); } } else if (stringRight != null) { if (integerLeft != null) { result = new StringConstantExpression(integerLeft.Value.ToString() + stringRight.Value); return(true); } } // prefer constants on right if (integerLeft != null && integerRight == null) { var temp = left; left = right; right = temp; integerRight = integerLeft; integerLeft = null; } if (integerRight != null) { if (integerRight.Value == 0) // anything plus 0 is itself { result = left; return(true); } if (integerLeft != null) { result = new IntegerConstantExpression(integerLeft.Value + integerRight.Value); return(true); } } break; case MathematicOperation.Subtract: if (integerRight != null) { if (integerRight.Value == 0) // anything minus 0 is itself { result = left; return(true); } if (integerLeft != null) { result = new IntegerConstantExpression(integerLeft.Value - integerRight.Value); return(true); } } break; case MathematicOperation.Multiply: // prefer constants on right if (integerLeft != null && integerRight == null) { var temp = left; left = right; right = temp; integerRight = integerLeft; integerLeft = null; } if (integerRight != null) { if (integerRight.Value == 0) // anything times 0 is 0 { result = right; return(true); } if (integerRight.Value == 1) // anything times 1 is itself { result = left; return(true); } if (integerLeft != null) { result = new IntegerConstantExpression(integerLeft.Value * integerRight.Value); return(true); } } break; case MathematicOperation.Divide: if (integerRight != null) { if (integerRight.Value == 0) // division by 0 is impossible { result = new ParseErrorExpression("division by zero", this); return(false); } if (integerRight.Value == 1) // anything divided by 1 is itself { result = left; return(true); } if (integerLeft != null) { result = new IntegerConstantExpression(integerLeft.Value / integerRight.Value); return(true); } } break; case MathematicOperation.Modulus: if (integerRight != null) { if (integerRight.Value == 0) // division by 0 is impossible { result = new ParseErrorExpression("division by zero", this); return(false); } if (integerRight.Value == 1) // anything modulus 1 is 0 { result = new IntegerConstantExpression(0); return(true); } if (integerLeft != null) { result = new IntegerConstantExpression(integerLeft.Value % integerRight.Value); return(true); } } break; } var mathematic = new MathematicExpression(left, Operation, right); mathematic.Line = Line; mathematic.Column = Column; result = mathematic; return(true); }
/// <summary> /// Moves the IntegerConstant (if present) to be the Right operand of the root node. /// </summary> /// <remarks>Combines nodes where possible.</remarks> internal static MathematicExpression BubbleUpIntegerConstant(MathematicExpression mathematic) { var priority = GetPriority(mathematic.Operation); var mathematicLeft = mathematic.Left as MathematicExpression; if (mathematicLeft != null) { if (GetPriority(mathematicLeft.Operation) == priority) { mathematic.Left = mathematicLeft = BubbleUpIntegerConstant(mathematicLeft); if (mathematicLeft.Right is IntegerConstantExpression) { if (mathematic.Right is IntegerConstantExpression) { return(mathematic); } mathematic.Left = mathematicLeft.Left; mathematicLeft.Left = BubbleUpIntegerConstant(mathematic); mathematic = mathematicLeft; } } } var mathematicRight = mathematic.Right as MathematicExpression; if (mathematicRight != null) { if (GetPriority(mathematicRight.Operation) == priority) { mathematic.Right = mathematicRight = BubbleUpIntegerConstant(mathematicRight); if (mathematicRight.Right is IntegerConstantExpression) { if (mathematic.Operation == MathematicOperation.Add) { mathematic.Right = mathematicRight.Left; mathematicRight.Left = BubbleUpIntegerConstant(mathematic); mathematic = mathematicRight; } else if (mathematic.Operation == MathematicOperation.Subtract) { mathematic.Right = mathematicRight.Left; mathematicRight.Left = BubbleUpIntegerConstant(mathematic); mathematicRight.Right = new IntegerConstantExpression(-((IntegerConstantExpression)mathematicRight.Right).Value); mathematic = mathematicRight; } } } } if (mathematic.Right is IntegerConstantExpression) { mathematicLeft = mathematic.Left as MathematicExpression; if (mathematicLeft != null && GetPriority(mathematicLeft.Operation) == priority && mathematicLeft.Right is IntegerConstantExpression) { ExpressionBase result; mathematic.Left = mathematicLeft.Right; if (MergeOperands(mathematic.Left, mathematic.Operation, mathematic.Right, out result)) { mathematicLeft.Right = result; if (MergeOperands(mathematicLeft.Left, mathematicLeft.Operation, mathematicLeft.Right, out result)) { mathematicLeft = (MathematicExpression)result; } mathematic = mathematicLeft; } } } var integer = mathematic.Left as IntegerConstantExpression; if (integer != null) { switch (mathematic.Operation) { case MathematicOperation.Add: case MathematicOperation.Multiply: // switch the order so the constant is on the right mathematic = new MathematicExpression(mathematic.Right, mathematic.Operation, mathematic.Left); break; case MathematicOperation.Subtract: if (integer.Value == 0) { break; } // change "N - func" to "0 - func + N" so N is on the right. the 0 will be optimized out later mathematic = new MathematicExpression( new MathematicExpression(new IntegerConstantExpression(0), MathematicOperation.Subtract, mathematic.Right), MathematicOperation.Add, integer); break; default: break; } } return(mathematic); }
private static bool MergeNonConstantMathematic(MathematicExpression mathematicLeft, MathematicOperation operation, ExpressionBase right, out ExpressionBase result) { var left = mathematicLeft.Right; result = null; var newLeft = mathematicLeft.Left; var newOperation = mathematicLeft.Operation; ExpressionBase newRight; switch (mathematicLeft.Operation) { case MathematicOperation.Add: if (operation == MathematicOperation.Add) { // (a + 3) + 2 => a + (3 + 2) if (!MergeAddition(left, right, out newRight)) { result = newRight; return(false); } } else if (operation == MathematicOperation.Subtract) { if (IsGreater(left, right)) { // (a + 3) - 2 => a + (3 - 2) if (!MergeSubtraction(left, right, out newRight)) { result = newRight; return(false); } } else { // (a + 2) - 3 => a - (3 - 2) if (!MergeSubtraction(right, left, out newRight)) { result = newRight; return(false); } newOperation = MathematicOperation.Subtract; } } else { return(false); } break; case MathematicOperation.Subtract: if (operation == MathematicOperation.Add) { if (IsGreater(left, right)) { // (a - 3) + 2 => a - (3 - 2) if (!MergeSubtraction(left, right, out newRight)) { result = newRight; return(false); } } else { // (a - 2) + 3 => a + (3 - 2) if (!MergeSubtraction(right, left, out newRight)) { result = newRight; return(false); } newOperation = MathematicOperation.Add; } } else if (operation == MathematicOperation.Subtract) { // (a - 3) - 2 => a - (3 + 2) if (!MergeAddition(left, right, out newRight)) { result = newRight; return(false); } } else { return(false); } break; case MathematicOperation.Multiply: switch (operation) { case MathematicOperation.Multiply: // (a * 3) * 2 => a * (3 * 2) if (!MergeMultiplication(left, right, out newRight)) { result = newRight; return(false); } break; case MathematicOperation.Divide: if (left.Type == ExpressionType.FloatConstant) { right = FloatConstantExpression.ConvertFrom(right); if (right.Type == ExpressionType.ParseError) { return(false); } } else if (right.Type == ExpressionType.FloatConstant) { left = FloatConstantExpression.ConvertFrom(left); if (left.Type == ExpressionType.ParseError) { return(false); } } else { // (a * 8) / 4 => a * (8 / 4) => a * 2 // can only merge these if the constant on the left is a multiple of the constant on the right if (!IsMultiple(left, right)) { return(false); } } if (!MergeDivision(left, right, out newRight)) { result = newRight; return(false); } break; case MathematicOperation.Modulus: // (a * 8) % 4 => a % 4 // can only merge these if the constant on the left is a multiple of the constant on the right if (!IsMultiple(left, right)) { return(false); } newRight = right; newOperation = MathematicOperation.Modulus; break; default: return(false); } break; case MathematicOperation.Divide: if (operation == MathematicOperation.Divide) { // (a / 3) / 2 => a / (3 * 2) var multiplication = new MathematicExpression(left, MathematicOperation.Multiply, right); if (!MergeMultiplication(left, right, out newRight)) { result = newRight; return(false); } } else if (operation == MathematicOperation.Multiply) { if (left.Type == ExpressionType.FloatConstant || right.Type == ExpressionType.FloatConstant) { // (a / 3.0) * 2.0 => a * (2.0 / 3.0) if (!MergeDivision(right, left, out newRight)) { result = newRight; return(false); } newOperation = MathematicOperation.Multiply; } else { // (a / 3) * 2 => a * (2 / 3) // when integer division is performed first, the result may be floored before applying // the multiplication, so don't automatically merge them. return(false); } } else { return(false); } break; case MathematicOperation.BitwiseAnd: // (a & 12) & 5 => a & (12 & 5) if (operation != MathematicOperation.BitwiseAnd) { return(false); } if (!MergeBitwiseAnd(left, right, out newRight)) { result = newRight; return(false); } break; default: return(false); } return(MergeOperands(newLeft, newOperation, newRight, out result)); }
private static bool MergeOperands(ExpressionBase left, MathematicOperation operation, ExpressionBase right, out ExpressionBase result) { // ASSERT: expression tree has already been rebalanced and variables have been replaced if (!left.IsLiteralConstant) { // cannot combine non-constant, leave as is. if (right.IsLiteralConstant) { // attempt to merge the right constant into the left mathematic expression var mathematicLeft = left as MathematicExpression; if (mathematicLeft != null && MergeNonConstantMathematic(mathematicLeft, operation, right, out result)) { return(true); } // attempt to eliminate the right constant if (MergeIdentity(left, operation, right, out result)) { return(true); } if (result is ParseErrorExpression) { return(false); } } } else if (!right.IsLiteralConstant) { // cannot combine non-constant. when possible, prefer constants on the right. switch (operation) { case MathematicOperation.Add: case MathematicOperation.Multiply: if (MergeIdentity(right, operation, left, out result)) { return(true); } if (result is ParseErrorExpression) { return(false); } var temp = left; left = right; right = temp; break; default: // cannot swap break; } } else { switch (operation) { case MathematicOperation.Add: return(MergeAddition(left, right, out result)); case MathematicOperation.Subtract: return(MergeSubtraction(left, right, out result)); case MathematicOperation.Multiply: return(MergeMultiplication(left, right, out result)); case MathematicOperation.Divide: return(MergeDivision(left, right, out result)); case MathematicOperation.Modulus: return(MergeModulus(left, right, out result)); case MathematicOperation.BitwiseAnd: return(MergeBitwiseAnd(left, right, out result)); } } result = new MathematicExpression(left, operation, right); return(true); }
private static ExpressionBase ParseMathematic(PositionalTokenizer tokenizer, ExpressionBase left, MathematicOperation operation, int joinerLine, int joinerColumn) { OperationPriority priority; switch (operation) { case MathematicOperation.Add: if (left is StringConstantExpression) { priority = OperationPriority.AppendString; break; } priority = OperationPriority.AddSubtract; break; case MathematicOperation.Subtract: priority = OperationPriority.AddSubtract; break; case MathematicOperation.Multiply: case MathematicOperation.Divide: case MathematicOperation.Modulus: priority = OperationPriority.MulDivMod; break; case MathematicOperation.BitwiseAnd: priority = OperationPriority.BitwiseAnd; break; default: return(new ParseErrorExpression("Unknown operator: " + operation)); } var right = ParseExpression(tokenizer, priority); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.Comparison: // will be rebalanced case ExpressionType.Conditional: // will be rebalanced case ExpressionType.FloatConstant: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: var expressionTokenizer = tokenizer as ExpressionTokenizer; if (expressionTokenizer != null) { expressionTokenizer.QueueExpression(right); } right = new KeywordExpression(MathematicExpression.GetOperatorCharacter(operation).ToString(), joinerLine, joinerColumn); return(ParseError(tokenizer, "Incompatible mathematical operation", right)); } if (priority == OperationPriority.AddSubtract) { var mathematicRight = right as MathematicExpression; } return(new MathematicExpression(left, operation, right)); }
private static ExpressionBase ParseMathematic(PositionalTokenizer tokenizer, ExpressionBase left, MathematicOperation operation, int joinerLine, int joinerColumn) { var right = ExpressionBase.Parse(tokenizer); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.Comparison: case ExpressionType.Conditional: case ExpressionType.Dictionary: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: ParseError(tokenizer, "incompatible mathematical operation", new KeywordExpression(MathematicExpression.GetOperatorCharacter(operation).ToString(), joinerLine, joinerColumn)); break; } return(new MathematicExpression(left, operation, right)); }