public void TestGetConditionString(string input, string expected) { ExpressionBase error; InterpreterScope scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.Context = new TriggerBuilderContext(); var expression = Parse(input); ExpressionBase processed; Assert.That(expression.ReplaceVariables(scope, out processed), Is.True); var result = TriggerBuilderContext.GetConditionString(processed, scope, out error); if (error != null) { Assert.That(((ParseErrorExpression)error).InnermostError.Message, Is.EqualTo(expected)); } else { Assert.That(error, Is.Null); Assert.That(result, Is.EqualTo(expected)); } }
public static bool ContainsMemoryAccessor(ExpressionBase expression) { var funcCall = expression as FunctionCallExpression; if (funcCall != null) { var func = AchievementScriptInterpreter.GetGlobalScope().GetFunction(funcCall.FunctionName.Name); if (func is MemoryAccessorFunction) { return(true); } foreach (var parameter in funcCall.Parameters) { if (ContainsMemoryAccessor(parameter)) { return(true); } } return(false); } var leftRightExpression = expression as LeftRightExpressionBase; if (leftRightExpression != null) { return(ContainsMemoryAccessor(leftRightExpression.Left) || ContainsMemoryAccessor(leftRightExpression.Right)); } return(false); }
public void TestFunctionReference() { string input = "once(f)"; var requirements = new List <Requirement>(); var funcDef = new OnceFunction(); 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()); scope.AssignVariable(new VariableExpression("f"), new FunctionReferenceExpression("f2")); ExpressionBase error; scope = funcCall.GetParameters(funcDef, scope, out error); var context = new TriggerBuilderContext { Trigger = requirements }; scope.Context = context; ExpressionBase evaluated; Assert.That(funcDef.ReplaceVariables(scope, out evaluated), Is.True); funcCall = evaluated as FunctionCallExpression; var parseError = funcDef.BuildTrigger(context, scope, funcCall); Assert.That(parseError, Is.Not.Null); Assert.That(parseError.InnermostError.Message, Is.EqualTo("Function used like a variable")); }
private List <Requirement> Evaluate(string input, string expectedError = null) { var requirements = new List <Requirement>(); var funcDef = new RepeatedFunction(); var expression = ExpressionBase.Parse(new PositionalTokenizer(Tokenizer.CreateTokenizer(input))); Assert.That(expression, Is.InstanceOf <FunctionCallExpression>()); var funcCall = (FunctionCallExpression)expression; ExpressionBase error; var scope = funcCall.GetParameters(funcDef, AchievementScriptInterpreter.GetGlobalScope(), out error); var context = new TriggerBuilderContext { Trigger = requirements }; scope.Context = context; ExpressionBase evaluated; Assert.That(funcDef.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 TestSizes() { var scope = AchievementScriptInterpreter.GetGlobalScope(); var sizes = new Dictionary <string, FieldSize> { { "byte(0x1234)", FieldSize.Byte }, { "bit0(0x1234)", FieldSize.Bit0 }, { "bit1(0x1234)", FieldSize.Bit1 }, { "bit2(0x1234)", FieldSize.Bit2 }, { "bit3(0x1234)", FieldSize.Bit3 }, { "bit4(0x1234)", FieldSize.Bit4 }, { "bit5(0x1234)", FieldSize.Bit5 }, { "bit6(0x1234)", FieldSize.Bit6 }, { "bit7(0x1234)", FieldSize.Bit7 }, { "low4(0x1234)", FieldSize.LowNibble }, { "high4(0x1234)", FieldSize.HighNibble }, { "word(0x1234)", FieldSize.Word }, { "dword(0x1234)", FieldSize.DWord }, { "bit(0,0x1234)", FieldSize.Bit0 }, { "bit(1,0x1234)", FieldSize.Bit1 }, { "bit(2,0x1234)", FieldSize.Bit2 }, { "bit(3,0x1234)", FieldSize.Bit3 }, { "bit(4,0x1234)", FieldSize.Bit4 }, { "bit(5,0x1234)", FieldSize.Bit5 }, { "bit(6,0x1234)", FieldSize.Bit6 }, { "bit(7,0x1234)", FieldSize.Bit7 }, }; foreach (var kvp in sizes) { var requirements = Evaluate(kvp.Key); Assert.That(requirements.Count, Is.EqualTo(1), kvp.Key); Assert.That(requirements[0].Left.Size, Is.EqualTo(kvp.Value), kvp.Key); } }
public void TestTriggerWhenDefinition() { var funcDef = AchievementScriptInterpreter.GetGlobalScope().GetFunction("trigger_when"); Assert.That(funcDef, Is.Not.Null); Assert.That(funcDef.Name.Name, Is.EqualTo("trigger_when")); Assert.That(funcDef.Parameters.Count, Is.EqualTo(1)); Assert.That(funcDef.Parameters.ElementAt(0).Name, Is.EqualTo("comparison")); }
public void TestDefinition() { var def = AchievementScriptInterpreter.GetGlobalScope().GetFunction("disable_when"); Assert.That(def, Is.Not.Null); Assert.That(def.Name.Name, Is.EqualTo("disable_when")); Assert.That(def.Parameters.Count, Is.EqualTo(2)); Assert.That(def.Parameters.ElementAt(0).Name, Is.EqualTo("comparison")); Assert.That(def.Parameters.ElementAt(1).Name, Is.EqualTo("until")); }
public void TestIsTrueMemoryAccessorFunction() { var expr = new FunctionCallExpression("byte", new ExpressionBase[] { new IntegerConstantExpression(0x1234) }); var scope = AchievementScriptInterpreter.GetGlobalScope(); ParseErrorExpression error; Assert.That(expr.IsTrue(scope, out error), Is.Null); Assert.That(error, Is.Null); }
public void TestIsTrueAlwaysFalseFunction() { var expr = AlwaysFalseFunction.CreateAlwaysFalseFunctionCall(); var scope = AchievementScriptInterpreter.GetGlobalScope(); ParseErrorExpression error; Assert.That(expr.IsTrue(scope, out error), Is.False); Assert.That(error, Is.Null); }
public void TestIsTrue(string input, bool?expected) { var tokenizer = Tokenizer.CreateTokenizer(input); var expr = ExpressionBase.Parse(new PositionalTokenizer(tokenizer)); var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.Context = new RATools.Parser.TriggerBuilderContext(); ParseErrorExpression error; Assert.That(expr.IsTrue(scope, out error), Is.EqualTo(expected)); Assert.That(error, Is.Null); }
public void TestIsTrueUserFunctionReturningBoolean() { var userFunc = Parse("function u() => always_true()"); var expr = new FunctionCallExpression("u", new ExpressionBase[0]); var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.AddFunction(userFunc); ParseErrorExpression error; Assert.That(expr.IsTrue(scope, out error), Is.True); Assert.That(error, Is.Null); }
public void TestGetValueString(string input, string expected) { ExpressionBase error; InterpreterScope scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.Context = new TriggerBuilderContext(); var expression = Parse(input); var result = TriggerBuilderContext.GetValueString(expression, scope, out error); Assert.That(error, Is.Null); Assert.That(result, Is.EqualTo(expected)); }
public void TestIsTrue(string input, bool expected) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); var expression = ExpressionBase.Parse(tokenizer); Assert.That(expression, Is.InstanceOf <ConditionalExpression>()); ParseErrorExpression error; var scope = AchievementScriptInterpreter.GetGlobalScope(); var result = expression.IsTrue(scope, out error); Assert.That(error, Is.Null); Assert.That(result, Is.EqualTo(expected)); }
"trigger_when(byte(1) == 56) && trigger_when(byte(2) == 3) && trigger_when(byte(1) == 55 || byte(2) == 4)")] // and can be separated, but not nested ors public void TestReplaceVariables(string input, string expected) { var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.Context = new TriggerBuilderContext(); var expression = ExpressionBase.Parse(new PositionalTokenizer(Tokenizer.CreateTokenizer(input))); ExpressionBase evaluated; Assert.That(expression.ReplaceVariables(scope, out evaluated), Is.True); var builder = new StringBuilder(); evaluated.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expected)); }
public void TestPushMemoryComparison() { var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); var array = new ArrayExpression(); scope.DefineVariable(new VariableDefinitionExpression("arr"), array); AddHappyFunction(scope); Evaluate("array_push(arr, byte(1) == 2)", scope); var comparison = (ComparisonExpression)array.Entries[0]; Assert.That(comparison.Left, Is.InstanceOf <FunctionCallExpression>()); Assert.That(((FunctionCallExpression)comparison.Left).FunctionName.Name, Is.EqualTo("byte")); Assert.That(comparison.Right, Is.InstanceOf <IntegerConstantExpression>()); Assert.That(((IntegerConstantExpression)comparison.Right).Value, Is.EqualTo(2)); }
private RichPresenceBuilder Evaluate(string input, string expectedError = null) { var funcDef = new RichPresenceValueFunction(); var expression = ExpressionBase.Parse(new PositionalTokenizer(Tokenizer.CreateTokenizer(input))); Assert.That(expression, Is.InstanceOf <FunctionCallExpression>()); var funcCall = (FunctionCallExpression)expression; ExpressionBase error; var scope = funcCall.GetParameters(funcDef, AchievementScriptInterpreter.GetGlobalScope(), out error); var context = new RichPresenceDisplayFunction.RichPresenceDisplayContext { RichPresence = new RichPresenceBuilder() }; scope.Context = context; ExpressionBase evaluated; if (expectedError != null && expectedError.EndsWith(" format")) { Assert.That(funcDef.ReplaceVariables(scope, out evaluated), Is.False); var parseError = evaluated as ParseErrorExpression; Assert.That(parseError, Is.Not.Null); Assert.That(parseError.Message, Is.EqualTo(expectedError)); return(context.RichPresence); } ExpressionBase result; Assert.That(funcDef.ReplaceVariables(scope, out evaluated), Is.True); if (expectedError == null) { Assert.That(funcDef.BuildMacro(context, scope, out result), Is.True); context.RichPresence.DisplayString = ((StringConstantExpression)result).Value; } else { Assert.That(funcDef.BuildMacro(context, scope, out result), Is.False); Assert.That(result, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)result).Message, Is.EqualTo(expectedError)); } return(context.RichPresence); }
public void TestExplicitCall() { // not providing a TriggerBuilderContext simulates calling the function at a global scope var funcDef = new MemoryAccessorFunction("byte", FieldSize.Byte); var input = "byte(0x1234)"; var expression = ExpressionBase.Parse(new PositionalTokenizer(Tokenizer.CreateTokenizer(input))); Assert.That(expression, Is.InstanceOf <FunctionCallExpression>()); var funcCall = (FunctionCallExpression)expression; ExpressionBase error; var scope = funcCall.GetParameters(funcDef, AchievementScriptInterpreter.GetGlobalScope(), out error); Assert.That(funcDef.Evaluate(scope, out error), Is.False); Assert.That(error, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)error).Message, Is.EqualTo("byte has no meaning outside of a trigger clause")); }
public void TestExplicitCall() { // not providing a RichPresenceDisplayContext simulates calling the function at a global scope var funcDef = new RichPresenceValueFunction(); var input = "rich_presence_value(\"Name\", byte(0x1234))"; var expression = ExpressionBase.Parse(new PositionalTokenizer(Tokenizer.CreateTokenizer(input))); Assert.That(expression, Is.InstanceOf <FunctionCallExpression>()); var funcCall = (FunctionCallExpression)expression; ExpressionBase error; var scope = funcCall.GetParameters(funcDef, AchievementScriptInterpreter.GetGlobalScope(), out error); Assert.That(funcDef.Evaluate(scope, out error), Is.False); Assert.That(error, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)error).Message, Is.EqualTo("rich_presence_value has no meaning outside of a rich_presence_display call")); }
public void TestReplaceVariablesMemoryAccessor() { var value = new FunctionCallExpression("byte", new[] { new IntegerConstantExpression(1) }); var expr = new ArrayExpression(); expr.Entries.Add(value); var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <ArrayExpression>()); var arrayResult = (ArrayExpression)result; Assert.That(arrayResult.Entries.Count, Is.EqualTo(1)); Assert.That(arrayResult.Entries[0].ToString(), Is.EqualTo(value.ToString())); }
public void TestReplaceVariablesLogicalFunctionCall() { var functionDefinition = UserFunctionDefinitionExpression.ParseForTest("function func(i) => byte(i) == 1"); var functionCall = new FunctionCallExpression("func", new ExpressionBase[] { new IntegerConstantExpression(2) }); var value1 = new IntegerConstantExpression(98); var expr = new DictionaryExpression(); expr.Add(functionCall, value1); var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.AddFunction(functionDefinition); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.False); Assert.That(result, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)result).Message, Is.EqualTo("Dictionary key must evaluate to a constant")); }
protected override void OnUpdateSyntax(ContentChangedEventArgs e) { bool needsUpdate = false; // parse immediately so we can update the syntax highlighting var tokenizer = Tokenizer.CreateTokenizer(e.Content); if (_parsedContent == null || e.Type == ContentChangeType.Refresh) { _parsedContent = new ExpressionGroupCollection(); _parsedContent.Scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); _parsedContent.Scope.Context = new AchievementScriptContext(); lock (_parsedContent) { _parsedContent.Parse(tokenizer); needsUpdate = true; } } else if (e.Type == ContentChangeType.Update) { lock (_parsedContent) { needsUpdate = _parsedContent.Update(tokenizer, e.AffectedLines); } if (!needsUpdate && !e.IsAborted) { UpdateErrorList(); } } if (needsUpdate && !e.IsAborted) { // running the script can take a lot of time, push that work onto a background thread and show progress bar UpdateProgress(1, 0); // make sure to at least show the script file in the editor list if (!_owner.Editors.Any()) { _owner.PopulateEditorList(null); } ServiceRepository.Instance.FindService <IBackgroundWorkerService>().RunAsync(() => { if (!e.IsAborted) { // run the script var callback = new ScriptInterpreterCallback(this, e); var interpreter = new AchievementScriptInterpreter(); bool hadErrors, hasErrors; lock (_parsedContent) { hadErrors = _parsedContent.HasEvaluationErrors; hasErrors = !interpreter.Run(_parsedContent, callback); } if (!e.IsAborted) { UpdateProgress(100, 0); // if any errors were added or removed, update the highlighting if (hasErrors != hadErrors) { UpdateSyntaxHighlighting(e); } // report any errors UpdateErrorList(); if (!e.IsAborted) { // update the editor list _owner.PopulateEditorList(interpreter); } } } // make sure the progress bar is hidden UpdateProgress(0, 0); }); } base.OnUpdateSyntax(e); }
protected new ExpressionBase Parse(PositionalTokenizer tokenizer) { ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != '(') { return(ExpressionBase.ParseError(tokenizer, "Expected '(' after function name", Name)); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != ')') { do { var line = tokenizer.Line; var column = tokenizer.Column; var parameter = tokenizer.ReadIdentifier(); if (parameter.IsEmpty) { return(ExpressionBase.ParseError(tokenizer, "Invalid parameter name", line, column)); } var variableDefinition = new VariableDefinitionExpression(parameter.ToString(), line, column); Parameters.Add(variableDefinition); ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar == '=') { tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); var value = ExpressionBase.Parse(tokenizer); if (value.Type == ExpressionType.ParseError) { return(ExpressionBase.ParseError(tokenizer, "Invalid default value for " + parameter.ToString(), value)); } var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); scope.Context = new TriggerBuilderContext(); // prevent errors passing memory references as default parameters ExpressionBase evaluated; if (!value.ReplaceVariables(scope, out evaluated)) { return(ExpressionBase.ParseError(tokenizer, "Default value for " + parameter.ToString() + " is not constant", evaluated)); } DefaultParameters[parameter.ToString()] = evaluated; } else if (DefaultParameters.Count > 0) { return(ExpressionBase.ParseError(tokenizer, string.Format("Non-default parameter {0} appears after default parameters", parameter.ToString()), variableDefinition)); } if (tokenizer.NextChar == ')') { break; } if (tokenizer.NextChar != ',') { return(ExpressionBase.ParseError(tokenizer, "Expected ',' or ')' after parameter name, found: " + tokenizer.NextChar)); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); } while (true); } tokenizer.Advance(); // closing parenthesis ExpressionBase.SkipWhitespace(tokenizer); ExpressionBase expression; if (tokenizer.Match("=>")) { return(ParseShorthandBody(tokenizer)); } if (tokenizer.NextChar != '{') { return(ExpressionBase.ParseError(tokenizer, "Expected '{' after function declaration", Name)); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); bool seenReturn = false; while (tokenizer.NextChar != '}') { expression = ExpressionBase.Parse(tokenizer); if (expression.Type == ExpressionType.ParseError) { // the ExpressionTokenizer will capture the error, we should still return the incomplete FunctionDefinition if (tokenizer is ExpressionTokenizer) { break; } // not an ExpressionTokenizer, just return the error return(expression); } if (expression.Type == ExpressionType.Return) { seenReturn = true; } else if (seenReturn) { ExpressionBase.ParseError(tokenizer, "Expression after return statement", expression); } Expressions.Add(expression); ExpressionBase.SkipWhitespace(tokenizer); } Location = new TextRange(Location.Start, tokenizer.Location); tokenizer.Advance(); return(MakeReadOnly(this)); }
public void TestComparison() { var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope()); Evaluate("length(byte(0x1234) != 12)", scope, "Cannot calculate length of Comparison"); }