Adds support for parenthesized expressions
This commit is contained in:
parent
ae25598864
commit
aec07bd29a
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <catch.hpp>
|
||||
|
||||
TEST_CASE( "Parse single true keyword", "[parser]" ) {
|
||||
vector<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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
|
|
@ -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<IToken*> tokens, Script* scriptData){
|
||||
|
@ -35,6 +36,7 @@ public:
|
|||
_position = 0;
|
||||
ScriptData = scriptData;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
#ifdef TESTS_BUILD
|
||||
#include <catch.hpp>
|
||||
#include "Parser.hpp"
|
||||
|
||||
TEST_CASE( "Parse single true keyword", "[parser]" ) {
|
||||
vector<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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<IToken*> 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
|
|
@ -12,6 +12,8 @@ enum class TokenKind{
|
|||
StarToken,
|
||||
AssignmentToken,
|
||||
EqualityToken,
|
||||
OpenParenthesis,
|
||||
CloseParenthesis,
|
||||
|
||||
Identifier,
|
||||
|
||||
|
|
Loading…
Reference in New Issue