public void TestRebalanceAddString() { // "A" + B - C => "A" + (B - C) var str = new StringConstantExpression("ban"); var exprLeft = new IntegerConstantExpression(6); var exprRight = new IntegerConstantExpression(2); var clause = new MathematicExpression(exprLeft, MathematicOperation.Subtract, exprRight); var expr = new MathematicExpression(str, MathematicOperation.Add, clause); var result = expr.Rebalance() as MathematicExpression; Assert.That(result, Is.Not.Null); Assert.That(result.Left, Is.EqualTo(str)); Assert.That(result.Operation, Is.EqualTo(MathematicOperation.Add)); Assert.That(result.Right, Is.EqualTo(clause)); // A - B + "C" => (A - B) + "C" clause = new MathematicExpression(exprRight, MathematicOperation.Add, str); expr = new MathematicExpression(exprLeft, MathematicOperation.Subtract, clause); result = expr.Rebalance() as MathematicExpression; Assert.That(result, Is.Not.Null); var expectedLeft = new MathematicExpression(exprLeft, MathematicOperation.Subtract, exprRight); Assert.That(result.Left, Is.EqualTo(expectedLeft)); Assert.That(result.Operation, Is.EqualTo(MathematicOperation.Add)); Assert.That(result.Right, Is.EqualTo(str)); }
public void TestRebalanceMathematic(MathematicOperation op1, MathematicOperation op2, bool rebalanceExpected) { // parser will result in a right-weighted tree: A * B + C => A * {B + C} // if the left operator is a multiply or divide and the right is an add or subtract, // the left operator takes precedence and tree should be rebalanced. // // A * B + C => {A * B} + C rebalanced // A + B * C => A + {B * C} not rebalanced // var variable1 = new VariableExpression("variable1"); var variable2 = new VariableExpression("variable2"); var value = new IntegerConstantExpression(99); var clause = new MathematicExpression(variable2, op2, value); var expr = new MathematicExpression(variable1, op1, clause); var result = expr.Rebalance() as MathematicExpression; Assert.That(result, Is.Not.Null); if (rebalanceExpected) { var expectedLeft = new MathematicExpression(variable1, op1, variable2); Assert.That(result.Left, Is.EqualTo(expectedLeft)); Assert.That(result.Operation, Is.EqualTo(op2)); Assert.That(result.Right, Is.EqualTo(value)); } else { Assert.That(result.Left, Is.EqualTo(expr.Left)); Assert.That(result.Operation, Is.EqualTo(expr.Operation)); Assert.That(result.Right, Is.EqualTo(expr.Right)); } }
public override bool Evaluate(InterpreterScope scope, out ExpressionBase result) { var left = GetIntegerParameter(scope, "left", out result); var right = GetIntegerParameter(scope, "right", out result); var mathematic = new MathematicExpression(left, MathematicOperation.Add, right); return(mathematic.ReplaceVariables(scope, out result)); }
public void TestRebalanceComplex() { // var1 * 2 + 3 + 4 => {{var1 * 2} + 3} + 4 // // initial parsing will create a right-weighted tree // // * // var1 + // 2 + // 3 4 // // recursive rebalancing while parsing will update the lower tree first (test builds the tree in this state) // // * // var1 + // + 4 // 2 3 // // so when we rebalance the upper tree, we need to pull the 2 all the way up // // + // * + // var1 2 3 4 // // then rebalance the new upper tree // // + // + 4 // * 3 // var1 2 var variable1 = new VariableExpression("variable1"); var variable2 = new VariableExpression("variable2"); var value2 = new IntegerConstantExpression(2); var value3 = new IntegerConstantExpression(3); var value4 = new IntegerConstantExpression(4); var clause2 = new MathematicExpression(value2, MathematicOperation.Add, value3); var clause1 = new MathematicExpression(clause2, MathematicOperation.Add, value4); var expr = new MathematicExpression(variable1, MathematicOperation.Multiply, clause1); var result = expr.Rebalance() as MathematicExpression; Assert.That(result, Is.Not.Null); Assert.That(result.Operation, Is.EqualTo(MathematicOperation.Add)); Assert.That(result.Right, Is.InstanceOf <IntegerConstantExpression>()); var expectedLeft = result.Left as MathematicExpression; Assert.That(expectedLeft, Is.Not.Null); Assert.That(expectedLeft.Operation, Is.EqualTo(MathematicOperation.Add)); Assert.That(expectedLeft.Right, Is.InstanceOf <IntegerConstantExpression>()); var expectedLeftLeft = expectedLeft.Left as MathematicExpression; Assert.That(expectedLeftLeft, Is.Not.Null); Assert.That(expectedLeftLeft.Operation, Is.EqualTo(MathematicOperation.Multiply)); Assert.That(expectedLeftLeft.Left, Is.InstanceOf <VariableExpression>()); Assert.That(expectedLeftLeft.Right, Is.InstanceOf <IntegerConstantExpression>()); }
public void TestAppendString(MathematicOperation op, string expected) { var variable = new VariableExpression("variable"); var value = new IntegerConstantExpression(99); var expr = new MathematicExpression(variable, op, value); var builder = new StringBuilder(); expr.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expected)); }
protected override ExpressionBase Combine(ExpressionBase left, ExpressionBase right) { if (left == null) { return(right); } var combined = new MathematicExpression(left, MathematicOperation.Add, right); return(combined.MergeOperands()); }
public void TestBitwiseAndZero() { var left = new IntegerConstantExpression(0xCF); var right = new IntegerConstantExpression(0); var expr = new MathematicExpression(left, MathematicOperation.BitwiseAnd, right); var scope = new InterpreterScope(); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <IntegerConstantExpression>()); Assert.That(((IntegerConstantExpression)result).Value, Is.EqualTo(0)); }
public void TestRebalance() { var variable = new VariableExpression("variable"); var value = new IntegerConstantExpression(99); var expr = new MathematicExpression(variable, MathematicOperation.Add, value); var result = expr.Rebalance() as MathematicExpression; Assert.That(result, Is.Not.Null); Assert.That(result.Left, Is.EqualTo(expr.Left)); Assert.That(result.Operation, Is.EqualTo(expr.Operation)); Assert.That(result.Right, Is.EqualTo(expr.Right)); }
public void TestAddStrings() { var left = new StringConstantExpression("ban"); var right = new StringConstantExpression("ana"); var expr = new MathematicExpression(left, MathematicOperation.Add, right); var scope = new InterpreterScope(); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <StringConstantExpression>()); Assert.That(((StringConstantExpression)result).Value, Is.EqualTo("banana")); }
public void TestGetModificationsUpdateSelf() { var variable = new VariableExpression("variable1"); var value = new IntegerConstantExpression(1); var mathematic = new MathematicExpression(variable, MathematicOperation.Add, value); var expr = new AssignmentExpression(variable, mathematic); var modifications = new HashSet <string>(); ((INestedExpressions)expr).GetModifications(modifications); Assert.That(modifications.Count, Is.EqualTo(1)); Assert.That(modifications.Contains("variable1")); }
public void TestGetDependenciesUpdateSelf() { var variable = new VariableExpression("variable1"); var value = new IntegerConstantExpression(1); var mathematic = new MathematicExpression(variable, MathematicOperation.Add, value); var expr = new AssignmentExpression(variable, mathematic); var dependencies = new HashSet <string>(); ((INestedExpressions)expr).GetDependencies(dependencies); Assert.That(dependencies.Count, Is.EqualTo(1)); Assert.That(dependencies.Contains("variable1")); // variable1 is read from before it's assigned }
private bool ReplaceVariablesMathematic(MathematicExpression mathematic, InterpreterScope scope, out ExpressionBase result) { // assert: left/right already expanded by calling function var left = mathematic.Left; var mathematicLeft = left as MathematicExpression; if (mathematicLeft != null) { if (!ReplaceVariablesMathematic(mathematicLeft, scope, out result)) { return(false); } left = result; } else if (left.Type != ExpressionType.IntegerConstant) { if (!WrapMemoryAccessor(left, scope, out result)) { return(false); } left = result; } var right = mathematic.Right; var mathematicRight = right as MathematicExpression; if (mathematicRight != null) { if (!ReplaceVariablesMathematic(mathematicRight, scope, out result)) { return(false); } right = result; } else if (right.Type != ExpressionType.IntegerConstant) { if (!WrapMemoryAccessor(right, scope, out result)) { return(false); } right = result; } result = new MathematicExpression(left, mathematic.Operation, right); CopyLocation(result); return(true); }
public void TestAddZero() { var left = new FunctionCallExpression("byte", new[] { new IntegerConstantExpression(0) }); var right = new IntegerConstantExpression(0); var expr = new MathematicExpression(left, MathematicOperation.Add, right); var scope = new InterpreterScope(); scope.Context = new TriggerBuilderContext(); scope.AddFunction(new MemoryAccessorFunction("byte", FieldSize.Byte)); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result.ToString(), Is.EqualTo(left.ToString())); }
public void TestModulusZero() { var left = new FunctionCallExpression("byte", new[] { new IntegerConstantExpression(0) }); var right = new IntegerConstantExpression(0); var expr = new MathematicExpression(left, MathematicOperation.Modulus, right); var scope = new InterpreterScope(); scope.Context = new TriggerBuilderContext(); scope.AddFunction(new MemoryAccessorFunction("byte", FieldSize.Byte)); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.False); Assert.That(((ParseErrorExpression)result).Message, Is.EqualTo("Division by zero")); }
public void TestReplaceVariablesAppendMathematic() { var variable = new VariableExpression("variable"); var value = new IntegerConstantExpression(1); var append = new MathematicExpression(variable, MathematicOperation.Add, value); var expr = new AssignmentExpression(variable, append); var scope = new InterpreterScope(); // variable doesn't have a current value, addition not supported ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.False); Assert.That(result, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)result).Message, Is.EqualTo("Unknown variable: variable")); }
public void TestRebalanceMathematical() { // "A < B + C" => "A < (B + C)" var variable1 = new VariableExpression("variable1"); var variable2 = new VariableExpression("variable2"); var value = new IntegerConstantExpression(99); var conditional = new MathematicExpression(value, MathematicOperation.Add, variable2); var expr = new ComparisonExpression(variable1, ComparisonOperation.LessThan, conditional); var result = expr.Rebalance() as ComparisonExpression; Assert.That(result, Is.Not.Null); Assert.That(result.Left, Is.EqualTo(expr.Left)); Assert.That(result.Operation, Is.EqualTo(expr.Operation)); Assert.That(result.Right, Is.EqualTo(expr.Right)); }
public void TestRebalanceCondition() { // A + B && C => (A + B) && C var variable1 = new VariableExpression("variable1"); var variable2 = new VariableExpression("variable2"); var value = new IntegerConstantExpression(99); var clause = new ConditionalExpression(variable2, ConditionalOperation.And, value); var expr = new MathematicExpression(variable1, MathematicOperation.Add, clause); var result = expr.Rebalance() as ConditionalExpression; Assert.That(result, Is.Not.Null); var expectedLeft = new MathematicExpression(variable1, MathematicOperation.Add, variable2); Assert.That(result.Left, Is.EqualTo(expectedLeft)); Assert.That(result.Operation, Is.EqualTo(clause.Operation)); Assert.That(result.Right, Is.EqualTo(value)); }
public void TestAddVariables() { var value1 = new IntegerConstantExpression(1); var value2 = new IntegerConstantExpression(2); var left = new VariableExpression("left"); var right = new VariableExpression("right"); var expr = new MathematicExpression(left, MathematicOperation.Add, right); var scope = new InterpreterScope(); scope.AssignVariable(left, value1); scope.AssignVariable(right, value2); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <IntegerConstantExpression>()); Assert.That(((IntegerConstantExpression)result).Value, Is.EqualTo(3)); }
public void TestReplaceVariablesIndexMathematical() { var variable = new VariableExpression("variable"); var key = new IntegerConstantExpression(6); var index = new MathematicExpression(new IntegerConstantExpression(2), MathematicOperation.Add, new IntegerConstantExpression(4)); var value = new IntegerConstantExpression(99); var dict = new DictionaryExpression(); dict.Add(key, value); var expr = new IndexedVariableExpression(variable, index); var scope = new InterpreterScope(); scope.AssignVariable(variable, dict); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <IntegerConstantExpression>()); Assert.That(((IntegerConstantExpression)result).Value, Is.EqualTo(99)); }
private static bool MergeFields(Field field, Term term, MathematicOperation operation) { ExpressionBase right; if (field.Type == FieldType.Value) { right = new IntegerConstantExpression((int)field.Value); } else if (field.Type == FieldType.Float) { right = new FloatConstantExpression(field.Float); } else { return(false); } ExpressionBase left = null; if (term.multiplier == 1.0) { if (term.field.Type == FieldType.Value) { left = new IntegerConstantExpression((int)term.field.Value); } else if (term.field.Type == FieldType.Float) { left = new FloatConstantExpression(term.field.Float); } } if (left == null) { FloatConstantExpression floatRight; switch (operation) { case MathematicOperation.Multiply: floatRight = FloatConstantExpression.ConvertFrom(right) as FloatConstantExpression; if (floatRight == null) { return(false); } term.multiplier *= floatRight.Value; return(true); case MathematicOperation.Divide: floatRight = FloatConstantExpression.ConvertFrom(right) as FloatConstantExpression; if (floatRight == null) { return(false); } term.multiplier /= floatRight.Value; return(true); default: return(false); } } var mathematicExpression = new MathematicExpression(left, operation, right); term.field = AchievementBuilder.CreateFieldFromExpression(mathematicExpression.MergeOperands()); return(true); }
private ParseErrorExpression ExecuteAchievementComparison(ComparisonExpression comparison, InterpreterScope scope) { _equalityModifiers.Clear(); var context = scope.GetContext <TriggerBuilderContext>(); var insertIndex = context.Trigger.Count; var left = comparison.Left; var right = comparison.Right; var op = GetRequirementOperator(comparison.Operation); if (left.Type == ExpressionType.IntegerConstant) { if (right.Type == ExpressionType.IntegerConstant) { // comparing two constants, convert to always_true or always_false var requirement = new Requirement { Left = new Field { Type = FieldType.Value, Value = (uint)((IntegerConstantExpression)left).Value }, Operator = op, Right = new Field { Type = FieldType.Value, Value = (uint)((IntegerConstantExpression)right).Value }, }; if (Evaluate(requirement)) { context.Trigger.Add(AlwaysTrueFunction.CreateAlwaysTrueRequirement()); } else { context.Trigger.Add(AlwaysFalseFunction.CreateAlwaysFalseRequirement()); } return(null); } // swap the operands and operator so the constant is on the right var temp = left; left = right; right = temp; op = GetReversedRequirementOperator(op); } var error = ExecuteAchievementExpression(left, scope); if (error != null) { return(error); } var integerRight = right as IntegerConstantExpression; if (integerRight != null) { int newValue = integerRight.Value; // SubSource of a memory accessor may cause overflow - if comparing for less than, // modifiers should not be merged into the compare target if (_equalityModifiers.Count > 0 && (op == RequirementOperator.LessThan || op == RequirementOperator.LessThanOrEqual)) { bool hasSubSource = false; for (int i = context.Trigger.Count - 2; i >= 0; i++) { var requirementType = context.Trigger.ElementAt(i).Type; if (requirementType == RequirementType.SubSource) { hasSubSource = true; break; } else if (requirementType != RequirementType.AddSource && requirementType != RequirementType.AddHits) { break; } } if (hasSubSource) { var last = context.Trigger.Last(); context.Trigger.Remove(last); foreach (var modifier in _equalityModifiers) { if (modifier.Operation != MathematicOperation.Subtract && modifier.Operation != MathematicOperation.Add) { return(new ParseErrorExpression("Cannot normalize expression containing SubSource", left)); } context.Trigger.Add(new Requirement { Type = (modifier.Operation == MathematicOperation.Subtract) ? RequirementType.AddSource : RequirementType.SubSource, Left = new Field { Type = FieldType.Value, Value = (uint)modifier.Amount }, Operator = RequirementOperator.None, Right = new Field() }); } context.Trigger.Add(last); _equalityModifiers.Clear(); } } while (_equalityModifiers.Count > 0) { var originalValue = newValue; var modifier = _equalityModifiers.Pop(); newValue = modifier.Apply(newValue); var restoredValue = modifier.Remove(newValue); switch (op) { case RequirementOperator.Equal: if (restoredValue != originalValue) { return(new ParseErrorExpression("Result can never be true using integer math", comparison)); } break; case RequirementOperator.NotEqual: if (restoredValue != originalValue) { return(new ParseErrorExpression("Result is always true using integer math", comparison)); } break; case RequirementOperator.GreaterThanOrEqual: if (restoredValue != originalValue) { op = RequirementOperator.GreaterThan; } break; } } var requirement = context.LastRequirement; requirement.Operator = op; requirement.Right = new Field { Size = requirement.Left.Size, Type = FieldType.Value, Value = (uint)newValue }; } else { var leftModifiers = new Stack <ValueModifier>(_equalityModifiers.Reverse()); _equalityModifiers.Clear(); error = ExecuteAchievementExpression(right, scope); if (error != null) { return(error); } if (leftModifiers.Count > 0 || _equalityModifiers.Count > 0) { var rightValue = 1234567; var leftValue = rightValue; while (leftModifiers.Count > 0) { var modifier = leftModifiers.Pop(); leftValue = ValueModifier.Apply(leftValue, MathematicExpression.GetOppositeOperation(modifier.Operation), modifier.Amount); } while (_equalityModifiers.Count > 0) { var modifier = _equalityModifiers.Pop(); rightValue = ValueModifier.Apply(rightValue, MathematicExpression.GetOppositeOperation(modifier.Operation), modifier.Amount); } var diff = leftValue - rightValue; if (diff != 0) { var modifier = new Requirement(); if (diff < 0) { modifier.Left = new Field { Type = FieldType.Value, Value = (uint)(-diff) }; modifier.Type = RequirementType.SubSource; } else { modifier.Left = new Field { Type = FieldType.Value, Value = (uint)diff }; modifier.Type = RequirementType.AddSource; } ((IList <Requirement>)context.Trigger).Insert(insertIndex, modifier); } } var extraRequirement = context.LastRequirement; context.Trigger.Remove(extraRequirement); var requirement = context.LastRequirement; if (requirement != null) { requirement.Operator = op; requirement.Right = extraRequirement.Left; } } return(null); }
private static bool ProcessValueExpression(ExpressionBase expression, InterpreterScope scope, List <Term> terms, out ExpressionBase result) { var functionCall = expression as FunctionCallExpression; if (functionCall != null) { var requirements = new List <Requirement>(); var context = new ValueBuilderContext() { Trigger = requirements }; var valueScope = new InterpreterScope(scope) { Context = context }; var error = context.CallFunction(functionCall, valueScope); if (error != null) { result = error; return(false); } SetImpliedMeasuredTarget(requirements); return(ProcessMeasuredValue(requirements, expression, terms, out result)); } var field = AchievementBuilder.CreateFieldFromExpression(expression); if (field.Type != FieldType.None) { terms.Last().field = field; result = null; return(true); } var mathematic = expression as MathematicExpression; if (mathematic != null) { if (mathematic.Operation == MathematicOperation.Multiply || mathematic.Operation == MathematicOperation.Divide) { var mathematicLeft = mathematic.Left as MathematicExpression; if (mathematicLeft != null && MathematicExpression.GetPriority(mathematicLeft.Operation) == MathematicPriority.Add) { var newLeft = new MathematicExpression(mathematicLeft.Left, mathematic.Operation, mathematic.Right); var newRight = new MathematicExpression(mathematicLeft.Right, mathematic.Operation, mathematic.Right); mathematic = new MathematicExpression(newLeft, mathematicLeft.Operation, newRight); } } if (!ProcessValueExpression(mathematic.Left, scope, terms, out result)) { return(false); } field = AchievementBuilder.CreateFieldFromExpression(mathematic.Right); if (MergeFields(field, terms.Last(), mathematic.Operation)) { return(true); } switch (mathematic.Operation) { case MathematicOperation.Add: terms.Add(new Term { multiplier = 1.0 }); return(ProcessValueExpression(mathematic.Right, scope, terms, out result)); case MathematicOperation.Subtract: terms.Add(new Term { multiplier = -1.0 }); return(ProcessValueExpression(mathematic.Right, scope, terms, out result)); case MathematicOperation.Multiply: case MathematicOperation.Divide: return(ProcessValueExpression(WrapInMeasured(expression), scope, terms, out result)); } } var conditionalExpression = expression as ConditionalExpression; if (conditionalExpression != null) { ParseErrorExpression parseError; var achievement = new ScriptInterpreterAchievementBuilder(); if (!achievement.PopulateFromExpression(expression, scope, out parseError)) { result = parseError; return(false); } SetImpliedMeasuredTarget(achievement.CoreRequirements); foreach (var alt in achievement.AlternateRequirements) { SetImpliedMeasuredTarget(alt); } var message = achievement.Optimize(); if (message != null) { result = new ParseErrorExpression(message, expression); return(false); } if (achievement.AlternateRequirements.Any()) { result = new ParseErrorExpression("Alt groups not supported in value expression", expression); return(false); } return(ProcessMeasuredValue(achievement.CoreRequirements, expression, terms, out result)); } result = new ParseErrorExpression("Value must be a constant or a memory accessor", expression); return(false); }
private ParseErrorExpression ExecuteAchievementMathematic(MathematicExpression mathematic, InterpreterScope scope) { var left = mathematic.Left; var operation = mathematic.Operation; var context = scope.GetContext <TriggerBuilderContext>(); ParseErrorExpression error; ExpressionBase right; if (!mathematic.Right.ReplaceVariables(scope, out right)) { return((ParseErrorExpression)right); } if (operation == MathematicOperation.Subtract && (right is FunctionCallExpression || right is MathematicExpression)) { // if subtracting a non-integer, swap the order to perform a SubSource var newEqualityModifiers = new Stack <ValueModifier>(); var oldEqualityModifiers = _equalityModifiers; _equalityModifiers = newEqualityModifiers; var requirements = new List <Requirement>(); var innerContext = new TriggerBuilderContext() { Trigger = requirements }; var innerScope = new InterpreterScope(scope) { Context = innerContext }; // generate the condition for the right side error = ExecuteAchievementExpression(right, innerScope); _equalityModifiers = oldEqualityModifiers; if (error != null) { return(error); } foreach (var requirement in requirements) { switch (requirement.Type) { case RequirementType.None: case RequirementType.AddSource: requirement.Type = RequirementType.SubSource; break; case RequirementType.SubSource: requirement.Type = RequirementType.AddSource; break; default: return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } context.Trigger.Add(requirement); } foreach (var modifier in newEqualityModifiers) { switch (modifier.Operation) { case MathematicOperation.Add: _equalityModifiers.Push(new ValueModifier(MathematicOperation.Subtract, modifier.Amount)); break; case MathematicOperation.Subtract: _equalityModifiers.Push(new ValueModifier(MathematicOperation.Add, modifier.Amount)); break; default: return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } } right = mathematic.Left; operation = MathematicOperation.Add; } else { // generate the condition for the first expression error = ExecuteAchievementExpression(left, scope); if (error != null) { return(error); } } var integerOperand = right as IntegerConstantExpression; if (integerOperand != null) { var oppositeOperation = MathematicExpression.GetOppositeOperation(operation); if (oppositeOperation == MathematicOperation.None) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } var priority = MathematicExpression.GetPriority(mathematic.Operation); if (priority != MathematicPriority.Add) { if (context.Trigger.Count > 1) { var previousRequirementType = context.Trigger.ElementAt(context.Trigger.Count - 2).Type; if (previousRequirementType == RequirementType.AddSource || previousRequirementType == RequirementType.SubSource) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } } if (_equalityModifiers.Any(e => MathematicExpression.GetPriority(e.Operation) != priority)) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } } _equalityModifiers.Push(new ValueModifier(oppositeOperation, integerOperand.Value)); return(null); } if (operation == MathematicOperation.Add) { foreach (var modifier in _equalityModifiers) { if (MathematicExpression.GetPriority(modifier.Operation) != MathematicPriority.Add) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(MathematicExpression.GetOppositeOperation(modifier.Operation))), mathematic)); } } // adding two memory accessors - make sure previous is AddSource or SubSource if (context.LastRequirement.Type != RequirementType.SubSource) { context.LastRequirement.Type = RequirementType.AddSource; context.LastRequirement.Operator = RequirementOperator.None; context.LastRequirement.Right = new Field(); } } else { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } // generate the condition for the second expression error = ExecuteAchievementExpression(right, scope); if (error != null) { error = new ParseErrorExpression(error.Message, mathematic); } return(error); }
private ParseErrorExpression ExecuteAchievementMathematic(MathematicExpression mathematic, InterpreterScope scope) { ParseErrorExpression error; var context = scope.GetContext <TriggerBuilderContext>(); var operation = mathematic.Operation; switch (operation) { case MathematicOperation.Add: case MathematicOperation.Subtract: break; case MathematicOperation.Multiply: case MathematicOperation.Divide: case MathematicOperation.BitwiseAnd: // generate the condition for the right side Field operand = CreateFieldFromExpression(mathematic.Right); if (operand.Type == FieldType.None) { var requirements = new List <Requirement>(); var innerContext = new TriggerBuilderContext() { Trigger = requirements }; var innerScope = new InterpreterScope(scope) { Context = innerContext }; error = ExecuteAchievementExpression(mathematic.Right, innerScope); if (error != null) { return(error); } if (requirements.Count > 1) { return(new ParseErrorExpression("Multiplication by complex value not supported", mathematic)); } operand = requirements[0].Left; } // generate the conditions for the left side error = ExecuteAchievementExpression(mathematic.Left, scope); if (error != null) { return(error); } if (context.LastRequirement.Operator != RequirementOperator.None) { return(new ParseErrorExpression("Cannot generate condition using both " + context.LastRequirement.Operator + " and " + operation)); } switch (operation) { case MathematicOperation.Multiply: context.LastRequirement.Operator = RequirementOperator.Multiply; break; case MathematicOperation.Divide: context.LastRequirement.Operator = RequirementOperator.Divide; break; case MathematicOperation.BitwiseAnd: context.LastRequirement.Operator = RequirementOperator.BitwiseAnd; break; } context.LastRequirement.Right = operand; return(null); default: return(new ParseErrorExpression("Cannot normalize expression to eliminate " + MathematicExpression.GetOperatorType(operation), mathematic)); } var left = mathematic.Left; ExpressionBase right; if (!mathematic.Right.ReplaceVariables(scope, out right)) { return((ParseErrorExpression)right); } Field field = CreateFieldFromExpression(right); if (field.Type != FieldType.None) { context.Trigger.Add(new Requirement { Type = (operation == MathematicOperation.Add) ? RequirementType.AddSource : RequirementType.SubSource, Left = field, Operator = RequirementOperator.None, Right = new Field() }); } if (operation == MathematicOperation.Subtract && (right is FunctionCallExpression || right is MathematicExpression)) { // if subtracting a non-integer, swap the order to perform a SubSource var requirements = new List <Requirement>(); var innerContext = new TriggerBuilderContext() { Trigger = requirements }; var innerScope = new InterpreterScope(scope) { Context = innerContext }; // generate the condition for the right side error = ExecuteAchievementExpression(right, innerScope); if (error != null) { return(error); } foreach (var requirement in requirements) { switch (requirement.Type) { case RequirementType.None: case RequirementType.AddSource: requirement.Type = RequirementType.SubSource; break; case RequirementType.SubSource: requirement.Type = RequirementType.AddSource; break; case RequirementType.AddAddress: // AddAddress is allowed as long as it's not the last requirement if (ReferenceEquals(requirement, requirements.Last())) { return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } break; default: return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } context.Trigger.Add(requirement); } right = mathematic.Left; operation = MathematicOperation.Add; } else { // generate the condition for the first expression error = ExecuteAchievementExpression(left, scope); if (error != null) { return(error); } } if (field.Type != FieldType.None) { return(null); } if (operation == MathematicOperation.Add) { // adding two memory accessors - make sure previous is AddSource or SubSource var lastRequirement = context.LastRequirement; if (lastRequirement.Type != RequirementType.SubSource) { lastRequirement.Type = RequirementType.AddSource; if (lastRequirement.IsComparison) { lastRequirement.Operator = RequirementOperator.None; lastRequirement.Right = new Field(); } } } else { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } field = CreateFieldFromExpression(right); if (field.Type != FieldType.None) { context.Trigger.Add(new Requirement { Type = RequirementType.None, Left = field, Operator = RequirementOperator.None, Right = new Field() }); return(null); } // generate the condition for the second expression error = ExecuteAchievementExpression(right, scope); if (error != null) { error = new ParseErrorExpression(error.Message, mathematic); } // make sure the mathematic expression doesn't result in a comparison { var lastRequirement = context.LastRequirement; if (lastRequirement.IsComparison) { lastRequirement.Operator = RequirementOperator.None; lastRequirement.Right = new Field(); } } return(error); }
public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result) { if (!IsInTriggerClause(scope, out result)) { return(false); } var parameter = GetParameter(scope, "accessor", out result); if (parameter == null) { return(false); } if (!parameter.ReplaceVariables(scope, out result)) { return(false); } parameter = result; var mathematic = parameter as MathematicExpression; if (mathematic != null) { var left = mathematic.Left; if (!(left is IntegerConstantExpression)) { left = new FunctionCallExpression(Name.Name, new ExpressionBase[] { mathematic.Left }); if (!left.ReplaceVariables(scope, out result)) { return(false); } left = result; } var right = mathematic.Right; if (!(right is IntegerConstantExpression)) { right = new FunctionCallExpression(Name.Name, new ExpressionBase[] { mathematic.Right }); if (!right.ReplaceVariables(scope, out result)) { return(false); } right = result; } result = new MathematicExpression(left, mathematic.Operation, right); return(true); } var functionCall = parameter as FunctionCallExpression; if (functionCall == null) { result = new ParseErrorExpression("accessor did not evaluate to a memory accessor", parameter); return(false); } var functionDefinition = scope.GetFunction(functionCall.FunctionName.Name); var memoryAccessor = functionDefinition as MemoryAccessorFunction; if (memoryAccessor == null) { result = new ParseErrorExpression("accessor did not evaluate to a memory accessor", parameter); return(false); } result = new FunctionCallExpression(Name.Name, new ExpressionBase[] { functionCall }); return(true); }