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 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); } }
/// <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); }
/// <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 locationStart = new TextLocation(line, column); // location of 'function' keyword ExpressionBase.SkipWhitespace(tokenizer); line = tokenizer.Line; column = tokenizer.Column; var functionName = tokenizer.ReadIdentifier(); var functionNameVariable = new VariableDefinitionExpression(functionName.ToString(), line, column); var function = new UserFunctionDefinitionExpression(functionNameVariable); function.Location = new TextRange(locationStart.Line, locationStart.Column, 0, 0); if (functionName.IsEmpty) { return(ExpressionBase.ParseError(tokenizer, "Invalid function name")); } return(function.Parse(tokenizer)); }
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); }
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); }