diff --git a/Makefile b/Makefile index 1658408..0db231e 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,10 @@ doc: Doxyfile src/*.cc src/*.hh doxygen cd doc; $(MAKE) html +.PHONY: doc-open +doc-open: doc + xdg-open ./doc/build/html/index.html + bin/unit-tests: src/tests/*.cc $(Obj) g++ $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ diff --git a/src/errors.cc b/src/errors.cc index 68b17d8..1b8c68c 100644 --- a/src/errors.cc +++ b/src/errors.cc @@ -103,6 +103,15 @@ Error errors::unrecognized_character(u32 invalid_character, Location location) return unrecognized_character(invalid_character).with(std::move(location)); } +Error errors::unexpected_token(Token const& unexpected) +{ + Error err; + err.type = errors::Unexpected_Token_Type; + err.location = unexpected.location; + err.message = format("unexpected ", unexpected.type); + return err; +} + Error errors::unexpected_token(Token::Type expected, Token const& unexpected) { Error err; diff --git a/src/musique.hh b/src/musique.hh index c793dfa..88119a1 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -289,11 +289,13 @@ struct Ast // Named constructors of AST structure static Ast literal(Token); static Ast binary(Token, Ast lhs, Ast rhs); + static Ast call(std::vector call); enum class Type { - Literal, // Compile time known constant like c or 1 - Binary // Binary operator application like 1 + 2 + Literal, // Compile time known constant like `c` or `1` + Binary, // Binary operator application like `1` + `2` + Call // Function call application like `print 42` }; Type type; @@ -314,9 +316,10 @@ struct Parser static Result parse(std::string_view source, std::string_view filename); Result parse_expression(); - Result parse_binary_operator(); - Result parse_literal(); + Result parse_infix_expression(); + Result parse_atomic_expression(); + Result peek() const; Result peek_type() const; Token consume(); @@ -334,6 +337,7 @@ namespace errors Error unrecognized_character(u32 invalid_character, Location location); Error unexpected_token(Token::Type expected, Token const& unexpected); + Error unexpected_token(Token const& unexpected); Error unexpected_end_of_source(Location location); [[noreturn]] diff --git a/src/parser.cc b/src/parser.cc index 8b02fc8..3f871d0 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1,4 +1,5 @@ #include +#include Result Parser::parse(std::string_view source, std::string_view filename) { @@ -25,26 +26,39 @@ Result Parser::parse(std::string_view source, std::string_view filename) Result Parser::parse_expression() { - return parse_binary_operator(); + return parse_infix_expression(); } -Result Parser::parse_binary_operator() +Result Parser::parse_infix_expression() { - return parse_literal().and_then([&](Ast lhs) -> tl::expected { - if (expect(Token::Type::Operator)) { - auto op = consume(); - return parse_expression().map([&](Ast rhs) { - return Ast::binary(std::move(op), std::move(lhs), std::move(rhs)); - }); - } else { - return lhs; - } - }); + std::vector atomics; + Result expr; + while ((expr = parse_atomic_expression()).has_value()) { + atomics.push_back(std::move(expr).value()); + } + + if (atomics.empty()) { + return expr; + } + + auto lhs = atomics.size() == 1 + ? std::move(atomics[0]) + : Ast::call(std::move(atomics)); + + if (expect(Token::Type::Operator)) { + auto op = consume(); + return parse_expression().map([&](Ast rhs) { + return Ast::binary(std::move(op), std::move(lhs), std::move(rhs)); + }); + } + + return lhs; } -Result Parser::parse_literal() +Result Parser::parse_atomic_expression() { switch (Try(peek_type())) { + case Token::Type::Symbol: case Token::Type::Numeric: return Ast::literal(consume()); @@ -57,15 +71,22 @@ Result Parser::parse_literal() }); default: - unimplemented(); + return peek().and_then([](auto const& token) -> tl::expected { + return tl::unexpected(errors::unexpected_token(token)); + }); } } +Result Parser::peek() const +{ + return token_id >= tokens.size() + ? errors::unexpected_end_of_source(tokens.back().location) + : Result(tokens[token_id]); +} + Result Parser::peek_type() const { - return token_id >= tokens.size() - ? errors::unexpected_end_of_source(tokens.back().location) - : Result(tokens[token_id].type); + return peek().map([](Token const& token) { return token.type; }); } Token Parser::consume() @@ -105,6 +126,14 @@ Ast Ast::binary(Token token, Ast lhs, Ast rhs) return ast; } +Ast Ast::call(std::vector call) +{ + Ast ast; + ast.type = Type::Call; + ast.arguments = std::move(call); + return ast; +} + bool operator==(Ast const& lhs, Ast const& rhs) { if (lhs.type != rhs.type) { @@ -120,6 +149,10 @@ bool operator==(Ast const& lhs, Ast const& rhs) && lhs.token.source == rhs.token.source && lhs.arguments.size() == rhs.arguments.size() && std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin()); + + case Ast::Type::Call: + return lhs.arguments.size() == rhs.arguments.size() + && std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin()); } unreachable(); diff --git a/src/tests/parser.cc b/src/tests/parser.cc index d2ebdca..6d29fd2 100644 --- a/src/tests/parser.cc +++ b/src/tests/parser.cc @@ -78,4 +78,35 @@ suite parser_test = [] { ) )); }; + + "Explicit function call"_test = [] { + expect_ast("foo 1 2", Ast::call({ + Ast::literal({ Token::Type::Symbol, "foo", {} }), + Ast::literal({ Token::Type::Numeric, "1", {} }), + Ast::literal({ Token::Type::Numeric, "2", {} }) + })); + + expect_ast("say (fib (n - 1) + fib (n - 2))", Ast::call({ + Ast::literal({ Token::Type::Symbol, "say", {} }), + Ast::binary( + { Token::Type::Operator, "+", {} }, + Ast::call({ + Ast::literal({ Token::Type::Symbol, "fib", {} }), + Ast::binary( + { Token::Type::Operator, "-", {} }, + Ast::literal({ Token::Type::Symbol, "n", {} }), + Ast::literal({ Token::Type::Numeric, "1", {} }) + ) + }), + Ast::call({ + Ast::literal({ Token::Type::Symbol, "fib", {} }), + Ast::binary( + { Token::Type::Operator, "-", {} }, + Ast::literal({ Token::Type::Symbol, "n", {} }), + Ast::literal({ Token::Type::Numeric, "2", {} }) + ) + }) + ) + })); + }; };