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);
        }
Example #2
0
        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);
        }