/// <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);
     }
 }
Example #4
0
        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);
        }
Example #7
0
 public abstract ParseErrorExpression BuildTrigger(TriggerBuilderContext context, InterpreterScope scope, FunctionCallExpression functionCall);
Example #8
0
        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);
        }
Example #9
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);
        }