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)); }
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)); }
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)); }
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); }
/// <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); }
private static void ParseGroups(ExpressionTokenizer tokenizer, List <ExpressionGroup> groups) { while (tokenizer.NextChar != '\0') { // create a separate group for comments var newGroup = new ExpressionGroup(); groups.Add(newGroup); tokenizer.ChangeExpressionGroup(newGroup); ExpressionBase.SkipWhitespace(tokenizer); // if comments were found, start a new group if (!newGroup.IsEmpty) { newGroup = new ExpressionGroup(); groups.Add(newGroup); tokenizer.ChangeExpressionGroup(newGroup); } if (tokenizer.NextChar == '\0') { break; } var expression = ExpressionBase.Parse(tokenizer); // sometimes parsing an expression will process trailing whitespace looking for a continuation character. // move the trailing comments into a separate group. do not move the internal comments var commentGroup = newGroup.ExtractTrailingComments(expression); if (commentGroup != null) { groups.Add(commentGroup); } if (expression.Type != ExpressionType.ParseError) { newGroup.AddExpression(expression); } } var lastGroup = groups.LastOrDefault(); if (lastGroup != null && lastGroup.IsEmpty) { groups.RemoveAt(groups.Count - 1); } }
private static ExpressionBase ParseParameters(PositionalTokenizer tokenizer, ICollection <ExpressionBase> parameters) { int line = tokenizer.Line; int column = tokenizer.Column; SkipWhitespace(tokenizer); if (tokenizer.NextChar != ')') { do { var parameter = ExpressionBase.Parse(tokenizer); if (parameter.Type == ExpressionType.ParseError) { return(ParseError(tokenizer, "Invalid expression", parameter)); } parameters.Add(parameter); SkipWhitespace(tokenizer); var commaLine = tokenizer.Line; var commaColumn = tokenizer.Column; if (tokenizer.NextChar != ',') { break; } tokenizer.Advance(); SkipWhitespace(tokenizer); } while (true); } if (tokenizer.NextChar == ')') { tokenizer.Advance(); return(null); } if (tokenizer.NextChar == '\0') { return(ParseError(tokenizer, "No closing parenthesis found", line, column)); } return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar)); }
/// <summary> /// Parses a if definition. /// </summary> /// <remarks> /// Assumes the 'if' keyword has already been consumed. /// </remarks> internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0) { ExpressionBase.SkipWhitespace(tokenizer); var condition = ExpressionBase.Parse(tokenizer); if (condition.Type == ExpressionType.ParseError) { return(condition); } if (condition.Type != ExpressionType.Conditional && condition.Type != ExpressionType.Comparison) { return(ParseError(tokenizer, "Expected conditional statement following if", condition)); } var ifExpression = new IfExpression(condition); ifExpression._keyword = new KeywordExpression("if", line, column); var error = ExpressionBase.ParseStatementBlock(tokenizer, ifExpression.Expressions); if (error != null) { return(error); } ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.Match("else")) { error = ExpressionBase.ParseStatementBlock(tokenizer, ifExpression.ElseExpressions); if (error != null) { return(error); } } return(ifExpression); }
/// <summary> /// Parses a if definition. /// </summary> /// <remarks> /// Assumes the 'if' keyword has already been consumed. /// </remarks> internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0) { ExpressionBase.SkipWhitespace(tokenizer); var condition = ExpressionBase.Parse(tokenizer); if (condition.Type == ExpressionType.ParseError) { return(condition); } var ifExpression = new IfExpression(condition); ifExpression._keyword = new KeywordExpression("if", line, column); var error = ExpressionBase.ParseStatementBlock(tokenizer, ifExpression.Expressions); if (error != null) { return(error); } ExpressionBase.SkipWhitespace(tokenizer); line = tokenizer.Line; column = tokenizer.Column; if (tokenizer.Match("else")) { ifExpression._elseKeyword = new KeywordExpression("else", line, column); error = ExpressionBase.ParseStatementBlock(tokenizer, ifExpression.ElseExpressions); if (error != null) { return(error); } } return(ifExpression); }
private static ExpressionBase ParseConditional(PositionalTokenizer tokenizer, ExpressionBase left, ConditionalOperation 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.FunctionCall: case ExpressionType.Variable: break; default: ParseError(tokenizer, "incompatible logical condition", new KeywordExpression(ConditionalExpression.GetOperatorString(operation), joinerLine, joinerColumn)); break; } return(new ConditionalExpression(left, operation, right)); }
public ExpressionGroup Parse(Tokenizer tokenizer) { var expressionGroup = new ExpressionGroup(); var expressionTokenizer = new ExpressionTokenizer(tokenizer, expressionGroup); ExpressionBase.SkipWhitespace(expressionTokenizer); while (expressionTokenizer.NextChar != '\0') { var expression = ExpressionBase.Parse(expressionTokenizer); if (expression != null) { if (expression is VariableExpression) { expressionGroup.Errors.Add(new ParseErrorExpression("standalone variable has no meaning", expression)); } expressionGroup.Expressions.Add(expression); } } return(expressionGroup); }
private static ExpressionBase ParseComparison(PositionalTokenizer tokenizer, ExpressionBase left, ComparisonOperation operation, int joinerLine, int joinerColumn) { var right = ExpressionBase.Parse(tokenizer); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.Conditional: // will be rebalanced case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: ParseError(tokenizer, "incompatible comparison", new KeywordExpression(ComparisonExpression.GetOperatorString(operation), joinerLine, joinerColumn)); break; } return(new ComparisonExpression(left, operation, right)); }
private static ExpressionBase ParseClauseCore(PositionalTokenizer tokenizer) { ExpressionBase clause; switch (tokenizer.NextChar) { case '!': tokenizer.Advance(); clause = ParseClause(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } return(new ConditionalExpression(null, ConditionalOperation.Not, clause)); case '(': if (AnonymousUserFunctionDefinitionExpression.IsAnonymousParameterList(tokenizer)) { return(AnonymousUserFunctionDefinitionExpression.ParseAnonymous(tokenizer)); } tokenizer.Advance(); clause = ExpressionBase.Parse(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } if (tokenizer.NextChar != ')') { if (tokenizer.NextChar == '\0') { return(ParseError(tokenizer, "No closing parenthesis found")); } return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar)); } clause.IsLogicalUnit = true; tokenizer.Advance(); return(clause); case '"': try { var stringValue = tokenizer.ReadQuotedString().ToString(); return(new StringConstantExpression(stringValue)); } catch (InvalidOperationException ex) { return(ParseError(tokenizer, ex.Message)); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return(ParseNumber(tokenizer, true)); case '-': var tokenStart = tokenizer.Location; tokenizer.Advance(); if (tokenizer.NextChar >= '0' && tokenizer.NextChar <= '9') { var result = ParseNumber(tokenizer, false); var tokenEnd = result.Location.End; switch (result.Type) { case ExpressionType.IntegerConstant: result = new IntegerConstantExpression(-((IntegerConstantExpression)result).Value); break; case ExpressionType.FloatConstant: result = new FloatConstantExpression(-((FloatConstantExpression)result).Value); break; default: return(result); } result.Location = new TextRange(tokenStart, tokenEnd); return(result); } return(ParseError(tokenizer, "Minus without value", tokenStart.Line, tokenStart.Column)); case '{': tokenizer.Advance(); return(DictionaryExpression.Parse(tokenizer)); case '[': tokenizer.Advance(); return(ParseArray(tokenizer)); default: var line = tokenizer.Line; var column = tokenizer.Column; var identifier = tokenizer.ReadIdentifier(); if (identifier.IsEmpty) { var error = ParseError(tokenizer, "Unexpected character: " + tokenizer.NextChar); tokenizer.Advance(); return(error); } SkipWhitespace(tokenizer); if (identifier == "return") { clause = ExpressionBase.Parse(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } return(new ReturnExpression(new KeywordExpression(identifier.ToString(), line, column), clause)); } if (identifier == "function") { return(UserFunctionDefinitionExpression.Parse(tokenizer, line, column)); } if (identifier == "for") { return(ForExpression.Parse(tokenizer, line, column)); } if (identifier == "if") { return(IfExpression.Parse(tokenizer, line, column)); } if (identifier == "true") { return(new BooleanConstantExpression(true, line, column)); } if (identifier == "false") { return(new BooleanConstantExpression(false, line, column)); } if (tokenizer.NextChar == '(') { tokenizer.Advance(); var parameters = new List <ExpressionBase>(); ParseParameters(tokenizer, parameters); var functionCall = new FunctionCallExpression(new FunctionNameExpression(identifier.ToString(), line, column), parameters); functionCall.Location = new TextRange(line, column, tokenizer.Line, tokenizer.Column - 1); return(functionCall); } if (tokenizer.NextChar == '[') { IndexedVariableExpression parent = null; do { tokenizer.Advance(); var index = ExpressionBase.Parse(tokenizer); if (index.Type == ExpressionType.ParseError) { return(index); } SkipWhitespace(tokenizer); if (tokenizer.NextChar != ']') { return(ParseError(tokenizer, "Expecting closing bracket after index")); } tokenizer.Advance(); SkipWhitespace(tokenizer); if (parent != null) { parent = new IndexedVariableExpression(parent, index); } else { parent = new IndexedVariableExpression(new VariableExpression(identifier.ToString(), line, column), index); } } while (tokenizer.NextChar == '['); return(parent); } return(new VariableExpression(identifier.ToString(), line, column)); } }
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)); }
/// <summary> /// Parses a function definition. /// </summary> /// <remarks> /// Assumes the 'function' keyword has already been consumed. /// </remarks> internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0) { var function = new FunctionDefinitionExpression(); function._keyword = new KeywordExpression("function", line, column); ExpressionBase.SkipWhitespace(tokenizer); line = tokenizer.Line; column = tokenizer.Column; var functionName = tokenizer.ReadIdentifier(); function.Name = new VariableDefinitionExpression(functionName.ToString(), line, column); if (functionName.IsEmpty) { ExpressionBase.ParseError(tokenizer, "Invalid function name"); return(function); } ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != '(') { ExpressionBase.ParseError(tokenizer, "Expected '(' after function name", function.Name); return(function); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar != ')') { do { line = tokenizer.Line; column = tokenizer.Column; var parameter = tokenizer.ReadIdentifier(); if (parameter.IsEmpty) { ExpressionBase.ParseError(tokenizer, "Invalid parameter name", line, column); return(function); } function.Parameters.Add(new VariableDefinitionExpression(parameter.ToString(), line, column)); ExpressionBase.SkipWhitespace(tokenizer); if (tokenizer.NextChar == ')') { break; } if (tokenizer.NextChar != ',') { ExpressionBase.ParseError(tokenizer, "Expected ',' or ')' after parameter name, found: " + tokenizer.NextChar); return(function); } tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); } while (true); } tokenizer.Advance(); // closing parenthesis ExpressionBase.SkipWhitespace(tokenizer); ExpressionBase expression; if (tokenizer.Match("=>")) { ExpressionBase.SkipWhitespace(tokenizer); expression = ExpressionBase.Parse(tokenizer); if (expression.Type == ExpressionType.ParseError) { return(expression); } if (expression.Type == ExpressionType.Return) { return(new ParseErrorExpression("Return statement is implied by =>", ((ReturnExpression)expression).Keyword)); } var returnExpression = new ReturnExpression(expression); function.Expressions.Add(returnExpression); return(function); } if (tokenizer.NextChar != '{') { ExpressionBase.ParseError(tokenizer, "Expected '{' after function declaration", function.Name); return(function); } line = tokenizer.Line; column = tokenizer.Column; tokenizer.Advance(); ExpressionBase.SkipWhitespace(tokenizer); bool seenReturn = false; while (tokenizer.NextChar != '}') { expression = ExpressionBase.Parse(tokenizer); if (expression.Type == ExpressionType.ParseError) { return(expression); } if (expression.Type == ExpressionType.Return) { seenReturn = true; } else if (seenReturn) { ExpressionBase.ParseError(tokenizer, "Expression after return statement", expression); } function.Expressions.Add(expression); ExpressionBase.SkipWhitespace(tokenizer); } tokenizer.Advance(); return(function); }
private static ExpressionBase ParseClauseCore(PositionalTokenizer tokenizer) { ExpressionBase clause; switch (tokenizer.NextChar) { case '!': tokenizer.Advance(); clause = ParseClause(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } return(new ConditionalExpression(null, ConditionalOperation.Not, clause)); case '(': tokenizer.Advance(); clause = ExpressionBase.Parse(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } if (tokenizer.NextChar != ')') { if (tokenizer.NextChar == '\0') { return(ParseError(tokenizer, "No closing parenthesis found")); } return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar)); } clause.IsLogicalUnit = true; tokenizer.Advance(); return(clause); case '"': try { var stringValue = tokenizer.ReadQuotedString().ToString(); return(new StringConstantExpression(stringValue)); } catch (InvalidOperationException ex) { return(ParseError(tokenizer, ex.Message)); } case '0': if (tokenizer.Match("0x")) { string hexNumber = ""; while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f')) { hexNumber += tokenizer.NextChar; tokenizer.Advance(); } int hexValue = 0; Int32.TryParse(hexNumber, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out hexValue); return(new IntegerConstantExpression(hexValue)); } goto case '1'; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { var number = tokenizer.ReadNumber(); uint value; UInt32.TryParse(number.ToString(), out value); return(new IntegerConstantExpression((int)value)); } case '-': tokenizer.Advance(); if (tokenizer.NextChar >= '0' && tokenizer.NextChar <= '9') { if (tokenizer.Match("0x")) { string hexNumber = ""; while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f')) { hexNumber += tokenizer.NextChar; tokenizer.Advance(); } int hexValue = 0; Int32.TryParse(hexNumber, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out hexValue); return(new IntegerConstantExpression(-hexValue)); } var number = tokenizer.ReadNumber(); int value; Int32.TryParse(number.ToString(), out value); return(new IntegerConstantExpression(-value)); } return(ParseError(tokenizer, "Minus without value")); case '{': tokenizer.Advance(); return(DictionaryExpression.Parse(tokenizer)); case '[': tokenizer.Advance(); return(ParseArray(tokenizer)); default: var line = tokenizer.Line; var column = tokenizer.Column; var identifier = tokenizer.ReadIdentifier(); if (identifier.IsEmpty) { var error = ParseError(tokenizer, "Unexpected character: " + tokenizer.NextChar); tokenizer.Advance(); return(error); } SkipWhitespace(tokenizer); if (identifier == "return") { clause = ExpressionBase.Parse(tokenizer); if (clause.Type == ExpressionType.ParseError) { return(clause); } return(new ReturnExpression(new KeywordExpression(identifier.ToString(), line, column), clause)); } if (identifier == "function") { return(FunctionDefinitionExpression.Parse(tokenizer, line, column)); } if (identifier == "for") { return(ForExpression.Parse(tokenizer, line, column)); } if (identifier == "if") { return(IfExpression.Parse(tokenizer, line, column)); } if (tokenizer.NextChar == '(') { tokenizer.Advance(); var parameters = new List <ExpressionBase>(); ParseParameters(tokenizer, parameters); var functionCall = new FunctionCallExpression(new VariableExpression(identifier.ToString(), line, column), parameters); functionCall.EndLine = tokenizer.Line; functionCall.EndColumn = tokenizer.Column - 1; return(functionCall); } if (tokenizer.NextChar == '[') { IndexedVariableExpression parent = null; do { tokenizer.Advance(); var index = ExpressionBase.Parse(tokenizer); if (index.Type == ExpressionType.ParseError) { return(index); } SkipWhitespace(tokenizer); if (tokenizer.NextChar != ']') { return(ParseError(tokenizer, "Expecting closing bracket after index")); } tokenizer.Advance(); SkipWhitespace(tokenizer); if (parent != null) { parent = new IndexedVariableExpression(parent, index); } else { parent = new IndexedVariableExpression(new VariableExpression(identifier.ToString(), line, column), index); } } while (tokenizer.NextChar == '['); return(parent); } return(new VariableExpression(identifier.ToString(), line, column)); } }