private static ExpressionBase WrapInMeasured(ExpressionBase expression) { return(new FunctionCallExpression("measured", new ExpressionBase[] { expression, AlwaysTrueFunction.CreateAlwaysTrueFunctionCall(), new StringConstantExpression("raw") })); }
public void TestIsTrueAlwaysTrueFunction() { var expr = AlwaysTrueFunction.CreateAlwaysTrueFunctionCall(); var scope = AchievementScriptInterpreter.GetGlobalScope(); ParseErrorExpression error; Assert.That(expr.IsTrue(scope, out error), Is.True); Assert.That(error, Is.Null); }
private ParseErrorExpression ExecuteAchievementExpression(ExpressionBase expression, InterpreterScope scope) { ExpressionBase operand; switch (expression.Type) { case ExpressionType.FunctionCall: return(ExecuteAchievementFunction((FunctionCallExpression)expression, scope)); case ExpressionType.Conditional: return(ExecuteAchievementConditional((ConditionalExpression)expression, scope)); case ExpressionType.Comparison: return(ExecuteAchievementComparison((ComparisonExpression)expression, scope)); case ExpressionType.Mathematic: return(ExecuteAchievementMathematic((MathematicExpression)expression, scope)); case ExpressionType.Variable: if (!expression.ReplaceVariables(scope, out operand)) { return(new ParseErrorExpression(operand, expression)); } if (expression is FunctionReferenceExpression) { return(new ParseErrorExpression("Function used like a variable", expression)); } return(ExecuteAchievementExpression(operand, scope)); case ExpressionType.BooleanConstant: { var context = scope.GetContext <TriggerBuilderContext>(); if (((BooleanConstantExpression)expression).Value == true) { context.Trigger.Add(AlwaysTrueFunction.CreateAlwaysTrueRequirement()); } else { context.Trigger.Add(AlwaysFalseFunction.CreateAlwaysFalseRequirement()); } return(null); } } return(new ParseErrorExpression("Cannot generate trigger from " + expression.Type, expression)); }
private ParseErrorExpression ExecuteAchievementComparison(ComparisonExpression comparison, InterpreterScope scope) { var left = comparison.Left; var right = comparison.Right; if (left.Type == ExpressionType.Comparison || right.Type == ExpressionType.Comparison) { return(new ParseErrorExpression("comparison did not evaluate to a valid comparison", comparison)); } var context = scope.GetContext <TriggerBuilderContext>(); var op = GetRequirementOperator(comparison.Operation); var leftField = CreateFieldFromExpression(left); if (leftField.Type != FieldType.None) { var rightField = CreateFieldFromExpression(right); if (rightField.Type != FieldType.None) { // comparing two constants, convert to always_true or always_false var requirement = new Requirement { Left = leftField, Operator = op, Right = rightField, }; if (requirement.Evaluate() == true) { 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 = Requirement.GetReversedRequirementOperator(op); } var error = ExecuteAchievementExpression(left, scope); if (error != null) { return(ParseErrorExpression.WrapError(error, "Invalid value", left)); } if (right.Type == ExpressionType.IntegerConstant || right.Type == ExpressionType.FloatConstant) { var lastRequirement = context.LastRequirement; if (lastRequirement.Operator != RequirementOperator.None) { // try to rearrange so the last requirement doesn't have a modifier for (int i = context.Trigger.Count - 2; i >= 0; --i) { var requirement = context.Trigger.ElementAt(i); if (requirement.Type == RequirementType.AddSource && requirement.Operator == RequirementOperator.None) { context.Trigger.Remove(lastRequirement); context.Trigger.Add(new Requirement { Left = requirement.Left }); requirement.Left = lastRequirement.Left; requirement.Operator = lastRequirement.Operator; requirement.Right = lastRequirement.Right; lastRequirement = context.LastRequirement; break; } if (requirement.Type != RequirementType.AddSource && requirement.Type != RequirementType.SubSource && requirement.Type != RequirementType.AddAddress) { break; } } if (lastRequirement.Operator != RequirementOperator.None) { // last requirement still has a modifier, have to add a dummy condition. lastRequirement.Type = RequirementType.AddSource; context.Trigger.Add(new Requirement { Left = new Field { Size = lastRequirement.Left.Size, Type = FieldType.Value, Value = 0 } }); lastRequirement = context.LastRequirement; } } lastRequirement.Operator = op; lastRequirement.Right = CreateFieldFromExpression(right); } else { error = ExecuteAchievementExpression(right, scope); if (error != null) { return(ParseErrorExpression.WrapError(error, "Invalid value", right)); } var extraRequirement = context.LastRequirement; ((IList <Requirement>)context.Trigger).RemoveAt(context.Trigger.Count - 1); var requirement = context.LastRequirement; if (requirement != null) { if (requirement.Type == RequirementType.AddAddress) { return(HandleAddAddressComparison(comparison, (IList <Requirement>)context.Trigger, op, extraRequirement)); } else if (context.Trigger.Count > 1 && context.Trigger.ElementAt(context.Trigger.Count - 2).Type == RequirementType.AddAddress) { // if left side is an AddAddress chain, but right side is a not, we have to keep the // dummy condition to prevent the AddAddress from modifying the memory address on the // right side. integers are handled above. requirement.Type = RequirementType.AddSource; extraRequirement.Right = extraRequirement.Left; extraRequirement.Left = new Field { Type = FieldType.Value, Value = 0 }; extraRequirement.Operator = op; context.Trigger.Add(extraRequirement); } else if (requirement.Operator != RequirementOperator.None) { // if left side is a complex expression (a * 4), add a new dummy condition for the comparison requirement.Type = RequirementType.AddSource; extraRequirement.Right = extraRequirement.Left; extraRequirement.Left = new Field { Type = FieldType.Value, Value = 0 }; extraRequirement.Operator = op; context.Trigger.Add(extraRequirement); } else { // no AddAddress on either side, just merge the conditions requirement.Operator = op; requirement.Right = extraRequirement.Left; } } } return(null); }
internal static ExpressionBase InvertExpression(ExpressionBase expression) { // logical inversion var condition = expression as ConditionalExpression; if (condition != null) { var newConditions = new List <ExpressionBase>(condition._conditions.Count); foreach (var oldCondition in condition._conditions) { newConditions.Add(InvertExpression(oldCondition)); } switch (condition.Operation) { case ConditionalOperation.Not: // !(!A) => A return(newConditions[0]); case ConditionalOperation.And: // !(A && B) => !A || !B return(new ConditionalExpression(ConditionalOperation.Or, newConditions)); case ConditionalOperation.Or: // !(A || B) => !A && !B return(new ConditionalExpression(ConditionalOperation.And, newConditions)); default: throw new NotImplementedException("Unsupported condition operation"); } } // comparative inversion var comparison = expression as ComparisonExpression; if (comparison != null) { // !(A == B) => A != B, !(A < B) => A >= B, ... return(new ComparisonExpression( comparison.Left, ComparisonExpression.GetOppositeComparisonOperation(comparison.Operation), comparison.Right)); } // boolean constant var boolean = expression as BooleanConstantExpression; if (boolean != null) { return(new BooleanConstantExpression(!boolean.Value)); } // special handling for built-in functions var function = expression as FunctionCallExpression; if (function != null && function.Parameters.Count() == 0) { if (function.FunctionName.Name == "always_true") { return(AlwaysFalseFunction.CreateAlwaysFalseFunctionCall()); } if (function.FunctionName.Name == "always_false") { return(AlwaysTrueFunction.CreateAlwaysTrueFunctionCall()); } } // unsupported inversion return(new ParseErrorExpression("! operator cannot be applied to " + expression.Type, expression)); }
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); }