private static ExpressionBase ParseAssignment(PositionalTokenizer tokenizer, ExpressionBase variable, int joinerLine, int joinerColumn) { if (variable.Type != ExpressionType.Variable) { return(ParseError(tokenizer, "Cannot assign value to non-variable", variable)); } var value = ExpressionBase.Parse(tokenizer); switch (value.Type) { case ExpressionType.ParseError: return(value); case ExpressionType.Array: case ExpressionType.Comparison: case ExpressionType.Conditional: case ExpressionType.Dictionary: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: ParseError(tokenizer, "incompatible assignment", new KeywordExpression("=", joinerLine, joinerColumn)); break; } return(new AssignmentExpression((VariableExpression)variable, value)); }
public void TestParseExpressionGroupingLogical(string input, bool leftPrioritized) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); var expression = ExpressionBase.Parse(tokenizer) as ConditionalExpression; Assert.That(expression, Is.Not.Null); if (leftPrioritized) { var builder = new StringBuilder(); expression.Conditions.ElementAt(0).AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(input.Substring(0, 5))); builder.Clear(); expression.Conditions.ElementAt(1).AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(input.Substring(8, 1))); } else { var builder = new StringBuilder(); expression.Conditions.ElementAt(0).AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(input.Substring(0, 1))); builder.Clear(); expression.Conditions.ElementAt(1).AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(input.Substring(4, 5))); } }
protected static ExpressionBase ParseClause(PositionalTokenizer tokenizer) { var start = tokenizer.Location; var clause = ParseClauseCore(tokenizer); var end = clause.Location.End; if (end.Column == 0 || clause.Location.Start.Column == 0) { if (clause.Location.Start.Column != 0) { start = clause.Location.Start; } if (end.Line == 0) { end = new TextLocation(tokenizer.Line, (tokenizer.Column > 1) ? tokenizer.Column - 1 : 1); } clause.Location = new TextRange(start, end); } return(clause); }
private static ExpressionBase ParseArray(PositionalTokenizer tokenizer) { SkipWhitespace(tokenizer); var array = new ArrayExpression(); while (tokenizer.NextChar != ']') { var value = Parse(tokenizer); if (value.Type == ExpressionType.ParseError) { return(value); } array.Entries.Add(value); SkipWhitespace(tokenizer); if (tokenizer.NextChar == ']') { break; } if (tokenizer.NextChar != ',') { return(ParseError(tokenizer, "Expecting comma between entries")); } tokenizer.Advance(); SkipWhitespace(tokenizer); } tokenizer.Advance(); return(array); }
public void TestSkipWhitespaceMultiComment() { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(" // comment\r\n// comment2\r\nhi")); ExpressionBase.SkipWhitespace(tokenizer); Assert.That(tokenizer.NextChar, Is.EqualTo('h')); }
public void TestReplaceVariables(string input, string expected) { input = input.Replace("A", "byte(10)"); input = input.Replace("B", "byte(11)"); input = input.Replace("C", "byte(12)"); expected = expected.Replace("A", "byte(10)"); expected = expected.Replace("B", "byte(11)"); expected = expected.Replace("C", "byte(12)"); var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); var expression = ExpressionBase.Parse(tokenizer); var scope = new InterpreterScope(); scope.AddFunction(new MemoryAccessorFunction("byte", FieldSize.Byte)); scope.AddFunction(new AlwaysTrueFunction()); scope.AddFunction(new AlwaysFalseFunction()); scope.Context = new TriggerBuilderContext(); ExpressionBase result; Assert.That(expression.ReplaceVariables(scope, out result), Is.True); var builder = new StringBuilder(); result.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expected)); }
public void TestReplaceVariablesMethodCall() { var input = "function func(i) { j = i }"; var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); tokenizer.Match("function"); var functionDefinition = (FunctionDefinitionExpression)FunctionDefinitionExpression.Parse(tokenizer); var functionCall = new FunctionCallExpression("func", new ExpressionBase[] { new IntegerConstantExpression(2) }); var value1 = new IntegerConstantExpression(98); var expr = new DictionaryExpression(); expr.Entries.Add(new DictionaryExpression.DictionaryEntry { Key = functionCall, Value = value1 }); var scope = new InterpreterScope(); 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("func did not return a value")); }
public void TestParseExpressionGroupingMathematic(string input, bool leftPrioritized) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); var expression = ExpressionBase.Parse(tokenizer) as MathematicExpression; Assert.That(expression, Is.Not.Null); var parts = input.Split(' '); string expectedLeft, expectedRight; if (leftPrioritized) { expectedLeft = parts[0] + ' ' + parts[1] + ' ' + parts[2]; expectedRight = parts[4]; } else { expectedLeft = parts[0]; expectedRight = parts[2] + ' ' + parts[3] + ' ' + parts[4]; } var builder = new StringBuilder(); expression.Left.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expectedLeft)); builder.Clear(); expression.Right.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expectedRight)); }
public void TestParseExpressionGroupingStringBuildingWithSubtraction() { var input = "\"A\" + B + \"C\" + D - E + \"F\""; var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); var expression = ExpressionBase.Parse(tokenizer) as MathematicExpression; Assert.That(expression, Is.Not.Null); Assert.That(expression.Left.ToString(), Is.EqualTo("StringConstant: \"A\"")); expression = expression.Right as MathematicExpression; Assert.That(expression, Is.Not.Null); Assert.That(expression.Left.ToString(), Is.EqualTo("Variable: B")); expression = expression.Right as MathematicExpression; Assert.That(expression, Is.Not.Null); Assert.That(expression.Left.ToString(), Is.EqualTo("StringConstant: \"C\"")); expression = expression.Right as MathematicExpression; Assert.That(expression, Is.Not.Null); Assert.That(expression.Right.ToString(), Is.EqualTo("StringConstant: \"F\"")); expression = expression.Left as MathematicExpression; Assert.That(expression, Is.Not.Null); Assert.That(expression.ToString(), Is.EqualTo("Mathematic: D - E")); }
/// <summary> /// Determines if the tokenizer is pointing at a parameter list for an anonymous function. /// </summary> internal static bool IsAnonymousParameterList(PositionalTokenizer tokenizer) { var result = false; tokenizer.PushState(); if (tokenizer.Match("(")) { tokenizer.SkipWhitespace(); do { tokenizer.ReadIdentifier(); tokenizer.SkipWhitespace(); if (tokenizer.NextChar != ',') { break; } tokenizer.Advance(); tokenizer.SkipWhitespace(); } while (true); if (tokenizer.Match(")")) { tokenizer.SkipWhitespace(); result = (tokenizer.NextChar == '{' || tokenizer.Match("=>")); } } tokenizer.PopState(); return(result); }
private static ExpressionBase ParseComparison(PositionalTokenizer tokenizer, ExpressionBase left, ComparisonOperation operation, int joinerLine, int joinerColumn) { var right = ParseExpression(tokenizer, OperationPriority.Compare); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.BooleanConstant: case ExpressionType.Conditional: // will be rebalanced case ExpressionType.FloatConstant: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: var expressionTokenizer = tokenizer as ExpressionTokenizer; if (expressionTokenizer != null) { expressionTokenizer.QueueExpression(right); } right = new KeywordExpression(ComparisonExpression.GetOperatorString(operation), joinerLine, joinerColumn); return(ParseError(tokenizer, "Incompatible comparison", right)); } return(new ComparisonExpression(left, operation, right)); }
internal static void SkipWhitespace(PositionalTokenizer tokenizer) { tokenizer.SkipWhitespace(); while (tokenizer.Match("//")) { var expressionTokenizer = tokenizer as ExpressionTokenizer; if (expressionTokenizer != null) { int line = tokenizer.Line; int column = tokenizer.Column - 2; var comment = tokenizer.ReadTo('\n'); if (comment.Length > 0 && comment[comment.Length - 1] == '\r') { comment = comment.SubToken(0, comment.Length - 1); } expressionTokenizer.AddComment(new CommentExpression("//" + comment.ToString()) { Location = new TextRange(line, column, line, column + comment.Length + 1) }); } else { tokenizer.ReadTo('\n'); } tokenizer.SkipWhitespace(); } }
private static ExpressionBase ParseMathematic(PositionalTokenizer tokenizer, ExpressionBase left, MathematicOperation operation, int joinerLine, int joinerColumn) { var right = ExpressionBase.Parse(tokenizer); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.Comparison: case ExpressionType.Conditional: case ExpressionType.Dictionary: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: ParseError(tokenizer, "incompatible mathematical operation", new KeywordExpression(MathematicExpression.GetOperatorCharacter(operation).ToString(), joinerLine, joinerColumn)); break; } return(new MathematicExpression(left, operation, right)); }
protected ExpressionBase ParseShorthandBody(PositionalTokenizer tokenizer) { ExpressionBase.SkipWhitespace(tokenizer); var expression = ExpressionBase.Parse(tokenizer); if (expression.Type == ExpressionType.ParseError) { return(expression); } switch (expression.Type) { case ExpressionType.Return: return(ParseError(tokenizer, "Return statement is implied by =>", ((ReturnExpression)expression).Keyword)); case ExpressionType.For: return(ParseError(tokenizer, "Shorthand function definition does not support loops.", expression)); case ExpressionType.If: return(ParseError(tokenizer, "Shorthand function definition does not support branches.", expression)); } var returnExpression = new ReturnExpression(expression); Expressions.Add(returnExpression); Location = new TextRange(Location.Start, expression.Location.End); return(MakeReadOnly(this)); }
internal static UserFunctionDefinitionExpression ParseForTest(string definition) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(definition)); tokenizer.Match("function"); return(Parse(tokenizer, 0, 0) as UserFunctionDefinitionExpression); }
public void TestReplaceVariablesIndexFunctionCall() { var input = "function func(i) => 6"; var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); tokenizer.Match("function"); var functionDefinition = (FunctionDefinitionExpression)FunctionDefinitionExpression.Parse(tokenizer); var functionCall = new FunctionCallExpression("func", new ExpressionBase[] { new IntegerConstantExpression(2) }); var value = new IntegerConstantExpression(98); var variable = new VariableExpression("variable"); var dict = new DictionaryExpression(); dict.Entries.Add(new DictionaryExpression.DictionaryEntry { Key = new IntegerConstantExpression(6), Value = value }); var scope = new InterpreterScope(); scope.AssignVariable(variable, dict); scope.AddFunction(functionDefinition); var expr = new IndexedVariableExpression(variable, functionCall); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <IntegerConstantExpression>()); Assert.That(((IntegerConstantExpression)result).Value, Is.EqualTo(98)); }
public void TestReplaceVariablesFunctionCall() { var input = "function func(i) => 6"; var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); tokenizer.Match("function"); var functionDefinition = (FunctionDefinitionExpression)FunctionDefinitionExpression.Parse(tokenizer); var functionCall = new FunctionCallExpression("func", new ExpressionBase[] { new IntegerConstantExpression(2) }); var value1 = new IntegerConstantExpression(98); var expr = new DictionaryExpression(); expr.Entries.Add(new DictionaryExpression.DictionaryEntry { Key = functionCall, Value = value1 }); var scope = new InterpreterScope(); scope.AddFunction(functionDefinition); ExpressionBase result; Assert.That(expr.ReplaceVariables(scope, out result), Is.True); Assert.That(result, Is.InstanceOf <DictionaryExpression>()); var dictResult = (DictionaryExpression)result; Assert.That(dictResult.Entries.Count, Is.EqualTo(1)); Assert.That(dictResult.Entries[0].Key, Is.EqualTo(new IntegerConstantExpression(6))); Assert.That(dictResult.Entries[0].Value, Is.EqualTo(value1)); }
/// <summary> /// Parses an anonymous function definition in the format "(a) => body" or "(a) { body }" /// </summary> public static ExpressionBase ParseAnonymous(PositionalTokenizer tokenizer) { var name = CreateAnonymousFunctionName(tokenizer.Line, tokenizer.Column); var function = new AnonymousUserFunctionDefinitionExpression(name); function.Location = new TextRange(tokenizer.Line, tokenizer.Column, 0, 0); return(function.Parse(tokenizer)); }
internal static ParseErrorExpression ParseError(PositionalTokenizer tokenizer, string message, ExpressionBase expression) { var error = ParseError(tokenizer, message); error.InnerError = expression as ParseErrorExpression; expression.CopyLocation(error); return(error); }
public void TestParseErrorInsideDefinition() { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer("function func() { if (j) { j = i } }")); tokenizer.Match("function"); var expr = FunctionDefinitionExpression.Parse(tokenizer); Assert.That(expr, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)expr).Message, Is.EqualTo("Expected conditional statement following if")); }
public void TestParseErrorInsideDefinition() { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer("function func() { j = }")); tokenizer.Match("function"); var expr = UserFunctionDefinitionExpression.Parse(tokenizer); Assert.That(expr, Is.InstanceOf <ParseErrorExpression>()); Assert.That(((ParseErrorExpression)expr).Message, Is.EqualTo("Unexpected end of script")); }
private IfExpression Parse(string input) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); tokenizer.Match("if"); var expr = IfExpression.Parse(tokenizer); Assert.That(expr, Is.InstanceOf <IfExpression>()); return((IfExpression)expr); }
internal static ParseErrorExpression ParseError(PositionalTokenizer tokenizer, string message, ExpressionBase expression) { var error = ParseError(tokenizer, message); error.Line = expression.Line; error.Column = expression.Column; error.EndLine = expression.EndLine; error.EndColumn = expression.EndColumn; return(error); }
private FunctionDefinitionExpression Parse(string input) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); tokenizer.Match("function"); var expr = FunctionDefinitionExpression.Parse(tokenizer); Assert.That(expr, Is.InstanceOf <FunctionDefinitionExpression>()); return((FunctionDefinitionExpression)expr); }
[TestCase("(A && B || C) && (D || E && F)", "((A && B) || C) && (D || (E && F))")] // AND has higher priority than OR public void TestParseExpressionGrouping(string input, string expected) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); var expression = ExpressionBase.Parse(tokenizer); SetLogicalUnit(expression); var builder = new StringBuilder(); expression.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expected)); }
internal static ExpressionBase ParseStatementBlock(PositionalTokenizer tokenizer, ICollection <ExpressionBase> expressions) { ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != '{') { var statement = ExpressionBase.Parse(tokenizer); if (statement.Type == ExpressionType.ParseError) { return(statement); } expressions.Add(statement); } else { var line = tokenizer.Line; var column = tokenizer.Column; tokenizer.Advance(); do { ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar == '}') { break; } if (tokenizer.NextChar == '\0') { return(ParseError(tokenizer, "No matching closing brace found", line, column)); } var statement = ExpressionBase.Parse(tokenizer); if (statement.Type == ExpressionType.ParseError) { return(statement); } if (statement.Type == ExpressionType.Variable) { return(new ParseErrorExpression("standalone variable has no meaning", statement)); } expressions.Add(statement); } while (true); tokenizer.Advance(); } return(null); }
internal static ParseErrorExpression ParseError(PositionalTokenizer tokenizer, string message, int line, int column) { var error = new ParseErrorExpression(message, line, column, tokenizer.Line, tokenizer.Column); var expressionTokenizer = tokenizer as ExpressionTokenizer; if (expressionTokenizer != null) { expressionTokenizer.AddError(error); } return(error); }
internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0) { SkipWhitespace(tokenizer); var dict = new DictionaryExpression(); while (tokenizer.NextChar != '}') { var key = ExpressionBase.ParseClause(tokenizer); if (key.Type == ExpressionType.ParseError) { return(key); } SkipWhitespace(tokenizer); if (tokenizer.NextChar != ':') { ParseError(tokenizer, "Expecting colon following key expression"); break; } tokenizer.Advance(); SkipWhitespace(tokenizer); var value = ExpressionBase.ParseClause(tokenizer); if (value.Type == ExpressionType.ParseError) { break; } dict.Entries.Add(new DictionaryExpression.DictionaryEntry { Key = key, Value = value }); SkipWhitespace(tokenizer); if (tokenizer.NextChar == '}') { break; } if (tokenizer.NextChar != ',') { ParseError(tokenizer, "Expecting comma between entries"); break; } tokenizer.Advance(); SkipWhitespace(tokenizer); } tokenizer.Advance(); return(dict); }
/// <summary> /// Parses a for loop. /// </summary> /// <remarks> /// Assumes the 'for' keyword has already been consumed. /// </remarks> internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0) { ExpressionBase.SkipWhitespace(tokenizer); var keywordFor = new KeywordExpression("for", line, column); line = tokenizer.Line; column = tokenizer.Column; var iteratorName = tokenizer.ReadIdentifier(); if (iteratorName.IsEmpty) { return(ParseError(tokenizer, "Invalid function name", line, column)); } var iterator = new VariableDefinitionExpression(iteratorName.ToString(), line, column); ExpressionBase.SkipWhitespace(tokenizer); line = tokenizer.Line; column = tokenizer.Column; if (!tokenizer.Match("in")) { return(ParseError(tokenizer, "Expected 'in' after loop variable")); } var keywordIn = new KeywordExpression("in", line, column); var range = ExpressionBase.Parse(tokenizer); if (range.Type == ExpressionType.ParseError) { return(range); } var loop = new ForExpression(iterator, range); var error = ExpressionBase.ParseStatementBlock(tokenizer, loop.Expressions); if (error != null) { return(error); } loop._keywordFor = keywordFor; loop._keywordIn = keywordIn; loop.Line = keywordFor.Line; loop.Column = keywordFor.Column; loop.EndLine = tokenizer.Line; loop.EndColumn = tokenizer.Column; return(loop); }
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)); }