diff --git a/src/musique.hh b/src/musique.hh index 25d0bee..670d7a5 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -321,8 +321,9 @@ struct Ast { // Named constructors of AST structure static Ast binary(Token, Ast lhs, Ast rhs); - static Ast block(Location location, Ast seq = sequence({}), std::vector parameters = {}); + static Ast block(Location location, Ast seq = sequence({})); static Ast call(std::vector call); + static Ast lambda(Location location, Ast seq = sequence({}), std::vector parameters = {}); static Ast literal(Token); static Ast sequence(std::vector call); static Ast variable_declaration(Location loc, std::vector lvalues, std::optional rvalue); @@ -331,6 +332,7 @@ struct Ast { Binary, // Binary operator application like `1` + `2` Block, // Block expressions like `[42; hello]` + Lambda, // Block expression beeing functions like `[i|i+1]` Call, // Function call application like `print 42` Literal, // Compile time known constant like `c` or `1` Sequence, // Several expressions sequences like `42`, `42; 32` @@ -418,9 +420,19 @@ struct Number std::ostream& operator<<(std::ostream& os, Number const& num); struct Value; +struct Interpreter; using Function = std::function(std::vector)>; +struct Lambda +{ + Location location; + std::vector parameters; + Ast body; + + Result operator()(Interpreter &i, std::vector params); +}; + template constexpr auto is_one_of = (std::is_same_v || ...); diff --git a/src/parser.cc b/src/parser.cc index 0b431ee..b5e4161 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -102,18 +102,30 @@ Result Parser::parse_atomic_expression() auto start = token_id; std::vector parameters; + bool is_lambda = false; - if (auto p = parse_many(*this, &Parser::parse_identifier_with_trailing_separators, std::nullopt, At_Least::One); p && expect(Token::Type::Parameter_Separator)) { + if (expect(Token::Type::Parameter_Separator)) { consume(); - parameters = std::move(p).value(); + is_lambda = true; } else { - token_id = start; + auto p = parse_many(*this, &Parser::parse_identifier_with_trailing_separators, std::nullopt, At_Least::One); + if (p && expect(Token::Type::Parameter_Separator)) { + consume(); + parameters = std::move(p).value(); + is_lambda = true; + } else { + token_id = start; + } } return parse_sequence().and_then([&](Ast &&ast) -> Result { Try(ensure(Token::Type::Close_Block)); consume(); - return Ast::block(opening.location, std::move(ast), std::move(parameters)); + if (is_lambda) { + return Ast::lambda(opening.location, std::move(ast), std::move(parameters)); + } else { + return Ast::block(opening.location, std::move(ast)); + } }); } @@ -253,16 +265,25 @@ Ast Ast::sequence(std::vector expressions) return ast; } -Ast Ast::block(Location location, Ast seq, std::vector parameters) +Ast Ast::block(Location location, Ast seq) { Ast ast; ast.type = Type::Block; ast.location = location; - ast.arguments = std::move(parameters); ast.arguments.push_back(std::move(seq)); return ast; } +Ast Ast::lambda(Location location, Ast body, std::vector parameters) +{ + Ast ast; + ast.type = Type::Lambda; + ast.location = location; + ast.arguments = std::move(parameters); + ast.arguments.push_back(std::move(body)); + return ast; +} + Ast Ast::variable_declaration(Location loc, std::vector lvalues, std::optional rvalue) { Ast ast; @@ -300,6 +321,7 @@ bool operator==(Ast const& lhs, Ast const& rhs) case Ast::Type::Block: case Ast::Type::Call: + case Ast::Type::Lambda: case Ast::Type::Sequence: case Ast::Type::Variable_Declaration: return lhs.arguments.size() == rhs.arguments.size() @@ -315,6 +337,7 @@ std::ostream& operator<<(std::ostream& os, Ast::Type type) case Ast::Type::Binary: return os << "BINARY"; case Ast::Type::Block: return os << "BLOCK"; case Ast::Type::Call: return os << "CALL"; + case Ast::Type::Lambda: return os << "LAMBDA"; case Ast::Type::Literal: return os << "LITERAL"; case Ast::Type::Sequence: return os << "SEQUENCE"; case Ast::Type::Variable_Declaration: return os << "VAR"; diff --git a/src/tests/parser.cc b/src/tests/parser.cc index ec4c2c5..81e25b0 100644 --- a/src/tests/parser.cc +++ b/src/tests/parser.cc @@ -139,9 +139,9 @@ suite parser_test = [] { Ast::literal({ Token::Type::Symbol, "i", {} }), Ast::literal({ Token::Type::Symbol, "j", {} }), Ast::literal({ Token::Type::Symbol, "k", {} }) - }), {})); + }))); - expect_ast("[ i j k | i + j + k ]", Ast::block({}, Ast::binary( + expect_ast("[ i j k | i + j + k ]", Ast::lambda({}, Ast::binary( { Token::Type::Operator, "+", {} }, Ast::literal({ Token::Type::Symbol, "i", {} }), Ast::binary( @@ -155,7 +155,7 @@ suite parser_test = [] { Ast::literal({ Token::Type::Symbol, "k", {} }) })); - expect_ast("[ i; j; k | i + j + k ]", Ast::block({}, Ast::binary( + expect_ast("[ i; j; k | i + j + k ]", Ast::lambda({}, Ast::binary( { Token::Type::Operator, "+", {} }, Ast::literal({ Token::Type::Symbol, "i", {} }), Ast::binary( @@ -168,6 +168,8 @@ suite parser_test = [] { Ast::literal({ Token::Type::Symbol, "j", {} }), Ast::literal({ Token::Type::Symbol, "k", {} }) })); + + expect_ast("[|1]", Ast::lambda({}, Ast::literal({ Token::Type::Numeric, "1", {} }), {})); }; "Variable declarations"_test = [] {