Parse binop expressions
Signed-off-by: CHatingPython <chatingpython@gmail.com>
This commit is contained in:
@@ -48,6 +48,8 @@ public:
|
|||||||
literal_node_t literal_type() const override { return literal_node_t::Integer; }
|
literal_node_t literal_type() const override { return literal_node_t::Integer; }
|
||||||
|
|
||||||
const handle<front::integer_token>& value() const { return m_value; }
|
const handle<front::integer_token>& value() const { return m_value; }
|
||||||
|
|
||||||
|
bool operator==(front::integer_token integer) const { return m_value == integer; }
|
||||||
public:
|
public:
|
||||||
std::ostream& print(std::ostream& os) const override;
|
std::ostream& print(std::ostream& os) const override;
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ private:
|
|||||||
ast::node_handle<ast::expression_node> parse_expression();
|
ast::node_handle<ast::expression_node> parse_expression();
|
||||||
ast::node_handle<ast::literal_node> parse_literal();
|
ast::node_handle<ast::literal_node> parse_literal();
|
||||||
|
|
||||||
|
ast::expression_node_h parse_expression_primary();
|
||||||
|
ast::expression_node_h parse_expression_rhs(const ast::expression_node_h& init, std::uint32_t precedence);
|
||||||
|
|
||||||
ast::function_body_handle parse_body();
|
ast::function_body_handle parse_body();
|
||||||
private:
|
private:
|
||||||
token_handle<> next_token();
|
token_handle<> next_token();
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ enum class token_t {
|
|||||||
Colon,
|
Colon,
|
||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
|
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
Percent,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline std::ostream& operator<<(std::ostream& os, token_t type) {
|
static inline std::ostream& operator<<(std::ostream& os, token_t type) {
|
||||||
@@ -45,7 +51,13 @@ static inline std::ostream& operator<<(std::ostream& os, token_t type) {
|
|||||||
case token_t::Colon: return os << "':'";
|
case token_t::Colon: return os << "':'";
|
||||||
case token_t::Comma: return os << "','";
|
case token_t::Comma: return os << "','";
|
||||||
case token_t::Dot: return os << "'.'";
|
case token_t::Dot: return os << "'.'";
|
||||||
|
case token_t::Plus: return os << "'+'";
|
||||||
|
case token_t::Minus: return os << "'-'";
|
||||||
|
case token_t::Star: return os << "'*'";
|
||||||
|
case token_t::Slash: return os << "'/'";
|
||||||
|
case token_t::Percent: return os << "'%'";
|
||||||
}
|
}
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline std::string operator+(const std::string& str, token_t type) {
|
static inline std::string operator+(const std::string& str, token_t type) {
|
||||||
@@ -65,7 +77,13 @@ static inline std::string operator+(const std::string& str, token_t type) {
|
|||||||
case token_t::Colon: return str + "':'";
|
case token_t::Colon: return str + "':'";
|
||||||
case token_t::Comma: return str + "','";
|
case token_t::Comma: return str + "','";
|
||||||
case token_t::Dot: return str + "'.'";
|
case token_t::Dot: return str + "'.'";
|
||||||
|
case token_t::Plus: return str + "'+'";
|
||||||
|
case token_t::Minus: return str + "'-'";
|
||||||
|
case token_t::Star: return str + "'*'";
|
||||||
|
case token_t::Slash: return str + "'/'";
|
||||||
|
case token_t::Percent: return str + "'%'";
|
||||||
}
|
}
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class keyword_token {
|
enum class keyword_token {
|
||||||
@@ -144,7 +162,12 @@ struct token {
|
|||||||
case token_t::Semicolon:
|
case token_t::Semicolon:
|
||||||
case token_t::Colon:
|
case token_t::Colon:
|
||||||
case token_t::Comma:
|
case token_t::Comma:
|
||||||
case token_t::Dot: return true;
|
case token_t::Dot:
|
||||||
|
case token_t::Plus:
|
||||||
|
case token_t::Minus:
|
||||||
|
case token_t::Star:
|
||||||
|
case token_t::Slash:
|
||||||
|
case token_t::Percent: return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+15
-10
@@ -45,16 +45,21 @@ token_handle<> lexer::next_token() {
|
|||||||
|
|
||||||
char ch = get();
|
char ch = get();
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '(': return { location, token_t::Lparen, m_content.substr(m_cursor++, 1) };
|
case '(': ++m_cursor; return { location, token_t::Lparen };
|
||||||
case ')': return { location, token_t::Rparen, m_content.substr(m_cursor++, 1) };
|
case ')': ++m_cursor; return { location, token_t::Rparen };
|
||||||
case '{': return { location, token_t::Lbrace, m_content.substr(m_cursor++, 1) };
|
case '{': ++m_cursor; return { location, token_t::Lbrace };
|
||||||
case '}': return { location, token_t::Rbrace, m_content.substr(m_cursor++, 1) };
|
case '}': ++m_cursor; return { location, token_t::Rbrace };
|
||||||
case '[': return { location, token_t::Lbracket, m_content.substr(m_cursor++, 1) };
|
case '[': ++m_cursor; return { location, token_t::Lbracket };
|
||||||
case ']': return { location, token_t::Rbracket, m_content.substr(m_cursor++, 1) };
|
case ']': ++m_cursor; return { location, token_t::Rbracket };
|
||||||
case ';': return { location, token_t::Semicolon, m_content.substr(m_cursor++, 1) };
|
case ';': ++m_cursor; return { location, token_t::Semicolon };
|
||||||
case ':': return { location, token_t::Colon, m_content.substr(m_cursor++, 1) };
|
case ':': ++m_cursor; return { location, token_t::Colon };
|
||||||
case ',': return { location, token_t::Comma, m_content.substr(m_cursor++, 1) };
|
case ',': ++m_cursor; return { location, token_t::Comma };
|
||||||
case '.': return { location, token_t::Dot, m_content.substr(m_cursor++, 1) };
|
case '.': ++m_cursor; return { location, token_t::Dot };
|
||||||
|
case '+': ++m_cursor; return { location, token_t::Plus };
|
||||||
|
case '-': ++m_cursor; return { location, token_t::Minus };
|
||||||
|
case '*': ++m_cursor; return { location, token_t::Star };
|
||||||
|
case '/': ++m_cursor; return { location, token_t::Slash };
|
||||||
|
case '%': ++m_cursor; return { location, token_t::Percent };
|
||||||
case '"': {
|
case '"': {
|
||||||
std::size_t begin = ++m_cursor;
|
std::size_t begin = ++m_cursor;
|
||||||
while (m_cursor < m_content.size() && m_content[m_cursor] != '"')
|
while (m_cursor < m_content.size() && m_content[m_cursor] != '"')
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace furc::front {
|
namespace furc::front {
|
||||||
|
|
||||||
@@ -121,11 +122,7 @@ ast::node_handle<ast::statement_node> parser::parse_statement() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ast::node_handle<ast::expression_node> parser::parse_expression() {
|
ast::node_handle<ast::expression_node> parser::parse_expression() {
|
||||||
const auto& tok = peek_token();
|
return parse_expression_rhs(parse_expression_primary(), 16);
|
||||||
|
|
||||||
auto literal = parse_literal();
|
|
||||||
if (literal.present()) return std::move(literal);
|
|
||||||
return { tok.location(), "unexpected token"s + tok->type + ", expected expression or literal" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::node_handle<ast::literal_node> parser::parse_literal() {
|
ast::node_handle<ast::literal_node> parser::parse_literal() {
|
||||||
@@ -149,6 +146,64 @@ ast::node_handle<ast::literal_node> parser::parse_literal() {
|
|||||||
return { tok.location(), "unexpected token "s + tok->type + ", expected literal" };
|
return { tok.location(), "unexpected token "s + tok->type + ", expected literal" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class binop_associativity {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct binop_info {
|
||||||
|
ast::binop_expression_node_t type;
|
||||||
|
std::uint32_t precedence;
|
||||||
|
binop_associativity associativity;
|
||||||
|
|
||||||
|
static binop_info left(ast::binop_expression_node_t type, std::uint32_t precedence) {
|
||||||
|
return { type, precedence, binop_associativity::Left };
|
||||||
|
}
|
||||||
|
|
||||||
|
static binop_info right(ast::binop_expression_node_t type, std::uint32_t precedence) {
|
||||||
|
return { type, precedence, binop_associativity::Right };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ast::expression_node_h parser::parse_expression_primary() {
|
||||||
|
const auto& tok = peek_token();
|
||||||
|
|
||||||
|
auto literal = parse_literal();
|
||||||
|
if (literal.present()) return std::move(literal);
|
||||||
|
return { tok.location(), "unexpected token"s + tok->type + ", expected expression or literal" };
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::expression_node_h parser::parse_expression_rhs(const ast::expression_node_h& init, std::uint32_t precedence) {
|
||||||
|
static std::unordered_map<token_t, binop_info> s_binops = {
|
||||||
|
{ token_t::Plus, binop_info::left(ast::binop_expression_node_t::Add, 5) },
|
||||||
|
{ token_t::Minus, binop_info::left(ast::binop_expression_node_t::Sub, 5) },
|
||||||
|
{ token_t::Star, binop_info::left(ast::binop_expression_node_t::Mul, 4) },
|
||||||
|
{ token_t::Slash, binop_info::left(ast::binop_expression_node_t::Div, 4) },
|
||||||
|
{ token_t::Percent, binop_info::left(ast::binop_expression_node_t::Mod, 5) },
|
||||||
|
};
|
||||||
|
|
||||||
|
ast::expression_node_h lhs = init;
|
||||||
|
while (true) {
|
||||||
|
auto it = s_binops.find(peek_token()->type);
|
||||||
|
if (it == s_binops.end()) return lhs;
|
||||||
|
binop_info current = it->second;
|
||||||
|
if (current.precedence >= precedence) return lhs;
|
||||||
|
auto opToken = next_token();
|
||||||
|
|
||||||
|
auto rhs = parse_expression_primary();
|
||||||
|
|
||||||
|
auto nextIt = s_binops.find(peek_token()->type);
|
||||||
|
if (nextIt != s_binops.end()) {
|
||||||
|
binop_info next = nextIt->second;
|
||||||
|
|
||||||
|
rhs = std::move(parse_expression_rhs(rhs,
|
||||||
|
current.precedence + static_cast<std::uint32_t>(current.associativity == binop_associativity::Right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs = ast::binop_expression_node_h{ opToken.location(), m_arena, current.type, std::move(lhs), std::move(rhs) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ast::function_body_handle parser::parse_body() {
|
ast::function_body_handle parser::parse_body() {
|
||||||
ast::function_body body;
|
ast::function_body body;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
furc::front::parser parser("<TEMP>", "func main() {\n return 67;return \"uwu\";\n}");
|
furc::front::parser parser("<TEMP>", "func main() {\n return 7 + 6 * 10; // 130\n}");
|
||||||
std::cout << parser.parse() << '\n';
|
std::cout << parser.parse() << '\n';
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+13
-13
@@ -18,17 +18,17 @@ using namespace std::string_view_literals;
|
|||||||
|
|
||||||
TEST(Lexer, Tokens) {
|
TEST(Lexer, Tokens) {
|
||||||
lexer lexer("<TEMP>", "()\n\t\t{\n}[\"shto-to\"]; :,.main return func");
|
lexer lexer("<TEMP>", "()\n\t\t{\n}[\"shto-to\"]; :,.main return func");
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Lparen, "("sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Lparen }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Rparen, ")"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Rparen }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Lbrace, "{"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Lbrace }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Rbrace, "}"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Rbrace }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Lbracket, "["sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Lbracket }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::String, "shto-to"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::String, "shto-to"sv }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Rbracket, "]"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Rbracket }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Semicolon, ";"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Semicolon }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Colon, ":"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Colon }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Comma, ","sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Comma }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Dot, "."sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Dot }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Identifier, "main"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Identifier, "main"sv }));
|
||||||
EXPECT_TOKEN(lexer, (token{ keyword_token::Return }));
|
EXPECT_TOKEN(lexer, (token{ keyword_token::Return }));
|
||||||
EXPECT_TOKEN(lexer, (token{ keyword_token::Func }));
|
EXPECT_TOKEN(lexer, (token{ keyword_token::Func }));
|
||||||
@@ -37,10 +37,10 @@ TEST(Lexer, Tokens) {
|
|||||||
|
|
||||||
TEST(Lexer, Comments) {
|
TEST(Lexer, Comments) {
|
||||||
lexer lexer("<TEMP>", "(/** skibidi **/func{//)\n}");
|
lexer lexer("<TEMP>", "(/** skibidi **/func{//)\n}");
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Lparen, "("sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Lparen, "("sv })); // left out the string-view deliberately
|
||||||
EXPECT_TOKEN(lexer, (token{ keyword_token::Func }));
|
EXPECT_TOKEN(lexer, (token{ keyword_token::Func }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Lbrace, "{"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Lbrace }));
|
||||||
EXPECT_TOKEN(lexer, (token{ token_t::Rbrace, "}"sv }));
|
EXPECT_TOKEN(lexer, (token{ token_t::Rbrace }));
|
||||||
EXPECT_EOF(lexer);
|
EXPECT_EOF(lexer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,4 +57,80 @@ TEST(Parser, Literals) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EXPECT_INTLIT(expr, integer) \
|
||||||
|
do { \
|
||||||
|
EXPECT_EQ((expr)->expression_type(), expression_node_t::Literal); \
|
||||||
|
node_handle<literal_node> literal = (expr); \
|
||||||
|
EXPECT_EQ(literal->literal_type(), literal_node_t::Integer); \
|
||||||
|
node_handle<integer_literal_node> intLit = literal; \
|
||||||
|
EXPECT_EQ(*intLit, (integer)); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
// TODO: Use arena (I am too exhausted rn to do it)
|
||||||
|
TEST(Parser, OperatorPrecedence_AddMul) {
|
||||||
|
parser parser("<TEMP>", "func main() { return 1 + 2 * 3; }");
|
||||||
|
auto program = parser.parse();
|
||||||
|
EXPECT_TRUE(program.present());
|
||||||
|
EXPECT_EQ(program->declarations().size(), 1);
|
||||||
|
auto func = program->declarations()[0];
|
||||||
|
EXPECT_TRUE(func.present());
|
||||||
|
EXPECT_EQ(func->declaration_type(), declaration_node_t::FunctionDefinition);
|
||||||
|
node_handle<function_definition_node> funcDef = func;
|
||||||
|
EXPECT_EQ(funcDef->name()->string, "main");
|
||||||
|
EXPECT_EQ(funcDef->body()->statements.size(), 1);
|
||||||
|
node_handle<return_statement_node> ret = funcDef->body()->statements[0];
|
||||||
|
|
||||||
|
auto retVal = ret->value();
|
||||||
|
EXPECT_TRUE(retVal.present());
|
||||||
|
|
||||||
|
EXPECT_EQ(retVal->expression_type(), expression_node_t::Binop);
|
||||||
|
binop_expression_node_h add = retVal;
|
||||||
|
EXPECT_EQ(add->type(), binop_expression_node_t::Add);
|
||||||
|
EXPECT_INTLIT(add->lhs(), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(add->rhs()->expression_type(), expression_node_t::Binop);
|
||||||
|
binop_expression_node_h mul = add->rhs();
|
||||||
|
EXPECT_EQ(mul->type(), binop_expression_node_t::Mul);
|
||||||
|
EXPECT_INTLIT(mul->lhs(), 2);
|
||||||
|
EXPECT_INTLIT(mul->rhs(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Parser, OperatorPrecedence_Complex) {
|
||||||
|
parser parser("<TEMP>", "func main() { return 1 + 2 * 3 - 4 / 2; }");
|
||||||
|
auto program = parser.parse();
|
||||||
|
EXPECT_TRUE(program.present());
|
||||||
|
EXPECT_EQ(program->declarations().size(), 1);
|
||||||
|
auto func = program->declarations()[0];
|
||||||
|
EXPECT_TRUE(func.present());
|
||||||
|
EXPECT_EQ(func->declaration_type(), declaration_node_t::FunctionDefinition);
|
||||||
|
node_handle<function_definition_node> funcDef = func;
|
||||||
|
EXPECT_EQ(funcDef->name()->string, "main");
|
||||||
|
EXPECT_EQ(funcDef->body()->statements.size(), 1);
|
||||||
|
node_handle<return_statement_node> ret = funcDef->body()->statements[0];
|
||||||
|
|
||||||
|
auto retVal = ret->value();
|
||||||
|
EXPECT_TRUE(retVal.present());
|
||||||
|
|
||||||
|
EXPECT_EQ(retVal->expression_type(), expression_node_t::Binop);
|
||||||
|
binop_expression_node_h sub = retVal;
|
||||||
|
EXPECT_EQ(sub->type(), binop_expression_node_t::Sub);
|
||||||
|
|
||||||
|
EXPECT_EQ(sub->lhs()->expression_type(), expression_node_t::Binop);
|
||||||
|
binop_expression_node_h add = sub->lhs();
|
||||||
|
EXPECT_EQ(add->type(), binop_expression_node_t::Add);
|
||||||
|
EXPECT_INTLIT(add->lhs(), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(add->rhs()->expression_type(), expression_node_t::Binop);
|
||||||
|
binop_expression_node_h mul = add->rhs();
|
||||||
|
EXPECT_EQ(mul->type(), binop_expression_node_t::Mul);
|
||||||
|
EXPECT_INTLIT(mul->lhs(), 2);
|
||||||
|
EXPECT_INTLIT(mul->rhs(), 3);
|
||||||
|
|
||||||
|
EXPECT_EQ(sub->rhs()->expression_type(), expression_node_t::Binop);
|
||||||
|
binop_expression_node_h div = sub->rhs();
|
||||||
|
EXPECT_EQ(div->type(), binop_expression_node_t::Div);
|
||||||
|
EXPECT_INTLIT(div->lhs(), 4);
|
||||||
|
EXPECT_INTLIT(div->rhs(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
Reference in New Issue
Block a user