/// <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); }
/// <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); }
private FunctionDefinitionExpression Parse(string input, string expectedError = null) { var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(input)); tokenizer.Match("function"); var expr = UserFunctionDefinitionExpression.Parse(tokenizer); if (expectedError != null) { Assert.That(expr, Is.InstanceOf <ParseErrorExpression>()); var error = (ParseErrorExpression)expr; var formattedErrorMessage = string.Format("{0}:{1} {2}", error.Location.Start.Line, error.Location.Start.Column, error.Message); Assert.That(formattedErrorMessage, Is.EqualTo(expectedError)); return(null); } else { Assert.That(expr, Is.InstanceOf <FunctionDefinitionExpression>()); return((FunctionDefinitionExpression)expr); } }
/// <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 ParseNumber(PositionalTokenizer tokenizer, bool isUnsigned) { int line = tokenizer.Line; int column = tokenizer.Column; int endLine = line; int endColumn = column; uint value; string number = ""; if (tokenizer.Match("0x")) { while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f')) { number += tokenizer.NextChar; endLine = tokenizer.Line; endColumn = tokenizer.Column; tokenizer.Advance(); } if (!UInt32.TryParse(number, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out value)) { return(new ParseErrorExpression("Number too large")); } } else { while (Char.IsDigit(tokenizer.NextChar)) { number += tokenizer.NextChar; endLine = tokenizer.Line; endColumn = tokenizer.Column; tokenizer.Advance(); } if (tokenizer.NextChar == '.') { number += tokenizer.NextChar; tokenizer.Advance(); while (Char.IsDigit(tokenizer.NextChar)) { number += tokenizer.NextChar; endLine = tokenizer.Line; endColumn = tokenizer.Column; tokenizer.Advance(); } float floatValue; if (!float.TryParse(number, System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture, out floatValue)) { return(new ParseErrorExpression("Number too large")); } var floatExpression = new FloatConstantExpression(floatValue); floatExpression.Location = new TextRange(line, column, endLine, endColumn); return(floatExpression); } if (!UInt32.TryParse(number, out value)) { return(new ParseErrorExpression("Number too large")); } } if (value > Int32.MaxValue && !isUnsigned) { return(new ParseErrorExpression("Number too large")); } var integerExpression = new IntegerConstantExpression((int)value); integerExpression.Location = new TextRange(line, column, endLine, endColumn); return(integerExpression); }
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)); } }