Signed-off-by: erick-alcachofa <erick@artichoke.dev>
Implement support for object literals using a unified syntax for both
struct and slice initialization. Since the parser lacks the semantic
context to distinguish between a struct or a slice at this stage, both
are represented by the new `ObjectLiteral` AST node.
initialization within curly braces following a type expression:
* **Named Initializers**: Uses the `.field = value` syntax (e.g.,
`Point { .x = 10, .y = 20 }`).
* **Positional Initializers**: Uses a comma-separated list of
expressions (e.g., `[]i32 { 1, 2, 3 }`).
* Renamed `StructLiteral` and `SliceLiteral` nodes to `ObjectLiteral`.
* Refactored initialization helper nodes (e.g.,
`StructLiteralNamedFieldInit` is now `ObjectLiteralNamedFieldInit`).
* Unified the representation in `Expressions.hpp` and `Literals.hpp` to
use a single `ObjectLiteral` struct containing a `type` and an
optional `initializer`.
* Integrated the opening brace `{` (`opLSquirly`) as a high-precedence
postfix operator (binding power 19).
* Implemented parsing logic in `Expressions.cpp` to handle the
transition from a type expression to an object initializer.
* Updated `toDot` and `toString` visitors to handle the unified
`ObjectLiteral` nodes and their respective initializer variants.
* Improved robustness in `Declarations.cpp` by ensuring list parsing
correctly handles closing braces in specific edge cases.
823 lines
24 KiB
C++
823 lines
24 KiB
C++
//============================================================================//
|
|
// //
|
|
// artichoke programming language //
|
|
// //
|
|
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
|
// //
|
|
// //
|
|
// This program is free software: you can redistribute it and/or modify //
|
|
// it under the terms of the GNU Affero General Public License as published //
|
|
// by the Free Software Foundation, either version 3 of the License, or //
|
|
// (at your option) any later version. //
|
|
// //
|
|
// This program is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU Affero General Public License for more details. //
|
|
// //
|
|
// You should have received a copy of the GNU Affero General Public License //
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
|
// //
|
|
//============================================================================//
|
|
|
|
#include <artichoke/Parser/Parser.hpp>
|
|
|
|
namespace arti::lang {
|
|
|
|
Expected<ast::Optional<ast::TopLevelDeclNode>>
|
|
Parser::parseTopLevelDeclaration() {
|
|
bool exportable = false;
|
|
|
|
if (auto exported = matchAndConsume(TokenV::kwExport); ! exported) {
|
|
return Unexpected<>{ std::move(exported).error() };
|
|
}
|
|
else if (exported.value()) {
|
|
exportable = true;
|
|
}
|
|
|
|
auto 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"
|
|
);
|
|
}
|
|
|
|
if (auto node = parseImportDeclaration(); ! node) {
|
|
return Unexpected<>{ std::move(node).error() };
|
|
}
|
|
else {
|
|
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"
|
|
);
|
|
}
|
|
|
|
if (auto node = parseAliasDeclaration(); ! node) {
|
|
return Unexpected<>{ std::move(node).error() };
|
|
}
|
|
else {
|
|
return ast::TopLevelDeclNode{ std::move(node).value() };
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwModule) {
|
|
if (auto node = parseModuleDeclaration(); ! node) {
|
|
return Unexpected<>{ std::move(node).error() };
|
|
}
|
|
else {
|
|
(*node)->isExported = exportable;
|
|
return ast::TopLevelDeclNode{ std::move(node).value() };
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwStruct) {
|
|
if (auto node = parseStructDeclaration(); ! node) {
|
|
return Unexpected<>{ std::move(node).error() };
|
|
}
|
|
else {
|
|
(*node)->isExported = exportable;
|
|
return ast::TopLevelDeclNode{ std::move(node).value() };
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwEnum) {
|
|
if (auto node = parseEnumDeclaration(); ! node) {
|
|
return Unexpected<>{ std::move(node).error() };
|
|
}
|
|
else {
|
|
(*node)->isExported = exportable;
|
|
return ast::TopLevelDeclNode{ std::move(node).value() };
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwFn) {
|
|
if (auto node = parseFunctionDeclaration(); ! node) {
|
|
return Unexpected<>{ std::move(node).error() };
|
|
}
|
|
else {
|
|
(*node)->isExported = exportable;
|
|
return ast::TopLevelDeclNode{ std::move(node).value() };
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
Expected<ast::ImportDeclNode> Parser::parseImportDeclaration() {
|
|
auto node = ast::MakeNode<ast::ImportDeclNode>();
|
|
|
|
if (auto kw = consume(TokenV::kwImport, "'import' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto target = parseNamespacedIdentifier(); ! target) {
|
|
return Unexpected<>{ std::move(target).error() };
|
|
}
|
|
else {
|
|
node->importTarget = std::move(target).value();
|
|
}
|
|
|
|
if (auto acc = matchAndConsume(TokenV::opAccess); ! acc) {
|
|
return Unexpected<>{ std::move(acc).error() };
|
|
}
|
|
else if (acc.value()) {
|
|
if (auto star = matchAndConsume(TokenV::opStar); ! star) {
|
|
return Unexpected<>{ std::move(star).error() };
|
|
}
|
|
else if (star.value()) {
|
|
node->importAll = true;
|
|
}
|
|
else {
|
|
auto star = tokenizer.peek();
|
|
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
star->line,
|
|
star->column,
|
|
toString(*star),
|
|
"identifier or '*'"
|
|
);
|
|
}
|
|
}
|
|
|
|
if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) {
|
|
return Unexpected<>{ std::move(semicolon).error() };
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::AliasDeclNode> Parser::parseAliasDeclaration() {
|
|
auto node = ast::MakeNode<ast::AliasDeclNode>();
|
|
|
|
if (auto kw = consume(TokenV::kwUsing, "'using' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
node->alias = ident->strValue;
|
|
}
|
|
|
|
if (auto eq = consume(TokenV::opAssign, "'='"); ! eq) {
|
|
return Unexpected{ std::move(eq).error() };
|
|
}
|
|
|
|
if (auto type = parseType(); ! type) {
|
|
return Unexpected{ std::move(type).error() };
|
|
}
|
|
else {
|
|
node->target = std::move(type).value();
|
|
}
|
|
|
|
if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) {
|
|
return Unexpected<>{ std::move(semicolon).error() };
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::ModuleDeclNode> Parser::parseModuleDeclaration() {
|
|
auto node = ast::MakeNode<ast::ModuleDeclNode>();
|
|
auto decl = ast::Optional<ast::TopLevelDeclNode>{};
|
|
bool keepParsing = true;
|
|
|
|
if (auto kw = consume(TokenV::kwModule, "'module' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto name = parseNamespacedIdentifier(); ! name) {
|
|
return Unexpected<>{ std::move(name).error() };
|
|
}
|
|
else {
|
|
node->name = std::move(name).value();
|
|
}
|
|
|
|
if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) {
|
|
return Unexpected<>{ std::move(lsquirly).error() };
|
|
}
|
|
|
|
while (keepParsing) {
|
|
if (auto ok = parseTopLevelDeclaration(); ! ok) {
|
|
return Unexpected<>{ std::move(ok).error() };
|
|
}
|
|
else {
|
|
decl = std::move(ok).value();
|
|
|
|
if (! decl.has_value()) {
|
|
keepParsing = false;
|
|
continue;
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto rsquirly = consume(TokenV::opRSquirly, "'{'"); ! rsquirly) {
|
|
return Unexpected<>{ std::move(rsquirly).error() };
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::StructDeclNode> Parser::parseStructDeclaration() {
|
|
auto node = ast::MakeNode<ast::StructDeclNode>();
|
|
|
|
if (auto kw = consume(TokenV::kwStruct, "'struct' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) {
|
|
return Unexpected<>{ std::move(name).error() };
|
|
}
|
|
else {
|
|
node->name = name->strValue;
|
|
}
|
|
|
|
if (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) {
|
|
return Unexpected<>{ std::move(hasLt).error() };
|
|
}
|
|
else if (hasLt.value()) {
|
|
if (auto params = parseGenericParamsList(); ! params) {
|
|
return Unexpected<>{ std::move(params).error() };
|
|
}
|
|
else {
|
|
node->genericParams = std::move(params).value();
|
|
|
|
if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) {
|
|
return Unexpected<>{ std::move(hasGt ).error() };
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) {
|
|
return Unexpected<>{ std::move(lsquirly).error() };
|
|
}
|
|
|
|
if (auto members = parseStructMembersList(); ! members) {
|
|
return Unexpected<>{ std::move(members).error() };
|
|
}
|
|
else {
|
|
node->structMembers = std::move(members).value();
|
|
}
|
|
|
|
if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) {
|
|
return Unexpected<>{ std::move(rsquirly).error() };
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::EnumDeclNode> Parser::parseEnumDeclaration() {
|
|
auto node = ast::MakeNode<ast::EnumDeclNode>();
|
|
|
|
if (auto kw = consume(TokenV::kwEnum, "'enum' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) {
|
|
return Unexpected<>{ std::move(name).error() };
|
|
}
|
|
else {
|
|
node->name = name->strValue;
|
|
}
|
|
|
|
if (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) {
|
|
return Unexpected<>{ std::move(hasLt).error() };
|
|
}
|
|
else if (hasLt.value()) {
|
|
if (auto params = parseGenericParamsList(); ! params) {
|
|
return Unexpected<>{ std::move(params).error() };
|
|
}
|
|
else {
|
|
node->genericParams = std::move(params).value();
|
|
|
|
if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) {
|
|
return Unexpected<>{ std::move(hasGt ).error() };
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) {
|
|
return Unexpected<>{ std::move(lsquirly).error() };
|
|
}
|
|
|
|
if (auto members = parseEnumMembersList(); ! members) {
|
|
return Unexpected<>{ std::move(members).error() };
|
|
}
|
|
else {
|
|
node->enumMembers = std::move(members).value();
|
|
}
|
|
|
|
if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) {
|
|
return Unexpected<>{ std::move(rsquirly).error() };
|
|
}
|
|
|
|
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() };
|
|
}
|
|
|
|
bool keepParsing = true;
|
|
|
|
if (auto comma = tokenizer.peek();
|
|
comma and comma->value == TokenV::opComma) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
comma->line,
|
|
comma->column,
|
|
toString(*comma),
|
|
"'typename' keyword"
|
|
);
|
|
}
|
|
|
|
while (keepParsing) {
|
|
if (auto param = parseGenericParam(); ! param) {
|
|
return Unexpected{ std::move(param).error() };
|
|
}
|
|
else {
|
|
paramsList.push_back(std::move(param).value());
|
|
}
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected{ std::move(comma).error() };
|
|
}
|
|
else if (! comma.value()) {
|
|
if (peekToken = tokenizer.peek(); ! peekToken) {
|
|
return Unexpected{ std::move(peekToken).error() };
|
|
}
|
|
else {
|
|
if (peekToken->value != TokenV::opGt) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
peekToken->line,
|
|
peekToken->column,
|
|
toString(*peekToken),
|
|
"',' or '>'"
|
|
);
|
|
}
|
|
else {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return paramsList;
|
|
}
|
|
|
|
Expected<ast::GenericParamNode> Parser::parseGenericParam() {
|
|
auto node = ast::MakeNode<ast::GenericParamNode>();
|
|
|
|
if (auto kw = consume(TokenV::kwTypename, "'typename' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) {
|
|
return Unexpected{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
node->name = ident->strValue;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::Vector<ast::StructMemberNode>>
|
|
Parser::parseStructMembersList() {
|
|
auto membersList = ast::Vector<ast::StructMemberNode>{};
|
|
|
|
if (auto comma = tokenizer.peek();
|
|
comma and comma->value == TokenV::opComma) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
comma->line,
|
|
comma->column,
|
|
toString(*comma),
|
|
"identifier or '}'"
|
|
);
|
|
}
|
|
|
|
bool keepParsing = true;
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
keepParsing = false;
|
|
}
|
|
|
|
while (keepParsing) {
|
|
if (auto member = parseStructMember(); ! member) {
|
|
return Unexpected{ std::move(member).error() };
|
|
}
|
|
else {
|
|
membersList.push_back(std::move(member).value());
|
|
}
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected<>{ std::move(comma).error() };
|
|
}
|
|
else if (! comma.value()) {
|
|
if (auto peekToken = tokenizer.peek(); ! peekToken) {
|
|
return Unexpected{ std::move(peekToken).error() };
|
|
}
|
|
else {
|
|
if (peekToken->value != TokenV::opRSquirly) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
peekToken->line,
|
|
peekToken->column,
|
|
toString(*peekToken),
|
|
"',' or '}'"
|
|
);
|
|
}
|
|
else {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
|
|
return membersList;
|
|
}
|
|
|
|
Expected<ast::StructMemberNode> Parser::parseStructMember() {
|
|
auto node = ast::MakeNode<ast::StructMemberNode>();
|
|
|
|
if (auto ident = consume(TokenV::tkIdentifier, "'identifier'"); ! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
node->location = { ident->line, ident->column };
|
|
node->name = ident->strValue;
|
|
}
|
|
|
|
if (auto colon = consume(TokenV::opColon, "':'"); ! colon) {
|
|
return Unexpected{ std::move(colon).error() };
|
|
}
|
|
|
|
if (auto type = parseType(); ! type) {
|
|
return Unexpected{ std::move(type).error() };
|
|
}
|
|
else {
|
|
node->type = std::move(type).value();
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::Vector<ast::EnumMemberNode>> Parser::parseEnumMembersList() {
|
|
auto membersList = ast::Vector<ast::EnumMemberNode>{};
|
|
|
|
if (auto comma = tokenizer.peek();
|
|
comma and comma->value == TokenV::opComma) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
comma->line,
|
|
comma->column,
|
|
toString(*comma),
|
|
"identifier or '}'"
|
|
);
|
|
}
|
|
|
|
bool keepParsing = true;
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
keepParsing = false;
|
|
}
|
|
|
|
while (keepParsing) {
|
|
if (auto member = parseEnumMember(); ! member) {
|
|
return Unexpected{ std::move(member).error() };
|
|
}
|
|
else {
|
|
membersList.push_back(std::move(member).value());
|
|
}
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected<>{ std::move(comma).error() };
|
|
}
|
|
else if (! comma.value()) {
|
|
if (auto peekToken = tokenizer.peek(); ! peekToken) {
|
|
return Unexpected{ std::move(peekToken).error() };
|
|
}
|
|
else {
|
|
if (peekToken->value != TokenV::opRSquirly) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
peekToken->line,
|
|
peekToken->column,
|
|
toString(*peekToken),
|
|
"',' or '}'"
|
|
);
|
|
}
|
|
else {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
|
|
return membersList;
|
|
}
|
|
|
|
Expected<ast::EnumMemberNode> Parser::parseEnumMember() {
|
|
auto node = ast::MakeNode<ast::EnumMemberNode>();
|
|
|
|
if (auto ident = consume(TokenV::tkIdentifier, "'identifier'"); ! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
node->location = { ident->line, ident->column };
|
|
node->name = ident->strValue;
|
|
}
|
|
|
|
if (auto hasLParen = matchAndConsume(TokenV::opLParen); ! hasLParen) {
|
|
return Unexpected{ std::move(hasLParen).error() };
|
|
}
|
|
else if (hasLParen.value()) {
|
|
if (auto type = parseType(); ! type) {
|
|
return Unexpected{ std::move(type).error() };
|
|
}
|
|
else {
|
|
node->type = std::move(type).value();
|
|
|
|
if (auto hasRParen = consume(TokenV::opRParen, "')'"); ! hasRParen) {
|
|
return Unexpected{ std::move(hasRParen).error() };
|
|
}
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::FunctionDeclNode> Parser::parseFunctionDeclaration() {
|
|
auto node = ast::MakeNode<ast::FunctionDeclNode>();
|
|
|
|
if (auto kw = consume(TokenV::kwFn, "'fn' keyword"); ! kw) {
|
|
return Unexpected<>{ std::move(kw).error() };
|
|
}
|
|
else {
|
|
node->location = { kw->line, kw->column };
|
|
}
|
|
|
|
if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) {
|
|
return Unexpected<>{ std::move(name).error() };
|
|
}
|
|
else {
|
|
node->name = name->strValue;
|
|
}
|
|
|
|
if (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) {
|
|
return Unexpected<>{ std::move(hasLt).error() };
|
|
}
|
|
else if (hasLt.value()) {
|
|
if (auto params = parseGenericParamsList(); ! params) {
|
|
return Unexpected<>{ std::move(params).error() };
|
|
}
|
|
else {
|
|
node->genericParams = std::move(params).value();
|
|
|
|
if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) {
|
|
return Unexpected<>{ std::move(hasGt ).error() };
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto lparen = consume(TokenV::opLParen, "'('"); ! lparen) {
|
|
return Unexpected<>{ std::move(lparen).error() };
|
|
}
|
|
|
|
if (auto rparen = tokenizer.peek(); ! rparen) {
|
|
return Unexpected<>{ std::move(rparen).error() };
|
|
}
|
|
else if (rparen->value != TokenV::opRParen) {
|
|
if (auto params = parseFunctionParamsList(); ! params) {
|
|
return Unexpected<>{ std::move(params).error() };
|
|
}
|
|
else {
|
|
node->functionParams = std::move(params).value();
|
|
}
|
|
}
|
|
|
|
if (auto rparen = consume(TokenV::opRParen, "')'"); ! rparen) {
|
|
return Unexpected<>{ std::move(rparen).error() };
|
|
}
|
|
|
|
if (auto hasArrow = matchAndConsume(TokenV::opArrow); ! hasArrow) {
|
|
return Unexpected<>{ std::move(hasArrow).error() };
|
|
}
|
|
else if (hasArrow.value()) {
|
|
if (auto type = parseType(); ! type) {
|
|
return Unexpected<>{ std::move(type).error() };
|
|
}
|
|
else {
|
|
node->returnType = std::move(type).value();
|
|
}
|
|
}
|
|
|
|
if (auto body = parseCodeBlock(); ! body) {
|
|
return Unexpected<>{ std::move(body).error() };
|
|
}
|
|
else {
|
|
node->functionBody = std::move(body).value();
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::Vector<ast::FunctionParamNode>>
|
|
Parser::parseFunctionParamsList() {
|
|
auto params = ast::Vector<ast::FunctionParamNode>{};
|
|
|
|
if (auto comma = tokenizer.peek();
|
|
comma and comma->value == TokenV::opComma) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
comma->line,
|
|
comma->column,
|
|
toString(*comma),
|
|
"identifier, 'this' keyword or ')'"
|
|
);
|
|
}
|
|
|
|
if (auto hasThis = match(TokenV::kwThis); ! hasThis) {
|
|
return Unexpected<>{ std::move(hasThis).value() };
|
|
}
|
|
else if (hasThis.value()) {
|
|
auto thisParam = parseFunctionParamThis();
|
|
if (auto thisṔaram = parseFunctionParamThis(); ! thisParam) {
|
|
return Unexpected<>{ std::move(thisParam).error() };
|
|
}
|
|
else {
|
|
params.push_back(std::move(thisParam).value());
|
|
}
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected<>{ std::move(comma).error() };
|
|
}
|
|
}
|
|
|
|
bool keepParsing = true;
|
|
|
|
while (keepParsing) {
|
|
if (auto param = parseFunctionParam(); ! param) {
|
|
return Unexpected<>{ std::move(param).error() };
|
|
}
|
|
else {
|
|
params.push_back(std::move(param).value());
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected<>{ std::move(comma).error() };
|
|
}
|
|
|
|
if (auto peekToken = tokenizer.peek(); ! peekToken) {
|
|
return Unexpected{ std::move(peekToken).error() };
|
|
}
|
|
else {
|
|
if (peekToken->value == TokenV::opRParen) {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
Expected<ast::FunctionParamNode> Parser::parseFunctionParamThis() {
|
|
auto node = ast::MakeNode<ast::FunctionParamNode>();
|
|
|
|
if (auto thisToken = consume(TokenV::kwThis, "'this' keyword");
|
|
! thisToken) {
|
|
return Unexpected<>{ std::move(thisToken).error() };
|
|
}
|
|
else {
|
|
node->isThis = true;
|
|
node->location.line = thisToken->line;
|
|
node->location.column = thisToken->column;
|
|
node->name = "this";
|
|
|
|
if (auto type = parseType(); ! type) {
|
|
return Unexpected<>{ std::move(type).error() };
|
|
}
|
|
else {
|
|
node->type = std::move(type).value();
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::FunctionParamNode> Parser::parseFunctionParam() {
|
|
auto node = ast::MakeNode<ast::FunctionParamNode>();
|
|
|
|
node->isThis = false;
|
|
|
|
if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
node->name = ident->strValue;
|
|
node->location.line = ident->line;
|
|
node->location.column = ident->column;
|
|
}
|
|
|
|
if (auto colon = consume(TokenV::opColon, "':'"); ! colon) {
|
|
return Unexpected{ std::move(colon).error() };
|
|
}
|
|
|
|
if (auto type = parseType(); ! type) {
|
|
return Unexpected{ std::move(type).error() };
|
|
}
|
|
else {
|
|
node->type = std::move(type).value();
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
} // namespace arti::lang
|