/// <summary> /// <para>Handles *expression* "==" *expression</para> /// <para>*expression* "!=" *expression*</para> /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression4() { ASTExpressionBase expression = ParseExpression5(); while (Match(TokenType.EQUALS_EQUALS) || Match(TokenType.NOT_EQUALS)) { switch (_buffer[0].Type) { case TokenType.EQUALS_EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression5(); expression = new ASTCompare(ASTCompare.OperationType.EQUALS_EQUALS, expression, expressionRight); } break; case TokenType.NOT_EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression5(); expression = new ASTCompare(ASTCompare.OperationType.NOT_EQUALS, expression, expressionRight); } break; } } return(expression); }
/// <summary> /// <para>Handles *expression* "*" *expression*</para> /// <para>*expression* "/" *expression*</para> /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression7() { ASTExpressionBase expression = ParseExpression8(); while (Match(TokenType.TIMES) || Match(TokenType.DIVIDE)) { switch (_buffer[0].Type) { case TokenType.TIMES: { Consume(); ASTExpressionBase expressionRight = ParseExpression8(); expression = new ASTBinaryMathOperation(ASTBinaryMathOperation.OperationType.TIMES, expression, expressionRight); } break; case TokenType.DIVIDE: { Consume(); ASTExpressionBase expressionRight = ParseExpression8(); expression = new ASTBinaryMathOperation(ASTBinaryMathOperation.OperationType.DIVIDE, expression, expressionRight); } break; } } return(expression); }
private ASTStatementBase ParseStatement() { switch (_buffer[0].Type) { case TokenType.LEFT_BRACE: return(ParseBlock()); case TokenType.IF: return(ParseIf()); case TokenType.WHILE: return(ParseWhile()); case TokenType.FOR: return(ParseFor()); case TokenType.SWITCH: return(ParseSwitch()); case TokenType.BREAK: return(ParseBreak()); case TokenType.CONTINUE: return(ParseContinue()); case TokenType.RETURN: return(ParseReturn()); default: { ASTExpressionBase expression = ParseExpression(); Require(TokenType.SEMICOLON); return(new ASTStatementExpression(expression)); } } }
private ASTStatementBase ParseReturn() { Require(TokenType.RETURN); ASTExpressionBase returnExpression = ParseExpression(); Require(TokenType.SEMICOLON); return(new ASTReturn(returnExpression)); }
/// <summary> /// Handles *leftValue* "=" *expression* (assignment) /// *variable* "+=" *expression* /// *variable* "-=" *expression* /// *variable* *= *expression* /// *variable* /= *expression* /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression1() { ASTExpressionBase expression = ParseExpression2(); while (Match(TokenType.EQUALS) || Match(TokenType.PLUS_EQUALS) || Match(TokenType.MINUS_EQUALS) || Match(TokenType.TIMES_EQUALS) || Match(TokenType.DIVIDE_EQUALS)) { if (expression.Type != ExpressionType.IDENTIFIER) { throw new ParserException("left side of an assignment must be a variable.", _lexer.Line, _lexer.Character); } switch (_buffer[0].Type) { case TokenType.EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression1(); expression = GetASTAssignment(ASTAssignment.AssignmentType.ASSIGNMENT, (expression as ASTIdentifier).Name, expressionRight, expression as ASTMemberAccess); } break; case TokenType.PLUS_EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression1(); expression = GetASTAssignment(ASTAssignment.AssignmentType.ADDITION, (expression as ASTIdentifier).Name, expressionRight, expression as ASTMemberAccess); } break; case TokenType.MINUS_EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression1(); expression = GetASTAssignment(ASTAssignment.AssignmentType.SUBTRACTION, (expression as ASTIdentifier).Name, expressionRight, expression as ASTMemberAccess); } break; case TokenType.TIMES_EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression1(); expression = GetASTAssignment(ASTAssignment.AssignmentType.MULTIPLICATION, (expression as ASTIdentifier).Name, expressionRight, expression as ASTMemberAccess); } break; case TokenType.DIVIDE_EQUALS: { Consume(); ASTExpressionBase expressionRight = ParseExpression1(); expression = GetASTAssignment(ASTAssignment.AssignmentType.DIVISION, (expression as ASTIdentifier).Name, expressionRight, expression as ASTMemberAccess); } break; } } return(expression); }
/// <summary> /// Handles *expression* "&&" *expression* (and) /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression3() { ASTExpressionBase expression = ParseExpression4(); while (Match(TokenType.AND)) { Consume(); ASTExpressionBase expressionRight = ParseExpression4(); expression = new ASTCompare(ASTCompare.OperationType.AND, expression, expressionRight); } return(expression); }
/// <summary> /// Handles *expression* "," *expression* /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression() { ASTExpressionBase expression = ParseExpression1(); List <ASTExpressionBase> expressionList = new List <ASTExpressionBase>(); expressionList.Add(expression); while (Match(TokenType.COMMA)) { expressionList.Add(ParseExpression1()); } return(new ASTExpressionBase(expressionList)); }
private ASTStatementBase ParseWhile() { Require(TokenType.WHILE); Require(TokenType.LEFT_PARENTHESIS); ASTExpressionBase condition = ParseExpression(); Require(TokenType.RIGHT_PARENTHESIS); _loopLevel++; ASTStatementBase body = ParseStatement(); _loopLevel--; return(new ASTWhile(condition, body)); }
private ASTAssignment GetASTAssignment(ASTAssignment.AssignmentType type, string variable, ASTExpressionBase expression, ASTMemberAccess memberAccess) { if (!_insideClass) { if (memberAccess != null) { // assigning a member variable from outside the class return(new ASTMemberAssignmentInstance(type, variable, expression, memberAccess.InstanceName)); } else { // assigning a local variable return(new ASTAssignment(type, variable, expression)); } } else { // assigning a member variable from inside the class return(new ASTMemberAssignment(type, variable, expression)); } }
private ASTStatementBase ParseFor() { Require(TokenType.FOR); Require(TokenType.LEFT_PARENTHESIS); ASTExpressionBase initializer = ParseExpression(); Require(TokenType.SEMICOLON); ASTExpressionBase condition = ParseExpression(); Require(TokenType.SEMICOLON); ASTExpressionBase incrementer = ParseExpression(); Require(TokenType.RIGHT_PARENTHESIS); _loopLevel++; ASTStatementBase body = ParseStatement(); _loopLevel--; return(new ASTFor(initializer, condition, incrementer, body)); }
private ASTStatementBase ParseIf() { Require(TokenType.IF); Require(TokenType.LEFT_PARENTHESIS); ASTExpressionBase condition = ParseExpression(); Require(TokenType.RIGHT_PARENTHESIS); ASTStatementBase ifPart = ParseStatement(); ASTStatementBase elsePart = null; if (Match(TokenType.ELSE)) { Consume(); elsePart = ParseStatement(); } if (elsePart == null) { return(new ASTIf(condition, ifPart)); } return(new ASTIf(condition, ifPart, elsePart)); }
/// <summary> /// <para>Handles *identifier* "[" *tableKey* "]"</para> /// <para>*identifier* "(" *argumentList* ")"</para> /// <para>*identifier* -> *identifier* "(" *argumentList* ")"</para> /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression9() { ASTExpressionBase expression = ParseExpression10(); while (Match(TokenType.LEFT_PARENTHESIS) || Match(TokenType.ARROW) || Match(TokenType.LEFT_BRACKET)) { switch (_buffer[0].Type) { case TokenType.LEFT_BRACKET: { Consume(); if (expression.Type != ExpressionType.IDENTIFIER) { throw new ParserException($"Table expected.", _lexer.Line, _lexer.Character); } var indexes = new List <ASTExpressionBase>(); while (!Match(TokenType.RIGHT_BRACKET)) { indexes.Add(ParseExpression1()); if (Match(TokenType.COMMA)) { Consume(); } } Require(TokenType.RIGHT_BRACKET); expression = new ASTTableGet(expression, indexes); } break; case TokenType.LEFT_PARENTHESIS: { Consume(); if (expression.Type != ExpressionType.IDENTIFIER) { throw new ParserException($"Function call expected.", _lexer.Line, _lexer.Character); } List <ASTExpressionBase> arguments = new List <ASTExpressionBase>(); while (!Match(TokenType.RIGHT_PARENTHESIS)) { arguments.Add(ParseExpression1()); if (Match(TokenType.COMMA)) { Consume(); } } Require(TokenType.RIGHT_PARENTHESIS); expression = new ASTGlobalFunctionCall((expression as ASTIdentifier).Name, arguments); } break; case TokenType.ARROW: { Consume(); if (expression.Type != ExpressionType.IDENTIFIER) { throw new ParserException($"class object expected.", _lexer.Line, _lexer.Character); } string memberName = Require(TokenType.IDENTIFIER).Text; if (!Match(TokenType.LEFT_PARENTHESIS)) { // return class variable expression = new ASTMemberAccess(memberName, (expression as ASTIdentifier).Name); break; } Require(TokenType.LEFT_PARENTHESIS); List <ASTExpressionBase> arguments = new List <ASTExpressionBase>(); while (!Match(TokenType.RIGHT_PARENTHESIS)) { arguments.Add(ParseExpression1()); if (Match(TokenType.COMMA)) { Consume(); } } Require(TokenType.RIGHT_PARENTHESIS); expression = new ASTMemberFunctionCall(memberName, (expression as ASTIdentifier).Name, arguments); } break; } } return(expression); }
private ASTStatementBase ParseSwitch() { Require(TokenType.SWITCH); if (_switchLevel > 0) { throw new ParserException("Nested switch statements are currently not supported.", _lexer.Line, _lexer.Character); } Require(TokenType.LEFT_PARENTHESIS); ASTExpressionBase check = ParseExpression(); Require(TokenType.RIGHT_PARENTHESIS); Require(TokenType.LEFT_BRACE); ASTSwitch switchStatement = new ASTSwitch(check); while (!Match(TokenType.RIGHT_BRACE)) { if (Match(TokenType.END_OF_FILE)) { throw new ParserException("'}' expected!", _lexer.Line, _lexer.Character); } if (Match(TokenType.DEFAULT)) { Require(TokenType.DEFAULT); Require(TokenType.COLON); _switchLevel++; switchStatement.AddCase(null, ParseStatement()); _switchLevel--; if (!Match(TokenType.RIGHT_BRACE)) { throw new ParserException("A default case must be the final case of a switch statement.", _lexer.Line, _lexer.Character); } break; } Require(TokenType.CASE); ASTExpressionBase _case = ParseExpression(); if (_case == null) { throw new ParserException("Identifier expected.", _lexer.Line, _lexer.Character); } Require(TokenType.COLON); if (Match(TokenType.CASE) || Match(TokenType.DEFAULT)) { switchStatement.AddCase(_case, null); } else { _switchLevel++; switchStatement.AddCase(_case, ParseStatement()); _switchLevel--; } /* for now, I don't think forcing the scripter to use braces to have a "break;" after a case is necessary, eg. in the situation of * case 5: * { * print("hello"); * break; * } * * you might as well just type * case 5: * print("hello"); * break; */ if (Match(TokenType.BREAK)) // so let's allow that { Consume(); // consume 'break' Require(TokenType.SEMICOLON); // require ';' var currentCase = switchStatement.Cases[switchStatement.Cases.Count - 1]; // get statement of current case // and add the 'break' to the end of it switchStatement.Cases[switchStatement.Cases.Count - 1] = (currentCase.expression, new ASTBlock(new List <ASTStatementBase> { currentCase.statement, new ASTBreak() })); } } Require(TokenType.RIGHT_BRACE); if (switchStatement.Cases.Count == 0) { throw new ParserException("Empty switch statements are not allowed.", _lexer.Line, _lexer.Character); } return(switchStatement); }
/// <summary> /// <para>Handles "{" *key = value* list "}"</para> /// <para>"(" *expression* ")"</para> /// <para>*identifier*</para> /// <para>*number*</para> /// <para>*string*</para> /// <para>"null"</para> /// <para>"true"</para> /// <para>"false"</para> /// </summary> /// <returns></returns> private ASTExpressionBase ParseExpression10() { switch (_buffer[0].Type) { case TokenType.LEFT_BRACE: { Consume(); ASTTable table = new ASTTable(); while (!Match(TokenType.RIGHT_BRACE)) { bool good = true; if (Match(TokenType.LEFT_BRACKET)) // [key] = value { Consume(); if (!Match(TokenType.NUMBER) && !Match(TokenType.STRING)) { throw new ParserException("Expected string or number key for table.", _lexer.Line, _lexer.Character); } ASTExpressionBase key = ParseExpression10(); Require(TokenType.RIGHT_BRACKET); Require(TokenType.EQUALS); ASTExpressionBase value = ParseExpression2(); good = table.InsertValue(key, value); } else { ASTExpressionBase key = ParseExpression2(); if (key.Type == ExpressionType.IDENTIFIER) // key = expvalue || value { if (Match(TokenType.EQUALS)) { Require(TokenType.EQUALS); ASTExpressionBase value = ParseExpression2(); good = table.InsertValue(key, value); } else // expvalue { good = table.InsertValue(null, key); } } else // value (no key) { good = table.InsertValue(null, key); } } if (!good) { throw new ParserException("Invalid key/value pair for table (possible duplicate?).", _lexer.Line, _lexer.Character); } if (Match(TokenType.COMMA)) { Consume(); } } table.NormalizeValues(); Require(TokenType.RIGHT_BRACE); return(table); } case TokenType.LEFT_PARENTHESIS: { Consume(); ASTExpressionBase expression = ParseExpression1(); Require(TokenType.RIGHT_PARENTHESIS); return(expression); } case TokenType.IDENTIFIER: { var variable = Require(TokenType.IDENTIFIER).Text; return(new ASTIdentifier(variable)); } case TokenType.NUMBER: { double num = Require(TokenType.NUMBER).Number; return(new ASTNumber(num)); } case TokenType.STRING: { var str = Require(TokenType.STRING).Text; return(new ASTString(str)); } case TokenType.NULL: { Consume(); return(new ASTNull()); } case TokenType.TRUE: { Consume(); return(new ASTTrue()); } case TokenType.FALSE: { Consume(); return(new ASTFalse()); } case TokenType.COUNT: { Consume(); var identifier = Require(TokenType.IDENTIFIER).Text; return(new ASTCount(identifier)); } default: throw new ParserException("Primary expression expected", _lexer.Line, _lexer.Character); } }