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
|
doxygen
|
||||||
cd doc; $(MAKE) html
|
cd doc; $(MAKE) html
|
||||||
|
|
||||||
|
.PHONY: doc-open
|
||||||
|
doc-open: doc
|
||||||
|
xdg-open ./doc/build/html/index.html
|
||||||
|
|
||||||
bin/unit-tests: src/tests/*.cc $(Obj)
|
bin/unit-tests: src/tests/*.cc $(Obj)
|
||||||
g++ $(CXXFLAGS) $(CPPFLAGS) -o $@ $^
|
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));
|
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 errors::unexpected_token(Token::Type expected, Token const& unexpected)
|
||||||
{
|
{
|
||||||
Error err;
|
Error err;
|
||||||
|
@ -289,11 +289,13 @@ struct Ast
|
|||||||
// Named constructors of AST structure
|
// Named constructors of AST structure
|
||||||
static Ast literal(Token);
|
static Ast literal(Token);
|
||||||
static Ast binary(Token, Ast lhs, Ast rhs);
|
static Ast binary(Token, Ast lhs, Ast rhs);
|
||||||
|
static Ast call(std::vector<Ast> call);
|
||||||
|
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
Literal, // Compile time known constant like c or 1
|
Literal, // Compile time known constant like `c` or `1`
|
||||||
Binary // Binary operator application like 1 + 2
|
Binary, // Binary operator application like `1` + `2`
|
||||||
|
Call // Function call application like `print 42`
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
@ -314,9 +316,10 @@ struct Parser
|
|||||||
static Result<Ast> parse(std::string_view source, std::string_view filename);
|
static Result<Ast> parse(std::string_view source, std::string_view filename);
|
||||||
|
|
||||||
Result<Ast> parse_expression();
|
Result<Ast> parse_expression();
|
||||||
Result<Ast> parse_binary_operator();
|
Result<Ast> parse_infix_expression();
|
||||||
Result<Ast> parse_literal();
|
Result<Ast> parse_atomic_expression();
|
||||||
|
|
||||||
|
Result<Token> peek() const;
|
||||||
Result<Token::Type> peek_type() const;
|
Result<Token::Type> peek_type() const;
|
||||||
Token consume();
|
Token consume();
|
||||||
|
|
||||||
@ -334,6 +337,7 @@ namespace errors
|
|||||||
Error unrecognized_character(u32 invalid_character, Location location);
|
Error unrecognized_character(u32 invalid_character, Location location);
|
||||||
|
|
||||||
Error unexpected_token(Token::Type expected, Token const& unexpected);
|
Error unexpected_token(Token::Type expected, Token const& unexpected);
|
||||||
|
Error unexpected_token(Token const& unexpected);
|
||||||
Error unexpected_end_of_source(Location location);
|
Error unexpected_end_of_source(Location location);
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <musique.hh>
|
#include <musique.hh>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
Result<Ast> Parser::parse(std::string_view source, std::string_view filename)
|
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()
|
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> {
|
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)) {
|
if (expect(Token::Type::Operator)) {
|
||||||
auto op = consume();
|
auto op = consume();
|
||||||
return parse_expression().map([&](Ast rhs) {
|
return parse_expression().map([&](Ast rhs) {
|
||||||
return Ast::binary(std::move(op), std::move(lhs), std::move(rhs));
|
return Ast::binary(std::move(op), std::move(lhs), std::move(rhs));
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return lhs;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Ast> Parser::parse_literal()
|
Result<Ast> Parser::parse_atomic_expression()
|
||||||
{
|
{
|
||||||
switch (Try(peek_type())) {
|
switch (Try(peek_type())) {
|
||||||
|
case Token::Type::Symbol:
|
||||||
case Token::Type::Numeric:
|
case Token::Type::Numeric:
|
||||||
return Ast::literal(consume());
|
return Ast::literal(consume());
|
||||||
|
|
||||||
@ -57,15 +71,22 @@ Result<Ast> Parser::parse_literal()
|
|||||||
});
|
});
|
||||||
|
|
||||||
default:
|
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
|
Result<Token::Type> Parser::peek_type() const
|
||||||
{
|
{
|
||||||
return token_id >= tokens.size()
|
return peek().map([](Token const& token) { return token.type; });
|
||||||
? errors::unexpected_end_of_source(tokens.back().location)
|
|
||||||
: Result<Token::Type>(tokens[token_id].type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Parser::consume()
|
Token Parser::consume()
|
||||||
@ -105,6 +126,14 @@ Ast Ast::binary(Token token, Ast lhs, Ast rhs)
|
|||||||
return ast;
|
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)
|
bool operator==(Ast const& lhs, Ast const& rhs)
|
||||||
{
|
{
|
||||||
if (lhs.type != rhs.type) {
|
if (lhs.type != rhs.type) {
|
||||||
@ -120,6 +149,10 @@ bool operator==(Ast const& lhs, Ast const& rhs)
|
|||||||
&& lhs.token.source == rhs.token.source
|
&& lhs.token.source == rhs.token.source
|
||||||
&& lhs.arguments.size() == rhs.arguments.size()
|
&& lhs.arguments.size() == rhs.arguments.size()
|
||||||
&& std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin());
|
&& 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();
|
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