diff --git a/docs/examples/enum.wtx b/docs/examples/enum.wtx new file mode 100644 index 0000000..deb1f30 --- /dev/null +++ b/docs/examples/enum.wtx @@ -0,0 +1,12 @@ + +type E = enum { + val_1, + val_2, +} + +let main = func() i32 { + type E2 = enum { + foo, + bar + } +} diff --git a/src/frontend/ast.h b/src/frontend/ast.h index 5518671..bbca7eb 100644 --- a/src/frontend/ast.h +++ b/src/frontend/ast.h @@ -20,6 +20,7 @@ namespace Winter { boolNode, caseNode, callNode, + enumNode, exprNode, forNode, funcNode, @@ -32,11 +33,55 @@ namespace Winter { returnNode, strLitNode, switchNode, + typeNode, varNode, error }; +} // namespace Winter + +template <> +struct std::formatter { + constexpr auto parse(std::format_parse_context& ctx) { + auto it = ctx.begin(); + if (it != ctx.end() && *it != '}') { + throw std::format_error("Invalid format specifier for NodeType"); + } + return it; + } + auto format(Winter::NodeType node, std::format_context& ctx) const { + switch (node) { + case Winter::NodeType::aliasNode: return std::format_to(ctx.out(), "aliasNode"); + case Winter::NodeType::typeAlias: return std::format_to(ctx.out(), "typeAlias"); + case Winter::NodeType::funcAlias: return std::format_to(ctx.out(), "funcAlias"); + case Winter::NodeType::argNode: return std::format_to(ctx.out(), "argNode"); + case Winter::NodeType::bodyNode: return std::format_to(ctx.out(), "bodyNode"); + case Winter::NodeType::boolNode: return std::format_to(ctx.out(), "boolNode"); + case Winter::NodeType::caseNode: return std::format_to(ctx.out(), "caseNode"); + case Winter::NodeType::callNode: return std::format_to(ctx.out(), "callNode"); + case Winter::NodeType::enumNode: return std::format_to(ctx.out(), "enumNode"); + case Winter::NodeType::exprNode: return std::format_to(ctx.out(), "exprNode"); + case Winter::NodeType::funcNode: return std::format_to(ctx.out(), "funcNode"); + case Winter::NodeType::forNode: return std::format_to(ctx.out(), "forNode"); + case Winter::NodeType::identNode: return std::format_to(ctx.out(), "identNode"); + case Winter::NodeType::ifNode: return std::format_to(ctx.out(), "ifNode"); + case Winter::NodeType::modNode: return std::format_to(ctx.out(), "modNode"); + case Winter::NodeType::letNode: return std::format_to(ctx.out(), "letNode"); + case Winter::NodeType::numlitNode: return std::format_to(ctx.out(), "numlitNode"); + case Winter::NodeType::paramNode: return std::format_to(ctx.out(), "paramNode"); + case Winter::NodeType::returnNode: return std::format_to(ctx.out(), "returnNode"); + case Winter::NodeType::strLitNode: return std::format_to(ctx.out(), "strLitNode"); + case Winter::NodeType::switchNode: return std::format_to(ctx.out(), "switchNode"); + case Winter::NodeType::typeNode: return std::format_to(ctx.out(), "typeNode"); + case Winter::NodeType::varNode: return std::format_to(ctx.out(), "varNode"); + case Winter::NodeType::error: return std::format_to(ctx.out(), "error"); + } + return std::format_to(ctx.out(), ""); + } +}; + +namespace Winter { struct aliasNode; struct typeAlias; struct funcAlias; @@ -44,6 +89,7 @@ namespace Winter { struct boolNode; struct bodyNode; struct caseNode; + struct enumNode; struct exprNode; struct forNode; struct funcNode; @@ -57,6 +103,7 @@ namespace Winter { struct returnNode; struct strLitNode; struct switchNode; + struct typeNode; struct varNode; struct TOMBSTONE { @@ -90,6 +137,7 @@ namespace Winter { bodyNode, boolNode, caseNode, + enumNode, exprNode, forNode, funcCallNode, @@ -103,6 +151,7 @@ namespace Winter { returnNode, strLitNode, switchNode, + typeNode, varNode, TOMBSTONE>; @@ -178,6 +227,14 @@ namespace Winter { } }; + struct enumNode { + int count; + + [[nodiscard]] std::string display() const { + return std::format("EnumNode[ count:{} ]", count); + } + }; + struct exprNode { int childCount; std::optional op; @@ -285,7 +342,15 @@ namespace Winter { [[nodiscard]] std::string display() const { return std::format( - "SwitchNode[ ident:{}, cases:{}, defaultCase:{}]", ident, caseCount, defaultCase); + "SwitchNode[ ident:{}, cases:{}, defaultCase:{} ]", ident, caseCount, defaultCase); + } + }; + + struct typeNode { + NodeType child; + + [[nodiscard]] std::string display() const { + return std::format("typeNode[ child:{} ]", child); } }; @@ -304,43 +369,4 @@ namespace Winter { } // namespace Winter -template <> -struct std::formatter { - constexpr auto parse(std::format_parse_context& ctx) { - auto it = ctx.begin(); - if (it != ctx.end() && *it != '}') { - throw std::format_error("Invalid format specifier for NodeType"); - } - return it; - } - - auto format(Winter::NodeType node, std::format_context& ctx) const { - switch (node) { - case Winter::NodeType::aliasNode: return std::format_to(ctx.out(), "aliasNode"); - case Winter::NodeType::typeAlias: return std::format_to(ctx.out(), "typeAlias"); - case Winter::NodeType::funcAlias: return std::format_to(ctx.out(), "funcAlias"); - case Winter::NodeType::argNode: return std::format_to(ctx.out(), "argNode"); - case Winter::NodeType::bodyNode: return std::format_to(ctx.out(), "bodyNode"); - case Winter::NodeType::boolNode: return std::format_to(ctx.out(), "boolNode"); - case Winter::NodeType::caseNode: return std::format_to(ctx.out(), "caseNode"); - case Winter::NodeType::callNode: return std::format_to(ctx.out(), "callNode"); - case Winter::NodeType::exprNode: return std::format_to(ctx.out(), "exprNode"); - case Winter::NodeType::funcNode: return std::format_to(ctx.out(), "funcNode"); - case Winter::NodeType::forNode: return std::format_to(ctx.out(), "forNode"); - case Winter::NodeType::identNode: return std::format_to(ctx.out(), "identNode"); - case Winter::NodeType::ifNode: return std::format_to(ctx.out(), "ifNode"); - case Winter::NodeType::modNode: return std::format_to(ctx.out(), "modNode"); - case Winter::NodeType::letNode: return std::format_to(ctx.out(), "letNode"); - case Winter::NodeType::numlitNode: return std::format_to(ctx.out(), "numlitNode"); - case Winter::NodeType::paramNode: return std::format_to(ctx.out(), "paramNode"); - case Winter::NodeType::returnNode: return std::format_to(ctx.out(), "returnNode"); - case Winter::NodeType::strLitNode: return std::format_to(ctx.out(), "strLitNode"); - case Winter::NodeType::switchNode: return std::format_to(ctx.out(), "switchNode"); - case Winter::NodeType::varNode: return std::format_to(ctx.out(), "varNode"); - case Winter::NodeType::error: return std::format_to(ctx.out(), "error"); - } - return std::format_to(ctx.out(), ""); - } -}; - #endif // WINTER_AST_H diff --git a/src/frontend/lexer.h b/src/frontend/lexer.h index dd70dd8..842f911 100644 --- a/src/frontend/lexer.h +++ b/src/frontend/lexer.h @@ -51,6 +51,7 @@ namespace Winter { kw_case, kw_class, kw_default, + kw_enum, kw_else, kw_fallthrough, kw_false, @@ -111,6 +112,7 @@ namespace Winter { {"case"sv, TokenType::kw_case}, {"class"sv, TokenType::kw_class}, {"default"sv, TokenType::kw_default}, + {"enum"sv, TokenType::kw_enum}, {"else"sv, TokenType::kw_else}, {"fallthrough"sv, TokenType::kw_fallthrough}, {"false"sv, TokenType::kw_false}, @@ -194,6 +196,7 @@ struct std::formatter { case Winter::TokenType::kw_case: return std::format_to(ctx.out(), "kw_case"); case Winter::TokenType::kw_class: return std::format_to(ctx.out(), "kw_class"); case Winter::TokenType::kw_default: return std::format_to(ctx.out(), "kw_default"); + case Winter::TokenType::kw_enum: return std::format_to(ctx.out(), "kw_enum"); case Winter::TokenType::kw_else: return std::format_to(ctx.out(), "kw_else"); case Winter::TokenType::kw_fallthrough: return std::format_to(ctx.out(), "kw_fallthrough"); diff --git a/src/frontend/parser.cpp b/src/frontend/parser.cpp index 85bc2b0..433dccc 100644 --- a/src/frontend/parser.cpp +++ b/src/frontend/parser.cpp @@ -118,45 +118,41 @@ namespace Winter { std::vector children = {}; while (!check(TokenType::rbrace)) { + Node_Result maybe_return = + std::unexpected(Error(ErrType::Parser, "Token not known in body")); + if (check(TokenType::kw_return)) { - Node_Result maybe_return = parseReturn(); - if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } - children.push_back(maybe_return.value()); + maybe_return = parseReturn(); } else if (check(TokenType::ident)) { - Node_Result maybe_return = parseCallOrVariable(); - if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } - children.push_back(maybe_return.value()); + maybe_return = parseCallOrVariable(); } else if (check(TokenType::kw_if)) { - Node_Result maybe_return = parseIf(); - if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } - children.push_back(maybe_return.value()); + maybe_return = parseIf(); } else if (check(TokenType::kw_let)) { Node_Result maybe_return = parseLet(false); if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } children.push_back(maybe_return.value()); consume(); // consume ';' + continue; } else if (check(TokenType::kw_for)) { - Node_Result maybe_return = parseFor(); - if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } - children.push_back(maybe_return.value()); + maybe_return = parseFor(); } else if (check(TokenType::kw_const)) { - Node_Result maybe_return = parseConst(); - if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } - children.push_back(maybe_return.value()); + maybe_return = parseConst(); } else if (check(TokenType::kw_switch)) { - Node_Result maybe_return = parseSwitch(); - if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } - children.push_back(maybe_return.value()); + maybe_return = parseSwitch(); - } else { - return std::unexpected(Error(ErrType::Parser, "Token not known in body")); + } else if (check(TokenType::kw_type)) { + maybe_return = parseType(); + consume(); // consume final rbrace } + + if (!maybe_return.has_value()) { return std::unexpected(maybe_return.error()); } + children.push_back(maybe_return.value()); } consume(); // consume '}' @@ -220,6 +216,29 @@ namespace Winter { } } + [[nodiscard]] Node_Result Parser::parseEnum() noexcept { + if (!check({TokenType::kw_enum})) { + return std::unexpected(Error(ErrType::Parser, "Unexpected token: expected kw_enum")); + } + + if (!consume({TokenType::lbrace})) { + return std::unexpected(Error(ErrType::Parser, "Unexpected token: expected lbrace")); + } + consume(); + + std::vector idents = {}; + while (!check(TokenType::rbrace)) { + std::string ident = current.toString(&L); + idents.push_back(Node(NodeType::identNode, identNode(ident))); + consume(); + + if (check(TokenType::comma)) { consume(); } + if (!check(TokenType::ident)) { break; } + } + + return Node(NodeType::enumNode, enumNode(static_cast(idents.size())), idents); + } + [[nodiscard]] Node_Result Parser::parseExpr(std::size_t min_bp) noexcept { Node lhs = Node::tombstone(); switch (current.type) { @@ -622,6 +641,34 @@ namespace Winter { cases); } + [[nodiscard]] Node_Result Parser::parseType() noexcept { + if (!check(TokenType::kw_type)) { + return std::unexpected(Error(ErrType::Parser, "Unexpected token: expected kw_type")); + } + consume(); + + const std::string name = current.toString(&L); + if (!consume({TokenType::op_equal})) { + return std::unexpected(Error(ErrType::Parser, "Unexpected token: No type body found")); + } + consume(); + + Node_Result body = Node::tombstone(); + NodeType childType; + + switch (current.type) { + case TokenType::kw_enum: + body = parseEnum(); + childType = NodeType::enumNode; + break; + default: return std::unexpected(Error(ErrType::Parser, "Unexpected type found")); + } + + if (!body.has_value()) { return std::unexpected(body.error()); } + + return Node(NodeType::typeNode, typeNode(childType), {body.value()}); + } + [[nodiscard]] Node_Result Parser::parseVariable() noexcept { return std::unexpected(Error(ErrType::NotImplemented, "parseVariable")); } @@ -631,26 +678,24 @@ namespace Winter { consume(); // start while (!check(TokenType::eof)) { + Node_Result expected = std::unexpected( + Error(ErrType::Parser, "Unexpected token found. Expected top-level keyword")); + if (current.type == TokenType::kw_let) { - Node_Result expected = parseLet(false); - if (!expected.has_value()) { return std::unexpected(expected.error()); } - code.push_back(expected.value()); + expected = parseLet(false); } else if (current.type == TokenType::kw_mod) { - Node_Result expected = parseMod(); - if (!expected.has_value()) { return std::unexpected(expected.error()); } - code.push_back(expected.value()); + expected = parseMod(); } else if (current.type == TokenType::kw_const) { - Node_Result expected = parseConst(); - if (!expected.has_value()) { return std::unexpected(expected.error()); } - code.push_back(expected.value()); + expected = parseConst(); } else if (current.type == TokenType::kw_alias) { - Node_Result expected = parseAlias(); - if (!expected.has_value()) { return std::unexpected(expected.error()); } - code.push_back(expected.value()); - } else { - return std::unexpected( - Error(ErrType::Parser, "Unexpected token found. Expected top-level keyword")); + expected = parseAlias(); + } else if (current.type == TokenType::kw_type) { + expected = parseType(); + consume(); } + + if (!expected.has_value()) { return std::unexpected(expected.error()); } + code.push_back(expected.value()); } return code; diff --git a/src/frontend/parser.h b/src/frontend/parser.h index e59173c..f4365ee 100644 --- a/src/frontend/parser.h +++ b/src/frontend/parser.h @@ -56,6 +56,7 @@ namespace Winter { [[nodiscard]] Node_Result parseCallOrVariable() noexcept; [[nodiscard]] Node_Result parseCase() noexcept; [[nodiscard]] Node_Result parseConst() noexcept; + [[nodiscard]] Node_Result parseEnum() noexcept; [[nodiscard]] Node_Result parseExpr(std::size_t) noexcept; [[nodiscard]] Node_Result parseFor() noexcept; [[nodiscard]] Node_Result parseFunc() noexcept; @@ -68,6 +69,7 @@ namespace Winter { [[nodiscard]] Node_Result parseReturn() noexcept; [[nodiscard]] Node_Result parseStrLit() noexcept; [[nodiscard]] Node_Result parseSwitch() noexcept; + [[nodiscard]] Node_Result parseType() noexcept; [[nodiscard]] Node_Result parseVariable() noexcept; [[nodiscard]] std::expected, Error> operator()(); diff --git a/tests/parser_test.h b/tests/parser_test.h index ed50dbc..8e4aa53 100644 --- a/tests/parser_test.h +++ b/tests/parser_test.h @@ -293,6 +293,33 @@ using namespace std::literals::string_view_literals; return 0; } +[[nodiscard]] int test_parser_parseEnum([[maybe_unused]] Willow::Test* test) noexcept { + Parser P("enum { val_1, val_2 }"sv); + P.consume(); + auto r = P.parseEnum(); + + if (!r.has_value()) { return 1; } + if (r.value().type != NodeType::enumNode) { return 2; } + + enumNode* node = std::get_if(&r.value().data); + if (node == nullptr) { return 3; } + if (node->count != 2) { return 4; } + if (r.value().children.size() != 2) { + test->alert("found: " + r.value().children.size()); + return 5; + } + + identNode* c1 = std::get_if(&r.value().children.at(0).data); + if (c1 == nullptr) { return 6; } + if (c1->value != "val_1") { return 7; } + + identNode* c2 = std::get_if(&r.value().children.at(1).data); + if (c2 == nullptr) { return 8; } + if (c2->value != "val_2") { return 9; } + + return 0; +} + [[nodiscard]] int test_parser_parseExpr([[maybe_unused]] Willow::Test* test) noexcept { Parser P("42;"sv); P.consume(); @@ -518,6 +545,21 @@ using namespace std::literals::string_view_literals; return 0; } +[[nodiscard]] int test_parser_parseType([[maybe_unused]] Willow::Test* test) noexcept { + Parser P("type E = enum { val_1 }"sv); + P.consume(); + auto r = P.parseType(); + + if (!r.has_value()) { return 1; } + if (r.value().type != NodeType::typeNode) { return 2; } + + typeNode* node = std::get_if(&r.value().data); + if (node == nullptr) { return 3; } + if (node->child != NodeType::enumNode) { return 4; } + + return 0; +} + [[nodiscard]] int test_parser_parseVariable([[maybe_unused]] Willow::Test* test) noexcept { Parser P("y"sv); P.consume(); diff --git a/tests/test.cpp b/tests/test.cpp index b6787d8..6bcdf2c 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -35,6 +35,7 @@ int main(int argc, char* argv[]) { {"parserParseBody", test_parser_parseBody}, {"parserParseCallOrVariable", test_parser_parseCallOrVariable}, {"parserParseCase", test_parser_parseCase}, + {"parserParseEnum", test_parser_parseEnum}, {"parserParseExpr", test_parser_parseExpr}, {"parserParseFor", test_parser_parseFor}, {"parserParseForEach", test_parser_parseForEach}, @@ -48,6 +49,7 @@ int main(int argc, char* argv[]) { {"parserParseReturn", test_parser_parseReturn}, {"parserParseSwitch", test_parser_parseSwitch}, {"parserParseStrLit", test_parser_parseStrLit}, + {"parserParseType", test_parser_parseType}, {"parserParseVariable", test_parser_parseVariable}, {"parserOperatorCall", test_parser_operatorCall},