示例#1
0
        protected override void OnFormatLine(LineFormatEventArgs e)
        {
            int line        = e.Line.Line;
            var expressions = new List <ExpressionBase>();

            _parsedContent.GetExpressionsForLine(expressions, line);

            foreach (var expression in expressions)
            {
                string tooltip = null;

                if (_scope != null)
                {
                    var variable = expression as VariableExpression;
                    if (variable != null)
                    {
                        var value = _scope.GetVariable(variable.Name);
                        if (value != null)
                        {
                            tooltip = BuildTooltip(value);
                        }
                    }

                    var functionCall = expression as FunctionCallExpression;
                    if (functionCall != null)
                    {
                        var function = _scope.GetFunction(functionCall.FunctionName.Name);
                        if (function != null && function.Expressions.Count == 1)
                        {
                            tooltip = BuildTooltip(function.Expressions.First());
                        }
                    }
                }

                var expressionStart = (expression.Line == line) ? expression.Column : 1;
                var expressionEnd   = (expression.EndLine == line) ? expression.EndColumn : e.Line.Text.Length + 1;

                var parseError = expression as ParseErrorExpression;
                if (parseError != null)
                {
                    e.SetError(expressionStart, expressionEnd - expressionStart + 1, parseError.Message);
                }
                else
                {
                    e.SetColor(expressionStart, expressionEnd - expressionStart + 1, (int)expression.Type, tooltip);
                }
            }

            base.OnFormatLine(e);
        }
示例#2
0
        public ParseErrorExpression CallFunction(FunctionCallExpression functionCall, InterpreterScope scope)
        {
            var functionDefinition = scope.GetFunction(functionCall.FunctionName.Name);

            if (functionDefinition == null)
            {
                return(new UnknownVariableParseErrorExpression("Unknown function: " + functionCall.FunctionName.Name, functionCall.FunctionName));
            }

            var triggerBuilderFunction = functionDefinition as FunctionDefinition;

            if (triggerBuilderFunction == null)
            {
                return(new ParseErrorExpression(functionCall.FunctionName.Name + " cannot be called from within a trigger clause", functionCall));
            }

            var error = triggerBuilderFunction.BuildTrigger(this, scope, functionCall);

            if (error != null)
            {
                return(ParseErrorExpression.WrapError(error, "Function call failed.", functionCall));
            }

            return(null);
        }
示例#3
0
        private string ProcessValue(InterpreterScope scope, string parameter, out ExpressionBase result)
        {
            var expression = GetParameter(scope, parameter, out result);

            if (expression == null)
            {
                return(null);
            }

            var functionCallExpression = expression as FunctionCallExpression;

            if (functionCallExpression != null)
            {
                var functionDefinition = scope.GetFunction(functionCallExpression.FunctionName.Name);
                if (functionDefinition is MaxOfFunction)
                {
                    var builder = new StringBuilder();
                    foreach (var value in functionCallExpression.Parameters)
                    {
                        if (builder.Length > 0)
                        {
                            builder.Append('$');
                        }

                        builder.Append(TriggerBuilderContext.GetValueString(value, scope, out result));
                    }
                    return(builder.ToString());
                }
            }

            return(TriggerBuilderContext.GetValueString(expression, scope, out result));
        }
示例#4
0
        public void TestAddAndGetFunction()
        {
            var function = new FunctionDefinitionExpression("test");
            var scope    = new InterpreterScope();

            scope.AddFunction(function);
            Assert.That(scope.GetFunction("test"), Is.SameAs(function));
        }
示例#5
0
        protected bool SplitConditions(InterpreterScope scope, ConditionalExpression condition,
                                       ConditionalOperation joiningOperation, out ExpressionBase result)
        {
            var conditions = new List <ExpressionBase>();

            SplitConditions(conditions, condition, condition.Operation);

            ExpressionBase newChain = null;

            for (int i = 0; i < conditions.Count; i++)
            {
                if (!conditions[i].ReplaceVariables(scope, out result))
                {
                    return(false);
                }

                bool wrap = true;
                var  functionCallExpression = result as FunctionCallExpression;
                if (functionCallExpression != null)
                {
                    var functionDefinition = scope.GetFunction(functionCallExpression.FunctionName.Name);
                    if (functionDefinition is FlagConditionFunction)
                    {
                        wrap = false;
                    }
                }

                if (wrap)
                {
                    // no need to force a logical unit when provided as a parameter.
                    // allows ReplaceVariables to further separate it if necessary.
                    result.IsLogicalUnit = false;

                    result = new FunctionCallExpression(Name.Name, new ExpressionBase[] { result });
                    if (!result.ReplaceVariables(scope, out result))
                    {
                        return(false);
                    }
                }

                if (newChain == null)
                {
                    newChain = result;
                }
                else if (wrap)
                {
                    newChain = new ConditionalExpression(newChain, joiningOperation, result);
                }
                else
                {
                    newChain = new ConditionalExpression(newChain, condition.Operation, result);
                }
            }

            result = newChain;
            CopyLocation(result);
            return(true);
        }
示例#6
0
        private bool WrapMemoryAccessor(ExpressionBase expression, InterpreterScope scope, out ExpressionBase result)
        {
            var functionCall = expression as FunctionCallExpression;

            if (functionCall == null)
            {
                result = new ParseErrorExpression("accessor did not evaluate to a memory accessor", expression);
                return(false);
            }

            var functionDefinition = scope.GetFunction(functionCall.FunctionName.Name);
            var memoryAccessor     = functionDefinition as MemoryAccessorFunction;

            if (memoryAccessor == null)
            {
                if (!(functionDefinition is PrevPriorFunction))
                {
                    result = new ParseErrorExpression("accessor did not evaluate to a memory accessor", expression);
                    return(false);
                }

                if (this.Name.Name == "bcd" && functionDefinition.Name.Name != "bcd")
                {
                    // bcd(prev(X)) => no change
                }
                else if (functionDefinition.Name.Name == "bcd" && this.Name.Name != "bcd")
                {
                    // prev(bcd(X)) => bcd(prev(X))
                    var nestedFunctionCall = new FunctionCallExpression(this.Name.Name, functionCall.Parameters);
                    functionCall.CopyLocation(nestedFunctionCall);

                    result = new FunctionCallExpression(functionDefinition.Name.Name, new[] { nestedFunctionCall });
                    CopyLocation(result);
                    return(true);
                }
                else
                {
                    result = new ParseErrorExpression("cannot apply multiple modifiers to memory accessor", expression);
                    return(false);
                }
            }

            result = new FunctionCallExpression(Name.Name, new ExpressionBase[] { functionCall });
            CopyLocation(result);
            return(true);
        }
示例#7
0
        private List <Requirement> Evaluate(string input, string expectedError = null)
        {
            var requirements = new List <Requirement>();

            var expression = ExpressionBase.Parse(new PositionalTokenizer(Tokenizer.CreateTokenizer(input)));

            Assert.That(expression, Is.InstanceOf <FunctionCallExpression>());
            var funcCall = (FunctionCallExpression)expression;

            var scope   = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope());
            var funcDef = scope.GetFunction(funcCall.FunctionName.Name) as MemoryAccessorFunction;

            Assert.That(funcDef, Is.Not.Null);

            var context = new TriggerBuilderContext {
                Trigger = requirements
            };

            scope.Context = context;

            ExpressionBase evaluated;

            Assert.That(funcCall.ReplaceVariables(scope, out evaluated), Is.True);

            if (expectedError == null)
            {
                Assert.That(funcDef.BuildTrigger(context, scope, funcCall), Is.Null);
            }
            else
            {
                var parseError = funcDef.BuildTrigger(context, scope, funcCall);
                Assert.That(parseError, Is.Not.Null);
                Assert.That(parseError.Message, Is.EqualTo(expectedError));
            }

            return(requirements);
        }
示例#8
0
        public void TestGetFunctionUndefined()
        {
            var scope = new InterpreterScope();

            Assert.That(scope.GetFunction("undefined"), Is.Null);
        }
示例#9
0
        public override ParseErrorExpression BuildTrigger(TriggerBuilderContext context, InterpreterScope scope, FunctionCallExpression functionCall)
        {
            ExpressionBase result;
            int            addHitsClauses = 0;
            int            subHitsClauses = 0;

            var requirements = new List <ICollection <Requirement> >();

            for (int i = 1; i < functionCall.Parameters.Count; ++i)
            {
                var condition             = functionCall.Parameters.ElementAt(i);
                var conditionRequirements = new List <Requirement>();
                var nestedContext         = new TriggerBuilderContext()
                {
                    Trigger = conditionRequirements
                };
                var modifier = RequirementType.AddHits;

                var funcCall = condition as FunctionCallExpression;
                if (funcCall != null && funcCall.FunctionName.Name == "deduct")
                {
                    var deductScope = funcCall.GetParameters(scope.GetFunction(funcCall.FunctionName.Name), scope, out result);
                    if (deductScope == null)
                    {
                        return((ParseErrorExpression)result);
                    }

                    condition = deductScope.GetVariable("comparison");
                    modifier  = RequirementType.SubHits;
                    ++subHitsClauses;
                }
                else
                {
                    ++addHitsClauses;
                }

                var error = BuildTriggerCondition(nestedContext, scope, condition);
                if (error != null)
                {
                    return(error);
                }

                conditionRequirements.Last().Type = modifier;
                requirements.Add(conditionRequirements);
            }

            // if no requirements were generated, we're done
            if (requirements.Count == 0)
            {
                return(null);
            }

            // if there's any SubHits clauses, add a dummy clause for the final count, regardless of whether
            // the AddHits clauses have hit targets.
            if (subHitsClauses > 0)
            {
                if (addHitsClauses == 0)
                {
                    return(new ParseErrorExpression("tally requires at least one non-deducted item"));
                }

                requirements.Add(new Requirement[] { AlwaysFalseFunction.CreateAlwaysFalseRequirement() });
            }

            // the last item cannot have its own HitCount as it will hold the HitCount for the group.
            // if necessary, find one without a HitCount and make it the last.
            AchievementBuilder.EnsureLastGroupHasNoHitCount(requirements);

            // load the requirements into the trigger
            foreach (var requirement in requirements)
            {
                foreach (var clause in requirement)
                {
                    context.Trigger.Add(clause);
                }
            }

            // the last item of each clause was set to AddHits, change the absolute last back to None
            context.LastRequirement.Type = RequirementType.None;

            // set the target hitcount
            var count = (IntegerConstantExpression)functionCall.Parameters.First();

            context.LastRequirement.HitCount = (uint)count.Value;

            return(null);
        }
示例#10
0
        private ParseErrorExpression ProcessRichPresenceDisplay(StringConstantExpression stringExpression, InterpreterScope scope, out string displayString)
        {
            displayString = null;

            ExpressionBase result;
            var            varargs = GetParameter(scope, "varargs", out result) as ArrayExpression;

            if (varargs == null)
            {
                var error = result as ParseErrorExpression;
                if (error == null)
                {
                    error = new ParseErrorExpression("unexpected varargs", stringExpression);
                }
                return(error);
            }

            var context = scope.GetContext <RichPresenceDisplayContext>();
            var builder = context.DisplayString;

            var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(stringExpression.Value));

            while (tokenizer.NextChar != '\0')
            {
                var token = tokenizer.ReadTo('{');
                builder.Append(token.ToString());

                if (tokenizer.NextChar == '\0')
                {
                    break;
                }

                var positionalTokenColumn = tokenizer.Column;
                tokenizer.Advance();
                var index = tokenizer.ReadNumber();
                if (tokenizer.NextChar != '}')
                {
                    return(new ParseErrorExpression("Invalid positional token",
                                                    stringExpression.Line, stringExpression.Column + positionalTokenColumn,
                                                    stringExpression.Line, stringExpression.Column + tokenizer.Column - 1));
                }
                tokenizer.Advance();

                var parameterIndex = Int32.Parse(index.ToString());
                if (parameterIndex >= varargs.Entries.Count)
                {
                    return(new ParseErrorExpression("Invalid parameter index: " + parameterIndex,
                                                    stringExpression.Line, stringExpression.Column + positionalTokenColumn,
                                                    stringExpression.Line, stringExpression.Column + tokenizer.Column - 1));
                }

                var functionCall = varargs.Entries[parameterIndex] as FunctionCallExpression;

                var functionDefinition = scope.GetFunction(functionCall.FunctionName.Name);
                if (functionDefinition == null)
                {
                    return(new ParseErrorExpression("Unknown function: " + functionCall.FunctionName.Name));
                }

                var richPresenceFunction = functionDefinition as FunctionDefinition;
                if (richPresenceFunction == null)
                {
                    return(new ParseErrorExpression(functionCall.FunctionName.Name + " cannot be called as a rich_presence_display parameter", functionCall));
                }

                var error = richPresenceFunction.BuildMacro(context, scope, functionCall);
                if (error != null)
                {
                    return(new ParseErrorExpression(error, functionCall));
                }
            }

            displayString = builder.ToString();
            return(null);
        }
示例#11
0
        protected ParseErrorExpression BuildTrigger(TriggerBuilderContext context, InterpreterScope scope, FunctionCallExpression functionCall, ExpressionBase address)
        {
            var requirement     = new Requirement();
            var integerConstant = address as IntegerConstantExpression;

            if (integerConstant != null)
            {
                requirement.Left = new Field {
                    Size = this.Size, Type = FieldType.MemoryAddress, Value = (uint)integerConstant.Value
                };
                context.Trigger.Add(requirement);
                return(null);
            }

            IntegerConstantExpression offsetConstant  = null;
            IntegerConstantExpression scalarConstant  = null;
            RequirementOperator       scalarOperation = RequirementOperator.None;
            var originalAddress = address;

            var funcCall = address as FunctionCallExpression;

            if (funcCall == null)
            {
                var mathematic = address as MathematicExpression;
                if (mathematic != null &&
                    (mathematic.Operation == MathematicOperation.Add || mathematic.Operation == MathematicOperation.Subtract))
                {
                    if (CountMathematicMemoryAccessors(mathematic, 2) >= 2)
                    {
                        return(new ParseErrorExpression("Cannot construct single address lookup from multiple memory references", address));
                    }

                    offsetConstant = mathematic.Right as IntegerConstantExpression;
                    if (offsetConstant != null)
                    {
                        address = mathematic.Left;
                    }
                    else
                    {
                        offsetConstant = mathematic.Left as IntegerConstantExpression;
                        if (offsetConstant != null)
                        {
                            address = mathematic.Right;
                        }
                    }

                    if (offsetConstant != null)
                    {
                        if (mathematic.Operation == MathematicOperation.Subtract)
                        {
                            offsetConstant = new IntegerConstantExpression(-offsetConstant.Value);
                        }
                    }

                    mathematic = address as MathematicExpression;
                }

                if (mathematic != null)
                {
                    switch (mathematic.Operation)
                    {
                    case MathematicOperation.Multiply:
                        scalarConstant = mathematic.Right as IntegerConstantExpression;
                        if (scalarConstant != null)
                        {
                            address         = mathematic.Left;
                            scalarOperation = RequirementOperator.Multiply;
                        }
                        else
                        {
                            scalarConstant = mathematic.Left as IntegerConstantExpression;
                            if (scalarConstant != null)
                            {
                                scalarOperation = RequirementOperator.Multiply;
                                address         = mathematic.Right;
                            }
                        }
                        break;

                    case MathematicOperation.Divide:
                        scalarConstant = mathematic.Right as IntegerConstantExpression;
                        if (scalarConstant != null)
                        {
                            address         = mathematic.Left;
                            scalarOperation = RequirementOperator.Divide;
                        }
                        break;

                    case MathematicOperation.BitwiseAnd:
                        scalarConstant = mathematic.Right as IntegerConstantExpression;
                        if (scalarConstant != null)
                        {
                            address         = mathematic.Left;
                            scalarOperation = RequirementOperator.BitwiseAnd;
                        }
                        break;
                    }
                }

                funcCall = address as FunctionCallExpression;
            }

            if (funcCall != null)
            {
                var funcDef = scope.GetFunction(funcCall.FunctionName.Name) as TriggerBuilderContext.FunctionDefinition;
                if (funcDef != null)
                {
                    if (funcDef is MemoryAccessorFunction || funcDef is PrevPriorFunction)
                    {
                        var error = funcDef.BuildTrigger(context, scope, funcCall);
                        if (error != null)
                        {
                            return(error);
                        }

                        var lastRequirement = context.LastRequirement;
                        lastRequirement.Type = RequirementType.AddAddress;

                        if (scalarConstant != null && scalarConstant.Value != 1)
                        {
                            lastRequirement.Operator = scalarOperation;
                            lastRequirement.Right    = new Field {
                                Size = FieldSize.DWord, Type = FieldType.Value, Value = (uint)scalarConstant.Value
                            };
                        }

                        // a memory reference without an offset has to be generated with a 0 offset.
                        uint offset = (offsetConstant != null) ? (uint)offsetConstant.Value : 0;

                        requirement.Left = new Field {
                            Size = this.Size, Type = FieldType.MemoryAddress, Value = offset
                        };
                        context.Trigger.Add(requirement);
                        return(null);
                    }
                }
            }

            var builder = new StringBuilder();

            builder.Append("Cannot convert to an address: ");
            originalAddress.AppendString(builder);

            return(new ParseErrorExpression(builder.ToString(), address));
        }
示例#12
0
        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);
        }