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); }
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); }
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)); }
public void TestAddAndGetFunction() { var function = new FunctionDefinitionExpression("test"); var scope = new InterpreterScope(); scope.AddFunction(function); Assert.That(scope.GetFunction("test"), Is.SameAs(function)); }
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); }
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); }
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); }
public void TestGetFunctionUndefined() { var scope = new InterpreterScope(); Assert.That(scope.GetFunction("undefined"), Is.Null); }
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); }
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); }
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)); }
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); }