diff --git a/src/musique.hh b/src/musique.hh index 88119a1..38b426e 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -290,12 +290,14 @@ struct Ast static Ast literal(Token); static Ast binary(Token, Ast lhs, Ast rhs); static Ast call(std::vector call); + static Ast sequence(std::vector call); enum class Type { Literal, // Compile time known constant like `c` or `1` Binary, // Binary operator application like `1` + `2` - Call // Function call application like `print 42` + Call, // Function call application like `print 42` + Sequence, // Several expressions sequences like `42`, `42; 32` }; Type type; @@ -315,6 +317,7 @@ struct Parser // using Parser structure internally static Result parse(std::string_view source, std::string_view filename); + Result parse_sequence(); Result parse_expression(); Result parse_infix_expression(); Result parse_atomic_expression(); diff --git a/src/parser.cc b/src/parser.cc index 3f871d0..25a8a04 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1,6 +1,9 @@ #include #include +static Ast wrap_if_several(std::vector &&ast, Ast(*wrapper)(std::vector)); +static Result> parse_one_or_more(Parser &p, Result (Parser::*parser)(), std::optional separator = std::nullopt); + Result Parser::parse(std::string_view source, std::string_view filename) { Lexer lexer{source}; @@ -15,7 +18,7 @@ Result Parser::parse(std::string_view source, std::string_view filename) return std::move(maybe_token).error(); } - auto const result = parser.parse_expression(); + auto const result = parser.parse_sequence(); if (parser.token_id < parser.tokens.size()) { errors::all_tokens_were_not_parsed(std::span(parser.tokens).subspan(parser.token_id)); @@ -24,6 +27,12 @@ Result Parser::parse(std::string_view source, std::string_view filename) return result; } +Result Parser::parse_sequence() +{ + auto seq = Try(parse_one_or_more(*this, &Parser::parse_expression, Token::Type::Expression_Separator)); + return wrap_if_several(std::move(seq), Ast::sequence); +} + Result Parser::parse_expression() { return parse_infix_expression(); @@ -31,19 +40,8 @@ Result Parser::parse_expression() Result Parser::parse_infix_expression() { - 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)); + auto atomics = Try(parse_one_or_more(*this, &Parser::parse_atomic_expression)); + auto lhs = wrap_if_several(std::move(atomics), Ast::call); if (expect(Token::Type::Operator)) { auto op = consume(); @@ -77,6 +75,27 @@ Result Parser::parse_atomic_expression() } } +Result> parse_one_or_more(Parser &p, Result (Parser::*parser)(), std::optional separator) +{ + std::vector trees; + Result expr; + while ((expr = (p.*parser)()).has_value()) { + trees.push_back(std::move(expr).value()); + if (separator) { + if (auto s = p.ensure(*separator); !s.has_value()) { + break; + } + do p.consume(); while (p.expect(*separator)); + } + } + + if (trees.empty()) { + return expr.error(); + } + + return trees; +} + Result Parser::peek() const { return token_id >= tokens.size() @@ -134,6 +153,21 @@ Ast Ast::call(std::vector call) return ast; } +Ast Ast::sequence(std::vector expressions) +{ + Ast ast; + ast.type = Type::Sequence; + ast.arguments = std::move(expressions); + return ast; +} + +Ast wrap_if_several(std::vector &&ast, Ast(*wrapper)(std::vector)) +{ + if (ast.size() == 1) + return std::move(ast)[0]; + return wrapper(std::move(ast)); +} + bool operator==(Ast const& lhs, Ast const& rhs) { if (lhs.type != rhs.type) { @@ -151,6 +185,7 @@ bool operator==(Ast const& lhs, Ast const& rhs) && std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin()); case Ast::Type::Call: + case Ast::Type::Sequence: return lhs.arguments.size() == rhs.arguments.size() && std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin()); } diff --git a/src/tests/parser.cc b/src/tests/parser.cc index f230403..7bebfc9 100644 --- a/src/tests/parser.cc +++ b/src/tests/parser.cc @@ -109,4 +109,22 @@ suite parser_test = [] { ) })); }; + + "Sequence"_test = [] { + expect_ast("42; 101", Ast::sequence({ + Ast::literal({ Token::Type::Numeric, "42", {} }), + Ast::literal({ Token::Type::Numeric, "101", {} }) + })); + + expect_ast("say hello; say world", Ast::sequence({ + Ast::call({ + Ast::literal({ Token::Type::Symbol, "say", {} }), + Ast::literal({ Token::Type::Symbol, "hello", {} }) + }), + Ast::call({ + Ast::literal({ Token::Type::Symbol, "say", {} }), + Ast::literal({ Token::Type::Symbol, "world", {} }) + }) + })); + }; };