added parsing of if statements
This commit is contained in:
23
src/ast/expressions/if_then_else.cpp
Normal file
23
src/ast/expressions/if_then_else.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
#include "if_then_else.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace ast {
|
||||||
|
if_then_else::if_then_else(token::token token)
|
||||||
|
: token(std::move(token)),
|
||||||
|
consequence(nullptr),
|
||||||
|
alternative(nullptr) {};
|
||||||
|
|
||||||
|
std::string if_then_else::token_literal() const {
|
||||||
|
return token.literal;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string if_then_else::str() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "if " << condition->str() << consequence->str();
|
||||||
|
if (alternative != nullptr)
|
||||||
|
ss << "else" << alternative->str();
|
||||||
|
return ss.str();
|
||||||
|
};
|
||||||
|
} // namespace ast
|
19
src/ast/expressions/if_then_else.hpp
Normal file
19
src/ast/expressions/if_then_else.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ast/ast.hpp"
|
||||||
|
#include "ast/statements/block.hpp"
|
||||||
|
#include "token/token.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ast {
|
||||||
|
struct if_then_else : expression {
|
||||||
|
if_then_else(token::token);
|
||||||
|
token::token token;
|
||||||
|
ast::expression* condition;
|
||||||
|
ast::block_stmt *consequence, *alternative;
|
||||||
|
|
||||||
|
std::string token_literal() const override;
|
||||||
|
std::string str() const override;
|
||||||
|
};
|
||||||
|
} // namespace ast
|
25
src/ast/statements/block.cpp
Normal file
25
src/ast/statements/block.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include "block.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace ast {
|
||||||
|
block_stmt::block_stmt(token::token token): token(std::move(token)) {}
|
||||||
|
|
||||||
|
std::string block_stmt::token_literal() const {
|
||||||
|
return token.literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_stmt::~block_stmt() {
|
||||||
|
for (const auto& stmt : statements)
|
||||||
|
delete stmt;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string block_stmt::str() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "{";
|
||||||
|
for (const auto& stmt : statements)
|
||||||
|
ss << stmt->str();
|
||||||
|
ss << "}";
|
||||||
|
return ss.str();
|
||||||
|
};
|
||||||
|
} // namespace ast
|
20
src/ast/statements/block.hpp
Normal file
20
src/ast/statements/block.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ast/ast.hpp"
|
||||||
|
#include "token/token.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ast {
|
||||||
|
struct block_stmt : statement {
|
||||||
|
block_stmt(token::token token);
|
||||||
|
|
||||||
|
token::token token;
|
||||||
|
std::vector<ast::statement*> statements;
|
||||||
|
|
||||||
|
std::string token_literal() const override;
|
||||||
|
std::string str() const override;
|
||||||
|
|
||||||
|
~block_stmt();
|
||||||
|
};
|
||||||
|
} // namespace ast
|
@@ -3,9 +3,11 @@
|
|||||||
#include "ast/errors/error.hpp"
|
#include "ast/errors/error.hpp"
|
||||||
#include "ast/expressions/boolean.hpp"
|
#include "ast/expressions/boolean.hpp"
|
||||||
#include "ast/expressions/identifier.hpp"
|
#include "ast/expressions/identifier.hpp"
|
||||||
|
#include "ast/expressions/if_then_else.hpp"
|
||||||
#include "ast/expressions/infix.hpp"
|
#include "ast/expressions/infix.hpp"
|
||||||
#include "ast/expressions/integer.hpp"
|
#include "ast/expressions/integer.hpp"
|
||||||
#include "ast/expressions/prefix.hpp"
|
#include "ast/expressions/prefix.hpp"
|
||||||
|
#include "ast/statements/block.hpp"
|
||||||
#include "token/token.hpp"
|
#include "token/token.hpp"
|
||||||
#include "token/type.hpp"
|
#include "token/type.hpp"
|
||||||
#include "utils/tracer.hpp"
|
#include "utils/tracer.hpp"
|
||||||
@@ -51,6 +53,11 @@ namespace parser {
|
|||||||
std::bind(&parser::parse_grouped_expr, this)
|
std::bind(&parser::parse_grouped_expr, this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
register_prefix(
|
||||||
|
token::type::IF,
|
||||||
|
std::bind(&parser::parse_if_then_else, this)
|
||||||
|
);
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
register_infix(
|
register_infix(
|
||||||
{token::type::PLUS,
|
{token::type::PLUS,
|
||||||
@@ -256,6 +263,59 @@ namespace parser {
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ast::expression* parser::parse_if_then_else() {
|
||||||
|
// TRACE_FUNCTION;
|
||||||
|
ast::if_then_else* ret = new ast::if_then_else(current);
|
||||||
|
if (!expect_next(token::type::LPAREN)) {
|
||||||
|
delete ret;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token();
|
||||||
|
ret->condition = parse_expression();
|
||||||
|
|
||||||
|
if (!expect_next(token::type::RPAREN)) {
|
||||||
|
delete ret;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expect_next(token::type::LBRACE)) {
|
||||||
|
delete ret;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->consequence = parse_block();
|
||||||
|
if (next.type != token::type::ELSE)
|
||||||
|
return ret;
|
||||||
|
next_token();
|
||||||
|
|
||||||
|
if (!expect_next(token::type::LBRACE)) {
|
||||||
|
delete ret;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->alternative = parse_block();
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
ast::block_stmt* parser::parse_block() {
|
||||||
|
// TRACE_FUNCTION;
|
||||||
|
ast::block_stmt* ret = new ast::block_stmt(current);
|
||||||
|
// ret->statements.push_back(parse_statement());
|
||||||
|
next_token();
|
||||||
|
int i = 0;
|
||||||
|
while (current.type != token::type::RBRACE && i++ < 10) {
|
||||||
|
// for (next_token(); next.type != token::type::RBRACE;
|
||||||
|
// next_token()) {
|
||||||
|
ast::statement* stmt = parse_statement();
|
||||||
|
if (stmt != nullptr)
|
||||||
|
ret->statements.push_back(stmt);
|
||||||
|
next_token();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ast::expression* parser::parse_infix_expr(ast::expression* left) {
|
ast::expression* parser::parse_infix_expr(ast::expression* left) {
|
||||||
// TRACE_FUNCTION;
|
// TRACE_FUNCTION;
|
||||||
ast::infix_expr* ret =
|
ast::infix_expr* ret =
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#include "ast/ast.hpp"
|
#include "ast/ast.hpp"
|
||||||
#include "ast/errors/error.hpp"
|
#include "ast/errors/error.hpp"
|
||||||
#include "ast/program.hpp"
|
#include "ast/program.hpp"
|
||||||
|
#include "ast/statements/block.hpp"
|
||||||
#include "ast/statements/expression.hpp"
|
#include "ast/statements/expression.hpp"
|
||||||
#include "ast/statements/let.hpp"
|
#include "ast/statements/let.hpp"
|
||||||
#include "ast/statements/return.hpp"
|
#include "ast/statements/return.hpp"
|
||||||
@@ -54,6 +55,8 @@ namespace parser {
|
|||||||
ast::expression* parse_boolean();
|
ast::expression* parse_boolean();
|
||||||
ast::expression* parse_prefix_expr();
|
ast::expression* parse_prefix_expr();
|
||||||
ast::expression* parse_grouped_expr();
|
ast::expression* parse_grouped_expr();
|
||||||
|
ast::expression* parse_if_then_else();
|
||||||
|
ast::block_stmt* parse_block();
|
||||||
|
|
||||||
ast::expression* parse_infix_expr(ast::expression*);
|
ast::expression* parse_infix_expr(ast::expression*);
|
||||||
};
|
};
|
||||||
|
94
test/parser/if.cpp
Normal file
94
test/parser/if.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include "ast/expressions/if_then_else.hpp"
|
||||||
|
#include "ast/expressions/infix.hpp"
|
||||||
|
#include "ast/statements/block.hpp"
|
||||||
|
#include "ast/statements/expression.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
TEST_SUITE("Parser: if") {
|
||||||
|
TEST_CASE("Malformed if then else (checking for memory leaks)") {}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ParserFixture, "Parse well formed simple if ") {
|
||||||
|
setup("\
|
||||||
|
if (x > 15) {\
|
||||||
|
return x;\
|
||||||
|
}\
|
||||||
|
");
|
||||||
|
REQUIRE(program->statements.size() == 1);
|
||||||
|
CHECK(program->statements[0]->token_literal() == "if");
|
||||||
|
|
||||||
|
ast::expression_stmt* expr_stmt =
|
||||||
|
cast<ast::expression_stmt>(program->statements[0]);
|
||||||
|
|
||||||
|
ast::if_then_else* if_stmt =
|
||||||
|
cast<ast::if_then_else>(expr_stmt->expression);
|
||||||
|
|
||||||
|
// condition
|
||||||
|
ast::infix_expr* cond = cast<ast::infix_expr>(if_stmt->condition);
|
||||||
|
test_infix_expression(cond, "x", ">", 15);
|
||||||
|
CHECK(if_stmt->condition->str() == "(x > 15)");
|
||||||
|
|
||||||
|
// consequence
|
||||||
|
REQUIRE(if_stmt->consequence->statements.size() == 1);
|
||||||
|
ast::return_stmt* ret_stmt =
|
||||||
|
cast<ast::return_stmt>(if_stmt->consequence->statements[0]);
|
||||||
|
test_identifier(ret_stmt->value, "x");
|
||||||
|
CHECK(if_stmt->consequence->str() == "{return x;}");
|
||||||
|
|
||||||
|
// alternative
|
||||||
|
CHECK(if_stmt->alternative == nullptr);
|
||||||
|
|
||||||
|
// full string
|
||||||
|
CHECK(if_stmt->str() == "if (x > 15){return x;}");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ParserFixture, "Parse well formed if then else") {
|
||||||
|
setup("\
|
||||||
|
if (x < 5) {\
|
||||||
|
return 15;\
|
||||||
|
} else {\
|
||||||
|
let b = 1;\
|
||||||
|
return x;\
|
||||||
|
}\
|
||||||
|
");
|
||||||
|
REQUIRE(program->statements.size() == 1);
|
||||||
|
CHECK(program->statements[0]->token_literal() == "if");
|
||||||
|
|
||||||
|
ast::expression_stmt* expr_stmt =
|
||||||
|
cast<ast::expression_stmt>(program->statements[0]);
|
||||||
|
|
||||||
|
ast::if_then_else* if_stmt =
|
||||||
|
cast<ast::if_then_else>(expr_stmt->expression);
|
||||||
|
|
||||||
|
// condition
|
||||||
|
ast::infix_expr* cond = cast<ast::infix_expr>(if_stmt->condition);
|
||||||
|
test_infix_expression(cond, "x", "<", 5);
|
||||||
|
CHECK(if_stmt->condition->str() == "(x < 5)");
|
||||||
|
|
||||||
|
// consequence
|
||||||
|
REQUIRE(if_stmt->consequence->statements.size() == 1);
|
||||||
|
ast::return_stmt* ret_stmt =
|
||||||
|
cast<ast::return_stmt>(if_stmt->consequence->statements[0]);
|
||||||
|
test_integer_literal(ret_stmt->value, 15);
|
||||||
|
CHECK(if_stmt->consequence->str() == "{return 15;}");
|
||||||
|
|
||||||
|
// alternative
|
||||||
|
REQUIRE(if_stmt->alternative != nullptr);
|
||||||
|
REQUIRE(if_stmt->alternative->statements.size() == 2);
|
||||||
|
ast::let_stmt* let_stmt =
|
||||||
|
cast<ast::let_stmt>(if_stmt->alternative->statements[0]);
|
||||||
|
test_identifier(let_stmt->name, "b");
|
||||||
|
test_integer_literal(let_stmt->value, 1);
|
||||||
|
|
||||||
|
ast::return_stmt* ret_stmt2 =
|
||||||
|
cast<ast::return_stmt>(if_stmt->alternative->statements[1]);
|
||||||
|
test_identifier(ret_stmt2->value, "x");
|
||||||
|
CHECK(if_stmt->alternative->str() == "{let b = 1;return x;}");
|
||||||
|
|
||||||
|
// full string
|
||||||
|
CHECK(
|
||||||
|
if_stmt->str() == "if (x < 5){return 15;}else{let b = 1;return x;}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user