diff --git a/furc/include/furc/ast/expression.hpp b/furc/include/furc/ast/expression.hpp index 58612ba..e0fe825 100644 --- a/furc/include/furc/ast/expression.hpp +++ b/furc/include/furc/ast/expression.hpp @@ -10,6 +10,7 @@ namespace ast { enum class expression_node_t { Literal, VarRead, + VarAssign, Unaryop, Binop, Paren, @@ -81,6 +82,7 @@ private: using unaryop_expression_node_h = node_handle; enum class binop_expression_node_t { + None = 0, Add, Sub, Mul, @@ -114,6 +116,31 @@ private: using binop_expression_node_h = node_handle; +class var_assign_expression_node : public expression_node { +public: + var_assign_expression_node(expression_node_h&& lhs, expression_node_h&& rhs) + : m_compound(binop_expression_node_t::None), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {} + + var_assign_expression_node(binop_expression_node_t compound, expression_node_h&& lhs, expression_node_h&& rhs) + : m_compound(compound), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {} + + binop_expression_node_t compound() const { return m_compound; } + const expression_node_h& lhs() const { return m_lhs; } + const expression_node_h& rhs() const { return m_rhs; } +public: + expression_node_t expression_type() const override { return expression_node_t::VarAssign; } + + std::ostream& print(std::ostream& os) const override; +protected: + bool equal(const node& rhs) const override; +private: + binop_expression_node_t m_compound; + expression_node_h m_lhs; + expression_node_h m_rhs; +}; + +using var_assign_expression_node_h = node_handle; + } // namespace ast } // namespace furc diff --git a/furc/include/furc/front/token.hpp b/furc/include/furc/front/token.hpp index 636bfb8..87f7bda 100644 --- a/furc/include/furc/front/token.hpp +++ b/furc/include/furc/front/token.hpp @@ -34,6 +34,13 @@ enum class token_t { Percent, DPlus, DMinus, + + Eq, + PlusEq, + MinusEq, + StarEq, + SlashEq, + PercentEq, }; static inline std::ostream& operator<<(std::ostream& os, token_t type) { @@ -60,6 +67,12 @@ static inline std::ostream& operator<<(std::ostream& os, token_t type) { case token_t::Percent: return os << "'%'"; case token_t::DPlus: return os << "++"; case token_t::DMinus: return os << "--"; + case token_t::Eq: return os << "="; + case token_t::PlusEq: return os << "+="; + case token_t::MinusEq: return os << "-="; + case token_t::StarEq: return os << "*="; + case token_t::SlashEq: return os << "/="; + case token_t::PercentEq: return os << "%="; } return os; } @@ -88,6 +101,12 @@ static inline std::string operator+(const std::string& str, token_t type) { case token_t::Percent: return str + "'%'"; case token_t::DPlus: return str + "++"; case token_t::DMinus: return str + "--"; + case token_t::Eq: return str + "="; + case token_t::PlusEq: return str + "+="; + case token_t::MinusEq: return str + "-="; + case token_t::StarEq: return str + "*="; + case token_t::SlashEq: return str + "/="; + case token_t::PercentEq: return str + "%="; } return str; } @@ -158,24 +177,7 @@ struct token { case token_t::String: return value.string == rhs.value.string; case token_t::Keyword: return value.keyword == rhs.value.keyword; case token_t::Integer: return value.integer == rhs.value.integer; - case token_t::None: - case token_t::Lparen: - case token_t::Rparen: - case token_t::Lbrace: - case token_t::Rbrace: - case token_t::Lbracket: - case token_t::Rbracket: - case token_t::Semicolon: - case token_t::Colon: - case token_t::Comma: - case token_t::Dot: - case token_t::Plus: - case token_t::Minus: - case token_t::Star: - case token_t::Slash: - case token_t::Percent: - case token_t::DPlus: - case token_t::DMinus: return true; + default: return true; } } }; diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index 41e65e4..32a74be 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -75,6 +75,8 @@ bool unaryop_expression_node::equal(const node& rhsNode) const { std::ostream& operator<<(std::ostream& os, binop_expression_node_t type) { switch (type) { + default: + case binop_expression_node_t::None: return os; case binop_expression_node_t::Add: return os << '+'; case binop_expression_node_t::Sub: return os << '-'; case binop_expression_node_t::Mul: return os << '*'; @@ -84,6 +86,7 @@ std::ostream& operator<<(std::ostream& os, binop_expression_node_t type) { } std::ostream& binop_expression_node::print(std::ostream& os) const { + if (m_type == binop_expression_node_t::None) return os; return os << '(' << *m_lhs << ' ' << m_type << ' ' << *m_rhs << ')'; } @@ -92,6 +95,15 @@ bool binop_expression_node::equal(const node& rhsNode) const { return expression_node::equal(rhsNode) && m_type == rhs.m_type && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs; } +std::ostream& var_assign_expression_node::print(std::ostream& os) const { + return os << *m_lhs << " = " << *m_rhs; +} + +bool var_assign_expression_node::equal(const node& rhsNode) const { + const auto& rhs = reinterpret_cast(rhsNode); + return expression_node::equal(rhsNode) && m_compound == rhs.m_compound && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs; +} + bool declaration_node::equal(const node& rhs) const { return declaration_type() == reinterpret_cast(rhs).declaration_type(); } diff --git a/furc/src/front/lexer.cpp b/furc/src/front/lexer.cpp index da7d72b..fd931c6 100644 --- a/furc/src/front/lexer.cpp +++ b/furc/src/front/lexer.cpp @@ -121,6 +121,12 @@ token_handle<> lexer::next_token() { { "%", token_t::Percent }, { "++", token_t::DPlus }, { "--", token_t::DMinus }, + { "=", token_t::Eq }, + { "+=", token_t::PlusEq }, + { "-=", token_t::MinusEq }, + { "*=", token_t::StarEq }, + { "/=", token_t::SlashEq }, + { "%=", token_t::PercentEq }, }; token_t type = token_t::None; diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index e795818..c44505e 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -212,6 +212,7 @@ enum class associativity { enum class rhsop_info_t { Unaryop, Binop, + Assignment, }; struct rhsop_info { @@ -221,6 +222,7 @@ struct rhsop_info { union { ast::unaryop_expression_node_t unary; ast::binop_expression_node_t binary; + ast::binop_expression_node_t assignment; }; static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) { @@ -242,6 +244,15 @@ struct rhsop_info { info.binary = type; return info; } + + static rhsop_info create(ast::binop_expression_node_t compound = ast::binop_expression_node_t::None) { + rhsop_info info{}; + info.type = rhsop_info_t::Assignment; + info.precedence = 14; + info.associativity = associativity::Right; + info.assignment = compound; + return info; + } }; ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& init, std::uint32_t precedence) { @@ -253,6 +264,13 @@ ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& ini { token_t::Percent, rhsop_info::create(ast::binop_expression_node_t::Mod, 5, associativity::Left) }, { token_t::DPlus, rhsop_info::create(ast::unaryop_expression_node_t::PostfixIncrement, 1) }, { token_t::DMinus, rhsop_info::create(ast::unaryop_expression_node_t::PostfixDecrement, 1) }, + { token_t::DMinus, rhsop_info::create(ast::unaryop_expression_node_t::PostfixDecrement, 1) }, + { token_t::Eq, rhsop_info::create() }, + { token_t::PlusEq, rhsop_info::create(ast::binop_expression_node_t::Add) }, + { token_t::MinusEq, rhsop_info::create(ast::binop_expression_node_t::Sub) }, + { token_t::StarEq, rhsop_info::create(ast::binop_expression_node_t::Mul) }, + { token_t::SlashEq, rhsop_info::create(ast::binop_expression_node_t::Div) }, + { token_t::PercentEq, rhsop_info::create(ast::binop_expression_node_t::Mod) }, }; ast::expression_node_h lhs = std::move(init); @@ -265,7 +283,7 @@ ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& ini auto opToken = next_token(); ast::expression_node_h rhs; - if (current.type == rhsop_info_t::Binop) { + if (current.type != rhsop_info_t::Unaryop) { rhs = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative } @@ -273,7 +291,7 @@ ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& ini if (nextIt != s_rhsops.end()) { rhsop_info next = nextIt->second; - if (current.type == rhsop_info_t::Binop) { + if (current.type != rhsop_info_t::Unaryop) { rhs = std::move(parse_expression_rhs(std::move(rhs), current.precedence + static_cast(current.associativity == associativity::Right))); } else { @@ -281,14 +299,25 @@ ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& ini } } - lhs = current.type == rhsop_info_t::Binop - ? ast::expression_node_h(ast::binop_expression_node_h{ opToken.location(), - m_arena, - current.binary, - std::move(lhs), - std::move(rhs) }) - : ast::expression_node_h( - ast::unaryop_expression_node_h{ opToken.location(), m_arena, current.unary, std::move(lhs) }); + switch (current.type) { + case rhsop_info_t::Unaryop: + lhs = ast::unaryop_expression_node_h{ opToken.location(), m_arena, current.unary, std::move(lhs) }; + break; + case rhsop_info_t::Binop: + lhs = ast::binop_expression_node_h{ opToken.location(), + m_arena, + current.binary, + std::move(lhs), + std::move(rhs) }; + break; + case rhsop_info_t::Assignment: + lhs = ast::var_assign_expression_node_h{ opToken.location(), + m_arena, + current.assignment, + std::move(lhs), + std::move(rhs) }; + break; + } } return lhs; } diff --git a/furc/test/parser.cpp b/furc/test/parser.cpp index 1c93ea6..8c73a42 100644 --- a/furc/test/parser.cpp +++ b/furc/test/parser.cpp @@ -213,4 +213,62 @@ TEST(Parser, Paren) { EXPECT_VARREAD(inc->get_node(), "x"); } +TEST(Parser, Assignment) { + parser parser("", "func main() { x = 10; }"); + 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); + function_definition_node_h funcDef = func; + EXPECT_EQ(funcDef->name()->string, "main"); + EXPECT_EQ(funcDef->body()->statements.size(), 1); + + EXPECT_EQ(funcDef->body()->statements[0]->statement_type(), statement_node_t::Expression); + expression_node_h expr = funcDef->body()->statements[0]; + + EXPECT_EQ(expr->expression_type(), expression_node_t::VarAssign); + var_assign_expression_node_h assign = expr; + EXPECT_EQ(assign->compound(), binop_expression_node_t::None); + + expression_node_h lhs = assign->lhs(); + EXPECT_EQ(lhs->expression_type(), expression_node_t::VarRead); + var_read_expression_node_h varRead = lhs; + EXPECT_EQ(varRead->get_name(), "x"); + + expression_node_h rhs = assign->rhs(); + EXPECT_EQ(rhs->expression_type(), expression_node_t::Literal); + EXPECT_INTLIT(rhs, 10); +} + +TEST(Parser, CompoundAssignment) { + parser parser("", "func main() { x += 10; }"); + 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); + function_definition_node_h funcDef = func; + EXPECT_EQ(funcDef->name()->string, "main"); + EXPECT_EQ(funcDef->body()->statements.size(), 1); + + EXPECT_EQ(funcDef->body()->statements[0]->statement_type(), statement_node_t::Expression); + expression_node_h expr = funcDef->body()->statements[0]; + + EXPECT_EQ(expr->expression_type(), expression_node_t::VarAssign); + var_assign_expression_node_h assign = expr; + EXPECT_EQ(assign->compound(), binop_expression_node_t::Add); + + expression_node_h lhs = assign->lhs(); + EXPECT_EQ(lhs->expression_type(), expression_node_t::VarRead); + var_read_expression_node_h varRead = lhs; + EXPECT_EQ(varRead->get_name(), "x"); + + expression_node_h rhs = assign->rhs(); + EXPECT_EQ(rhs->expression_type(), expression_node_t::Literal); + EXPECT_INTLIT(rhs, 10); +} + } // namespace \ No newline at end of file