diff --git a/furc/include/furc/ast/expression.hpp b/furc/include/furc/ast/expression.hpp index 195cd35..8b73b3a 100644 --- a/furc/include/furc/ast/expression.hpp +++ b/furc/include/furc/ast/expression.hpp @@ -9,7 +9,8 @@ namespace ast { enum class expression_node_t { Literal, - Binop + Unaryop, + Binop, }; class expression_node : public statement_node { @@ -25,6 +26,37 @@ protected: using expression_node_h = node_handle; +enum class unaryop_expression_node_t { + Positive, + Negative, + PrefixIncrement, + PostfixIncrement, + PrefixDecrement, + PostfixDecrement, +}; + +class unaryop_expression_node : public expression_node { +public: + unaryop_expression_node(unaryop_expression_node_t type, expression_node_h&& node) + : m_type(type), m_node(std::move(node)) {} + + void set_node(expression_node_h&& node) { m_node = std::move(node); } + + unaryop_expression_node_t type() const { return m_type; } + const expression_node_h& get_node() const { return m_node; } + expression_node_h& get_node() { return m_node; } + expression_node_h&& move_node() { return std::move(m_node); } +public: + expression_node_t expression_type() const override { return expression_node_t::Unaryop; } + + std::ostream& print(std::ostream& os) const override; +private: + unaryop_expression_node_t m_type; + expression_node_h m_node; +}; + +using unaryop_expression_node_h = node_handle; + enum class binop_expression_node_t { Add, Sub, @@ -40,7 +72,11 @@ public: binop_expression_node_t type() const { return m_type; }; const expression_node_h& lhs() const { return m_lhs; }; + expression_node_h& lhs() { return m_lhs; }; + expression_node_h&& move_lhs() { return std::move(m_lhs); }; const expression_node_h& rhs() const { return m_rhs; }; + expression_node_h& rhs() { return m_rhs; }; + expression_node_h&& move_rhs() { return std::move(m_rhs); }; public: expression_node_t expression_type() const override { return expression_node_t::Binop; } diff --git a/furc/include/furc/front/parser.hpp b/furc/include/furc/front/parser.hpp index c2645d1..f7f59c6 100644 --- a/furc/include/furc/front/parser.hpp +++ b/furc/include/furc/front/parser.hpp @@ -33,7 +33,8 @@ private: ast::literal_node_h 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::expression_node_h parse_expression_unary(std::uint32_t precedence); + ast::expression_node_h parse_expression_rhs(ast::expression_node_h&& init, std::uint32_t precedence); ast::function_body_h parse_body(); private: diff --git a/furc/include/furc/front/token.hpp b/furc/include/furc/front/token.hpp index 251bfc1..636bfb8 100644 --- a/furc/include/furc/front/token.hpp +++ b/furc/include/furc/front/token.hpp @@ -32,6 +32,8 @@ enum class token_t { Star, Slash, Percent, + DPlus, + DMinus, }; static inline std::ostream& operator<<(std::ostream& os, token_t type) { @@ -56,6 +58,8 @@ static inline std::ostream& operator<<(std::ostream& os, token_t type) { case token_t::Star: return os << "'*'"; case token_t::Slash: return os << "'/'"; case token_t::Percent: return os << "'%'"; + case token_t::DPlus: return os << "++"; + case token_t::DMinus: return os << "--"; } return os; } @@ -82,6 +86,8 @@ static inline std::string operator+(const std::string& str, token_t type) { case token_t::Star: return str + "'*'"; case token_t::Slash: return str + "'/'"; case token_t::Percent: return str + "'%'"; + case token_t::DPlus: return str + "++"; + case token_t::DMinus: return str + "--"; } return str; } @@ -167,7 +173,9 @@ struct token { case token_t::Minus: case token_t::Star: case token_t::Slash: - case token_t::Percent: return true; + case token_t::Percent: + case token_t::DPlus: + case token_t::DMinus: return true; } } }; diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index e11e19a..14d7b8d 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -14,7 +14,7 @@ bool literal_node::equal(const node& rhs) const { std::ostream& integer_literal_node::print(std::ostream& os) const { if (m_value.has_error()) return os << m_value.error(); - return os << "integer literal (" << *m_value << ")"; + return os << *m_value; } bool integer_literal_node::equal(const node& rhs) const { @@ -23,7 +23,7 @@ bool integer_literal_node::equal(const node& rhs) const { std::ostream& string_literal_node::print(std::ostream& os) const { if (m_value.has_error()) return os << m_value.error(); - return os << "string literal (" << *m_value << ")"; + return os << '"' << *m_value << '"'; } bool string_literal_node::equal(const node& rhs) const { @@ -34,6 +34,30 @@ bool expression_node::equal(const node& rhs) const { return expression_type() == reinterpret_cast(rhs).expression_type(); } +std::ostream& operator<<(std::ostream& os, unaryop_expression_node_t type) { + switch (type) { + case unaryop_expression_node_t::Positive: return os << "+"; + case unaryop_expression_node_t::Negative: return os << "-"; + case unaryop_expression_node_t::PrefixIncrement: + case unaryop_expression_node_t::PostfixIncrement: return os << "++"; + case unaryop_expression_node_t::PrefixDecrement: + case unaryop_expression_node_t::PostfixDecrement: return os << "--"; + } + return os; +} + +std::ostream& unaryop_expression_node::print(std::ostream& os) const { + switch (m_type) { + case unaryop_expression_node_t::Positive: + case unaryop_expression_node_t::Negative: + case unaryop_expression_node_t::PrefixIncrement: + case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')'; + case unaryop_expression_node_t::PostfixIncrement: + case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')'; + } + return os; +} + std::ostream& operator<<(std::ostream& os, binop_expression_node_t type) { switch (type) { case binop_expression_node_t::Add: return os << '+'; @@ -45,7 +69,7 @@ std::ostream& operator<<(std::ostream& os, binop_expression_node_t type) { } std::ostream& binop_expression_node::print(std::ostream& os) const { - return os << *m_lhs << ' ' << m_type << ' ' << *m_rhs; + return os << '(' << *m_lhs << ' ' << m_type << ' ' << *m_rhs << ')'; } bool binop_expression_node::equal(const node& rhsNode) const { @@ -87,7 +111,7 @@ bool statement_node::equal(const node& rhs) const { std::ostream& return_statement_node::print(std::ostream& os) const { os << "return statement"; - if (m_value.present()) return os << " (" << *m_value << ')'; + if (m_value.present()) return os << ' ' << *m_value; return os; } diff --git a/furc/src/front/lexer.cpp b/furc/src/front/lexer.cpp index 847187c..da7d72b 100644 --- a/furc/src/front/lexer.cpp +++ b/furc/src/front/lexer.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -45,23 +46,7 @@ token_handle<> lexer::next_token() { location location = current_location(); - char ch = get(); - switch (ch) { - case '(': ++m_cursor; return { location, token_t::Lparen }; - case ')': ++m_cursor; return { location, token_t::Rparen }; - case '{': ++m_cursor; return { location, token_t::Lbrace }; - case '}': ++m_cursor; return { location, token_t::Rbrace }; - case '[': ++m_cursor; return { location, token_t::Lbracket }; - case ']': ++m_cursor; return { location, token_t::Rbracket }; - case ';': ++m_cursor; return { location, token_t::Semicolon }; - case ':': ++m_cursor; return { location, token_t::Colon }; - case ',': ++m_cursor; return { location, token_t::Comma }; - 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 }; + switch (get()) { case '"': { std::size_t begin = ++m_cursor; while (m_cursor < m_content.size() && m_content[m_cursor] != '"') @@ -73,7 +58,7 @@ token_handle<> lexer::next_token() { } case std::char_traits::eof(): return { location, token_t::None }; default: { - if (std::isdigit(ch) != 0) { + if (std::isdigit(get()) != 0) { integer_token integer = 0; integer_token max = std::numeric_limits::max(); integer_token upperBound = max / 10; @@ -95,7 +80,7 @@ token_handle<> lexer::next_token() { return { location, integer }; } - if (std::isalnum(ch) != 0 || ch == '_') { + if (std::isalnum(get()) != 0 || get() == '_') { std::size_t start = m_cursor++; while (std::isalnum(get()) != 0 || get() == '_') next(); @@ -111,7 +96,47 @@ token_handle<> lexer::next_token() { return { location, token_t::Identifier, value }; } - return { location, "unexpected character '"s.append(m_content.substr(m_cursor, 1)) + "'" }; + struct compare { + bool operator()(const std::string_view& lhs, const std::string_view& rhs) const { + if (lhs.size() != rhs.size()) return lhs.size() > rhs.size(); + return lhs < rhs; + } + }; + + static std::map s_tokens = { + { "(", token_t::Lparen }, + { ")", token_t::Rparen }, + { "{", token_t::Lbrace }, + { "}", token_t::Rbrace }, + { "[", token_t::Lbracket }, + { "]", token_t::Rbracket }, + { ";", token_t::Semicolon }, + { ":", token_t::Colon }, + { ",", token_t::Comma }, + { ".", token_t::Dot }, + { "+", token_t::Plus }, + { "-", token_t::Minus }, + { "*", token_t::Star }, + { "/", token_t::Slash }, + { "%", token_t::Percent }, + { "++", token_t::DPlus }, + { "--", token_t::DMinus }, + }; + + token_t type = token_t::None; + std::size_t length = 1; + while (m_cursor + length <= m_content.size()) { + auto it = s_tokens.find(m_content.substr(m_cursor, length)); + if (it == s_tokens.end()) break; + type = it->second; + ++length; + } + if (type != token_t::None) { + m_cursor += length - 1; + return { location, type }; + } + + return { location, "unexpected character '"s.append(m_content.substr(m_cursor, length)) + "'" }; } } } diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index f0d080b..a2141f0 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -29,7 +29,7 @@ parser::parser(std::string_view filename) ast::program_node_h parser::parse() & { auto program = m_arena.allocate_shared(); - while (peek_token()->type != token_t::None && !m_lexer.empty()) { + while (peek_token().present() && peek_token()->type != token_t::None && !m_lexer.empty()) { program->push(std::move(parse_declaration())); } @@ -119,7 +119,7 @@ ast::statement_node_h parser::parse_statement() { } ast::expression_node_h parser::parse_expression() { - return parse_expression_rhs(parse_expression_primary(), 16); + return parse_expression_rhs(parse_expression_unary(16), 16); } ast::literal_node_h parser::parse_literal() { @@ -143,25 +143,6 @@ ast::literal_node_h parser::parse_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(); @@ -170,35 +151,130 @@ ast::expression_node_h parser::parse_expression_primary() { 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 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) }, +struct unaryop_info { + ast::unaryop_expression_node_t type; + std::uint32_t precedence; +}; + +ast::expression_node_h parser::parse_expression_unary(std::uint32_t precedence) { + static std::unordered_map s_prefixes = { + { token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 3 } }, + { token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 3 } }, + { token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } }, + { token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } }, }; - ast::expression_node_h lhs = init; + ast::unaryop_expression_node_h result; while (true) { - auto it = s_binops.find(peek_token()->type); - if (it == s_binops.end()) return lhs; - binop_info current = it->second; + auto it = s_prefixes.find(peek_token()->type); + if (it == s_prefixes.end()) break; + auto current = it->second; + if (current.precedence >= precedence) break; + auto token = next_token(); + + ast::expression_node_h expression; + + auto nextIt = s_prefixes.find(peek_token()->type); + if (nextIt != s_prefixes.end()) { + auto next = nextIt->second; + expression = parse_expression_unary(current.precedence + 1); + } + + result = { token.location(), m_arena, current.type, std::move(expression) }; + } + + if (!result.present()) return parse_expression_primary(); + if (!result->get_node().present()) result->set_node(parse_expression_primary()); + return result; +} + +enum class associativity { + Left, + Right, +}; + +enum class rhsop_info_t { + Unaryop, + Binop, +}; + +struct rhsop_info { + rhsop_info_t type; + std::uint32_t precedence; + associativity associativity; + union { + ast::unaryop_expression_node_t unary; + ast::binop_expression_node_t binary; + }; + + static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) { + rhsop_info info{}; + info.type = rhsop_info_t::Unaryop; + info.precedence = precedence; + info.associativity = associativity::Left; + info.unary = type; + return info; + } + + static rhsop_info create(ast::binop_expression_node_t type, + std::uint32_t precedence, + enum associativity associativity) { + rhsop_info info{}; + info.type = rhsop_info_t::Binop; + info.precedence = precedence; + info.associativity = associativity; + info.binary = type; + return info; + } +}; + +ast::expression_node_h parser::parse_expression_rhs(ast::expression_node_h&& init, std::uint32_t precedence) { + static std::unordered_map s_rhsops = { + { token_t::Plus, rhsop_info::create(ast::binop_expression_node_t::Add, 5, associativity::Left) }, + { token_t::Minus, rhsop_info::create(ast::binop_expression_node_t::Sub, 5, associativity::Left) }, + { token_t::Star, rhsop_info::create(ast::binop_expression_node_t::Mul, 4, associativity::Left) }, + { token_t::Slash, rhsop_info::create(ast::binop_expression_node_t::Div, 4, associativity::Left) }, + { 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) }, + }; + + ast::expression_node_h lhs = std::move(init); + while (peek_token().present()) { + auto it = s_rhsops.find(peek_token()->type); + if (it == s_rhsops.end()) return lhs; + + rhsop_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(current.associativity == binop_associativity::Right))); + ast::expression_node_h rhs; + if (current.type == rhsop_info_t::Binop) { + rhs = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative } - lhs = ast::binop_expression_node_h{ opToken.location(), m_arena, current.type, std::move(lhs), std::move(rhs) }; + auto nextIt = s_rhsops.find(peek_token()->type); + if (nextIt != s_rhsops.end()) { + rhsop_info next = nextIt->second; + + if (current.type == rhsop_info_t::Binop) { + rhs = std::move(parse_expression_rhs(std::move(rhs), + current.precedence + static_cast(current.associativity == associativity::Right))); + } else { + lhs = std::move(parse_expression_rhs(std::move(lhs), current.precedence)); + } + } + + 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) }); } + return lhs; } ast::function_body_h parser::parse_body() { diff --git a/furc/src/main.cpp b/furc/src/main.cpp index d32b8d7..f89b5ed 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -5,7 +5,7 @@ #include int main(void) { - furc::front::parser parser("", "func main() {\n return 7 + 6 * 10; // 130\n}"); + furc::front::parser parser("", "func main() {\n return 6 - -7 * 9++;\n}"); std::cout << parser.parse() << '\n'; return 0; diff --git a/furc/test/parser.cpp b/furc/test/parser.cpp index 310cdb6..ec098f9 100644 --- a/furc/test/parser.cpp +++ b/furc/test/parser.cpp @@ -133,4 +133,51 @@ TEST(Parser, OperatorPrecedence_Complex) { EXPECT_INTLIT(div->rhs(), 2); } +TEST(Parser, UnaryOperator_Simple) { + parser parser("", "func main() { return -5; }"); + 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); + return_statement_node_h ret = funcDef->body()->statements[0]; + + auto retVal = ret->value(); + EXPECT_TRUE(retVal.present()); + + EXPECT_EQ(retVal->expression_type(), expression_node_t::Unaryop); + unaryop_expression_node_h neg = retVal; + EXPECT_EQ(neg->type(), unaryop_expression_node_t::Negative); + EXPECT_INTLIT(neg->get_node(), 5); +} + +TEST(Parser, UnaryOperator_PrePost) { + parser parser("", "func main() { return --5++; }"); + 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); + return_statement_node_h ret = funcDef->body()->statements[0]; + + auto retVal = ret->value(); + EXPECT_TRUE(retVal.present()); + + EXPECT_EQ(retVal->expression_type(), expression_node_t::Unaryop); + unaryop_expression_node_h inc = retVal; + EXPECT_EQ(inc->type(), unaryop_expression_node_t::PostfixIncrement); + + EXPECT_EQ(inc->get_node()->expression_type(), expression_node_t::Unaryop); + unaryop_expression_node_h dec = inc->get_node(); + EXPECT_INTLIT(dec->get_node(), 5); +} + } // namespace \ No newline at end of file