From aec07bd29acf242fe1449db3859c2ea6c1fd07fd Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Tue, 21 May 2019 17:16:53 +0200 Subject: [PATCH] Adds support for parenthesized expressions --- CMakeLists.txt | 2 +- src/Parser/Lexer.cpp | 6 +- .../ParsedExpressions/ParsedExpression.hpp | 18 +++ src/Parser/Parser.cpp | 139 ++--------------- src/Parser/Parser.hpp | 2 + src/Parser/ParserTests.cpp | 144 ++++++++++++++++++ src/Parser/TokenKind.hpp | 2 + 7 files changed, 186 insertions(+), 127 deletions(-) create mode 100644 src/Parser/ParserTests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c4f2613..86c1c8e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ add_library(PorygonLang ${SRC_FILES}) add_executable(PorygonLangTests ${SRC_FILES} src/Parser/LexerTests.cpp - integration_tests/integration_tests.cpp) + integration_tests/integration_tests.cpp src/Parser/ParserTests.cpp) target_compile_definitions(PorygonLangTests PRIVATE TESTS_BUILD) diff --git a/src/Parser/Lexer.cpp b/src/Parser/Lexer.cpp index 7a272e9..085aa8d 100644 --- a/src/Parser/Lexer.cpp +++ b/src/Parser/Lexer.cpp @@ -38,7 +38,7 @@ IToken* Lexer::LexNext(char c){ switch (c) { case '\0': return new SimpleToken(TokenKind::EndOfFile, this -> Position - 1, 1); - case ' ': case '\t': case '\n': case '\r': case '\v': case '\f': + case ' ': case '\t': case '\n': case '\r': case '\v': case '\f': return new SimpleToken(TokenKind::WhiteSpace, this -> Position - 1, 1); case '+': return new SimpleToken(TokenKind::PlusToken, this -> Position - 1, 1); @@ -48,6 +48,10 @@ IToken* Lexer::LexNext(char c){ return new SimpleToken(TokenKind::SlashToken, this -> Position - 1, 1); case '*': return new SimpleToken(TokenKind::StarToken, this -> Position - 1, 1); + case '(': + return new SimpleToken(TokenKind::OpenParenthesis, this -> Position - 1, 1); + case ')': + return new SimpleToken(TokenKind::CloseParenthesis, this -> Position - 1, 1); case '=': if (Lexer::Peek() == '='){ Lexer::Next(); diff --git a/src/Parser/ParsedExpressions/ParsedExpression.hpp b/src/Parser/ParsedExpressions/ParsedExpression.hpp index f72b054..612da12 100644 --- a/src/Parser/ParsedExpressions/ParsedExpression.hpp +++ b/src/Parser/ParsedExpressions/ParsedExpression.hpp @@ -16,6 +16,7 @@ enum class ParsedExpressionKind{ Unary, Binary, + Parenthesized, }; class ParsedExpression { @@ -96,6 +97,23 @@ public: } }; +class ParenthesizedExpression : public ParsedExpression{ + ParsedExpression* _expression; +public: + ParsedExpressionKind GetKind() final{ + return ParsedExpressionKind::Parenthesized; + } + + explicit ParenthesizedExpression(ParsedExpression* innerExpression, unsigned int start, unsigned int length) + : ParsedExpression(start, length){ + _expression = innerExpression; + } + + ParsedExpression* GetInnerExpression(){ + return _expression; + } +}; + class UnaryExpression : public ParsedExpression{ UnaryOperatorKind _kind; ParsedExpression* _operand; diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index 1b98f18..0c138f8 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -97,7 +97,7 @@ ParsedExpression* Parser::ParseBinaryExpression(IToken* current, OperatorPrecede } auto operatorKind = GetBinaryOperatorKind(next -> GetKind()); this -> Next(); - auto right = ParseBinaryExpression(this -> Next(), binaryPrecedence); + auto right = this -> ParseBinaryExpression(this -> Next(), binaryPrecedence); auto startPos = left -> GetStartPosition(); left = new BinaryExpression(operatorKind, left, right, startPos, right -> GetEndPosition() - startPos); } @@ -110,6 +110,7 @@ ParsedExpression *Parser::ParsePrimaryExpression(IToken *current) { case TokenKind ::Float: return new LiteralFloatExpression((FloatToken*)current); case TokenKind ::TrueKeyword: return new LiteralBoolExpression(current); case TokenKind ::FalseKeyword: return new LiteralBoolExpression(current); + case TokenKind ::OpenParenthesis: return this -> ParseParenthesizedExpression(current); // If we find a bad token here, we should have already logged it in the lexer, so don't log another error. case TokenKind ::BadToken: return new BadExpression(current->GetStartPosition(), current->GetLength()); default: @@ -118,6 +119,18 @@ ParsedExpression *Parser::ParsePrimaryExpression(IToken *current) { } } +ParsedExpression *Parser::ParseParenthesizedExpression(IToken *current) { + auto next = this -> Next(); + auto expression = this -> ParseExpression(next); + auto closeToken = this -> Next(); + if (closeToken -> GetKind() != TokenKind::CloseParenthesis){ + this -> ScriptData -> Diagnostics -> LogError(DiagnosticCode::UnexpectedToken, closeToken->GetStartPosition(), closeToken->GetLength()); + return new BadExpression(closeToken->GetStartPosition(), closeToken->GetLength()); + } + auto start = current -> GetStartPosition(); + return new ParenthesizedExpression(expression, start, closeToken->GetEndPosition() - start); +} + IToken *Parser::Peek() { return this -> _tokens[_position]; @@ -128,127 +141,3 @@ IToken *Parser::Next() { return this -> _tokens[_position - 1]; } -#ifdef TESTS_BUILD -#include - -TEST_CASE( "Parse single true keyword", "[parser]" ) { - vector v {new SimpleToken(TokenKind::TrueKeyword,0,0), new SimpleToken(TokenKind::EndOfFile,0,0)}; - Parser parser = Parser(v, nullptr); - auto parsedStatements = parser.Parse() -> GetStatements(); - REQUIRE(parsedStatements.size() == 1); - auto firstStatement = parsedStatements[0]; - REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); - auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); - REQUIRE(expression -> GetKind() == ParsedExpressionKind::LiteralBool); - auto boolean = ((LiteralBoolExpression*)expression); - REQUIRE(boolean->GetValue() == true); -} - -TEST_CASE( "Parse single false keyword", "[parser]" ) { - vector v {new SimpleToken(TokenKind::FalseKeyword,0,0), new SimpleToken(TokenKind::EndOfFile,0,0)}; - Parser parser = Parser(v, nullptr); - auto parsedStatements = parser.Parse() -> GetStatements(); - REQUIRE(parsedStatements.size() == 1); - auto firstStatement = parsedStatements[0]; - REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); - auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); - REQUIRE(expression -> GetKind() == ParsedExpressionKind::LiteralBool); - auto boolean = ((LiteralBoolExpression*)expression); - REQUIRE(boolean->GetValue() == false); -} - -TEST_CASE( "Parse simple addition", "[parser]" ) { - vector v { - new IntegerToken(5, 0, 0), - new SimpleToken(TokenKind::PlusToken,0,0), - new IntegerToken(10, 0, 0), - new SimpleToken(TokenKind::EndOfFile,0,0) - }; - Parser parser = Parser(v, nullptr); - auto parsedStatements = parser.Parse() -> GetStatements(); - REQUIRE(parsedStatements.size() == 1); - auto firstStatement = parsedStatements[0]; - REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); - auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); - REQUIRE(expression -> GetKind() == ParsedExpressionKind::Binary); - auto binary = ((BinaryExpression*)expression); - CHECK(binary -> GetOperatorKind() == BinaryOperatorKind::Addition); - auto left = binary->GetLeft(); - auto right = binary->GetRight(); - REQUIRE(left->GetKind() == ParsedExpressionKind::LiteralInteger); - REQUIRE(right->GetKind() == ParsedExpressionKind::LiteralInteger); - CHECK(((LiteralIntegerExpression*)left)->GetValue() == 5); - CHECK(((LiteralIntegerExpression*)right)->GetValue() == 10); -} - -TEST_CASE( "Parse simple negation", "[parser]" ) { - vector v { - new SimpleToken(TokenKind::MinusToken,0,0), - new IntegerToken(10, 0, 0), - new SimpleToken(TokenKind::EndOfFile,0,0) - }; - Parser parser = Parser(v, nullptr); - auto parsedStatements = parser.Parse() -> GetStatements(); - REQUIRE(parsedStatements.size() == 1); - auto firstStatement = parsedStatements[0]; - REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); - auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); - REQUIRE(expression -> GetKind() == ParsedExpressionKind::Unary); - auto unary = ((UnaryExpression*)expression); - CHECK(unary -> GetOperatorKind() == UnaryOperatorKind::Negation); - auto operand = unary->GetOperand(); - REQUIRE(operand->GetKind() == ParsedExpressionKind::LiteralInteger); - CHECK(((LiteralIntegerExpression*)operand)->GetValue() == 10); -} - -TEST_CASE( "Parse logical negation", "[parser]" ) { - vector v { - new SimpleToken(TokenKind::NotKeyword,0,0), - new SimpleToken(TokenKind::FalseKeyword,0,0), - new SimpleToken(TokenKind::EndOfFile,0,0) - }; - Parser parser = Parser(v, nullptr); - auto parsedStatements = parser.Parse() -> GetStatements(); - REQUIRE(parsedStatements.size() == 1); - auto firstStatement = parsedStatements[0]; - REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); - auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); - REQUIRE(expression -> GetKind() == ParsedExpressionKind::Unary); - auto unary = ((UnaryExpression*)expression); - CHECK(unary -> GetOperatorKind() == UnaryOperatorKind::LogicalNegation); - auto operand = unary->GetOperand(); - REQUIRE(operand->GetKind() == ParsedExpressionKind::LiteralBool); - CHECK(((LiteralBoolExpression*)operand)->GetValue() == false); -} - -TEST_CASE( "Assert binary precedence", "[parser]" ) { - vector v { - new IntegerToken(5, 0, 0), - new SimpleToken(TokenKind::PlusToken,0,0), - new IntegerToken(10, 0, 0), - new SimpleToken(TokenKind::StarToken,0,0), - new IntegerToken(6, 0, 0), - new SimpleToken(TokenKind::EndOfFile,0,0) - }; - Parser parser = Parser(v, nullptr); - auto parsedStatements = parser.Parse() -> GetStatements(); - REQUIRE(parsedStatements.size() == 1); - auto firstStatement = parsedStatements[0]; - REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); - auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); - REQUIRE(expression -> GetKind() == ParsedExpressionKind::Binary); - auto binary = ((BinaryExpression*)expression); - CHECK(binary -> GetOperatorKind() == BinaryOperatorKind::Addition); - auto left = binary->GetLeft(); - auto right = binary->GetRight(); - REQUIRE(left->GetKind() == ParsedExpressionKind::LiteralInteger); - REQUIRE(right->GetKind() == ParsedExpressionKind::Binary); - CHECK(((LiteralIntegerExpression*)left)->GetValue() == 5); - left = ((BinaryExpression*)right)->GetLeft(); - right = ((BinaryExpression*)right)->GetRight(); - CHECK(((LiteralIntegerExpression*)left)->GetValue() == 10); - CHECK(((LiteralIntegerExpression*)right)->GetValue() == 6); -} - - -#endif \ No newline at end of file diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index cb8c8d1..282a71e 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -28,6 +28,7 @@ class Parser { ParsedExpression* ParseExpression(IToken* current); ParsedExpression* ParseBinaryExpression(IToken* current, OperatorPrecedence parentPrecedence); ParsedExpression* ParsePrimaryExpression(IToken* current); + ParsedExpression* ParseParenthesizedExpression(IToken *current); public: ParsedScriptStatement* Parse(); explicit Parser(vector tokens, Script* scriptData){ @@ -35,6 +36,7 @@ public: _position = 0; ScriptData = scriptData; } + }; diff --git a/src/Parser/ParserTests.cpp b/src/Parser/ParserTests.cpp new file mode 100644 index 0000000..bc0a1ef --- /dev/null +++ b/src/Parser/ParserTests.cpp @@ -0,0 +1,144 @@ + +#ifdef TESTS_BUILD +#include +#include "Parser.hpp" + +TEST_CASE( "Parse single true keyword", "[parser]" ) { + vector v {new SimpleToken(TokenKind::TrueKeyword,0,0), new SimpleToken(TokenKind::EndOfFile,0,0)}; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::LiteralBool); + auto boolean = ((LiteralBoolExpression*)expression); + REQUIRE(boolean->GetValue() == true); +} + +TEST_CASE( "Parse single false keyword", "[parser]" ) { + vector v {new SimpleToken(TokenKind::FalseKeyword,0,0), new SimpleToken(TokenKind::EndOfFile,0,0)}; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::LiteralBool); + auto boolean = ((LiteralBoolExpression*)expression); + REQUIRE(boolean->GetValue() == false); +} + +TEST_CASE( "Parse simple addition", "[parser]" ) { + vector v { + new IntegerToken(5, 0, 0), + new SimpleToken(TokenKind::PlusToken,0,0), + new IntegerToken(10, 0, 0), + new SimpleToken(TokenKind::EndOfFile,0,0) + }; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::Binary); + auto binary = ((BinaryExpression*)expression); + CHECK(binary -> GetOperatorKind() == BinaryOperatorKind::Addition); + auto left = binary->GetLeft(); + auto right = binary->GetRight(); + REQUIRE(left->GetKind() == ParsedExpressionKind::LiteralInteger); + REQUIRE(right->GetKind() == ParsedExpressionKind::LiteralInteger); + CHECK(((LiteralIntegerExpression*)left)->GetValue() == 5); + CHECK(((LiteralIntegerExpression*)right)->GetValue() == 10); +} + +TEST_CASE( "Parse simple negation", "[parser]" ) { + vector v { + new SimpleToken(TokenKind::MinusToken,0,0), + new IntegerToken(10, 0, 0), + new SimpleToken(TokenKind::EndOfFile,0,0) + }; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::Unary); + auto unary = ((UnaryExpression*)expression); + CHECK(unary -> GetOperatorKind() == UnaryOperatorKind::Negation); + auto operand = unary->GetOperand(); + REQUIRE(operand->GetKind() == ParsedExpressionKind::LiteralInteger); + CHECK(((LiteralIntegerExpression*)operand)->GetValue() == 10); +} + +TEST_CASE( "Parse logical negation", "[parser]" ) { + vector v { + new SimpleToken(TokenKind::NotKeyword,0,0), + new SimpleToken(TokenKind::FalseKeyword,0,0), + new SimpleToken(TokenKind::EndOfFile,0,0) + }; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::Unary); + auto unary = ((UnaryExpression*)expression); + CHECK(unary -> GetOperatorKind() == UnaryOperatorKind::LogicalNegation); + auto operand = unary->GetOperand(); + REQUIRE(operand->GetKind() == ParsedExpressionKind::LiteralBool); + CHECK(((LiteralBoolExpression*)operand)->GetValue() == false); +} + +TEST_CASE( "Are parenthesized expressions valid", "[parser]" ) { + vector v { + new IntegerToken(5, 0, 0), + new SimpleToken(TokenKind::PlusToken,0,0), + new IntegerToken(10, 0, 0), + new SimpleToken(TokenKind::StarToken,0,0), + new IntegerToken(6, 0, 0), + new SimpleToken(TokenKind::EndOfFile,0,0) + }; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::Binary); + auto binary = ((BinaryExpression*)expression); + CHECK(binary -> GetOperatorKind() == BinaryOperatorKind::Addition); + auto left = binary->GetLeft(); + auto right = binary->GetRight(); + REQUIRE(left->GetKind() == ParsedExpressionKind::LiteralInteger); + REQUIRE(right->GetKind() == ParsedExpressionKind::Binary); + CHECK(((LiteralIntegerExpression*)left)->GetValue() == 5); + left = ((BinaryExpression*)right)->GetLeft(); + right = ((BinaryExpression*)right)->GetRight(); + CHECK(((LiteralIntegerExpression*)left)->GetValue() == 10); + CHECK(((LiteralIntegerExpression*)right)->GetValue() == 6); +} + +TEST_CASE( "Assert binary precedence", "[parser]" ) { + vector v { + new SimpleToken(TokenKind::OpenParenthesis,0,0), + new IntegerToken(10, 0, 0), + new SimpleToken(TokenKind::CloseParenthesis,0,0), + new SimpleToken(TokenKind::EndOfFile,0,0) + }; + Parser parser = Parser(v, nullptr); + auto parsedStatements = parser.Parse() -> GetStatements(); + REQUIRE(parsedStatements.size() == 1); + auto firstStatement = parsedStatements[0]; + REQUIRE(firstStatement -> GetKind() == ParsedStatementKind::Expression); + auto expression = ((ParsedExpressionStatement*)firstStatement)->GetExpression(); + REQUIRE(expression -> GetKind() == ParsedExpressionKind::Parenthesized); + auto innerExpression = ((ParenthesizedExpression*)expression) -> GetInnerExpression(); + REQUIRE(innerExpression -> GetKind() == ParsedExpressionKind::LiteralInteger); + +} + +#endif \ No newline at end of file diff --git a/src/Parser/TokenKind.hpp b/src/Parser/TokenKind.hpp index 8ec4133..24d898c 100644 --- a/src/Parser/TokenKind.hpp +++ b/src/Parser/TokenKind.hpp @@ -12,6 +12,8 @@ enum class TokenKind{ StarToken, AssignmentToken, EqualityToken, + OpenParenthesis, + CloseParenthesis, Identifier,