/// <summary> /// Populates the <see cref="AchievementBuilder"/> from an expression. /// </summary> /// <param name="expression">The expression to populate from.</param> /// <returns><c>null</c> if successful, otherwise an error message indicating why it failed.</returns> public string PopulateFromExpression(ExpressionBase expression) { var context = new TriggerBuilderContext { Trigger = CoreRequirements }; var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()) { Context = context }; ExpressionBase result; if (!expression.ReplaceVariables(scope, out result)) { return(((ParseErrorExpression)result).Message); } ParseErrorExpression error; if (!PopulateFromExpression(result, scope, out error)) { return(error.Message); } return(null); }
internal bool PopulateFromExpression(ExpressionBase expression, InterpreterScope scope, out ParseErrorExpression error) { var andedConditions = new List <ExpressionBase>(); var orConditions = new List <ExpressionBase>(); if (!SortConditions(expression, andedConditions, orConditions, out error)) { return(false); } if (orConditions.Count() > 1) { var altPart = CrossMultiplyOrConditions(orConditions); if (altPart.Type == ExpressionType.ParseError) { expression.CopyLocation(altPart); error = (ParseErrorExpression)altPart; return(false); } andedConditions.Add(altPart); } else if (orConditions.Count() == 1) { andedConditions.Add(orConditions[0]); } var context = new TriggerBuilderContext { Trigger = CoreRequirements }; var innerScope = new InterpreterScope(scope) { Context = context }; foreach (var condition in andedConditions) { error = ExecuteAchievementClause(condition, innerScope); if (error != null) { switch (condition.Type) { case ExpressionType.Comparison: case ExpressionType.Conditional: break; default: error = ParseErrorExpression.WrapError(error, "Invalid condition", condition); break; } return(false); } } return(true); }
/// <summary> /// Begins an new alt group. /// </summary> /// <returns>The requirement collection for the new alt group.</returns> private void BeginAlt(TriggerBuilderContext context) { if (ReferenceEquals(context.Trigger, CoreRequirements) || context.Trigger.Count > 0) { var newAlt = new List <Requirement>(); context.Trigger = newAlt; AlternateRequirements.Add(newAlt); } }
internal bool PopulateFromExpression(ExpressionBase expression, InterpreterScope scope, out ParseErrorExpression error) { if (!NormalizeNots(ref expression, out error)) { return(false); } var andedConditions = new List <ExpressionBase>(); var orConditions = new List <ExpressionBase>(); if (!SortConditions(expression, andedConditions, orConditions, out error)) { return(false); } if (orConditions.Count() != 0) { var altPart = CrossMultiplyOrConditions(orConditions); andedConditions.Add(altPart); } var context = new TriggerBuilderContext { Trigger = CoreRequirements }; var innerScope = new InterpreterScope(scope) { Context = context }; foreach (var condition in andedConditions) { error = ExecuteAchievementExpression(condition, innerScope); if (error != null) { if (error.InnerError != null) { error = new ParseErrorExpression(error.InnermostError.Message, error.Line, error.Column, error.EndLine, error.EndColumn); } return(false); } } return(true); }
private ParseErrorExpression ExecuteAchievementConditional(ConditionalExpression condition, InterpreterScope scope, TriggerBuilderContext context) { if (condition.Operation == ConditionalOperation.Not) { return(new ParseErrorExpression("! operator should have been normalized out", condition)); } foreach (var clause in condition.Conditions) { // important to separate both clauses from the core group or previous alt group - // does nothing if previous alt group is empty if (condition.Operation == ConditionalOperation.Or) { BeginAlt(context); } var error = ExecuteAchievementClause(clause, scope); if (error != null) { return(error); } } return(null); }
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 abstract ParseErrorExpression BuildTrigger(TriggerBuilderContext context, InterpreterScope scope, FunctionCallExpression functionCall);
private static bool ProcessValueExpression(ExpressionBase expression, InterpreterScope scope, StringBuilder builder, out ExpressionBase result) { IntegerConstantExpression integer; var mathematic = expression as MathematicExpression; if (mathematic != null) { if (!ProcessValueExpression(mathematic.Left, scope, builder, out result)) { return(false); } integer = mathematic.Right as IntegerConstantExpression; switch (mathematic.Operation) { case MathematicOperation.Add: builder.Append('_'); if (integer != null) { builder.Append('v'); builder.Append(integer.Value); return(true); } return(ProcessValueExpression(mathematic.Right, scope, builder, out result)); case MathematicOperation.Subtract: if (integer != null) { builder.Append("_v-"); builder.Append(integer.Value); return(true); } result = new ParseErrorExpression("Value being subtracted must be an integer constant", mathematic.Right); return(false); case MathematicOperation.Multiply: if (integer != null) { builder.Append('*'); builder.Append(integer.Value); return(true); } result = new ParseErrorExpression("Multiplier must be an integer constant", mathematic.Right); return(false); case MathematicOperation.Divide: if (integer != null) { builder.Append('*'); var inverted = 1 / (double)integer.Value; builder.Append(inverted); return(true); } result = new ParseErrorExpression("Divisor must be an integer constant", mathematic.Right); return(false); } } integer = expression as IntegerConstantExpression; if (integer != null) { result = new ParseErrorExpression("value cannot start with integer constant", integer); return(false); } var functionCall = expression as FunctionCallExpression; if (functionCall == null) { result = new ParseErrorExpression("value can only contain memory accessors or arithmetic expressions", expression); return(false); } var requirements = new List <Requirement>(); var context = new TriggerBuilderContext { Trigger = requirements }; var innerScope = new InterpreterScope(scope) { Context = context }; result = context.CallFunction(functionCall, innerScope); if (result != null) { return(false); } if (requirements.Count != 1 || requirements[0].Operator != RequirementOperator.None) { result = new ParseErrorExpression(functionCall.FunctionName.Name + " did not evaluate to a memory accessor", functionCall.FunctionName); return(false); } requirements[0].Left.Serialize(builder); return(true); }
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); }