feat(Parser): Introduce AST toString and basic parser structure

Signed-off-by: erick-alcachofa <erick@artichoke.dev>

This commit introduces the foundational structure for the parser and
Abstract Syntax Tree (AST). It includes a new `Parser.hpp` header that
outlines the primary parsing functions for top-level declarations like
`modules`, `structs`, `enums`, and `functions`. It also adds a
`toString` function for the AST to aid in debugging and visualization.

The commit also updates the `Expected.hpp` utility by adding new error
codes like `ecUnexpectedToken`, `ecExpectedSemicolon`,
`ecImportInsideModule`, and `ecUnimplemented` to provide more granular
and descriptive parsing errors. The `Tokenizer` has been updated to use
these new, more specific exceptions.
This commit is contained in:
erick-alcachofa 2025-10-15 16:12:19 -06:00
parent 583b20230d
commit 552cda58e7
Signed by: me
GPG Key ID: 6FA5F8643444BAFA
11 changed files with 2957 additions and 20 deletions

View File

@ -28,4 +28,6 @@ namespace arti::lang::ast {
return std::make_unique<typename Node::element_type>();
}
std::string toString(const AST &tree, std::size_t padding = 0);
} // namespace arti::lang::ast

View File

@ -0,0 +1,78 @@
#pragma once
#include <artichoke/Parser/AST/AST.hpp>
#include <artichoke/Tokenizer/Tokenizer.hpp>
namespace arti::lang {
struct Parser {
Parser(std::string source) noexcept;
Parser(std::string unitName, std::string source) noexcept;
Parser(Parser &&) noexcept;
Parser &operator=(Parser &&) noexcept;
Parser(const Parser &) noexcept = delete;
Parser &operator=(const Parser &) noexcept = delete;
Expected<ast::AST> parse();
Expected<ast::Optional<ast::TopLevelDeclNode>>
parseTopLevelDeclaration();
Expected<ast::ImportDeclNode>
parseImportDeclaration();
Expected<ast::AliasDeclNode>
parseAliasDeclaration();
Expected<ast::ModuleDeclNode>
parseModuleDeclaration();
Expected<ast::StructDeclNode>
parseStructDeclaration();
Expected<ast::EnumDeclNode>
parseEnumDeclaration();
Expected<ast::Vector<ast::GenericParamNode>>
parseGenericParamsList();
Expected<ast::GenericParamNode>
parseGenericParam();
Expected<ast::Vector<ast::StructMemberNode>>
parseStructMembersList();
Expected<ast::StructMemberNode>
parseStructMember();
Expected<ast::Vector<ast::EnumMemberNode>>
parseEnumMembersList();
Expected<ast::EnumMemberNode>
parseEnumMember();
Expected<ast::FunctionDeclNode>
parseFunctionDeclaration();
Expected<ast::NamespacedIdentifierNode>
parseNamespacedIdentifier();
Expected<ast::TypeNode>
parseType();
Expected<ast::Vector<ast::TypeQualifier>>
parseTypeQualifiers();
Expected<ast::Vector<ast::TypeNode>>
parseGenericArgumentsList();
private:
std::string unitName;
std::string sourceCode;
Tokenizer tokenizer;
};
}

View File

@ -17,6 +17,10 @@ namespace arti::lang {
ecInvalidCharacter,
ecInvalidIndex,
ecInvalidComment,
ecUnexpectedToken,
ecExpectedSemicolon,
ecImportInsideModule,
ecUnimplemented,
};
struct Exception {
@ -71,6 +75,24 @@ namespace arti::lang {
else if constexpr (code == ecInvalidComment) {
return "Invalid comment found, missing '*/' end of comment";
}
else if constexpr (code == ecUnexpectedToken) {
return std::format(
"Found unexpected token '{}', expected {}",
std::forward<Args>(args)...
);
}
else if constexpr (code == ecExpectedSemicolon) {
return std::format(
"Expected ';', got '{}'",
std::forward<Args>(args)...
);
}
else if constexpr (code == ecImportInsideModule) {
return "Cannot use import statements inside a module declaration";
}
else if constexpr (code == ecUnimplemented) {
return "Unimplemented";
}
else {
return "Unknown error";
}

1742
lib/src/Parser/AST/AST.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,755 @@
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Expected<ast::Optional<ast::TopLevelDeclNode>>
Parser::parseTopLevelDeclaration() {
auto peekToken = tokenizer.peek();
bool exportable = false;
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
if (peekToken->value == TokenV::kwExport) {
exportable = true;
std::ignore = tokenizer.consume();
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
}
if (peekToken->value == TokenV::kwImport) {
if (exportable) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"exportable declaration, ie. Struct, Enum, Function, Module"
);
}
auto node = parseImportDeclaration();
if (! node) {
return Unexpected<>{ std::move(node).error() };
}
return ast::TopLevelDeclNode{ std::move(node).value() };
}
else if (peekToken->value == TokenV::kwUsing) {
if (exportable) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"exportable declaration, ie. Struct, Enum, Function, Module"
);
}
auto node = parseAliasDeclaration();
if (! node) {
return Unexpected<>{ std::move(node).error() };
}
return ast::TopLevelDeclNode{ std::move(node).value() };
}
else if (peekToken->value == TokenV::kwModule) {
auto node = parseModuleDeclaration();
if (! node) {
return Unexpected<>{ std::move(node).error() };
}
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
else if (peekToken->value == TokenV::kwStruct) {
auto node = parseStructDeclaration();
if (! node) {
return Unexpected<>{ std::move(node).error() };
}
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
else if (peekToken->value == TokenV::kwEnum) {
auto node = parseEnumDeclaration();
if (! node) {
return Unexpected<>{ std::move(node).error() };
}
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
else if (peekToken->value == TokenV::kwFn) {
auto node = parseFunctionDeclaration();
if (! node) {
return Unexpected<>{ std::move(node).error() };
}
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
else if (peekToken->value != TokenV::tkEOF) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"top level declaration"
);
}
return std::nullopt;
}
Expected<ast::ImportDeclNode> Parser::parseImportDeclaration() {
auto node = ast::MakeNode<ast::ImportDeclNode>();
node->importAll = false;
auto kw = tokenizer.peek();
if (! kw) {
return Unexpected<>{ std::move(kw).error() };
}
node->location.line = kw->line;
node->location.column = kw->column;
std::ignore = tokenizer.consume();
auto target = parseNamespacedIdentifier();
if (! target) {
return Unexpected<>{ std::move(target).error() };
}
node->importTarget = std::move(target).value();
auto peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
if (peekNext->value == TokenV::opAccess) {
auto peekStar = tokenizer.peek(1);
if (! peekStar) {
return Unexpected<>{ std::move(peekStar).error() };
}
if (peekStar->value != TokenV::opStar) {
return langException<ExceptCode::ecUnexpectedToken>(
peekStar->line,
peekStar->column,
toString(*peekStar),
"identifier or '*'"
);
}
node->importAll = true;
std::ignore = tokenizer.consume(2);
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
if (peekNext->value != TokenV::opSemicolon) {
return langException<ExceptCode::ecExpectedSemicolon>(
peekNext->line,
peekNext->column,
toString(*peekNext)
);
}
std::ignore = tokenizer.consume();
return node;
}
Expected<ast::AliasDeclNode> Parser::parseAliasDeclaration() {
auto node = ast::MakeNode<ast::AliasDeclNode>();
auto kw = tokenizer.peek();
if (! kw) {
return Unexpected<>{ std::move(kw).error() };
}
node->location.line = kw->line;
node->location.column = kw->column;
std::ignore = tokenizer.consume();
auto aliased = tokenizer.peek();
if (! aliased) {
return Unexpected<>{ std::move(aliased).error() };
}
if (aliased->value != TokenV::tkIdentifier) {
return langException<ExceptCode::ecUnexpectedToken>(
aliased->line,
aliased->column,
toString(*aliased),
"identifier"
);
}
std::ignore = tokenizer.consume();
node->alias = aliased->strValue;
auto eq = tokenizer.peekExpect(0, TokenV::opAssign);
if (! eq) {
return Unexpected{ std::move(eq).error() };
}
std::ignore = tokenizer.consume();
auto type = parseType();
if (! type) {
return Unexpected{ std::move(type).error() };
}
node->target = std::move(type).value();
auto semicolon = tokenizer.peekExpect(0, TokenV::opSemicolon);
if (! semicolon) {
return Unexpected{ std::move(semicolon).error() };
}
std::ignore = tokenizer.consume();
return node;
}
Expected<ast::ModuleDeclNode> Parser::parseModuleDeclaration() {
auto node = ast::MakeNode<ast::ModuleDeclNode>();
auto kw = tokenizer.peek();
if (! kw) {
return Unexpected<>{ std::move(kw).error() };
}
node->location.line = kw->line;
node->location.column = kw->column;
std::ignore = tokenizer.consume();
auto moduleName = parseNamespacedIdentifier();
if (! moduleName) {
return Unexpected<>{ std::move(moduleName).error() };
}
node->name = std::move(moduleName).value();
auto lsquirly = tokenizer.peekExpect(0, TokenV::opLSquirly);
if (! lsquirly) {
return Unexpected{ std::move(lsquirly).error() };
}
std::ignore = tokenizer.consume();
bool keepParsing = true;
auto decl = ast::Optional<ast::TopLevelDeclNode>{};
while (keepParsing) {
auto idecl = parseTopLevelDeclaration();
if (! idecl) {
return Unexpected<>{ std::move(idecl).error() };
}
decl = std::move(idecl).value();
if (! decl.has_value()) {
keepParsing = false;
}
else {
if (std::holds_alternative<ast::ModuleDeclNode>(*decl)) {
node->childModules.push_back(
std::get<ast::ModuleDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::StructDeclNode>(*decl)) {
node->innerDeclarations.push_back(
std::get<ast::StructDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::EnumDeclNode>(*decl)) {
node->innerDeclarations.push_back(
std::get<ast::EnumDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::FunctionDeclNode>(*decl)) {
node->innerDeclarations.push_back(
std::get<ast::FunctionDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::AliasDeclNode>(*decl)) {
node->aliasDeclarations.push_back(
std::get<ast::AliasDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::ImportDeclNode>(*decl)) {
auto importDecl = std::get<ast::ImportDeclNode>(std::move(*decl));
return langException<ExceptCode::ecImportInsideModule>(
importDecl->location.line,
importDecl->location.column
);
}
}
}
auto rsquirly = tokenizer.peekExpect(0, TokenV::opRSquirly);
if (! rsquirly) {
return Unexpected{ std::move(rsquirly).error() };
}
std::ignore = tokenizer.consume();
return node;
}
Expected<ast::StructDeclNode> Parser::parseStructDeclaration() {
auto node = ast::MakeNode<ast::StructDeclNode>();
auto kw = tokenizer.peek();
if (! kw) {
return Unexpected<>{ std::move(kw).error() };
}
node->location.line = kw->line;
node->location.column = kw->column;
std::ignore = tokenizer.consume();
auto name = tokenizer.peek();
if (! name) {
return Unexpected<>{ std::move(name).error() };
}
if (name->value != TokenV::tkIdentifier) {
return langException<ExceptCode::ecUnexpectedToken>(
name->line,
name->column,
toString(*name),
"identifier"
);
}
std::ignore = tokenizer.consume();
node->name = name->strValue;
auto peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
if (peekNext->value == TokenV::opLt) {
std::ignore = tokenizer.consume();
auto generics = parseGenericParamsList();
if (! generics) {
return Unexpected<>{ std::move(generics).error() };
}
node->genericParams = std::move(*generics);
if (auto closeG = tokenizer.peekExpect(0, TokenV::opGt); ! closeG) {
return Unexpected{ std::move(closeG).error() };
}
std::ignore = tokenizer.consume();
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
if (peekNext->value != TokenV::opLSquirly) {
return langException<ExceptCode::ecUnexpectedToken>(
peekNext->line,
peekNext->column,
toString(*peekNext),
"'{'"
);
}
std::ignore = tokenizer.consume();
auto members = parseStructMembersList();
if (! members) {
return Unexpected<>{ std::move(members).error() };
}
node->structMembers = std::move(members).value();
if (auto closeS = tokenizer.peekExpect(0, TokenV::opRSquirly); ! closeS) {
return Unexpected{ std::move(closeS).error() };
}
std::ignore = tokenizer.consume();
return node;
}
Expected<ast::EnumDeclNode> Parser::parseEnumDeclaration() {
auto node = ast::MakeNode<ast::EnumDeclNode>();
auto kw = tokenizer.peek();
if (! kw) {
return Unexpected<>{ std::move(kw).error() };
}
node->location.line = kw->line;
node->location.column = kw->column;
std::ignore = tokenizer.consume();
auto name = tokenizer.peek();
if (! name) {
return Unexpected<>{ std::move(name).error() };
}
if (name->value != TokenV::tkIdentifier) {
return langException<ExceptCode::ecUnexpectedToken>(
name->line,
name->column,
toString(*name),
"identifier"
);
}
std::ignore = tokenizer.consume();
node->name = name->strValue;
auto peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
if (peekNext->value == TokenV::opLt) {
std::ignore = tokenizer.consume();
auto generics = parseGenericParamsList();
if (! generics) {
return Unexpected<>{ std::move(generics).error() };
}
node->genericParams = std::move(*generics);
if (auto closeG = tokenizer.peekExpect(0, TokenV::opGt); ! closeG) {
return Unexpected{ std::move(closeG).error() };
}
std::ignore = tokenizer.consume();
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
if (peekNext->value != TokenV::opLSquirly) {
return langException<ExceptCode::ecUnexpectedToken>(
peekNext->line,
peekNext->column,
toString(*peekNext),
"'{'"
);
}
std::ignore = tokenizer.consume();
auto members = parseEnumMembersList();
if (! members) {
return Unexpected<>{ std::move(members).error() };
}
node->enumMembers = std::move(members).value();
if (auto closeS = tokenizer.peekExpect(0, TokenV::opRSquirly); ! closeS) {
return Unexpected{ std::move(closeS).error() };
}
std::ignore = tokenizer.consume();
return node;
}
Expected<ast::Vector<ast::GenericParamNode>>
Parser::parseGenericParamsList() {
auto paramsList = ast::Vector<ast::GenericParamNode>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
while (peekToken->value != TokenV::opGt) {
auto param = parseGenericParam();
if (! param) {
return Unexpected{ std::move(param).error() };
}
paramsList.push_back(std::move(param).value());
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
}
return paramsList;
}
Expected<ast::GenericParamNode> Parser::parseGenericParam() {
auto node = ast::MakeNode<ast::GenericParamNode>();
if (auto kwTypename = tokenizer.peekExpect(0, TokenV::kwTypename);
! kwTypename) {
return Unexpected{ std::move(kwTypename).error() };
}
else {
node->location = { .line = kwTypename->line,
.column = kwTypename->column };
}
std::ignore = tokenizer.consume();
if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) {
return Unexpected{ std::move(ident).error() };
}
else {
node->name = ident->strValue;
}
std::ignore = tokenizer.consume();
auto sc = tokenizer.peek();
if (! sc) {
return Unexpected{ std::move(sc).error() };
}
if (sc->value == TokenV::opComma) {
std::ignore = tokenizer.consume();
}
else if (sc->value != TokenV::opGt) {
return langException<ExceptCode::ecUnexpectedToken>(
sc->line,
sc->column,
toString(*sc),
"'}' or ','"
);
}
return node;
}
Expected<ast::Vector<ast::StructMemberNode>>
Parser::parseStructMembersList() {
auto membersList = ast::Vector<ast::StructMemberNode>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
while (peekToken->value != TokenV::opRSquirly) {
auto member = parseStructMember();
if (! member) {
return Unexpected{ std::move(member).error() };
}
membersList.push_back(std::move(member).value());
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
}
return membersList;
}
Expected<ast::StructMemberNode> Parser::parseStructMember() {
auto node = ast::MakeNode<ast::StructMemberNode>();
if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) {
return Unexpected{ std::move(ident).error() };
}
else {
node->location = { .line = ident->line, .column = ident->column };
node->name = ident->strValue;
}
std::ignore = tokenizer.consume();
if (auto colon = tokenizer.peekExpect(0, TokenV::opColon); ! colon) {
return Unexpected{ std::move(colon).error() };
}
std::ignore = tokenizer.consume();
auto type = parseType();
if (! type) {
return Unexpected{ std::move(type).error() };
}
node->type = std::move(type).value();
auto sc = tokenizer.peek();
if (! sc) {
return Unexpected{ std::move(sc).error() };
}
if (sc->value == TokenV::opComma) {
std::ignore = tokenizer.consume();
}
else if (sc->value != TokenV::opRSquirly) {
return langException<ExceptCode::ecUnexpectedToken>(
sc->line,
sc->column,
toString(*sc),
"'}' or ','"
);
}
return node;
}
Expected<ast::Vector<ast::EnumMemberNode>> Parser::parseEnumMembersList() {
auto membersList = ast::Vector<ast::EnumMemberNode>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
while (peekToken->value != TokenV::opRSquirly) {
auto member = parseEnumMember();
if (! member) {
return Unexpected{ std::move(member).error() };
}
membersList.push_back(std::move(member).value());
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
}
return membersList;
}
Expected<ast::EnumMemberNode> Parser::parseEnumMember() {
auto node = ast::MakeNode<ast::EnumMemberNode>();
if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) {
return Unexpected{ std::move(ident).error() };
}
else {
node->location = { .line = ident->line, .column = ident->column };
node->name = ident->strValue;
}
std::ignore = tokenizer.consume();
auto sc = tokenizer.peek();
if (! sc) {
return Unexpected{ std::move(sc).error() };
}
if (sc->value == TokenV::opLParen) {
std::ignore = tokenizer.consume();
auto type = parseType();
if (! type) {
return Unexpected{ std::move(type).error() };
}
node->type = std::move(type).value();
if (auto closeP = tokenizer.peekExpect(0, TokenV::opRParen); ! closeP) {
return Unexpected{ std::move(closeP).error() };
}
std::ignore = tokenizer.consume();
sc = tokenizer.peek();
if (! sc) {
return Unexpected{ std::move(sc).error() };
}
}
if (sc->value == TokenV::opComma) {
std::ignore = tokenizer.consume();
}
else if (sc->value != TokenV::opRSquirly) {
return langException<ExceptCode::ecUnexpectedToken>(
sc->line,
sc->column,
toString(*sc),
"'}' or ','"
);
}
return node;
}
Expected<ast::FunctionDeclNode> Parser::parseFunctionDeclaration() {
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
return langException<ExceptCode::ecUnimplemented>(
peekToken->line,
peekToken->column
);
}
} // namespace arti::lang

View File

View File

View File

@ -0,0 +1,47 @@
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Parser::Parser(std::string source) noexcept
: unitName{}
, sourceCode{ source }
, tokenizer{ source } { }
Parser::Parser(std::string unitName, std::string source) noexcept
: unitName{ unitName }
, sourceCode{ source }
, tokenizer{ source } { }
Expected<ast::AST> Parser::parse() {
auto unit = ast::MakeNode<ast::AST>();
auto tlDecl = ast::Optional<ast::TopLevelDeclNode>{};
bool keepParsing = true;
unit->unitName = this->unitName;
while (keepParsing) {
if (auto ok = parseTopLevelDeclaration(); ok) {
tlDecl = std::move(ok).value();
if (! tlDecl.has_value()) {
keepParsing = false;
}
else {
unit->declarations.push_back(std::move(tlDecl).value());
}
}
else {
return Unexpected<>{ std::move(ok).error() };
}
}
auto eof = tokenizer.peekExpect(0, TokenV::tkEOF);
if (! eof) {
return Unexpected<>{ std::move(eof).error() };
}
return unit;
}
} // namespace arti::lang

View File

299
lib/src/Parser/Types.cpp Normal file
View File

@ -0,0 +1,299 @@
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Expected<ast::NamespacedIdentifierNode> Parser::parseNamespacedIdentifier() {
auto node = ast::MakeNode<ast::NamespacedIdentifierNode>();
auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier);
if (! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
node->location = { .line = ident->line, .column = ident->column };
node->identParts.emplace_back(ident->strValue);
}
std::ignore = tokenizer.consume();
auto peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
while (peekNext->value == TokenV::opAccess) {
std::ignore = tokenizer.consume();
ident = tokenizer.peekExpect(0, TokenV::tkIdentifier);
if (! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
node->identParts.emplace_back(ident->strValue);
}
std::ignore = tokenizer.consume();
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
return node;
}
Expected<ast::TypeNode> Parser::parseType() {
auto node = ast::MakeNode<ast::TypeNode>();
if (auto peekNext = tokenizer.peek(); ! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
else {
node->location = { .line = peekNext->line, .column = peekNext->column };
if (peekNext->value != TokenV::tkIdentifier) {
auto qualifiers = parseTypeQualifiers();
if (! qualifiers) {
return Unexpected<>{ std::move(qualifiers).error() };
}
node->qualifiers = std::move(qualifiers).value();
}
}
auto identType = parseNamespacedIdentifier();
if (! identType) {
return Unexpected<>{ std::move(identType).error() };
}
auto currentNode = ast::TypeExpressionNode{};
currentNode = ast::MakeNode<ast::IdentifierTypeNode>();
std::get<ast::IdentifierTypeNode>(currentNode)->location =
(*identType)->location;
std::get<ast::IdentifierTypeNode>(currentNode)->typeName =
std::move(identType).value();
auto peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
if (peekNext->value == TokenV::opLt) {
std::ignore = tokenizer.consume();
auto args = parseGenericArgumentsList();
if (! args) {
return Unexpected<>{ std::move(args).error() };
}
if (auto closeG = tokenizer.peekExpect(0, TokenV::opGt); ! closeG) {
return Unexpected<>{ std::move(closeG).error() };
}
std::ignore = tokenizer.consume();
auto newNode = ast::MakeNode<ast::GenericTypeNode>();
newNode->location = std::visit(
[](const auto &node) { return node->location; },
currentNode
);
newNode->baseType = std::move(currentNode);
newNode->genericArgs = std::move(args).value();
currentNode = std::move(newNode);
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
while (peekNext->value == TokenV::opAccess) {
std::ignore = tokenizer.consume();
auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier);
if (! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
std::ignore = tokenizer.consume();
auto newNode = ast::MakeNode<ast::NamespacedTypeNode>();
newNode->location = std::visit(
[](const auto &node) { return node->location; },
currentNode
);
newNode->typeName = ident->strValue;
newNode->baseType = std::move(currentNode);
currentNode = std::move(newNode);
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
if (peekNext->value == TokenV::opLt) {
std::ignore = tokenizer.consume();
auto args = parseGenericArgumentsList();
if (! args) {
return Unexpected<>{ std::move(args).error() };
}
if (auto closeG = tokenizer.peekExpect(0, TokenV::opGt); ! closeG) {
return Unexpected<>{ std::move(closeG).error() };
}
std::ignore = tokenizer.consume();
auto newNode = ast::MakeNode<ast::GenericTypeNode>();
newNode->location = std::visit(
[](const auto &node) { return node->location; },
currentNode
);
newNode->baseType = std::move(currentNode);
newNode->genericArgs = std::move(args).value();
currentNode = std::move(newNode);
peekNext = tokenizer.peek();
if (! peekNext) {
return Unexpected<>{ std::move(peekNext).error() };
}
}
}
node->baseType = std::move(currentNode);
return node;
}
Expected<ast::Vector<ast::TypeQualifier>> Parser::parseTypeQualifiers() {
auto qualifs = ast::Vector<ast::TypeQualifier>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
enum { None, AfterOptional, AfterMutable } state = None;
while (true) {
switch (peekToken->value) {
using enum TokenV;
case opStar:
qualifs.push_back(ast::TypeQualifier::Pointer);
state = None;
break;
case opMut:
if (state == AfterMutable) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"non mutable type qualifier, i.e. any of ( *, ?, [] )"
);
}
qualifs.push_back(ast::TypeQualifier::Mutable);
state = AfterMutable;
break;
case opOpt:
if (state == AfterOptional) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"non optional type qualifier, i.e. any of ( *, $, [] )"
);
}
qualifs.push_back(ast::TypeQualifier::Optional);
state = AfterOptional;
break;
case opLBracket:
std::ignore = tokenizer.consume();
peekToken = tokenizer.peekExpect(0, opRBracket);
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
qualifs.push_back(ast::TypeQualifier::Slice);
state = None;
break;
default:
return qualifs;
}
std::ignore = tokenizer.consume();
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
}
return qualifs;
}
Expected<ast::Vector<ast::TypeNode>> Parser::parseGenericArgumentsList() {
auto args = ast::Vector<ast::TypeNode>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
while (peekToken->value != TokenV::opGt) {
auto type = parseType();
if (! type) {
return Unexpected<>{ std::move(type).error() };
}
args.push_back(std::move(type).value());
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
if (peekToken->value == TokenV::opComma) {
std::ignore = tokenizer.consume();
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
}
}
return args;
}
} // namespace arti::lang

View File

@ -112,16 +112,12 @@ namespace arti::lang {
auto tokenAt = tokensBuffer.at(n);
if (tokenAt.value != tokenType) {
return Unexpected<> {
Exception{
.line = tokenAt.line,
.column = tokenAt.column,
.message = std::format(
"Expected token of type {}, got {}",
toString(tokenType), toString(tokenAt)
)
}
};
return langException<ExceptCode::ecUnexpectedToken>(
tokenAt.line,
tokenAt.column,
toString(tokenAt),
toString(tokenType)
);
}
return tokenAt;
@ -147,16 +143,12 @@ namespace arti::lang {
}
if (token->value != tokenType) {
return Unexpected<> {
Exception{
.line = token->line,
.column = token->column,
.message = std::format(
"Expected token of type {}, got {}",
toString(tokenType), toString(*token)
)
}
};
return langException<ExceptCode::ecUnexpectedToken>(
token->line,
token->column,
toString(*token),
toString(tokenType)
);
}
return *token;