Binary operator's parsing
This commit is contained in:
parent
655cc57cde
commit
f17f3dc49f
@ -288,16 +288,22 @@ struct Ast
|
||||
{
|
||||
// Named constructors of AST structure
|
||||
static Ast literal(Token);
|
||||
static Ast binary(Token, Ast lhs, Ast rhs);
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Literal
|
||||
Literal, // Compile time known constant like c or 1
|
||||
Binary // Binary operator application like 1 + 2
|
||||
};
|
||||
|
||||
Type type;
|
||||
Token token;
|
||||
std::vector<Ast> arguments;
|
||||
};
|
||||
|
||||
bool operator==(Ast const& lhs, Ast const& rhs);
|
||||
std::ostream& operator<<(std::ostream& os, Ast const& tree);
|
||||
|
||||
struct Parser
|
||||
{
|
||||
std::vector<Token> tokens;
|
||||
|
@ -30,7 +30,16 @@ Result<Ast> Parser::parse_expression()
|
||||
|
||||
Result<Ast> Parser::parse_binary_operator()
|
||||
{
|
||||
return parse_literal();
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Result<Ast> Parser::parse_literal()
|
||||
@ -61,7 +70,50 @@ Result<void> Parser::ensure(Token::Type type) const
|
||||
Ast Ast::literal(Token token)
|
||||
{
|
||||
Ast ast;
|
||||
ast.type = Ast::Type::Literal;
|
||||
ast.type = Type::Literal;
|
||||
ast.token = std::move(token);
|
||||
return ast;
|
||||
}
|
||||
|
||||
Ast Ast::binary(Token token, Ast lhs, Ast rhs)
|
||||
{
|
||||
Ast ast;
|
||||
ast.type = Type::Binary;
|
||||
ast.token = std::move(token);
|
||||
ast.arguments.push_back(std::move(lhs));
|
||||
ast.arguments.push_back(std::move(rhs));
|
||||
return ast;
|
||||
}
|
||||
|
||||
bool operator==(Ast const& lhs, Ast const& rhs)
|
||||
{
|
||||
if (lhs.type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (lhs.type) {
|
||||
case Ast::Type::Literal:
|
||||
return lhs.token.type == rhs.token.type && lhs.token.source == rhs.token.source;
|
||||
|
||||
case Ast::Type::Binary:
|
||||
return lhs.token.type == rhs.token.type
|
||||
&& lhs.token.source == rhs.token.source
|
||||
&& lhs.arguments.size() == rhs.arguments.size()
|
||||
&& std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin());
|
||||
}
|
||||
|
||||
unreachable();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Ast const& tree)
|
||||
{
|
||||
os << "Ast(" << tree.token.source << ")";
|
||||
if (!tree.arguments.empty()) {
|
||||
os << " { ";
|
||||
for (auto const& arg : tree.arguments) {
|
||||
os << arg << ' ';
|
||||
}
|
||||
os << '}';
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
@ -3,9 +3,57 @@
|
||||
|
||||
using namespace boost::ut;
|
||||
|
||||
void expect_ast(
|
||||
std::string_view source,
|
||||
Ast const& expected,
|
||||
reflection::source_location sl = reflection::source_location::current())
|
||||
{
|
||||
auto result = Parser::parse(source, "test");
|
||||
expect(result.has_value(), sl) << "code was expect to parse, but had not";
|
||||
expect(eq(*result, expected)) << "parser yielded unexpected tree";
|
||||
}
|
||||
|
||||
suite parser_test = [] {
|
||||
"Literal parsing"_test = [] {
|
||||
auto result = Parser::parse("1", "test");
|
||||
expect(result.has_value()) << "code was expected to parse, but had not";
|
||||
expect_ast("1", Ast::literal(Token { Token::Type::Numeric, "1", {} }));
|
||||
};
|
||||
|
||||
"Binary opreator parsing"_test = [] {
|
||||
expect_ast("1 + 2", Ast::binary(
|
||||
Token { Token::Type::Operator, "+", {} },
|
||||
Ast::literal({ Token::Type::Numeric, "1", {} }),
|
||||
Ast::literal({ Token::Type::Numeric, "2", {} })
|
||||
));
|
||||
|
||||
expect_ast("100 * 200", Ast::binary(
|
||||
Token { Token::Type::Operator, "*", {} },
|
||||
Ast::literal({ Token::Type::Numeric, "100", {} }),
|
||||
Ast::literal({ Token::Type::Numeric, "200", {} })
|
||||
));
|
||||
|
||||
expect_ast("101 + 202 * 303", Ast::binary(
|
||||
Token { Token::Type::Operator, "+", {} },
|
||||
Ast::literal({ Token::Type::Numeric, "101", {} }),
|
||||
Ast::binary(
|
||||
Token { Token::Type::Operator, "*", {} },
|
||||
Ast::literal({ Token::Type::Numeric, "202", {} }),
|
||||
Ast::literal({ Token::Type::Numeric, "303", {} })
|
||||
)
|
||||
));
|
||||
|
||||
};
|
||||
|
||||
// This test shouldn't be skipped since language will support precedense.
|
||||
// It stays here as reminder that this feature should be implemented.
|
||||
skip / "Binary operator precedense"_test = [] {
|
||||
expect_ast("101 * 202 + 303", Ast::binary(
|
||||
Token { Token::Type::Operator, "+", {} },
|
||||
Ast::binary(
|
||||
Token { Token::Type::Operator, "*", {} },
|
||||
Ast::literal({ Token::Type::Numeric, "101", {} }),
|
||||
Ast::literal({ Token::Type::Numeric, "202", {} })
|
||||
),
|
||||
Ast::literal({ Token::Type::Numeric, "303", {} })
|
||||
));
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user