Function calls
This commit is contained in:
parent
a25719c987
commit
555a35ecc0
4
Makefile
4
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 $@ $^
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<Ast> 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<Ast> parse(std::string_view source, std::string_view filename);
|
||||
|
||||
Result<Ast> parse_expression();
|
||||
Result<Ast> parse_binary_operator();
|
||||
Result<Ast> parse_literal();
|
||||
Result<Ast> parse_infix_expression();
|
||||
Result<Ast> parse_atomic_expression();
|
||||
|
||||
Result<Token> peek() const;
|
||||
Result<Token::Type> 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]]
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <musique.hh>
|
||||
#include <iostream>
|
||||
|
||||
Result<Ast> Parser::parse(std::string_view source, std::string_view filename)
|
||||
{
|
||||
@ -25,26 +26,39 @@ Result<Ast> Parser::parse(std::string_view source, std::string_view filename)
|
||||
|
||||
Result<Ast> Parser::parse_expression()
|
||||
{
|
||||
return parse_binary_operator();
|
||||
return parse_infix_expression();
|
||||
}
|
||||
|
||||
Result<Ast> Parser::parse_binary_operator()
|
||||
Result<Ast> Parser::parse_infix_expression()
|
||||
{
|
||||
return parse_literal().and_then([&](Ast lhs) -> tl::expected<Ast, Error> {
|
||||
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<Ast> atomics;
|
||||
Result<Ast> 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<Ast> Parser::parse_literal()
|
||||
Result<Ast> 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<Ast> Parser::parse_literal()
|
||||
});
|
||||
|
||||
default:
|
||||
unimplemented();
|
||||
return peek().and_then([](auto const& token) -> tl::expected<Ast, Error> {
|
||||
return tl::unexpected(errors::unexpected_token(token));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Result<Token> Parser::peek() const
|
||||
{
|
||||
return token_id >= tokens.size()
|
||||
? errors::unexpected_end_of_source(tokens.back().location)
|
||||
: Result<Token>(tokens[token_id]);
|
||||
}
|
||||
|
||||
Result<Token::Type> Parser::peek_type() const
|
||||
{
|
||||
return token_id >= tokens.size()
|
||||
? errors::unexpected_end_of_source(tokens.back().location)
|
||||
: Result<Token::Type>(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<Ast> 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();
|
||||
|
@ -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", {} })
|
||||
)
|
||||
})
|
||||
)
|
||||
}));
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user