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:
parent
583b20230d
commit
552cda58e7
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@ -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
1742
lib/src/Parser/AST/AST.cpp
Normal file
File diff suppressed because it is too large
Load Diff
755
lib/src/Parser/Declarations.cpp
Normal file
755
lib/src/Parser/Declarations.cpp
Normal 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
|
||||
0
lib/src/Parser/Expressions.cpp
Normal file
0
lib/src/Parser/Expressions.cpp
Normal file
0
lib/src/Parser/Literals.cpp
Normal file
0
lib/src/Parser/Literals.cpp
Normal 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
|
||||
0
lib/src/Parser/Statements.cpp
Normal file
0
lib/src/Parser/Statements.cpp
Normal file
299
lib/src/Parser/Types.cpp
Normal file
299
lib/src/Parser/Types.cpp
Normal 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
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user