From 552cda58e7ee09afc0ccf6ef79ea2b589cdf2cdd Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Wed, 15 Oct 2025 16:12:19 -0600 Subject: [PATCH 01/15] feat(Parser): Introduce AST toString and basic parser structure Signed-off-by: erick-alcachofa 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. --- lib/include/artichoke/Parser/AST/AST.hpp | 2 + lib/include/artichoke/Parser/Parser.hpp | 78 + lib/include/artichoke/Util/Expected.hpp | 22 + lib/src/Parser/AST/AST.cpp | 1742 ++++++++++++++++++++++ lib/src/Parser/Declarations.cpp | 755 ++++++++++ lib/src/Parser/Expressions.cpp | 0 lib/src/Parser/Literals.cpp | 0 lib/src/Parser/Parser.cpp | 47 + lib/src/Parser/Statements.cpp | 0 lib/src/Parser/Types.cpp | 299 ++++ lib/src/Tokenizer/Tokenizer.cpp | 32 +- 11 files changed, 2957 insertions(+), 20 deletions(-) create mode 100644 lib/src/Parser/AST/AST.cpp create mode 100644 lib/src/Parser/Declarations.cpp create mode 100644 lib/src/Parser/Expressions.cpp create mode 100644 lib/src/Parser/Literals.cpp create mode 100644 lib/src/Parser/Statements.cpp create mode 100644 lib/src/Parser/Types.cpp diff --git a/lib/include/artichoke/Parser/AST/AST.hpp b/lib/include/artichoke/Parser/AST/AST.hpp index c44d640..4b41795 100644 --- a/lib/include/artichoke/Parser/AST/AST.hpp +++ b/lib/include/artichoke/Parser/AST/AST.hpp @@ -28,4 +28,6 @@ namespace arti::lang::ast { return std::make_unique(); } + std::string toString(const AST &tree, std::size_t padding = 0); + } // namespace arti::lang::ast diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index e69de29..9539643 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +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 parse(); + + Expected> + parseTopLevelDeclaration(); + + Expected + parseImportDeclaration(); + + Expected + parseAliasDeclaration(); + + Expected + parseModuleDeclaration(); + + Expected + parseStructDeclaration(); + + Expected + parseEnumDeclaration(); + + Expected> + parseGenericParamsList(); + + Expected + parseGenericParam(); + + Expected> + parseStructMembersList(); + + Expected + parseStructMember(); + + Expected> + parseEnumMembersList(); + + Expected + parseEnumMember(); + + Expected + parseFunctionDeclaration(); + + Expected + parseNamespacedIdentifier(); + + Expected + parseType(); + + Expected> + parseTypeQualifiers(); + + Expected> + parseGenericArgumentsList(); + + private: + std::string unitName; + std::string sourceCode; + Tokenizer tokenizer; + }; + +} diff --git a/lib/include/artichoke/Util/Expected.hpp b/lib/include/artichoke/Util/Expected.hpp index 2c2a20b..d439e41 100644 --- a/lib/include/artichoke/Util/Expected.hpp +++ b/lib/include/artichoke/Util/Expected.hpp @@ -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)... + ); + } + else if constexpr (code == ecExpectedSemicolon) { + return std::format( + "Expected ';', got '{}'", + std::forward(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"; } diff --git a/lib/src/Parser/AST/AST.cpp b/lib/src/Parser/AST/AST.cpp new file mode 100644 index 0000000..7242f01 --- /dev/null +++ b/lib/src/Parser/AST/AST.cpp @@ -0,0 +1,1742 @@ +#include + +#include +#include +#include + +#include + +namespace arti::lang::ast { + std::string createPadding(std::size_t padding) { + return std::views::repeat(' ') + | std::views::take(padding) + | std::ranges::to(); + } + + std::string toString(const ModuleDeclNode &, std::size_t); + std::string toString(const StructDeclNode &, std::size_t); + std::string toString(const EnumDeclNode &, std::size_t); + std::string toString(const FunctionDeclNode &, std::size_t); + std::string toString(const ImportDeclNode &, std::size_t); + std::string toString(const AliasDeclNode &, std::size_t); + std::string toString(const EnumMemberNode &, std::size_t); + std::string toString(const StructMemberNode &, std::size_t); + std::string toString(const GenericParamNode &, std::size_t); + std::string toString(const FunctionParamNode &, std::size_t); + std::string toString(const TopLevelDeclNode &, std::size_t); + std::string toString(const ModuleInnerDeclNode &, std::size_t); + std::string toString(const TypeNode &, std::size_t); + std::string toString(const GenericTypeNode &, std::size_t); + std::string toString(const IdentifierTypeNode &, std::size_t); + std::string toString(const NamespacedTypeNode &, std::size_t); + std::string toString(const NamespacedIdentifierNode &, std::size_t); + std::string toString(const TypeExpressionNode &, std::size_t); + std::string toString(const CharLtrlNode &, std::size_t); + std::string toString(const NullLtrlNode &, std::size_t); + std::string toString(const StringLtrlNode &, std::size_t); + std::string toString(const FloatLtrlNode &, std::size_t); + std::string toString(const IntegerLtrlNode &, std::size_t); + std::string toString(const BooleanLtrlNode &, std::size_t); + std::string toString(const StructLtrlNode &, std::size_t); + std::string toString(const SliceLtrlNode &, std::size_t); + std::string toString(const StructLtrlNamedFieldInitNode &, std::size_t); + std::string toString(const StructLtrlPositionalInitNode &, std::size_t); + std::string toString(const StructLtrlNamedInitializerNode &, std::size_t); + std::string toString(const StructLtrlPositionalInitializerNode &,std::size_t); + std::string toString(const StructLtrlInitializerNode &, std::size_t); + std::string toString(const IdentifierExprNode &, std::size_t); + std::string toString(const UnaryExprNode &, std::size_t); + std::string toString(const BinaryExprNode &, std::size_t); + std::string toString(const AssignExprNode &, std::size_t); + std::string toString(const CompoundAssignExprNode &, std::size_t); + std::string toString(const FunctionCallExprNode &, std::size_t); + std::string toString(const SliceAccessExprNode &, std::size_t); + std::string toString(const SliceRangeExprNode &, std::size_t); + std::string toString(const MemberAccessExprNode &, std::size_t); + std::string toString(const PointerAccessExprNode &, std::size_t); + std::string toString(const ScopeAccessExprNode &, std::size_t); + std::string toString(const ReflectionExprNode &, std::size_t); + std::string toString(const SliceCreationExprNode &, std::size_t); + std::string toString(const SliceLengthExprNode &, std::size_t); + std::string toString(const SlicePtrExprNode &, std::size_t); + std::string toString(const ExpressionNode &, std::size_t); + std::string toString(const CodeBlockStmtNode &, std::size_t); + std::string toString(const VariableStmtNode &, std::size_t); + std::string toString(const IfStmtNode &, std::size_t); + std::string toString(const ElseStmtNode &, std::size_t); + std::string toString(const DeferStmtNode &, std::size_t); + std::string toString(const ErrDeferStmtNode &, std::size_t); + std::string toString(const ReturnStmtNode &, std::size_t); + std::string toString(const BreakStmtNode &, std::size_t); + std::string toString(const ContinueStmtNode &, std::size_t); + std::string toString(const MatchStmtNode &, std::size_t); + std::string toString(const SwitchStmtNode &, std::size_t); + std::string toString(const CForStmtNode &, std::size_t); + std::string toString(const RangeForStmtNode &, std::size_t); + std::string toString(const WhileStmtNode &, std::size_t); + std::string toString(const DoWhileStmtNode &, std::size_t); + std::string toString(const InfLoopStmtNode &, std::size_t); + std::string toString(const ExpressionStmtNode &, std::size_t); + std::string toString(const MatchCaseNode &, std::size_t); + std::string toString(const SwitchCaseNode &, std::size_t); + std::string toString(const StatementNode &, std::size_t); + std::string toString(const ElseBranchNode &, std::size_t); + std::string toString(const DeferableNode &, std::size_t); + std::string toString(const PreLoopStmtNode &, std::size_t); + std::string toString(UnaryOperator op); + std::string toString(BinaryOperator op); + std::string toString(CompoundAssignOperator op); + + std::string toString(const AST &tree, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + std::vector childs; + + ss << std::format("- CompilationUnit {}", tree->unitName); + + for (const auto &d : tree->declarations) { + ss << std::format("\n{} - {}", paddingStr, toString(d, padding + 2)); + } + + return ss.str(); + } + + std::string toString(const TopLevelDeclNode &tlDecl, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const ModuleDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StructDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const EnumDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FunctionDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ImportDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const AliasDeclNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, tlDecl); + } + + std::string toString(const ModuleDeclNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "Module {} {}", + toString(node->name, padding), + node->isExported ? "[exported]" : "" + ); + + if (! node->aliasDeclarations.empty()) { + ss << std::format("\n{} - AliasedTypes", paddingStr); + + for (const auto &al : node->aliasDeclarations) { + ss + << std::format("\n{} - {}", paddingStr, toString(al, padding + 4)); + } + } + + if (! node->innerDeclarations.empty()) { + ss << std::format("\n{} - InnerDeclarations", paddingStr); + + for (const auto &id : node->innerDeclarations) { + ss + << std::format("\n{} - {}", paddingStr, toString(id, padding + 4)); + } + } + + if (! node->childModules.empty()) { + ss << std::format("\n{} - ChildModules", paddingStr); + + for (const auto &cm : node->childModules) { + ss + << std::format("\n{} - {}", paddingStr, toString(cm, padding + 4)); + } + } + + return ss.str(); + } + + std::string toString(const StructDeclNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "Struct {} {}", + node->name, + node->isExported ? "[exported]" : "" + ); + + if (! node->genericParams.empty()) { + ss << std::format("\n{} - GenericParams", paddingStr); + + for (const auto &gp : node->genericParams) { + ss + << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); + } + } + + if (! node->structMembers.empty()) { + ss << std::format("\n{} - FieldMembers", paddingStr); + + for (const auto &sm : node->structMembers) { + ss + << std::format("\n{} - {}", paddingStr, toString(sm, padding + 4)); + } + } + + return ss.str(); + } + + std::string toString(const EnumDeclNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "Enum {} {}", + node->name, + node->isExported ? "[exported]" : "" + ); + + if (! node->genericParams.empty()) { + ss << std::format("\n{} - GenericParams", paddingStr); + + for (const auto &gp : node->genericParams) { + ss + << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); + } + } + + ss << std::format("\n{} - EnumValues", paddingStr); + + for (const auto &em : node->enumMembers) { + ss << std::format("\n{} - {}", paddingStr, toString(em, padding + 4)); + } + + return ss.str(); + } + + std::string toString(const FunctionDeclNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "Function {} {}", + node->name, + node->isExported ? "[exported]" : "" + ); + + if (node->returnType) { + ss << std::format("\n{} - ReturnType", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->returnType, padding + 4) + ); + } + + if (! node->genericParams.empty()) { + ss << std::format("\n{} - GenericParams", paddingStr); + + for (const auto &gp : node->genericParams) { + ss + << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); + } + } + + if (! node->functionParams.empty()) { + ss << std::format("\n{} - FunctionParams", paddingStr); + + for (const auto &gp : node->functionParams) { + ss + << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); + } + } + + ss << std::format("\n{} - FunctionBody", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->functionBody, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const ImportDeclNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "Import {} {}", + toString(node->importTarget, padding), + node->importAll ? "::*" : "" + ); + + return ss.str(); + } + + std::string toString(const AliasDeclNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "Alias {}", + node->alias + ); + + ss << std::format("\n{} - AliasedType", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->target, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const EnumMemberNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "EnumMember {}", + node->name + ); + + if (node->type) { + ss << std::format("\n{} - StorageType", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->type, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const StructMemberNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << std::format( + "StructMember {}", + node->name + ); + + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->type, padding + 2) + ); + + return ss.str(); + } + + std::string toString(const GenericParamNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("typename {}", node->name); + } + + std::string toString(const FunctionParamNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + if (node->isThis) { + ss << std::format( + "This", + paddingStr + ); + } + else { + ss << std::format( + "Param {}", + node->name + ); + } + + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->type, padding + 2) + ); + + return ss.str(); + } + + std::string toString(const ModuleInnerDeclNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const StructDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const EnumDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FunctionDeclNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const TypeNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "Type"; + + if (!node->qualifiers.empty()) { + ss << std::format("\n{} - Qualifiers", paddingStr); + + for (const auto &q : node->qualifiers) { + switch(q) { + case TypeQualifier::Pointer: + ss << std::format("\n{} - Pointer (*)", paddingStr); + break; + case TypeQualifier::Slice: + ss << std::format("\n{} - Slice ([])", paddingStr); + break; + case TypeQualifier::Mutable: + ss << std::format("\n{} - Mutable ($)", paddingStr); + break; + case TypeQualifier::Optional: + ss << std::format("\n{} - Optional (?)", paddingStr); + break; + default: + break; + } + } + } + + ss << std::format("\n{} - BaseType", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->baseType, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const GenericTypeNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "GenericType"; + ss << std::format("\n{} - BaseType", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->baseType, padding + 4) + ); + + if (!node->genericArgs.empty()) { + ss << std::format("\n{} - GenericArgs", paddingStr); + for (const auto &ga : node->genericArgs) { + ss + << std::format("\n{} - {}", paddingStr, toString(ga, padding + 4)); + } + } + + return ss.str(); + } + + std::string toString(const IdentifierTypeNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "IdentifierType"; + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->typeName, padding + 2) + ); + + return ss.str(); + } + + std::string toString(const NamespacedTypeNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "NamespacedType"; + + ss << std::format("\n{} - BaseType", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->baseType, padding + 4) + ); + + ss << std::format("\n{} - TypeName", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + node->typeName + ); + + return ss.str(); + } + + std::string + toString(const NamespacedIdentifierNode &node, std::size_t padding) { + std::ignore = padding; + return node->identParts + | std::views::join_with(std::string_view{"::"}) + | std::ranges::to(); + } + + std::string toString(const TypeExpressionNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const GenericTypeNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IdentifierTypeNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const NamespacedTypeNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const CharLtrlNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("CharLiteral '{}'", node->value); + } + + std::string toString(const NullLtrlNode &node, std::size_t padding) { + std::ignore = node; + std::ignore = padding; + return std::format("NullLiteral 'null'"); + } + + std::string toString(const StringLtrlNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("StringLiteral \"{}\"", node->value); + } + + std::string toString(const FloatLtrlNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("FloatLiteral {}", node->value); + } + + std::string toString(const IntegerLtrlNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("IntegerLiteral {}", node->value); + } + + std::string toString(const BooleanLtrlNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("BooleanLiteral {}", node->value ? "true" : "false"); + } + + std::string toString(const StructLtrlNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "StructLiteral"; + + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->type, padding + 2) + ); + + if (node->initializer) { + ss << std::format("\n{} - Elements", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->initializer, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const SliceLtrlNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SliceLiteral"; + + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->type, padding + 4) + ); + + if (node->initializer) { + ss << std::format("\n{} - Elements", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->initializer, padding + 4) + ); + } + + return ss.str(); + } + + std::string + toString(const StructLtrlNamedFieldInitNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "FieldInitializer"; + + ss << std::format("\n{} - Field '{}'", paddingStr, node->fieldName); + ss << std::format("\n{} - Value", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->fieldValue, padding + 4) + ); + + return ss.str(); + } + + std::string + toString(const StructLtrlPositionalInitNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "PositionalInitializer"; + + ss << std::format("\n{} - Value", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->fieldValue, padding + 4) + ); + + return ss.str(); + } + + std::string + toString(const StructLtrlNamedInitializerNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "InitializerList"; + ss << std::format("\n{} - Elements", paddingStr); + + for (const auto &ele : node->fields) { + ss << std::format( + "\n{} - {}", + paddingStr, + toString(ele, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString( + const StructLtrlPositionalInitializerNode &node, + std::size_t padding + ) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "InitializerList"; + ss << std::format("\n{} - Elements", paddingStr); + + for (const auto &[idx, ele] : std::views::enumerate(node->fields)) { + ss << std::format( + "\n{} - [{}] {}", + paddingStr, + idx, + toString(ele, padding + 4) + ); + } + + return ss.str(); + } + + std::string + toString(const StructLtrlInitializerNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const StructLtrlNamedInitializerNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StructLtrlPositionalInitializerNode &node) + -> std::string { return toString(node, padding); }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const IdentifierExprNode &node, std::size_t padding) { + std::ignore = padding; + return std::format("Identifier {}", node->identifierName); + } + + std::string toString(const UnaryExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "UnaryExpression"; + ss << std::format("\n{} - Operator {}", paddingStr, toString(node->op)); + + ss << std::format("\n{} - Operand", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->right, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const BinaryExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "BinaryExpression"; + ss << std::format("\n{} - Operator {}", paddingStr, toString(node->op)); + + ss << std::format("\n{} - Left", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->left, padding + 4) + ); + + ss << std::format("\n{} - Right", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->right, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const AssignExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "AssignExpression"; + ss << std::format("\n{} - Left", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->left, padding + 4) + ); + + ss << std::format("\n{} - Right", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->right, padding + 4) + ); + + return ss.str(); + } + + std::string + toString(const CompoundAssignExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "CompoundAssignExpression"; + ss << std::format("\n{} - Operator {}", paddingStr, toString(node->op)); + + ss << std::format("\n{} - Left", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->left, padding + 4) + ); + + ss << std::format("\n{} - Right", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->left, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const FunctionCallExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "FunctionCallExpression"; + ss << std::format("\n{} - Callee", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->callee, padding + 4) + ); + + if (! node->arguments.empty()) { + ss << std::format("\n{} - Arguments", paddingStr); + + for (const auto &arg : node->arguments) { + ss << std::format( + "\n{} - {}", + paddingStr, + toString(arg, padding + 4) + ); + } + } + + return ss.str(); + } + + std::string toString(const SliceAccessExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SliceAccessExpression"; + ss << std::format("\n{} - Slice", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->slice, padding + 4) + ); + + ss << std::format("\n{} - Index", paddingStr); + + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->index, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const SliceRangeExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SliceRangeExpression"; + ss << std::format("\n{} - Slice", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->slice, padding + 4) + ); + + if (node->start) { + ss << std::format("\n{} - Start", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->start, padding + 4) + ); + } + + if (node->end) { + ss << std::format("\n{} - End", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->end, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const MemberAccessExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "MemberAccessExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->object, padding + 4) + ); + + ss << std::format("\n{} - Member '{}'", paddingStr, node->memberName); + + return ss.str(); + } + + std::string toString(const PointerAccessExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "PointerAccessExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->object, padding + 4) + ); + + ss << std::format("\n{} - Member '{}'", paddingStr, node->memberName); + + return ss.str(); + } + + std::string toString(const ScopeAccessExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ScopeAccessExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->scope, padding + 4) + ); + + if (! node->genericParams.empty()) { + ss << std::format("\n{} - GenericParams", paddingStr); + + for (const auto &gp : node->genericParams) { + ss + << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); + } + } + + ss << std::format("\n{} - Member '{}'", paddingStr, node->memberName); + + return ss.str(); + } + + std::string toString(const ReflectionExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ReflectionExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->object, padding + 4) + ); + + if (node->attribute) { + ss + << std::format("\n{} - Attribute '{}'", paddingStr, *node->attribute); + } + + return ss.str(); + } + + std::string toString(const SliceCreationExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SliceCreationExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->object, padding + 4) + ); + + ss << std::format("\n{} - Length", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->length, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const SliceLengthExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SliceLengthExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->object, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const SlicePtrExprNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SlicePtrExpression"; + ss << std::format("\n{} - Object", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->object, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const ExpressionNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const CharLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const NullLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StringLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FloatLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IntegerLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const BooleanLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StructLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IdentifierExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const UnaryExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const BinaryExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const AssignExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const CompoundAssignExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FunctionCallExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceRangeExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const MemberAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const PointerAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ScopeAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceCreationExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceLengthExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SlicePtrExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ReflectionExprNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const CodeBlockStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "CodeBlock"; + + for (const auto &gp : node->statements) { + ss << std::format("\n{} - {}", paddingStr, toString(gp, padding + 2)); + } + + return ss.str(); + } + + std::string toString(const VariableStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "VariableDeclaration"; + ss << std::format("\n{} - Name '{}'", paddingStr, node->name); + ss << std::format( + "\n{} - Mutability '{}'", + paddingStr, + node->mutability == Mutability::Mutable ? "let" : "def" + ); + + if (node->type) { + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->type, padding + 4) + ); + } + + if (node->initializer) { + ss << std::format("\n{} - Initializer", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->initializer, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const IfStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "IfStatement"; + + if (node->unwrappedVar) { + ss << std::format( + "\n{} - UnwrappedVar '{}'", + paddingStr, + *node->unwrappedVar + ); + } + + ss << std::format("\n{} - Condition", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->condition, padding + 4) + ); + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + if (node->elseBranch) { + ss << std::format("\n{} - ElseBranch", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->elseBranch, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const ElseStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ElseStatement"; + + if (node->unwrappedVar) { + ss << std::format( + "\n{} - UnwrappedVar '{}'", + paddingStr, + *node->unwrappedVar + ); + } + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const DeferStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "DeferStatement"; + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const ErrDeferStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ErrDeferStatement"; + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const ReturnStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ReturnStatement"; + + if (node->value) { + ss << std::format("\n{} - Expression", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->value, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const BreakStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "BreakStatement"; + + if (node->label) { + ss << std::format("\n{} - Label '{}'", paddingStr, *node->label); + } + + return ss.str(); + } + + std::string toString(const ContinueStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ContinueStatement"; + + if (node->label) { + ss << std::format("\n{} - Label '{}'", paddingStr, *node->label); + } + + return ss.str(); + } + + std::string toString(const MatchStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "MatchStatement"; + + ss << std::format("\n{} - Value", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->value, padding + 4) + ); + + if (!node->matchCases.empty()) { + ss << std::format("\n{} - Cases", paddingStr); + for (const auto &cas : node->matchCases) { + ss << std::format( + "\n{} - {}", + paddingStr, + toString(cas, padding + 4) + ); + } + } + + if (node->defaultCase) { + ss << std::format("\n{} - DefaultCase", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->defaultCase, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const SwitchStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "SwitchStatement"; + + ss << std::format("\n{} - Value", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->value, padding + 4) + ); + + if (!node->switchCases.empty()) { + ss << std::format("\n{} - Cases", paddingStr); + for (const auto &cas : node->switchCases) { + ss << std::format( + "\n{} - {}", + paddingStr, + toString(cas, padding + 4) + ); + } + } + + if (node->defaultCase) { + ss << std::format("\n{} - DefaultCase", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->defaultCase, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const CForStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "CForStatement"; + + if (node->label) { + ss << std::format( + "\n{} - Label '{}'", + paddingStr, + *node->label + ); + } + + if (node->preLoop) { + ss << std::format("\n{} - PreLoop", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->preLoop, padding + 4) + ); + } + + ss << std::format("\n{} - Condition", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->condition, padding + 4) + ); + + if (node->postLoop) { + ss << std::format("\n{} - PostLoop", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->postLoop, padding + 4) + ); + } + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const RangeForStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ForRangeStatement"; + + if (node->label) { + ss << std::format( + "\n{} - Label '{}'", + paddingStr, + *node->label + ); + } + + ss << std::format( + "\n{} - Variable '{}'{}", + paddingStr, + node->varName, + node->varMutability == Mutability::Mutable + ? " [mut]" + : "" + ); + + ss << std::format("\n{} - Range", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->range, padding + 4) + ); + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const WhileStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "WhileStatement"; + + if (node->label) { + ss << std::format( + "\n{} - Label '{}'", + paddingStr, + *node->label + ); + } + + if (node->unwrappedVar) { + ss << std::format( + "\n{} - UnwrappedVar '{}'", + paddingStr, + *node->unwrappedVar + ); + } + + ss << std::format("\n{} - Condition", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->condition, padding + 4) + ); + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + if (node->elseBranch) { + ss << std::format("\n{} - ElseBranch", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(*node->elseBranch, padding + 4) + ); + } + + return ss.str(); + } + + std::string toString(const DoWhileStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "DoWhileStatement"; + + if (node->label) { + ss << std::format( + "\n{} - Label '{}'", + paddingStr, + *node->label + ); + } + + ss << std::format("\n{} - Condition", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->condition, padding + 4) + ); + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const InfLoopStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "InfLoopStatement"; + + if (node->label) { + ss << std::format( + "\n{} - Label '{}'", + paddingStr, + *node->label + ); + } + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const ExpressionStmtNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "ExpressionStatement"; + + ss << std::format("\n{} - Expression", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->expression, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const MatchCaseNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "MatchCase"; + + if (node->unwrappedVar) { + ss << std::format( + "\n{} - UnwrappedVar '{}'", + paddingStr, + *node->unwrappedVar + ); + } + + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->matchType, padding + 2) + ); + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const SwitchCaseNode &node, std::size_t padding) { + std::stringstream ss; + auto paddingStr = createPadding(padding); + + ss << "MatchCase"; + + ss << std::format("\n{} - Matcher", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->matchExpr, padding + 4) + ); + + ss << std::format("\n{} - Body", paddingStr); + ss << std::format( + "\n{} - {}", + paddingStr, + toString(node->body, padding + 4) + ); + + return ss.str(); + } + + std::string toString(const StatementNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const VariableStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IfStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const DeferStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ErrDeferStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ReturnStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const BreakStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ContinueStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const MatchStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SwitchStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const CForStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const RangeForStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const WhileStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const DoWhileStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const InfLoopStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ExpressionStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const ElseBranchNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const ElseStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IfStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const DeferableNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const ExpressionStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const CodeBlockStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const PreLoopStmtNode &node, std::size_t padding) { + auto visitor = OverloadSet{ + [padding](const VariableStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ExpressionStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(UnaryOperator op) { + using enum UnaryOperator; + + switch (op) { + case Not: return "Not (!)"; + case Minus: return "Minus (-)"; + case BitNot: return "BitNot (~)"; + case Ampersand: return "Ampersand (&)"; + case Star: return "Star (*)"; + default: std::unreachable(); break; + } + + std::unreachable(); + } + + std::string toString(BinaryOperator op) { + using enum BinaryOperator; + + switch (op) { + case Equal: return "Equal (==)"; + case NotEqual: return "NotEqual (!=)"; + case GreaterThan: return "GreaterThan (>)"; + case LessThan: return "LessThan (<)"; + case GreaterEqual: return "GreaterEqual (>=)"; + case LessEqual: return "LessEqual (<=)"; + case BitAnd: return "BitAnd (&)"; + case BitXor: return "BitXor (^)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case Adition: return "Adition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + default: std::unreachable(); break; + } + + std::unreachable(); + } + + std::string toString(CompoundAssignOperator op) { + using enum CompoundAssignOperator; + + switch (op) { + case Addition: return "Addition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BitAnd: return "BitAnd (&)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + default: std::unreachable(); break; + } + + std::unreachable(); + } + +} // namespace arti::lang::ast diff --git a/lib/src/Parser/Declarations.cpp b/lib/src/Parser/Declarations.cpp new file mode 100644 index 0000000..f42fbc3 --- /dev/null +++ b/lib/src/Parser/Declarations.cpp @@ -0,0 +1,755 @@ +#include + +namespace arti::lang { + + Expected> + 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( + 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( + 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( + peekToken->line, + peekToken->column, + toString(*peekToken), + "top level declaration" + ); + } + + return std::nullopt; + } + + Expected Parser::parseImportDeclaration() { + auto node = ast::MakeNode(); + 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( + 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( + peekNext->line, + peekNext->column, + toString(*peekNext) + ); + } + + std::ignore = tokenizer.consume(); + + return node; + } + + Expected Parser::parseAliasDeclaration() { + auto node = ast::MakeNode(); + + 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( + 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 Parser::parseModuleDeclaration() { + auto node = ast::MakeNode(); + + 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{}; + + 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(*decl)) { + node->childModules.push_back( + std::get(std::move(*decl)) + ); + } + else if (std::holds_alternative(*decl)) { + node->innerDeclarations.push_back( + std::get(std::move(*decl)) + ); + } + else if (std::holds_alternative(*decl)) { + node->innerDeclarations.push_back( + std::get(std::move(*decl)) + ); + } + else if (std::holds_alternative(*decl)) { + node->innerDeclarations.push_back( + std::get(std::move(*decl)) + ); + } + else if (std::holds_alternative(*decl)) { + node->aliasDeclarations.push_back( + std::get(std::move(*decl)) + ); + } + else if (std::holds_alternative(*decl)) { + auto importDecl = std::get(std::move(*decl)); + + return langException( + 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 Parser::parseStructDeclaration() { + auto node = ast::MakeNode(); + + 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( + 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( + 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 Parser::parseEnumDeclaration() { + auto node = ast::MakeNode(); + + 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( + 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( + 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> + Parser::parseGenericParamsList() { + auto paramsList = ast::Vector{}; + + 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 Parser::parseGenericParam() { + auto node = ast::MakeNode(); + + 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( + sc->line, + sc->column, + toString(*sc), + "'}' or ','" + ); + } + + return node; + } + + Expected> + Parser::parseStructMembersList() { + auto membersList = ast::Vector{}; + + 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 Parser::parseStructMember() { + auto node = ast::MakeNode(); + + 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( + sc->line, + sc->column, + toString(*sc), + "'}' or ','" + ); + } + + return node; + } + + Expected> Parser::parseEnumMembersList() { + auto membersList = ast::Vector{}; + + 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 Parser::parseEnumMember() { + auto node = ast::MakeNode(); + + 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( + sc->line, + sc->column, + toString(*sc), + "'}' or ','" + ); + } + + return node; + } + + Expected Parser::parseFunctionDeclaration() { + auto peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + return langException( + peekToken->line, + peekToken->column + ); + } + +} // namespace arti::lang diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/Parser/Literals.cpp b/lib/src/Parser/Literals.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/Parser/Parser.cpp b/lib/src/Parser/Parser.cpp index e69de29..c27476f 100644 --- a/lib/src/Parser/Parser.cpp +++ b/lib/src/Parser/Parser.cpp @@ -0,0 +1,47 @@ +#include + +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 Parser::parse() { + auto unit = ast::MakeNode(); + auto tlDecl = ast::Optional{}; + 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 diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp new file mode 100644 index 0000000..c7b4053 --- /dev/null +++ b/lib/src/Parser/Types.cpp @@ -0,0 +1,299 @@ +#include + +namespace arti::lang { + + Expected Parser::parseNamespacedIdentifier() { + auto node = ast::MakeNode(); + + 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 Parser::parseType() { + auto node = ast::MakeNode(); + + 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(); + + std::get(currentNode)->location = + (*identType)->location; + + std::get(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(); + + 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(); + + 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(); + + 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> Parser::parseTypeQualifiers() { + auto qualifs = ast::Vector{}; + + 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( + 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( + 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> Parser::parseGenericArgumentsList() { + auto args = ast::Vector{}; + + 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 diff --git a/lib/src/Tokenizer/Tokenizer.cpp b/lib/src/Tokenizer/Tokenizer.cpp index d919078..f93e51d 100644 --- a/lib/src/Tokenizer/Tokenizer.cpp +++ b/lib/src/Tokenizer/Tokenizer.cpp @@ -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( + 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( + token->line, + token->column, + toString(*token), + toString(tokenType) + ); } return *token; -- 2.52.0 From 66eca2f24a7be69ec065e44f50aa72c0d7247622 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Thu, 16 Oct 2025 23:22:19 -0600 Subject: [PATCH 02/15] feat(Parser): Expanding parser capabilities (might clean later) Signed-off-by: erick-alcachofa --- frontend/src/main.cpp | 43 +++- lib/include/artichoke/Parser/Parser.hpp | 94 ++++++++ lib/src/Parser/AST/AST.cpp | 2 +- lib/src/Parser/Declarations.cpp | 288 +++++++++++++++++++++++- lib/src/Parser/Statements.cpp | 36 +++ lib/src/Parser/Types.cpp | 12 +- 6 files changed, 463 insertions(+), 12 deletions(-) diff --git a/frontend/src/main.cpp b/frontend/src/main.cpp index 3e67a25..73cea02 100644 --- a/frontend/src/main.cpp +++ b/frontend/src/main.cpp @@ -1,5 +1,44 @@ #include +#include -int main(int, char **) { - std::println("[LOG] Hello world"); +#include + +int main(int argc, char **argv) { + using namespace arti::lang; + + if (argc < 2) { + std::println("Usage:\n {} ", argv[0]); + return -1; + } + + std::ifstream file; + file.open(argv[1]); + + if (! file.is_open()) { + std::println("Failed to open file {}", argv[1]); + return -1; + } + + std::string buffer{ std::istreambuf_iterator(file), + std::istreambuf_iterator() }; + + auto parser = Parser{ buffer }; + + auto res = parser.parse(); + + if (! res) { + std::println( + "Error at line {} column {}", + res.error().line, + res.error().column + ); + + std::println("{}", res.error().message); + + return -1; + } + + auto ast = std::move(res).value(); + + std::println("{}", ast::toString(ast)); } diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index 9539643..14015ec 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -69,6 +69,100 @@ namespace arti::lang { Expected> parseGenericArgumentsList(); + Expected> + parseFunctionParamsList(); + + Expected + parseFunctionParam(); + + Expected + parseFunctionParamThis(); + + Expected + parseCodeBlock(); + + Expected> + parseStatement(); + + Expected + parseVariableStatement(); + + Expected + parseIfStatement(); + + Expected + parseDeferStatement(); + + Expected + parseErrDeferStatement(); + + Expected + parseReturnStatement(); + + Expected + parseBreakStatement(); + + Expected + parseContinueStatement(); + + Expected + parseMatchStatement(); + + Expected + parseSwitchStatement(); + + Expected + parseCForStatement(); + + Expected + parseRangeForStatement(); + + Expected + parseWhileStatement(); + + Expected + parseDoWhileStatement(); + + Expected + parseInfLoopStatement(); + + Expected consume(TokenV type, std::string_view expected_name) { + auto peeked = tokenizer.peek(); + + if (! peeked) { + return Unexpected<>{ std::move(peeked).error() }; + } + + if (peeked->value != type) { + return langException( + peeked->line, + peeked->column, + toString(*peeked), + expected_name + ); + } + + std::ignore = tokenizer.consume(); + + return peeked; + } + + Expected matchAndConsume(TokenV type) { + auto peeked = tokenizer.peek(); + + if (! peeked) { + return Unexpected<>{ std::move(peeked).error() }; + } + + if (peeked->value != type) { + return false; + } + + std::ignore = tokenizer.consume(); + + return true; + } + private: std::string unitName; std::string sourceCode; diff --git a/lib/src/Parser/AST/AST.cpp b/lib/src/Parser/AST/AST.cpp index 7242f01..c7e156c 100644 --- a/lib/src/Parser/AST/AST.cpp +++ b/lib/src/Parser/AST/AST.cpp @@ -278,7 +278,7 @@ namespace arti::lang::ast { auto paddingStr = createPadding(padding); ss << std::format( - "Import {} {}", + "Import {}{}", toString(node->importTarget, padding), node->importAll ? "::*" : "" ); diff --git a/lib/src/Parser/Declarations.cpp b/lib/src/Parser/Declarations.cpp index f42fbc3..85810d7 100644 --- a/lib/src/Parser/Declarations.cpp +++ b/lib/src/Parser/Declarations.cpp @@ -102,6 +102,9 @@ namespace arti::lang { return ast::TopLevelDeclNode{ std::move(node).value() }; } + else if (peekToken->value == TokenV::opRSquirly) { + return std::nullopt; + } else if (peekToken->value != TokenV::tkEOF) { return langException( peekToken->line, @@ -740,16 +743,293 @@ namespace arti::lang { } Expected Parser::parseFunctionDeclaration() { + auto node = ast::MakeNode(); + + 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( + 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::opLParen) { + return langException( + peekNext->line, + peekNext->column, + toString(*peekNext), + "'('" + ); + } + std::ignore = tokenizer.consume(); + + peekNext = tokenizer.peek(); + + if (! peekNext) { + return Unexpected<>{ std::move(peekNext).error() }; + } + + if (peekNext->value != TokenV::opRParen) { + auto params = parseFunctionParamsList(); + + if (! params) { + return Unexpected<>{ std::move(params).error() }; + } + + node->functionParams = std::move(params).value(); + + peekNext = tokenizer.peek(); + + if (! peekNext) { + return Unexpected<>{ std::move(peekNext).error() }; + } + + if (peekNext->value != TokenV::opRParen) { + return langException( + peekNext->line, + peekNext->column, + toString(*peekNext), + "')'" + ); + } + } + std::ignore = tokenizer.consume(); + + peekNext = tokenizer.peek(); + + if (! peekNext) { + return Unexpected<>{ std::move(peekNext).error() }; + } + + if (peekNext->value == TokenV::opArrow) { + std::ignore = tokenizer.consume(); + + auto retType = parseType(); + + if (! retType) { + return Unexpected<>{ std::move(retType).error() }; + } + + node->returnType = std::move(retType).value(); + + peekNext = tokenizer.peek(); + + if (! peekNext) { + return Unexpected<>{ std::move(peekNext).error() }; + } + } + + if (auto openS = tokenizer.peekExpect(0, TokenV::opLSquirly); ! openS) { + return Unexpected{ std::move(openS).error() }; + } + std::ignore = tokenizer.consume(); + + auto body = parseCodeBlock(); + + if (! body) { + return Unexpected{ std::move(body).error() }; + } + + node->functionBody = std::move(body).value(); + + if (auto closeS = tokenizer.peekExpect(0, TokenV::opRSquirly); ! closeS) { + return Unexpected{ std::move(closeS).error() }; + } + std::ignore = tokenizer.consume(); + + return node; + } + + Expected> + Parser::parseFunctionParamsList() { + auto params = ast::Vector{}; + auto peekToken = tokenizer.peek(); if (! peekToken) { return Unexpected<>{ std::move(peekToken).error() }; } - return langException( - peekToken->line, - peekToken->column - ); + if (peekToken->value == TokenV::kwThis) { + auto thisParam = parseFunctionParamThis(); + + if (! thisParam) { + return Unexpected<>{ std::move(thisParam).error() }; + } + + params.push_back(std::move(thisParam).value()); + + peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + } + + while (peekToken->value != TokenV::opRParen) { + auto param = parseFunctionParam(); + + if (! param) { + return Unexpected<>{ std::move(param).error() }; + } + + params.push_back(std::move(param).value()); + + peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + } + + return params; + } + + Expected Parser::parseFunctionParamThis() { + auto node = ast::MakeNode(); + + node->isThis = true; + + auto kw = tokenizer.peek(); + + if (! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + + node->location.line = kw->line; + node->location.column = kw->column; + node->name = "this"; + + 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::opRParen) { + return langException( + sc->line, + sc->column, + toString(*sc), + "'}' or ','" + ); + } + + return node; + } + + Expected Parser::parseFunctionParam() { + auto node = ast::MakeNode(); + + node->isThis = false; + + if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) { + return Unexpected{ std::move(ident).error() }; + } + else { + node->location.line = ident->line; + node->location.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::opRParen) { + return langException( + sc->line, + sc->column, + toString(*sc), + "'}' or ','" + ); + } + + return node; } } // namespace arti::lang diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp index e69de29..5008542 100644 --- a/lib/src/Parser/Statements.cpp +++ b/lib/src/Parser/Statements.cpp @@ -0,0 +1,36 @@ +#include + +namespace arti::lang { + + Expected Parser::parseCodeBlock() { + auto node = ast::MakeNode(); + + auto stmt = ast::Optional{}; + bool keepParsing = true; + + while (keepParsing) { + if (auto ok = parseStatement(); ok) { + stmt = std::move(ok).value(); + + if (! stmt.has_value()) { + keepParsing = false; + } + else { + node->statements.push_back(std::move(stmt).value()); + } + } + else { + return Unexpected<>{ std::move(ok).error() }; + } + } + + return node; + } + + Expected> + Parser::parseStatement() { + + } + + +} // namespace arti::lang diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp index c7b4053..e10d4a8 100644 --- a/lib/src/Parser/Types.cpp +++ b/lib/src/Parser/Types.cpp @@ -24,17 +24,19 @@ namespace arti::lang { } while (peekNext->value == TokenV::opAccess) { - std::ignore = tokenizer.consume(); - - ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); + ident = tokenizer.peek(1); if (! ident) { - return Unexpected<>{ std::move(ident).error() }; + return node; } else { + if (ident->value != TokenV::tkIdentifier) { + return node; + } + node->identParts.emplace_back(ident->strValue); } - std::ignore = tokenizer.consume(); + std::ignore = tokenizer.consume(2); peekNext = tokenizer.peek(); -- 2.52.0 From 8911702c0d2799eb42073d9710f84af7dc6db67d Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Thu, 25 Dec 2025 11:41:08 -0600 Subject: [PATCH 03/15] refactor(parser): overhaul parsing logic and enhance error reporting Signed-off-by: erick-alcachofa Major refactoring of the Parser and Tokenizer components to improve code maintainability, strengthen error messaging, and streamline AST generation. This version intentionally focuses on top-level declarations, with statement parsing stubbed for the next development phase. - **Path Sanitization**: Added `sanitizePath` to extract filenames from input paths, ensuring consistent `unitName` identification regardless of directory depth. - **Improved Output**: Wrapped AST string output in Markdown code blocks and added a commented-out entry for the new DOT graph visualization. - **Unified Consumption**: Replaced manual token checks with a more robust `consume()` method that leverages `peekExpect()` for centralized error handling. - **New Predicates**: Introduced `match()` and `matchAndConsume()` helpers to handle optional tokens and branching logic without redundant peek/consume calls. - **Exception Handling**: Standardized the use of `langException` across all parsing functions, providing more descriptive "Expected X, found Y" messages. - **Declarations**: Refactored `parseTopLevelDeclaration` and sub-parsers (Module, Struct, Enum, Fn) to use the new matching patterns. - **Looping Logic**: Replaced recursive-style parsing loops with `while(keepParsing)` iterative blocks to prevent stack depth issues and clarify termination conditions (e.g., finding a closing brace or failing to find a comma). - **Namespaced Identifiers**: Rewrote `parseNamespacedIdentifier` to correctly handle multi-part paths (`A::B::C`) and edge cases. - **Generic Support**: Improved handling of generic parameter and argument lists, ensuring strict enforcement of delimiters like `<` and `>`. - **Contextual Errors**: Updated `peekExpect` to accept a custom `message` string, allowing the parser to describe *what* it was looking for (e.g., "Expected ';'"). - **Token Lookahead**: Enhanced `peek` and `peekExpect` reliability with better bounds checking and buffer management. - **Removed `lib/src/Parser/AST/AST.cpp`**: Deleted the monolithic AST stringification file in favor of the previously introduced modular implementations. - **Build System**: Updated `.gitignore` to ignore `cpm-package-lock.cmake`. --- .gitignore | 2 + frontend/src/main.cpp | 16 +- lib/include/artichoke/Parser/Parser.hpp | 23 +- lib/include/artichoke/Tokenizer/Tokenizer.hpp | 6 +- lib/src/Parser/AST/AST.cpp | 1742 ----------------- lib/src/Parser/Declarations.cpp | 1021 ++++------ lib/src/Parser/Parser.cpp | 20 +- lib/src/Parser/Statements.cpp | 14 +- lib/src/Parser/Types.cpp | 283 +-- lib/src/Tokenizer/Tokenizer.cpp | 19 +- 10 files changed, 581 insertions(+), 2565 deletions(-) delete mode 100644 lib/src/Parser/AST/AST.cpp diff --git a/.gitignore b/.gitignore index 5a3697e..b368a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ build/** install/** +cpm-package-lock.cmake + TODO.md diff --git a/frontend/src/main.cpp b/frontend/src/main.cpp index 73cea02..1f689a0 100644 --- a/frontend/src/main.cpp +++ b/frontend/src/main.cpp @@ -1,8 +1,15 @@ #include #include +#include #include +std::string sanitizePath(std::string_view path) { + namespace fs = std::filesystem; + fs::path p{ path }; + return p.filename().string(); +} + int main(int argc, char **argv) { using namespace arti::lang; @@ -12,7 +19,7 @@ int main(int argc, char **argv) { } std::ifstream file; - file.open(argv[1]); + file.open(sanitizePath(argv[1])); if (! file.is_open()) { std::println("Failed to open file {}", argv[1]); @@ -22,7 +29,7 @@ int main(int argc, char **argv) { std::string buffer{ std::istreambuf_iterator(file), std::istreambuf_iterator() }; - auto parser = Parser{ buffer }; + auto parser = Parser{ sanitizePath(argv[1]), buffer }; auto res = parser.parse(); @@ -40,5 +47,10 @@ int main(int argc, char **argv) { auto ast = std::move(res).value(); + std::println("# AST"); + std::println("```markdown"); std::println("{}", ast::toString(ast)); + std::println("```"); + + // std::println("{}", ast::toDot(ast)); } diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index 14015ec..34a970a 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -126,22 +126,13 @@ namespace arti::lang { Expected parseInfLoopStatement(); - Expected consume(TokenV type, std::string_view expected_name) { - auto peeked = tokenizer.peek(); + Expected consume(TokenV type, std::string_view message) { + auto peeked = tokenizer.peekExpect(type, message); if (! peeked) { return Unexpected<>{ std::move(peeked).error() }; } - if (peeked->value != type) { - return langException( - peeked->line, - peeked->column, - toString(*peeked), - expected_name - ); - } - std::ignore = tokenizer.consume(); return peeked; @@ -163,6 +154,16 @@ namespace arti::lang { return true; } + Expected match(TokenV type, std::size_t offset = 0) { + auto peeked = tokenizer.peek(offset); + + if (! peeked) { + return Unexpected<>{ std::move(peeked).error() }; + } + + return (peeked->value == type); + } + private: std::string unitName; std::string sourceCode; diff --git a/lib/include/artichoke/Tokenizer/Tokenizer.hpp b/lib/include/artichoke/Tokenizer/Tokenizer.hpp index 33c5485..73b474c 100644 --- a/lib/include/artichoke/Tokenizer/Tokenizer.hpp +++ b/lib/include/artichoke/Tokenizer/Tokenizer.hpp @@ -25,7 +25,11 @@ namespace arti::lang { Expected consume(std::size_t n = 1) noexcept; Expected peek(std::size_t n = 0) noexcept; - Expected peekExpect(std::size_t n, TokenV tokenType) noexcept; + Expected peekExpect( + TokenV tokenType, + std::string_view message = "", + std::size_t n = 0 + ) noexcept; bool finished() const noexcept; diff --git a/lib/src/Parser/AST/AST.cpp b/lib/src/Parser/AST/AST.cpp deleted file mode 100644 index c7e156c..0000000 --- a/lib/src/Parser/AST/AST.cpp +++ /dev/null @@ -1,1742 +0,0 @@ -#include - -#include -#include -#include - -#include - -namespace arti::lang::ast { - std::string createPadding(std::size_t padding) { - return std::views::repeat(' ') - | std::views::take(padding) - | std::ranges::to(); - } - - std::string toString(const ModuleDeclNode &, std::size_t); - std::string toString(const StructDeclNode &, std::size_t); - std::string toString(const EnumDeclNode &, std::size_t); - std::string toString(const FunctionDeclNode &, std::size_t); - std::string toString(const ImportDeclNode &, std::size_t); - std::string toString(const AliasDeclNode &, std::size_t); - std::string toString(const EnumMemberNode &, std::size_t); - std::string toString(const StructMemberNode &, std::size_t); - std::string toString(const GenericParamNode &, std::size_t); - std::string toString(const FunctionParamNode &, std::size_t); - std::string toString(const TopLevelDeclNode &, std::size_t); - std::string toString(const ModuleInnerDeclNode &, std::size_t); - std::string toString(const TypeNode &, std::size_t); - std::string toString(const GenericTypeNode &, std::size_t); - std::string toString(const IdentifierTypeNode &, std::size_t); - std::string toString(const NamespacedTypeNode &, std::size_t); - std::string toString(const NamespacedIdentifierNode &, std::size_t); - std::string toString(const TypeExpressionNode &, std::size_t); - std::string toString(const CharLtrlNode &, std::size_t); - std::string toString(const NullLtrlNode &, std::size_t); - std::string toString(const StringLtrlNode &, std::size_t); - std::string toString(const FloatLtrlNode &, std::size_t); - std::string toString(const IntegerLtrlNode &, std::size_t); - std::string toString(const BooleanLtrlNode &, std::size_t); - std::string toString(const StructLtrlNode &, std::size_t); - std::string toString(const SliceLtrlNode &, std::size_t); - std::string toString(const StructLtrlNamedFieldInitNode &, std::size_t); - std::string toString(const StructLtrlPositionalInitNode &, std::size_t); - std::string toString(const StructLtrlNamedInitializerNode &, std::size_t); - std::string toString(const StructLtrlPositionalInitializerNode &,std::size_t); - std::string toString(const StructLtrlInitializerNode &, std::size_t); - std::string toString(const IdentifierExprNode &, std::size_t); - std::string toString(const UnaryExprNode &, std::size_t); - std::string toString(const BinaryExprNode &, std::size_t); - std::string toString(const AssignExprNode &, std::size_t); - std::string toString(const CompoundAssignExprNode &, std::size_t); - std::string toString(const FunctionCallExprNode &, std::size_t); - std::string toString(const SliceAccessExprNode &, std::size_t); - std::string toString(const SliceRangeExprNode &, std::size_t); - std::string toString(const MemberAccessExprNode &, std::size_t); - std::string toString(const PointerAccessExprNode &, std::size_t); - std::string toString(const ScopeAccessExprNode &, std::size_t); - std::string toString(const ReflectionExprNode &, std::size_t); - std::string toString(const SliceCreationExprNode &, std::size_t); - std::string toString(const SliceLengthExprNode &, std::size_t); - std::string toString(const SlicePtrExprNode &, std::size_t); - std::string toString(const ExpressionNode &, std::size_t); - std::string toString(const CodeBlockStmtNode &, std::size_t); - std::string toString(const VariableStmtNode &, std::size_t); - std::string toString(const IfStmtNode &, std::size_t); - std::string toString(const ElseStmtNode &, std::size_t); - std::string toString(const DeferStmtNode &, std::size_t); - std::string toString(const ErrDeferStmtNode &, std::size_t); - std::string toString(const ReturnStmtNode &, std::size_t); - std::string toString(const BreakStmtNode &, std::size_t); - std::string toString(const ContinueStmtNode &, std::size_t); - std::string toString(const MatchStmtNode &, std::size_t); - std::string toString(const SwitchStmtNode &, std::size_t); - std::string toString(const CForStmtNode &, std::size_t); - std::string toString(const RangeForStmtNode &, std::size_t); - std::string toString(const WhileStmtNode &, std::size_t); - std::string toString(const DoWhileStmtNode &, std::size_t); - std::string toString(const InfLoopStmtNode &, std::size_t); - std::string toString(const ExpressionStmtNode &, std::size_t); - std::string toString(const MatchCaseNode &, std::size_t); - std::string toString(const SwitchCaseNode &, std::size_t); - std::string toString(const StatementNode &, std::size_t); - std::string toString(const ElseBranchNode &, std::size_t); - std::string toString(const DeferableNode &, std::size_t); - std::string toString(const PreLoopStmtNode &, std::size_t); - std::string toString(UnaryOperator op); - std::string toString(BinaryOperator op); - std::string toString(CompoundAssignOperator op); - - std::string toString(const AST &tree, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - std::vector childs; - - ss << std::format("- CompilationUnit {}", tree->unitName); - - for (const auto &d : tree->declarations) { - ss << std::format("\n{} - {}", paddingStr, toString(d, padding + 2)); - } - - return ss.str(); - } - - std::string toString(const TopLevelDeclNode &tlDecl, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const ModuleDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const StructDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const EnumDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const FunctionDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ImportDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const AliasDeclNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, tlDecl); - } - - std::string toString(const ModuleDeclNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "Module {} {}", - toString(node->name, padding), - node->isExported ? "[exported]" : "" - ); - - if (! node->aliasDeclarations.empty()) { - ss << std::format("\n{} - AliasedTypes", paddingStr); - - for (const auto &al : node->aliasDeclarations) { - ss - << std::format("\n{} - {}", paddingStr, toString(al, padding + 4)); - } - } - - if (! node->innerDeclarations.empty()) { - ss << std::format("\n{} - InnerDeclarations", paddingStr); - - for (const auto &id : node->innerDeclarations) { - ss - << std::format("\n{} - {}", paddingStr, toString(id, padding + 4)); - } - } - - if (! node->childModules.empty()) { - ss << std::format("\n{} - ChildModules", paddingStr); - - for (const auto &cm : node->childModules) { - ss - << std::format("\n{} - {}", paddingStr, toString(cm, padding + 4)); - } - } - - return ss.str(); - } - - std::string toString(const StructDeclNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "Struct {} {}", - node->name, - node->isExported ? "[exported]" : "" - ); - - if (! node->genericParams.empty()) { - ss << std::format("\n{} - GenericParams", paddingStr); - - for (const auto &gp : node->genericParams) { - ss - << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); - } - } - - if (! node->structMembers.empty()) { - ss << std::format("\n{} - FieldMembers", paddingStr); - - for (const auto &sm : node->structMembers) { - ss - << std::format("\n{} - {}", paddingStr, toString(sm, padding + 4)); - } - } - - return ss.str(); - } - - std::string toString(const EnumDeclNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "Enum {} {}", - node->name, - node->isExported ? "[exported]" : "" - ); - - if (! node->genericParams.empty()) { - ss << std::format("\n{} - GenericParams", paddingStr); - - for (const auto &gp : node->genericParams) { - ss - << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); - } - } - - ss << std::format("\n{} - EnumValues", paddingStr); - - for (const auto &em : node->enumMembers) { - ss << std::format("\n{} - {}", paddingStr, toString(em, padding + 4)); - } - - return ss.str(); - } - - std::string toString(const FunctionDeclNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "Function {} {}", - node->name, - node->isExported ? "[exported]" : "" - ); - - if (node->returnType) { - ss << std::format("\n{} - ReturnType", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->returnType, padding + 4) - ); - } - - if (! node->genericParams.empty()) { - ss << std::format("\n{} - GenericParams", paddingStr); - - for (const auto &gp : node->genericParams) { - ss - << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); - } - } - - if (! node->functionParams.empty()) { - ss << std::format("\n{} - FunctionParams", paddingStr); - - for (const auto &gp : node->functionParams) { - ss - << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); - } - } - - ss << std::format("\n{} - FunctionBody", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->functionBody, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const ImportDeclNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "Import {}{}", - toString(node->importTarget, padding), - node->importAll ? "::*" : "" - ); - - return ss.str(); - } - - std::string toString(const AliasDeclNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "Alias {}", - node->alias - ); - - ss << std::format("\n{} - AliasedType", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->target, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const EnumMemberNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "EnumMember {}", - node->name - ); - - if (node->type) { - ss << std::format("\n{} - StorageType", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->type, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const StructMemberNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << std::format( - "StructMember {}", - node->name - ); - - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->type, padding + 2) - ); - - return ss.str(); - } - - std::string toString(const GenericParamNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("typename {}", node->name); - } - - std::string toString(const FunctionParamNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - if (node->isThis) { - ss << std::format( - "This", - paddingStr - ); - } - else { - ss << std::format( - "Param {}", - node->name - ); - } - - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->type, padding + 2) - ); - - return ss.str(); - } - - std::string toString(const ModuleInnerDeclNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const StructDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const EnumDeclNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const FunctionDeclNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const TypeNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "Type"; - - if (!node->qualifiers.empty()) { - ss << std::format("\n{} - Qualifiers", paddingStr); - - for (const auto &q : node->qualifiers) { - switch(q) { - case TypeQualifier::Pointer: - ss << std::format("\n{} - Pointer (*)", paddingStr); - break; - case TypeQualifier::Slice: - ss << std::format("\n{} - Slice ([])", paddingStr); - break; - case TypeQualifier::Mutable: - ss << std::format("\n{} - Mutable ($)", paddingStr); - break; - case TypeQualifier::Optional: - ss << std::format("\n{} - Optional (?)", paddingStr); - break; - default: - break; - } - } - } - - ss << std::format("\n{} - BaseType", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->baseType, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const GenericTypeNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "GenericType"; - ss << std::format("\n{} - BaseType", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->baseType, padding + 4) - ); - - if (!node->genericArgs.empty()) { - ss << std::format("\n{} - GenericArgs", paddingStr); - for (const auto &ga : node->genericArgs) { - ss - << std::format("\n{} - {}", paddingStr, toString(ga, padding + 4)); - } - } - - return ss.str(); - } - - std::string toString(const IdentifierTypeNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "IdentifierType"; - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->typeName, padding + 2) - ); - - return ss.str(); - } - - std::string toString(const NamespacedTypeNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "NamespacedType"; - - ss << std::format("\n{} - BaseType", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->baseType, padding + 4) - ); - - ss << std::format("\n{} - TypeName", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - node->typeName - ); - - return ss.str(); - } - - std::string - toString(const NamespacedIdentifierNode &node, std::size_t padding) { - std::ignore = padding; - return node->identParts - | std::views::join_with(std::string_view{"::"}) - | std::ranges::to(); - } - - std::string toString(const TypeExpressionNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const GenericTypeNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const IdentifierTypeNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const NamespacedTypeNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const CharLtrlNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("CharLiteral '{}'", node->value); - } - - std::string toString(const NullLtrlNode &node, std::size_t padding) { - std::ignore = node; - std::ignore = padding; - return std::format("NullLiteral 'null'"); - } - - std::string toString(const StringLtrlNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("StringLiteral \"{}\"", node->value); - } - - std::string toString(const FloatLtrlNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("FloatLiteral {}", node->value); - } - - std::string toString(const IntegerLtrlNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("IntegerLiteral {}", node->value); - } - - std::string toString(const BooleanLtrlNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("BooleanLiteral {}", node->value ? "true" : "false"); - } - - std::string toString(const StructLtrlNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "StructLiteral"; - - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->type, padding + 2) - ); - - if (node->initializer) { - ss << std::format("\n{} - Elements", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->initializer, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const SliceLtrlNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SliceLiteral"; - - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->type, padding + 4) - ); - - if (node->initializer) { - ss << std::format("\n{} - Elements", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->initializer, padding + 4) - ); - } - - return ss.str(); - } - - std::string - toString(const StructLtrlNamedFieldInitNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "FieldInitializer"; - - ss << std::format("\n{} - Field '{}'", paddingStr, node->fieldName); - ss << std::format("\n{} - Value", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->fieldValue, padding + 4) - ); - - return ss.str(); - } - - std::string - toString(const StructLtrlPositionalInitNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "PositionalInitializer"; - - ss << std::format("\n{} - Value", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->fieldValue, padding + 4) - ); - - return ss.str(); - } - - std::string - toString(const StructLtrlNamedInitializerNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "InitializerList"; - ss << std::format("\n{} - Elements", paddingStr); - - for (const auto &ele : node->fields) { - ss << std::format( - "\n{} - {}", - paddingStr, - toString(ele, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString( - const StructLtrlPositionalInitializerNode &node, - std::size_t padding - ) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "InitializerList"; - ss << std::format("\n{} - Elements", paddingStr); - - for (const auto &[idx, ele] : std::views::enumerate(node->fields)) { - ss << std::format( - "\n{} - [{}] {}", - paddingStr, - idx, - toString(ele, padding + 4) - ); - } - - return ss.str(); - } - - std::string - toString(const StructLtrlInitializerNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const StructLtrlNamedInitializerNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const StructLtrlPositionalInitializerNode &node) - -> std::string { return toString(node, padding); }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const IdentifierExprNode &node, std::size_t padding) { - std::ignore = padding; - return std::format("Identifier {}", node->identifierName); - } - - std::string toString(const UnaryExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "UnaryExpression"; - ss << std::format("\n{} - Operator {}", paddingStr, toString(node->op)); - - ss << std::format("\n{} - Operand", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->right, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const BinaryExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "BinaryExpression"; - ss << std::format("\n{} - Operator {}", paddingStr, toString(node->op)); - - ss << std::format("\n{} - Left", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->left, padding + 4) - ); - - ss << std::format("\n{} - Right", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->right, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const AssignExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "AssignExpression"; - ss << std::format("\n{} - Left", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->left, padding + 4) - ); - - ss << std::format("\n{} - Right", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->right, padding + 4) - ); - - return ss.str(); - } - - std::string - toString(const CompoundAssignExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "CompoundAssignExpression"; - ss << std::format("\n{} - Operator {}", paddingStr, toString(node->op)); - - ss << std::format("\n{} - Left", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->left, padding + 4) - ); - - ss << std::format("\n{} - Right", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->left, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const FunctionCallExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "FunctionCallExpression"; - ss << std::format("\n{} - Callee", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->callee, padding + 4) - ); - - if (! node->arguments.empty()) { - ss << std::format("\n{} - Arguments", paddingStr); - - for (const auto &arg : node->arguments) { - ss << std::format( - "\n{} - {}", - paddingStr, - toString(arg, padding + 4) - ); - } - } - - return ss.str(); - } - - std::string toString(const SliceAccessExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SliceAccessExpression"; - ss << std::format("\n{} - Slice", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->slice, padding + 4) - ); - - ss << std::format("\n{} - Index", paddingStr); - - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->index, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const SliceRangeExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SliceRangeExpression"; - ss << std::format("\n{} - Slice", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->slice, padding + 4) - ); - - if (node->start) { - ss << std::format("\n{} - Start", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->start, padding + 4) - ); - } - - if (node->end) { - ss << std::format("\n{} - End", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->end, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const MemberAccessExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "MemberAccessExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->object, padding + 4) - ); - - ss << std::format("\n{} - Member '{}'", paddingStr, node->memberName); - - return ss.str(); - } - - std::string toString(const PointerAccessExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "PointerAccessExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->object, padding + 4) - ); - - ss << std::format("\n{} - Member '{}'", paddingStr, node->memberName); - - return ss.str(); - } - - std::string toString(const ScopeAccessExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ScopeAccessExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->scope, padding + 4) - ); - - if (! node->genericParams.empty()) { - ss << std::format("\n{} - GenericParams", paddingStr); - - for (const auto &gp : node->genericParams) { - ss - << std::format("\n{} - {}", paddingStr, toString(gp, padding + 4)); - } - } - - ss << std::format("\n{} - Member '{}'", paddingStr, node->memberName); - - return ss.str(); - } - - std::string toString(const ReflectionExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ReflectionExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->object, padding + 4) - ); - - if (node->attribute) { - ss - << std::format("\n{} - Attribute '{}'", paddingStr, *node->attribute); - } - - return ss.str(); - } - - std::string toString(const SliceCreationExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SliceCreationExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->object, padding + 4) - ); - - ss << std::format("\n{} - Length", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->length, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const SliceLengthExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SliceLengthExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->object, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const SlicePtrExprNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SlicePtrExpression"; - ss << std::format("\n{} - Object", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->object, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const ExpressionNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const CharLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const NullLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const StringLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const FloatLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const IntegerLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const BooleanLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const StructLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SliceLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const IdentifierExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const UnaryExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const BinaryExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const AssignExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const CompoundAssignExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const FunctionCallExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SliceAccessExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SliceRangeExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const MemberAccessExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const PointerAccessExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ScopeAccessExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SliceCreationExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SliceLengthExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SlicePtrExprNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ReflectionExprNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const CodeBlockStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "CodeBlock"; - - for (const auto &gp : node->statements) { - ss << std::format("\n{} - {}", paddingStr, toString(gp, padding + 2)); - } - - return ss.str(); - } - - std::string toString(const VariableStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "VariableDeclaration"; - ss << std::format("\n{} - Name '{}'", paddingStr, node->name); - ss << std::format( - "\n{} - Mutability '{}'", - paddingStr, - node->mutability == Mutability::Mutable ? "let" : "def" - ); - - if (node->type) { - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->type, padding + 4) - ); - } - - if (node->initializer) { - ss << std::format("\n{} - Initializer", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->initializer, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const IfStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "IfStatement"; - - if (node->unwrappedVar) { - ss << std::format( - "\n{} - UnwrappedVar '{}'", - paddingStr, - *node->unwrappedVar - ); - } - - ss << std::format("\n{} - Condition", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->condition, padding + 4) - ); - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - if (node->elseBranch) { - ss << std::format("\n{} - ElseBranch", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->elseBranch, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const ElseStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ElseStatement"; - - if (node->unwrappedVar) { - ss << std::format( - "\n{} - UnwrappedVar '{}'", - paddingStr, - *node->unwrappedVar - ); - } - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const DeferStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "DeferStatement"; - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const ErrDeferStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ErrDeferStatement"; - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const ReturnStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ReturnStatement"; - - if (node->value) { - ss << std::format("\n{} - Expression", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->value, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const BreakStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "BreakStatement"; - - if (node->label) { - ss << std::format("\n{} - Label '{}'", paddingStr, *node->label); - } - - return ss.str(); - } - - std::string toString(const ContinueStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ContinueStatement"; - - if (node->label) { - ss << std::format("\n{} - Label '{}'", paddingStr, *node->label); - } - - return ss.str(); - } - - std::string toString(const MatchStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "MatchStatement"; - - ss << std::format("\n{} - Value", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->value, padding + 4) - ); - - if (!node->matchCases.empty()) { - ss << std::format("\n{} - Cases", paddingStr); - for (const auto &cas : node->matchCases) { - ss << std::format( - "\n{} - {}", - paddingStr, - toString(cas, padding + 4) - ); - } - } - - if (node->defaultCase) { - ss << std::format("\n{} - DefaultCase", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->defaultCase, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const SwitchStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "SwitchStatement"; - - ss << std::format("\n{} - Value", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->value, padding + 4) - ); - - if (!node->switchCases.empty()) { - ss << std::format("\n{} - Cases", paddingStr); - for (const auto &cas : node->switchCases) { - ss << std::format( - "\n{} - {}", - paddingStr, - toString(cas, padding + 4) - ); - } - } - - if (node->defaultCase) { - ss << std::format("\n{} - DefaultCase", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->defaultCase, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const CForStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "CForStatement"; - - if (node->label) { - ss << std::format( - "\n{} - Label '{}'", - paddingStr, - *node->label - ); - } - - if (node->preLoop) { - ss << std::format("\n{} - PreLoop", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->preLoop, padding + 4) - ); - } - - ss << std::format("\n{} - Condition", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->condition, padding + 4) - ); - - if (node->postLoop) { - ss << std::format("\n{} - PostLoop", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->postLoop, padding + 4) - ); - } - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const RangeForStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ForRangeStatement"; - - if (node->label) { - ss << std::format( - "\n{} - Label '{}'", - paddingStr, - *node->label - ); - } - - ss << std::format( - "\n{} - Variable '{}'{}", - paddingStr, - node->varName, - node->varMutability == Mutability::Mutable - ? " [mut]" - : "" - ); - - ss << std::format("\n{} - Range", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->range, padding + 4) - ); - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const WhileStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "WhileStatement"; - - if (node->label) { - ss << std::format( - "\n{} - Label '{}'", - paddingStr, - *node->label - ); - } - - if (node->unwrappedVar) { - ss << std::format( - "\n{} - UnwrappedVar '{}'", - paddingStr, - *node->unwrappedVar - ); - } - - ss << std::format("\n{} - Condition", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->condition, padding + 4) - ); - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - if (node->elseBranch) { - ss << std::format("\n{} - ElseBranch", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(*node->elseBranch, padding + 4) - ); - } - - return ss.str(); - } - - std::string toString(const DoWhileStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "DoWhileStatement"; - - if (node->label) { - ss << std::format( - "\n{} - Label '{}'", - paddingStr, - *node->label - ); - } - - ss << std::format("\n{} - Condition", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->condition, padding + 4) - ); - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const InfLoopStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "InfLoopStatement"; - - if (node->label) { - ss << std::format( - "\n{} - Label '{}'", - paddingStr, - *node->label - ); - } - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const ExpressionStmtNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "ExpressionStatement"; - - ss << std::format("\n{} - Expression", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->expression, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const MatchCaseNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "MatchCase"; - - if (node->unwrappedVar) { - ss << std::format( - "\n{} - UnwrappedVar '{}'", - paddingStr, - *node->unwrappedVar - ); - } - - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->matchType, padding + 2) - ); - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const SwitchCaseNode &node, std::size_t padding) { - std::stringstream ss; - auto paddingStr = createPadding(padding); - - ss << "MatchCase"; - - ss << std::format("\n{} - Matcher", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->matchExpr, padding + 4) - ); - - ss << std::format("\n{} - Body", paddingStr); - ss << std::format( - "\n{} - {}", - paddingStr, - toString(node->body, padding + 4) - ); - - return ss.str(); - } - - std::string toString(const StatementNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const VariableStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const IfStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const DeferStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ErrDeferStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ReturnStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const BreakStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ContinueStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const MatchStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SwitchStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const CForStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const RangeForStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const WhileStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const DoWhileStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const InfLoopStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ExpressionStmtNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const ElseBranchNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const ElseStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const IfStmtNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const DeferableNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const ExpressionStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const CodeBlockStmtNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(const PreLoopStmtNode &node, std::size_t padding) { - auto visitor = OverloadSet{ - [padding](const VariableStmtNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const ExpressionStmtNode &node) -> std::string { - return toString(node, padding); - }, - }; - - return std::visit(visitor, node); - } - - std::string toString(UnaryOperator op) { - using enum UnaryOperator; - - switch (op) { - case Not: return "Not (!)"; - case Minus: return "Minus (-)"; - case BitNot: return "BitNot (~)"; - case Ampersand: return "Ampersand (&)"; - case Star: return "Star (*)"; - default: std::unreachable(); break; - } - - std::unreachable(); - } - - std::string toString(BinaryOperator op) { - using enum BinaryOperator; - - switch (op) { - case Equal: return "Equal (==)"; - case NotEqual: return "NotEqual (!=)"; - case GreaterThan: return "GreaterThan (>)"; - case LessThan: return "LessThan (<)"; - case GreaterEqual: return "GreaterEqual (>=)"; - case LessEqual: return "LessEqual (<=)"; - case BitAnd: return "BitAnd (&)"; - case BitXor: return "BitXor (^)"; - case BitOr: return "BitOr (|)"; - case LeftShift: return "LeftShift (<<)"; - case RightShift: return "RightShift (>>)"; - case Adition: return "Adition (+)"; - case Substraction: return "Substraction (-)"; - case Multiplication: return "Multiplication (*)"; - case Division: return "Division (/)"; - case Modulo: return "Modulo (%)"; - case BoolAnd: return "BoolAnd (&&)"; - case BoolOr: return "BoolOr (||)"; - default: std::unreachable(); break; - } - - std::unreachable(); - } - - std::string toString(CompoundAssignOperator op) { - using enum CompoundAssignOperator; - - switch (op) { - case Addition: return "Addition (+)"; - case Substraction: return "Substraction (-)"; - case Multiplication: return "Multiplication (*)"; - case Division: return "Division (/)"; - case Modulo: return "Modulo (%)"; - case BitAnd: return "BitAnd (&)"; - case BitOr: return "BitOr (|)"; - case LeftShift: return "LeftShift (<<)"; - case RightShift: return "RightShift (>>)"; - case BoolAnd: return "BoolAnd (&&)"; - case BoolOr: return "BoolOr (||)"; - default: std::unreachable(); break; - } - - std::unreachable(); - } - -} // namespace arti::lang::ast diff --git a/lib/src/Parser/Declarations.cpp b/lib/src/Parser/Declarations.cpp index 85810d7..67be10e 100644 --- a/lib/src/Parser/Declarations.cpp +++ b/lib/src/Parser/Declarations.cpp @@ -4,24 +4,21 @@ namespace arti::lang { Expected> Parser::parseTopLevelDeclaration() { - auto peekToken = tokenizer.peek(); - 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::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( @@ -32,13 +29,12 @@ namespace arti::lang { ); } - auto node = parseImportDeclaration(); - - if (! node) { + if (auto node = parseImportDeclaration(); ! node) { return Unexpected<>{ std::move(node).error() }; } - - return ast::TopLevelDeclNode{ std::move(node).value() }; + else { + return ast::TopLevelDeclNode{ std::move(node).value() }; + } } else if (peekToken->value == TokenV::kwUsing) { if (exportable) { @@ -50,68 +46,48 @@ namespace arti::lang { ); } - auto node = parseAliasDeclaration(); - - if (! node) { + if (auto node = parseAliasDeclaration(); ! node) { return Unexpected<>{ std::move(node).error() }; } - - return ast::TopLevelDeclNode{ std::move(node).value() }; + else { + return ast::TopLevelDeclNode{ std::move(node).value() }; + } } else if (peekToken->value == TokenV::kwModule) { - auto node = parseModuleDeclaration(); - - if (! node) { + if (auto node = parseModuleDeclaration(); ! node) { return Unexpected<>{ std::move(node).error() }; } - - (*node)->isExported = exportable; - - return ast::TopLevelDeclNode{ std::move(node).value() }; + else { + (*node)->isExported = exportable; + return ast::TopLevelDeclNode{ std::move(node).value() }; + } } else if (peekToken->value == TokenV::kwStruct) { - auto node = parseStructDeclaration(); - - if (! node) { + if (auto node = parseStructDeclaration(); ! node) { return Unexpected<>{ std::move(node).error() }; } - - (*node)->isExported = exportable; - - return ast::TopLevelDeclNode{ std::move(node).value() }; + else { + (*node)->isExported = exportable; + return ast::TopLevelDeclNode{ std::move(node).value() }; + } } else if (peekToken->value == TokenV::kwEnum) { - auto node = parseEnumDeclaration(); - - if (! node) { + if (auto node = parseEnumDeclaration(); ! node) { return Unexpected<>{ std::move(node).error() }; } - - (*node)->isExported = exportable; - - return ast::TopLevelDeclNode{ std::move(node).value() }; + else { + (*node)->isExported = exportable; + return ast::TopLevelDeclNode{ std::move(node).value() }; + } } else if (peekToken->value == TokenV::kwFn) { - auto node = parseFunctionDeclaration(); - - if (! node) { + if (auto node = parseFunctionDeclaration(); ! node) { return Unexpected<>{ std::move(node).error() }; } - - (*node)->isExported = exportable; - - return ast::TopLevelDeclNode{ std::move(node).value() }; - } - else if (peekToken->value == TokenV::opRSquirly) { - return std::nullopt; - } - else if (peekToken->value != TokenV::tkEOF) { - return langException( - peekToken->line, - peekToken->column, - toString(*peekToken), - "top level declaration" - ); + else { + (*node)->isExported = exportable; + return ast::TopLevelDeclNode{ std::move(node).value() }; + } } return std::nullopt; @@ -119,176 +95,120 @@ namespace arti::lang { Expected Parser::parseImportDeclaration() { auto node = ast::MakeNode(); - node->importAll = false; - auto kw = tokenizer.peek(); - - if (! kw) { + if (auto kw = consume(TokenV::kwImport, "'import' keyword"); ! kw) { return Unexpected<>{ std::move(kw).error() }; } + else { + node->location = { kw->line, kw->column }; + } - node->location.line = kw->line; - node->location.column = kw->column; - - std::ignore = tokenizer.consume(); - - auto target = parseNamespacedIdentifier(); - - if (! target) { + if (auto target = parseNamespacedIdentifier(); ! 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() }; + else { + node->importTarget = std::move(target).value(); } - if (peekNext->value == TokenV::opAccess) { - auto peekStar = tokenizer.peek(1); - - if (! peekStar) { - return Unexpected<>{ std::move(peekStar).error() }; + 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(); - if (peekStar->value != TokenV::opStar) { return langException( - peekStar->line, - peekStar->column, - toString(*peekStar), + star->line, + star->column, + toString(*star), "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( - peekNext->line, - peekNext->column, - toString(*peekNext) - ); + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; } - std::ignore = tokenizer.consume(); - return node; } Expected Parser::parseAliasDeclaration() { auto node = ast::MakeNode(); - auto kw = tokenizer.peek(); - - if (! kw) { + if (auto kw = consume(TokenV::kwUsing, "'using' keyword"); ! 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() }; + else { + node->location = { kw->line, kw->column }; } - if (aliased->value != TokenV::tkIdentifier) { - return langException( - aliased->line, - aliased->column, - toString(*aliased), - "identifier" - ); + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->alias = ident->strValue; } - std::ignore = tokenizer.consume(); - node->alias = aliased->strValue; - - auto eq = tokenizer.peekExpect(0, TokenV::opAssign); - - if (! eq) { + if (auto eq = consume(TokenV::opAssign, "'='"); ! eq) { return Unexpected{ std::move(eq).error() }; } - std::ignore = tokenizer.consume(); - - auto type = parseType(); - - if (! type) { + if (auto type = parseType(); ! 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() }; + else { + node->target = std::move(type).value(); } - std::ignore = tokenizer.consume(); + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } return node; } Expected Parser::parseModuleDeclaration() { auto node = ast::MakeNode(); + auto decl = ast::Optional{}; + bool keepParsing = true; - auto kw = tokenizer.peek(); - - if (! kw) { + if (auto kw = consume(TokenV::kwModule, "'module' keyword"); ! 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() }; + else { + node->location = { kw->line, kw->column }; } - node->name = std::move(moduleName).value(); - - auto lsquirly = tokenizer.peekExpect(0, TokenV::opLSquirly); - - if (! lsquirly) { - return Unexpected{ std::move(lsquirly).error() }; + if (auto name = parseNamespacedIdentifier(); ! name) { + return Unexpected<>{ std::move(name).error() }; + } + else { + node->name = std::move(name).value(); } - std::ignore = tokenizer.consume(); - - bool keepParsing = true; - auto decl = ast::Optional{}; - + if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) { + return Unexpected<>{ std::move(lsquirly).error() }; + } + while (keepParsing) { - auto idecl = parseTopLevelDeclaration(); - - if (! idecl) { - return Unexpected<>{ std::move(idecl).error() }; - } - - decl = std::move(idecl).value(); - - if (! decl.has_value()) { - keepParsing = false; + 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(*decl)) { node->childModules.push_back( std::get(std::move(*decl)) @@ -325,100 +245,60 @@ namespace arti::lang { } } - auto rsquirly = tokenizer.peekExpect(0, TokenV::opRSquirly); - - if (! rsquirly) { - return Unexpected{ std::move(rsquirly).error() }; + if (auto rsquirly = consume(TokenV::opRSquirly, "'{'"); ! rsquirly) { + return Unexpected<>{ std::move(rsquirly).error() }; } - std::ignore = tokenizer.consume(); - return node; } Expected Parser::parseStructDeclaration() { auto node = ast::MakeNode(); - auto kw = tokenizer.peek(); - - if (! kw) { + if (auto kw = consume(TokenV::kwStruct, "'struct' keyword"); ! kw) { return Unexpected<>{ std::move(kw).error() }; } + else { + node->location = { kw->line, kw->column }; + } - node->location.line = kw->line; - node->location.column = kw->column; - - std::ignore = tokenizer.consume(); - - auto name = tokenizer.peek(); - - if (! name) { + if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) { return Unexpected<>{ std::move(name).error() }; } - - if (name->value != TokenV::tkIdentifier) { - return langException( - 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() }; + else { + node->name = name->strValue; } - if (peekNext->value == TokenV::opLt) { - std::ignore = tokenizer.consume(); - - auto generics = parseGenericParamsList(); - - if (! generics) { - return Unexpected<>{ std::move(generics).error() }; + 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(); - 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 (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) { + return Unexpected<>{ std::move(hasGt ).error() }; + } } } - if (peekNext->value != TokenV::opLSquirly) { - return langException( - peekNext->line, - peekNext->column, - toString(*peekNext), - "'{'" - ); + if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) { + return Unexpected<>{ std::move(lsquirly).error() }; } - std::ignore = tokenizer.consume(); - auto members = parseStructMembersList(); - - if (! members) { + if (auto members = parseStructMembersList(); ! 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() }; + else { + node->structMembers = std::move(members).value(); + } + + if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) { + return Unexpected<>{ std::move(rsquirly).error() }; } - std::ignore = tokenizer.consume(); return node; } @@ -426,86 +306,50 @@ namespace arti::lang { Expected Parser::parseEnumDeclaration() { auto node = ast::MakeNode(); - auto kw = tokenizer.peek(); - - if (! kw) { + if (auto kw = consume(TokenV::kwEnum, "'enum' keyword"); ! kw) { return Unexpected<>{ std::move(kw).error() }; } + else { + node->location = { kw->line, kw->column }; + } - node->location.line = kw->line; - node->location.column = kw->column; - - std::ignore = tokenizer.consume(); - - auto name = tokenizer.peek(); - - if (! name) { + if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) { return Unexpected<>{ std::move(name).error() }; } - - if (name->value != TokenV::tkIdentifier) { - return langException( - 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() }; + else { + node->name = name->strValue; } - if (peekNext->value == TokenV::opLt) { - std::ignore = tokenizer.consume(); - - auto generics = parseGenericParamsList(); - - if (! generics) { - return Unexpected<>{ std::move(generics).error() }; + 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(); - 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 (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) { + return Unexpected<>{ std::move(hasGt ).error() }; + } } } - if (peekNext->value != TokenV::opLSquirly) { - return langException( - peekNext->line, - peekNext->column, - toString(*peekNext), - "'{'" - ); + if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) { + return Unexpected<>{ std::move(lsquirly).error() }; } - std::ignore = tokenizer.consume(); - auto members = parseEnumMembersList(); - - if (! members) { + if (auto members = parseEnumMembersList(); ! 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() }; + else { + node->enumMembers = std::move(members).value(); + } + + if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) { + return Unexpected<>{ std::move(rsquirly).error() }; } - std::ignore = tokenizer.consume(); return node; } @@ -520,21 +364,48 @@ namespace arti::lang { return Unexpected{ std::move(peekToken).error() }; } - while (peekToken->value != TokenV::opGt) { - auto param = parseGenericParam(); + bool keepParsing = true; - if (! param) { + if (auto comma = tokenizer.peek(); + comma and comma->value == TokenV::opComma) { + return langException( + comma->line, + comma->column, + toString(*comma), + "'typename' keyword" + ); + } + + while (keepParsing) { + if (auto param = parseGenericParam(); ! 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() }; + 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( + peekToken->line, + peekToken->column, + toString(*peekToken), + "',' or '>'" + ); + } + else { + keepParsing = false; + } + } + } + } return paramsList; } @@ -542,41 +413,19 @@ namespace arti::lang { Expected Parser::parseGenericParam() { auto node = ast::MakeNode(); - if (auto kwTypename = tokenizer.peekExpect(0, TokenV::kwTypename); - ! kwTypename) { - return Unexpected{ std::move(kwTypename).error() }; + if (auto kw = consume(TokenV::kwTypename, "'typename' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; } else { - node->location = { .line = kwTypename->line, - .column = kwTypename->column }; + node->location = { kw->line, kw->column }; } - std::ignore = tokenizer.consume(); - if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! 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( - sc->line, - sc->column, - toString(*sc), - "'}' or ','" - ); - } return node; } @@ -585,26 +434,38 @@ namespace arti::lang { Parser::parseStructMembersList() { auto membersList = ast::Vector{}; - auto peekToken = tokenizer.peek(); - - if (! peekToken) { - return Unexpected{ std::move(peekToken).error() }; + if (auto comma = tokenizer.peek(); + comma and comma->value == TokenV::opComma) { + return langException( + comma->line, + comma->column, + toString(*comma), + "identifier or '}'" + ); } - while (peekToken->value != TokenV::opRSquirly) { - auto member = parseStructMember(); + bool keepParsing = true; - if (! member) { + while (keepParsing) { + if (auto member = parseStructMember(); ! member) { return Unexpected{ std::move(member).error() }; } + else { + membersList.push_back(std::move(member).value()); + } - membersList.push_back(std::move(member).value()); + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected<>{ std::move(comma).error() }; + } - peekToken = tokenizer.peek(); - - if (! peekToken) { + if (auto peekToken = tokenizer.peek(); ! peekToken) { return Unexpected{ std::move(peekToken).error() }; } + else { + if (peekToken->value == TokenV::opRSquirly) { + keepParsing = false; + } + } } return membersList; @@ -613,45 +474,23 @@ namespace arti::lang { Expected Parser::parseStructMember() { auto node = ast::MakeNode(); - if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) { - return Unexpected{ std::move(ident).error() }; + if (auto ident = consume(TokenV::tkIdentifier, "'identifier'"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; } else { - node->location = { .line = ident->line, .column = ident->column }; - + node->location = { ident->line, ident->column }; node->name = ident->strValue; } - std::ignore = tokenizer.consume(); - if (auto colon = tokenizer.peekExpect(0, TokenV::opColon); ! colon) { + if (auto colon = consume(TokenV::opColon, "':'"); ! colon) { return Unexpected{ std::move(colon).error() }; } - std::ignore = tokenizer.consume(); - - auto type = parseType(); - - if (! type) { + + if (auto type = parseType(); ! 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( - sc->line, - sc->column, - toString(*sc), - "'}' or ','" - ); + else { + node->type = std::move(type).value(); } return node; @@ -660,26 +499,38 @@ namespace arti::lang { Expected> Parser::parseEnumMembersList() { auto membersList = ast::Vector{}; - auto peekToken = tokenizer.peek(); - - if (! peekToken) { - return Unexpected{ std::move(peekToken).error() }; + if (auto comma = tokenizer.peek(); + comma and comma->value == TokenV::opComma) { + return langException( + comma->line, + comma->column, + toString(*comma), + "identifier or '}'" + ); } - while (peekToken->value != TokenV::opRSquirly) { - auto member = parseEnumMember(); + bool keepParsing = true; - if (! member) { + while (keepParsing) { + if (auto member = parseEnumMember(); ! member) { return Unexpected{ std::move(member).error() }; } + else { + membersList.push_back(std::move(member).value()); + } - membersList.push_back(std::move(member).value()); + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected<>{ std::move(comma).error() }; + } - peekToken = tokenizer.peek(); - - if (! peekToken) { + if (auto peekToken = tokenizer.peek(); ! peekToken) { return Unexpected{ std::move(peekToken).error() }; } + else { + if (peekToken->value == TokenV::opRSquirly) { + keepParsing = false; + } + } } return membersList; @@ -688,55 +539,28 @@ namespace arti::lang { Expected Parser::parseEnumMember() { auto node = ast::MakeNode(); - if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) { - return Unexpected{ std::move(ident).error() }; + if (auto ident = consume(TokenV::tkIdentifier, "'identifier'"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; } else { - node->location = { .line = ident->line, .column = ident->column }; - + node->location = { ident->line, ident->column }; node->name = ident->strValue; } - std::ignore = tokenizer.consume(); - auto sc = tokenizer.peek(); - - if (! sc) { - return Unexpected{ std::move(sc).error() }; + if (auto hasLParen = matchAndConsume(TokenV::opLParen); ! hasLParen) { + return Unexpected{ std::move(hasLParen).error() }; } - - if (sc->value == TokenV::opLParen) { - std::ignore = tokenizer.consume(); - - auto type = parseType(); - - if (! type) { + else if (hasLParen.value()) { + if (auto type = parseType(); ! type) { return Unexpected{ std::move(type).error() }; } + else { + node->type = std::move(type).value(); - node->type = std::move(type).value(); - - if (auto closeP = tokenizer.peekExpect(0, TokenV::opRParen); ! closeP) { - return Unexpected{ std::move(closeP).error() }; + if (auto hasRParen = consume(TokenV::opRParen, "')'"); ! hasRParen) { + return Unexpected{ std::move(hasRParen).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( - sc->line, - sc->column, - toString(*sc), - "'}' or ','" - ); } return node; @@ -745,147 +569,74 @@ namespace arti::lang { Expected Parser::parseFunctionDeclaration() { auto node = ast::MakeNode(); - auto kw = tokenizer.peek(); - - if (! kw) { + if (auto kw = consume(TokenV::kwFn, "'fn' keyword"); ! kw) { return Unexpected<>{ std::move(kw).error() }; } + else { + node->location = { kw->line, kw->column }; + } - node->location.line = kw->line; - node->location.column = kw->column; - - std::ignore = tokenizer.consume(); - - auto name = tokenizer.peek(); - - if (! name) { + if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) { return Unexpected<>{ std::move(name).error() }; } - - if (name->value != TokenV::tkIdentifier) { - return langException( - 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() }; + else { + node->name = name->strValue; } - 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 (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) { + return Unexpected<>{ std::move(hasLt).error() }; } - - if (peekNext->value != TokenV::opLParen) { - return langException( - peekNext->line, - peekNext->column, - toString(*peekNext), - "'('" - ); - } - std::ignore = tokenizer.consume(); - - peekNext = tokenizer.peek(); - - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; - } - - if (peekNext->value != TokenV::opRParen) { - auto params = parseFunctionParamsList(); - - if (! params) { + else if (hasLt.value()) { + if (auto params = parseGenericParamsList(); ! params) { return Unexpected<>{ std::move(params).error() }; } + else { + node->genericParams = std::move(params).value(); - node->functionParams = std::move(params).value(); - - peekNext = tokenizer.peek(); - - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; - } - - if (peekNext->value != TokenV::opRParen) { - return langException( - peekNext->line, - peekNext->column, - toString(*peekNext), - "')'" - ); - } - } - std::ignore = tokenizer.consume(); - - peekNext = tokenizer.peek(); - - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; - } - - if (peekNext->value == TokenV::opArrow) { - std::ignore = tokenizer.consume(); - - auto retType = parseType(); - - if (! retType) { - return Unexpected<>{ std::move(retType).error() }; - } - - node->returnType = std::move(retType).value(); - - peekNext = tokenizer.peek(); - - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; + if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) { + return Unexpected<>{ std::move(hasGt ).error() }; + } } } - if (auto openS = tokenizer.peekExpect(0, TokenV::opLSquirly); ! openS) { - return Unexpected{ std::move(openS).error() }; - } - std::ignore = tokenizer.consume(); - - auto body = parseCodeBlock(); - - if (! body) { - return Unexpected{ std::move(body).error() }; + if (auto lparen = consume(TokenV::opLParen, "'('"); ! lparen) { + return Unexpected<>{ std::move(lparen).error() }; } - node->functionBody = std::move(body).value(); - - if (auto closeS = tokenizer.peekExpect(0, TokenV::opRSquirly); ! closeS) { - return Unexpected{ std::move(closeS).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(); } - std::ignore = tokenizer.consume(); return node; } @@ -894,41 +645,54 @@ namespace arti::lang { Parser::parseFunctionParamsList() { auto params = ast::Vector{}; - auto peekToken = tokenizer.peek(); - - if (! peekToken) { - return Unexpected<>{ std::move(peekToken).error() }; + if (auto comma = tokenizer.peek(); + comma and comma->value == TokenV::opComma) { + return langException( + comma->line, + comma->column, + toString(*comma), + "identifier, 'this' keyword or ')'" + ); } - if (peekToken->value == TokenV::kwThis) { + if (auto hasThis = match(TokenV::kwThis); ! hasThis) { + return Unexpected<>{ std::move(hasThis).value() }; + } + else if (hasThis.value()) { auto thisParam = parseFunctionParamThis(); - - if (! thisParam) { + if (auto thisṔaram = parseFunctionParamThis(); ! thisParam) { return Unexpected<>{ std::move(thisParam).error() }; } + else { + params.push_back(std::move(thisParam).value()); + } - params.push_back(std::move(thisParam).value()); - - peekToken = tokenizer.peek(); - - if (! peekToken) { - return Unexpected<>{ std::move(peekToken).error() }; + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected<>{ std::move(comma).error() }; } } + + bool keepParsing = true; - while (peekToken->value != TokenV::opRParen) { - auto param = parseFunctionParam(); - - if (! param) { + while (keepParsing) { + if (auto param = parseFunctionParam(); ! param) { return Unexpected<>{ std::move(param).error() }; } + else { + params.push_back(std::move(param).value()); - params.push_back(std::move(param).value()); + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected<>{ std::move(comma).error() }; + } - peekToken = tokenizer.peek(); - - if (! peekToken) { - return Unexpected<>{ std::move(peekToken).error() }; + if (auto peekToken = tokenizer.peek(); ! peekToken) { + return Unexpected{ std::move(peekToken).error() }; + } + else { + if (peekToken->value == TokenV::opRParen) { + keepParsing = false; + } + } } } @@ -938,45 +702,23 @@ namespace arti::lang { Expected Parser::parseFunctionParamThis() { auto node = ast::MakeNode(); - node->isThis = true; - - auto kw = tokenizer.peek(); - - if (! kw) { - return Unexpected<>{ std::move(kw).error() }; + 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"; - node->location.line = kw->line; - node->location.column = kw->column; - node->name = "this"; - - 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::opRParen) { - return langException( - sc->line, - sc->column, - toString(*sc), - "'}' or ','" - ); - } + if (auto type = parseType(); ! type) { + return Unexpected<>{ std::move(type).error() }; + } + else { + node->type = std::move(type).value(); + } + } return node; } @@ -986,47 +728,24 @@ namespace arti::lang { node->isThis = false; - if (auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); ! ident) { - return Unexpected{ std::move(ident).error() }; + 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; - - node->name = ident->strValue; - - std::ignore = tokenizer.consume(); } - if (auto colon = tokenizer.peekExpect(0, TokenV::opColon); ! colon) { + if (auto colon = consume(TokenV::opColon, "':'"); ! colon) { return Unexpected{ std::move(colon).error() }; } - std::ignore = tokenizer.consume(); - auto type = parseType(); - - if (! type) { + if (auto type = parseType(); ! 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::opRParen) { - return langException( - sc->line, - sc->column, - toString(*sc), - "'}' or ','" - ); + else { + node->type = std::move(type).value(); } return node; diff --git a/lib/src/Parser/Parser.cpp b/lib/src/Parser/Parser.cpp index c27476f..3f4119d 100644 --- a/lib/src/Parser/Parser.cpp +++ b/lib/src/Parser/Parser.cpp @@ -14,30 +14,28 @@ namespace arti::lang { Expected Parser::parse() { auto unit = ast::MakeNode(); - auto tlDecl = ast::Optional{}; + auto decl = ast::Optional{}; bool keepParsing = true; unit->unitName = this->unitName; while (keepParsing) { - if (auto ok = parseTopLevelDeclaration(); ok) { - tlDecl = std::move(ok).value(); + if (auto ok = parseTopLevelDeclaration(); ! ok) { + return Unexpected<>{ std::move(ok).error() }; + } + else { + decl = std::move(ok).value(); - if (! tlDecl.has_value()) { + if (! decl.has_value()) { keepParsing = false; } else { - unit->declarations.push_back(std::move(tlDecl).value()); + unit->declarations.push_back(std::move(decl).value()); } } - else { - return Unexpected<>{ std::move(ok).error() }; - } } - auto eof = tokenizer.peekExpect(0, TokenV::tkEOF); - - if (! eof) { + if (auto eof = consume(TokenV::tkEOF, "end of compilation unit"); ! eof) { return Unexpected<>{ std::move(eof).error() }; } diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp index 5008542..9e41c81 100644 --- a/lib/src/Parser/Statements.cpp +++ b/lib/src/Parser/Statements.cpp @@ -8,6 +8,10 @@ namespace arti::lang { auto stmt = ast::Optional{}; bool keepParsing = true; + if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) { + return Unexpected<>{ std::move(lsquirly).error() }; + } + while (keepParsing) { if (auto ok = parseStatement(); ok) { stmt = std::move(ok).value(); @@ -24,12 +28,20 @@ namespace arti::lang { } } + if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) { + return Unexpected<>{ std::move(rsquirly).error() }; + } + return node; } Expected> Parser::parseStatement() { - + /* TODO: Implement statement parsing logic. + * This is intentionally stubbed while the parser architecture + * is being in development. + * Currently, the compiler is in a 'Declarations-Only' state. */ + return std::nullopt; } diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp index e10d4a8..49c6eb2 100644 --- a/lib/src/Parser/Types.cpp +++ b/lib/src/Parser/Types.cpp @@ -1,138 +1,130 @@ #include +#include + namespace arti::lang { Expected Parser::parseNamespacedIdentifier() { auto node = ast::MakeNode(); - auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); + bool keepParsing = true; - 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) { - ident = tokenizer.peek(1); - - if (! ident) { - return node; + while (keepParsing) { + if (auto ident = match(TokenV::tkIdentifier); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else if (ident.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->location = { .line = ident->line, .column = ident->column }; + node->identParts.emplace_back(ident->strValue); + } } else { - if (ident->value != TokenV::tkIdentifier) { - return node; - } - - node->identParts.emplace_back(ident->strValue); + return node; } - std::ignore = tokenizer.consume(2); - peekNext = tokenizer.peek(); - - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; + if (auto access = match(TokenV::opAccess); ! access) { + return Unexpected<>{ std::move(access).error() }; + } + else if (access.value()) { + if (auto ident = match(TokenV::tkIdentifier, 1); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else if (not ident.value()) { + keepParsing = false; + } + else { + if (auto colon = consume(TokenV::opAccess, "':'"); ! colon) { + return Unexpected<>{ std::move(colon).error() }; + } + } + } + else { + keepParsing = false; } } - + return node; } Expected Parser::parseType() { auto node = ast::MakeNode(); + auto currentNode = ast::TypeExpressionNode{}; - if (auto peekNext = tokenizer.peek(); ! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; + if (auto nextToken = tokenizer.peek(); ! nextToken) { + return Unexpected<>{ std::move(nextToken).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->location = { .line = nextToken->line, .column = nextToken->column }; + } + if (auto ident = match(TokenV::tkIdentifier); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else if (not ident.value()) { + if (auto qualifiers = parseTypeQualifiers(); ! qualifiers) { + return Unexpected<>{ std::move(qualifiers).error() }; + } + else { node->qualifiers = std::move(qualifiers).value(); } } - auto identType = parseNamespacedIdentifier(); - - if (! identType) { + if (auto identType = parseNamespacedIdentifier(); ! identType) { return Unexpected<>{ std::move(identType).error() }; } + else { + currentNode = ast::MakeNode(); - auto currentNode = ast::TypeExpressionNode{}; + std::get(currentNode)->location = + (*identType)->location; - currentNode = ast::MakeNode(); - - std::get(currentNode)->location = - (*identType)->location; - - std::get(currentNode)->typeName = - std::move(identType).value(); - - auto peekNext = tokenizer.peek(); - - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; + std::get(currentNode)->typeName = + std::move(identType).value(); } - if (peekNext->value == TokenV::opLt) { - std::ignore = tokenizer.consume(); - - auto args = parseGenericArgumentsList(); - - if (! args) { - return Unexpected<>{ std::move(args).error() }; + if (auto lt = matchAndConsume(TokenV::opLt); ! lt) { + return Unexpected<>{ std::move(lt).error() }; + } + else if (lt.value()) { + if (auto genericArgs = parseGenericArgumentsList(); ! genericArgs) { + return Unexpected<>{ std::move(genericArgs).error() }; } + else { + auto genParamsNode = ast::MakeNode(); - if (auto closeG = tokenizer.peekExpect(0, TokenV::opGt); ! closeG) { - return Unexpected<>{ std::move(closeG).error() }; - } - std::ignore = tokenizer.consume(); + genParamsNode->location = std::visit( + [](const auto &node) { return node->location; }, + currentNode + ); - auto newNode = ast::MakeNode(); + genParamsNode->baseType = std::move(currentNode); + genParamsNode->genericArgs = std::move(genericArgs).value(); + currentNode = std::move(genParamsNode); - 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() }; + if (auto gt = consume(TokenV::opGt, "'>'"); ! gt) { + return Unexpected<>{ std::move(gt).error() }; + } } } - while (peekNext->value == TokenV::opAccess) { - std::ignore = tokenizer.consume(); + bool keepParsing = false; - auto ident = tokenizer.peekExpect(0, TokenV::tkIdentifier); + if (auto access = matchAndConsume(TokenV::opAccess); ! access) { + return Unexpected<>{ std::move(access).error() }; + } + else if (access.value()) { + keepParsing = true; + } - if (! ident) { + while (keepParsing) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { return Unexpected<>{ std::move(ident).error() }; } else { - std::ignore = tokenizer.consume(); - auto newNode = ast::MakeNode(); newNode->location = std::visit( @@ -143,44 +135,38 @@ namespace arti::lang { newNode->typeName = ident->strValue; newNode->baseType = std::move(currentNode); currentNode = std::move(newNode); + } - peekNext = tokenizer.peek(); + if (auto lt = matchAndConsume(TokenV::opLt); ! lt) { + return Unexpected<>{ std::move(lt).error() }; + } + else if (lt.value()) { + if (auto genericArgs = parseGenericArgumentsList(); ! genericArgs) { + return Unexpected<>{ std::move(genericArgs).error() }; + } + else { + auto genParamsNode = ast::MakeNode(); - if (! peekNext) { - return Unexpected<>{ std::move(peekNext).error() }; + genParamsNode->location = std::visit( + [](const auto &node) { return node->location; }, + currentNode + ); + + genParamsNode->baseType = std::move(currentNode); + genParamsNode->genericArgs = std::move(genericArgs).value(); + currentNode = std::move(genParamsNode); + + if (auto gt = consume(TokenV::opGt, "'>'"); ! gt) { + return Unexpected<>{ std::move(gt).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(); - - 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() }; - } + if (auto access = matchAndConsume(TokenV::opAccess); ! access) { + return Unexpected<>{ std::move(access).error() }; + } + else { + keepParsing = access.value(); } } @@ -235,7 +221,7 @@ namespace arti::lang { case opLBracket: std::ignore = tokenizer.consume(); - peekToken = tokenizer.peekExpect(0, opRBracket); + peekToken = tokenizer.peekExpect(opRBracket); if (! peekToken) { return Unexpected<>{ std::move(peekToken).error() }; @@ -269,29 +255,46 @@ namespace arti::lang { return Unexpected{ std::move(peekToken).error() }; } - while (peekToken->value != TokenV::opGt) { - auto type = parseType(); + bool keepParsing = true; - if (! type) { + if (auto comma = tokenizer.peek(); + comma and comma->value == TokenV::opComma) { + return langException( + comma->line, + comma->column, + toString(*comma), + "type" + ); + } + + while (keepParsing) { + if (auto type = parseType(); ! 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() }; + else { + args.push_back(std::move(type).value()); } - if (peekToken->value == TokenV::opComma) { - std::ignore = tokenizer.consume(); - - peekToken = tokenizer.peek(); - - if (! peekToken) { + 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( + peekToken->line, + peekToken->column, + toString(*peekToken), + "',' or '>'" + ); + } + else { + keepParsing = false; + } + } } } diff --git a/lib/src/Tokenizer/Tokenizer.cpp b/lib/src/Tokenizer/Tokenizer.cpp index f93e51d..03a3a73 100644 --- a/lib/src/Tokenizer/Tokenizer.cpp +++ b/lib/src/Tokenizer/Tokenizer.cpp @@ -106,8 +106,15 @@ namespace arti::lang { return tokensBuffer.at(n); } - Expected - Tokenizer::peekExpect(std::size_t n, TokenV tokenType) noexcept { + Expected Tokenizer::peekExpect( + TokenV tokenType, + std::string_view message, + std::size_t n + ) noexcept { + if (message.empty()) { + message = toString(tokenType); + } + if (tokensBuffer.size() > (n + 1)) { auto tokenAt = tokensBuffer.at(n); @@ -116,7 +123,7 @@ namespace arti::lang { tokenAt.line, tokenAt.column, toString(tokenAt), - toString(tokenType) + message ); } @@ -125,7 +132,7 @@ namespace arti::lang { auto token = peek(n); - if (!token) { + if (! token) { return token; } @@ -137,7 +144,7 @@ namespace arti::lang { tokensBuffer.pop_back(); tokensBuffer.push_back(*token); token->column += 1; - token->strValue = std::string_view{token->strValue.begin() + 1, 1}; + token->strValue = std::string_view{ token->strValue.begin() + 1, 1 }; tokensBuffer.push_back(*token); token = peekTok; } @@ -147,7 +154,7 @@ namespace arti::lang { token->line, token->column, toString(*token), - toString(tokenType) + message ); } -- 2.52.0 From b99f3586dc56644a293304460467b26ebafb1efa Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Thu, 25 Dec 2025 13:12:41 -0600 Subject: [PATCH 04/15] chore(license): Added NOTICE header to all source files Signed-off-by: erick-alcachofa --- add-notice.sh | 27 +++++++++++++++++++ frontend/src/main.cpp | 22 +++++++++++++++ lib/include/artichoke/Coroutine/Generator.hpp | 22 +++++++++++++++ lib/include/artichoke/Coroutine/Utils.hpp | 22 +++++++++++++++ lib/include/artichoke/Parser/AST/AST.hpp | 22 +++++++++++++++ lib/include/artichoke/Parser/AST/Common.hpp | 22 +++++++++++++++ .../artichoke/Parser/AST/Declarations.hpp | 22 +++++++++++++++ .../artichoke/Parser/AST/Expressions.hpp | 22 +++++++++++++++ lib/include/artichoke/Parser/AST/Literals.hpp | 22 +++++++++++++++ .../artichoke/Parser/AST/Statements.hpp | 22 +++++++++++++++ lib/include/artichoke/Parser/AST/Types.hpp | 22 +++++++++++++++ lib/include/artichoke/Parser/Parser.hpp | 22 +++++++++++++++ lib/include/artichoke/Tokenizer/Token.hpp | 22 +++++++++++++++ lib/include/artichoke/Tokenizer/Tokenizer.hpp | 22 +++++++++++++++ .../artichoke/Tokenizer/TokenizerRange.hpp | 22 +++++++++++++++ lib/include/artichoke/Util/Demangle.hpp | 22 +++++++++++++++ lib/include/artichoke/Util/Expected.hpp | 22 +++++++++++++++ lib/include/artichoke/Util/OverloadSet.hpp | 22 +++++++++++++++ lib/include/artichoke/Util/Strings.hpp | 22 +++++++++++++++ lib/include/artichoke/Util/TrieMap.hpp | 22 +++++++++++++++ lib/src/Parser/AST/toDot.cpp | 22 +++++++++++++++ lib/src/Parser/AST/toString.cpp | 22 +++++++++++++++ lib/src/Parser/Declarations.cpp | 22 +++++++++++++++ lib/src/Parser/Expressions.cpp | 22 +++++++++++++++ lib/src/Parser/Literals.cpp | 22 +++++++++++++++ lib/src/Parser/Parser.cpp | 22 +++++++++++++++ lib/src/Parser/Statements.cpp | 22 +++++++++++++++ lib/src/Parser/Types.cpp | 22 +++++++++++++++ lib/src/Tokenizer/Token.cpp | 22 +++++++++++++++ lib/src/Tokenizer/Tokenizer.cpp | 22 +++++++++++++++ lib/src/Tokenizer/TokenizerRange.cpp | 22 +++++++++++++++ lib/src/Util/Demangle.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Api.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Comments.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Identifiers.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Keywords.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Numbers.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Operators.cpp | 22 +++++++++++++++ tests/Tokenizer/src/Strings.cpp | 22 +++++++++++++++ tests/include/Utils.hpp | 22 +++++++++++++++ 40 files changed, 885 insertions(+) create mode 100755 add-notice.sh diff --git a/add-notice.sh b/add-notice.sh new file mode 100755 index 0000000..963ec4b --- /dev/null +++ b/add-notice.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Path to your notice file +NOTICE_FILE="./NOTICE" + +# Check if notice file exists +if [ ! -f "$NOTICE_FILE" ]; then + echo "Error: $NOTICE_FILE not found!" + exit 1 +fi + +# Find all .cpp, .hpp, .h, and .cc files +# Excluding the .git directory +find . -type d -name ".git" -prune -o -type d -name "build" -prune -o -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.cc" \) -print | while read -r file; do + + # Check if the file already contains a specific keyword from your notice + # to avoid double-prepending (e.g., "Copyright") + if grep -q "Copyright" "$file"; then + echo "Skipping $file (License already exists)" + else + echo "Adding notice to $file" + # Create a temp file: notice + newline + original content + { cat "$NOTICE_FILE"; echo ""; cat "$file"; } > "$file.tmp" && mv "$file.tmp" "$file" + fi +done + +echo "Done!" diff --git a/frontend/src/main.cpp b/frontend/src/main.cpp index 1f689a0..67adcc8 100644 --- a/frontend/src/main.cpp +++ b/frontend/src/main.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include #include diff --git a/lib/include/artichoke/Coroutine/Generator.hpp b/lib/include/artichoke/Coroutine/Generator.hpp index 899f8f1..839bdbb 100644 --- a/lib/include/artichoke/Coroutine/Generator.hpp +++ b/lib/include/artichoke/Coroutine/Generator.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Coroutine/Utils.hpp b/lib/include/artichoke/Coroutine/Utils.hpp index 6a6341c..89f39eb 100644 --- a/lib/include/artichoke/Coroutine/Utils.hpp +++ b/lib/include/artichoke/Coroutine/Utils.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #define yield co_yield diff --git a/lib/include/artichoke/Parser/AST/AST.hpp b/lib/include/artichoke/Parser/AST/AST.hpp index d45cc33..8a8a10c 100644 --- a/lib/include/artichoke/Parser/AST/AST.hpp +++ b/lib/include/artichoke/Parser/AST/AST.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/AST/Common.hpp b/lib/include/artichoke/Parser/AST/Common.hpp index 740ba63..2d90531 100644 --- a/lib/include/artichoke/Parser/AST/Common.hpp +++ b/lib/include/artichoke/Parser/AST/Common.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/AST/Declarations.hpp b/lib/include/artichoke/Parser/AST/Declarations.hpp index 744eb8e..cac62f0 100644 --- a/lib/include/artichoke/Parser/AST/Declarations.hpp +++ b/lib/include/artichoke/Parser/AST/Declarations.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/AST/Expressions.hpp b/lib/include/artichoke/Parser/AST/Expressions.hpp index 64599d4..e210ea1 100644 --- a/lib/include/artichoke/Parser/AST/Expressions.hpp +++ b/lib/include/artichoke/Parser/AST/Expressions.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/AST/Literals.hpp b/lib/include/artichoke/Parser/AST/Literals.hpp index 3699b8e..e6871b7 100644 --- a/lib/include/artichoke/Parser/AST/Literals.hpp +++ b/lib/include/artichoke/Parser/AST/Literals.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/AST/Statements.hpp b/lib/include/artichoke/Parser/AST/Statements.hpp index 23834a0..f0b50a4 100644 --- a/lib/include/artichoke/Parser/AST/Statements.hpp +++ b/lib/include/artichoke/Parser/AST/Statements.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/AST/Types.hpp b/lib/include/artichoke/Parser/AST/Types.hpp index f3bf72e..040d76b 100644 --- a/lib/include/artichoke/Parser/AST/Types.hpp +++ b/lib/include/artichoke/Parser/AST/Types.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index 34a970a..eb0f82a 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Tokenizer/Token.hpp b/lib/include/artichoke/Tokenizer/Token.hpp index 5192d8a..d258958 100644 --- a/lib/include/artichoke/Tokenizer/Token.hpp +++ b/lib/include/artichoke/Tokenizer/Token.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Tokenizer/Tokenizer.hpp b/lib/include/artichoke/Tokenizer/Tokenizer.hpp index 73b474c..0bf5dda 100644 --- a/lib/include/artichoke/Tokenizer/Tokenizer.hpp +++ b/lib/include/artichoke/Tokenizer/Tokenizer.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Tokenizer/TokenizerRange.hpp b/lib/include/artichoke/Tokenizer/TokenizerRange.hpp index 63891d5..bca3f63 100644 --- a/lib/include/artichoke/Tokenizer/TokenizerRange.hpp +++ b/lib/include/artichoke/Tokenizer/TokenizerRange.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Util/Demangle.hpp b/lib/include/artichoke/Util/Demangle.hpp index 20db504..bffe357 100644 --- a/lib/include/artichoke/Util/Demangle.hpp +++ b/lib/include/artichoke/Util/Demangle.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Util/Expected.hpp b/lib/include/artichoke/Util/Expected.hpp index d439e41..5ab2933 100644 --- a/lib/include/artichoke/Util/Expected.hpp +++ b/lib/include/artichoke/Util/Expected.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Util/OverloadSet.hpp b/lib/include/artichoke/Util/OverloadSet.hpp index 5ad8bbb..3371304 100644 --- a/lib/include/artichoke/Util/OverloadSet.hpp +++ b/lib/include/artichoke/Util/OverloadSet.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once namespace arti::lang { diff --git a/lib/include/artichoke/Util/Strings.hpp b/lib/include/artichoke/Util/Strings.hpp index e011a69..cc18b4a 100644 --- a/lib/include/artichoke/Util/Strings.hpp +++ b/lib/include/artichoke/Util/Strings.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/include/artichoke/Util/TrieMap.hpp b/lib/include/artichoke/Util/TrieMap.hpp index c6cbe3c..a871ee2 100644 --- a/lib/include/artichoke/Util/TrieMap.hpp +++ b/lib/include/artichoke/Util/TrieMap.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp index 4be334a..811b923 100644 --- a/lib/src/Parser/AST/toDot.cpp +++ b/lib/src/Parser/AST/toDot.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp index c66949e..c86b264 100644 --- a/lib/src/Parser/AST/toString.cpp +++ b/lib/src/Parser/AST/toString.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/lib/src/Parser/Declarations.cpp b/lib/src/Parser/Declarations.cpp index 67be10e..00b4e79 100644 --- a/lib/src/Parser/Declarations.cpp +++ b/lib/src/Parser/Declarations.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include namespace arti::lang { diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index e69de29..c03cfc7 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -0,0 +1,22 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + diff --git a/lib/src/Parser/Literals.cpp b/lib/src/Parser/Literals.cpp index e69de29..c03cfc7 100644 --- a/lib/src/Parser/Literals.cpp +++ b/lib/src/Parser/Literals.cpp @@ -0,0 +1,22 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + diff --git a/lib/src/Parser/Parser.cpp b/lib/src/Parser/Parser.cpp index 3f4119d..637d8e1 100644 --- a/lib/src/Parser/Parser.cpp +++ b/lib/src/Parser/Parser.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include namespace arti::lang { diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp index 9e41c81..f418b64 100644 --- a/lib/src/Parser/Statements.cpp +++ b/lib/src/Parser/Statements.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include namespace arti::lang { diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp index 49c6eb2..c354de0 100644 --- a/lib/src/Parser/Types.cpp +++ b/lib/src/Parser/Types.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/lib/src/Tokenizer/Token.cpp b/lib/src/Tokenizer/Token.cpp index 9f74897..106b673 100644 --- a/lib/src/Tokenizer/Token.cpp +++ b/lib/src/Tokenizer/Token.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/lib/src/Tokenizer/Tokenizer.cpp b/lib/src/Tokenizer/Tokenizer.cpp index 03a3a73..11d7862 100644 --- a/lib/src/Tokenizer/Tokenizer.cpp +++ b/lib/src/Tokenizer/Tokenizer.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/lib/src/Tokenizer/TokenizerRange.cpp b/lib/src/Tokenizer/TokenizerRange.cpp index 299c816..f4b7f0e 100644 --- a/lib/src/Tokenizer/TokenizerRange.cpp +++ b/lib/src/Tokenizer/TokenizerRange.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/lib/src/Util/Demangle.cpp b/lib/src/Util/Demangle.cpp index 6b2569b..5fe39a4 100644 --- a/lib/src/Util/Demangle.cpp +++ b/lib/src/Util/Demangle.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Api.cpp b/tests/Tokenizer/src/Api.cpp index 68b6f50..3a61970 100644 --- a/tests/Tokenizer/src/Api.cpp +++ b/tests/Tokenizer/src/Api.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Comments.cpp b/tests/Tokenizer/src/Comments.cpp index 30cbdc6..3679a16 100644 --- a/tests/Tokenizer/src/Comments.cpp +++ b/tests/Tokenizer/src/Comments.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Identifiers.cpp b/tests/Tokenizer/src/Identifiers.cpp index b34317d..5b2841b 100644 --- a/tests/Tokenizer/src/Identifiers.cpp +++ b/tests/Tokenizer/src/Identifiers.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Keywords.cpp b/tests/Tokenizer/src/Keywords.cpp index fd85cec..cfc2c76 100644 --- a/tests/Tokenizer/src/Keywords.cpp +++ b/tests/Tokenizer/src/Keywords.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Numbers.cpp b/tests/Tokenizer/src/Numbers.cpp index 1fac2f5..bdb5e36 100644 --- a/tests/Tokenizer/src/Numbers.cpp +++ b/tests/Tokenizer/src/Numbers.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Operators.cpp b/tests/Tokenizer/src/Operators.cpp index cca6eca..d38a7db 100644 --- a/tests/Tokenizer/src/Operators.cpp +++ b/tests/Tokenizer/src/Operators.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/Tokenizer/src/Strings.cpp b/tests/Tokenizer/src/Strings.cpp index a16a57c..b518a0a 100644 --- a/tests/Tokenizer/src/Strings.cpp +++ b/tests/Tokenizer/src/Strings.cpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #include #include diff --git a/tests/include/Utils.hpp b/tests/include/Utils.hpp index 84f9d46..f42f02d 100644 --- a/tests/include/Utils.hpp +++ b/tests/include/Utils.hpp @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include -- 2.52.0 From f83f7761e7ebd3bebc0aa0f09563cf848b94b69f Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Thu, 25 Dec 2025 13:17:08 -0600 Subject: [PATCH 05/15] chore(license): Added NOTICE header to all source files Signed-off-by: erick-alcachofa --- lib/cmake/inputs/Info.hpp.in | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/cmake/inputs/Info.hpp.in b/lib/cmake/inputs/Info.hpp.in index 3ddea3f..5afa5a5 100644 --- a/lib/cmake/inputs/Info.hpp.in +++ b/lib/cmake/inputs/Info.hpp.in @@ -1,3 +1,25 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + #pragma once #include -- 2.52.0 From 923b8d7e2d8907eee669bab8eb8016a8f321bb1a Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Thu, 25 Dec 2025 13:35:50 -0600 Subject: [PATCH 06/15] refactor(parser): move parser utility methods to source file Signed-off-by: erick-alcachofa Relocate core parsing utility methods from the header to the implementation file to reduce header bloat and improve compilation times. - **Parser API**: Moved the definitions of `consume()`, `matchAndConsume()`, and `match()` from `Parser.hpp` to `Parser.cpp`. - **Cleanup**: Removed an unused `` include in `Types.cpp` discovered during the refactor. - **Organization**: Methods are now declared in the header and defined in the source file, maintaining a cleaner separation between interface and implementation. --- lib/include/artichoke/Parser/Parser.hpp | 47 +++++-------------------- lib/src/Parser/Parser.cpp | 38 ++++++++++++++++++++ lib/src/Parser/Types.cpp | 2 -- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index eb0f82a..eddba4e 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -40,6 +40,15 @@ namespace arti::lang { Expected parse(); + Expected + consume(TokenV type, std::string_view message); + + Expected + matchAndConsume(TokenV type); + + Expected + match(TokenV type, std::size_t offset = 0); + Expected> parseTopLevelDeclaration(); @@ -148,44 +157,6 @@ namespace arti::lang { Expected parseInfLoopStatement(); - Expected consume(TokenV type, std::string_view message) { - auto peeked = tokenizer.peekExpect(type, message); - - if (! peeked) { - return Unexpected<>{ std::move(peeked).error() }; - } - - std::ignore = tokenizer.consume(); - - return peeked; - } - - Expected matchAndConsume(TokenV type) { - auto peeked = tokenizer.peek(); - - if (! peeked) { - return Unexpected<>{ std::move(peeked).error() }; - } - - if (peeked->value != type) { - return false; - } - - std::ignore = tokenizer.consume(); - - return true; - } - - Expected match(TokenV type, std::size_t offset = 0) { - auto peeked = tokenizer.peek(offset); - - if (! peeked) { - return Unexpected<>{ std::move(peeked).error() }; - } - - return (peeked->value == type); - } - private: std::string unitName; std::string sourceCode; diff --git a/lib/src/Parser/Parser.cpp b/lib/src/Parser/Parser.cpp index 637d8e1..a701eb8 100644 --- a/lib/src/Parser/Parser.cpp +++ b/lib/src/Parser/Parser.cpp @@ -64,4 +64,42 @@ namespace arti::lang { return unit; } + Expected Parser::consume(TokenV type, std::string_view message) { + auto peeked = tokenizer.peekExpect(type, message); + + if (! peeked) { + return Unexpected<>{ std::move(peeked).error() }; + } + + std::ignore = tokenizer.consume(); + + return peeked; + } + + Expected Parser::matchAndConsume(TokenV type) { + auto peeked = tokenizer.peek(); + + if (! peeked) { + return Unexpected<>{ std::move(peeked).error() }; + } + + if (peeked->value != type) { + return false; + } + + std::ignore = tokenizer.consume(); + + return true; + } + + Expected Parser::match(TokenV type, std::size_t offset) { + auto peeked = tokenizer.peek(offset); + + if (! peeked) { + return Unexpected<>{ std::move(peeked).error() }; + } + + return (peeked->value == type); + } + } // namespace arti::lang diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp index c354de0..4d17bcf 100644 --- a/lib/src/Parser/Types.cpp +++ b/lib/src/Parser/Types.cpp @@ -22,8 +22,6 @@ #include -#include - namespace arti::lang { Expected Parser::parseNamespacedIdentifier() { -- 2.52.0 From a3d5c0ac68ab4be61de794ce49d0f7b9d3953371 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Thu, 25 Dec 2025 23:27:06 -0600 Subject: [PATCH 07/15] feat(parser): implement full statement parsing and control flow logic Signed-off-by: erick-alcachofa Complete the transition from a declarations-only parser to a functional imperative parser. This commit introduces the implementation for all major statement types, loop constructs, and core control flow logic. - **Match Case Update**: Updated `grammar.ebnf` to use pipe delimiters `|id|` for unwrapped variables in match cases, replacing the previous parenthetical syntax. - **Labels**: Implemented loop labeling using the `ident := loop` syntax. Labels are validated to ensure they only prefix valid loop constructs. - **Labels and Ranges**: Standardized the use of the `:=` operator for both loop labels (`label := loop`) and range-for declarations (`let i := range`). - **Conditional Branches**: - Fully implemented `if` and `else` statements. - Added support for optional variable unwrapping (e.g., `if (expr) |val|`). - Supported `else if` chaining by recursively parsing if-statements within else-branches. - **Loops**: - **C-Style For**: Implemented `for (init; cond; post)` with optional initializers and post-loop expressions. - **Range For**: Implemented `for (let i := range)` with mutability controls. - **While & Do-While**: Implemented standard condition-based loops. - **Infinite Loop**: Added the explicit `loop` keyword for infinite iteration. - **Loop Dispatch**: Added a lookahead mechanism in `parseForLoopStatement` to differentiate between C-style and Range-style loops based on token positioning. - **Variables**: Implemented `let`/`def` parsing within local scopes, including type annotations and initializers. - **Defer Logic**: Implemented `defer` and `errdefer` for scope-guarded execution. - **Jumps**: Implemented `break`, `continue` (with optional label targets), and `return` (with optional expressions). - **Match & Switch**: Fully implemented branch parsing, with possible default cases via the `_` (underscore) keyword. - **Expression Integration**: Stubbed `parseExpression` in a new `Expressions.cpp` to serve as the integration point for value parsing. - **OverloadSet**: Integrated `OverloadSet` utility in `Statements.cpp` to cleanly handle AST node variant visitation for label injection. - **Error Handling**: Standardized error reporting across all new paths using `langException`, providing specific "expected" messages for delimiters and keywords. --- docs/grammar.ebnf | 2 +- lib/include/artichoke/Parser/Parser.hpp | 12 + lib/src/Parser/Expressions.cpp | 10 + lib/src/Parser/Statements.cpp | 1238 ++++++++++++++++++++++- 4 files changed, 1251 insertions(+), 11 deletions(-) diff --git a/docs/grammar.ebnf b/docs/grammar.ebnf index a99b01b..23e99cc 100644 --- a/docs/grammar.ebnf +++ b/docs/grammar.ebnf @@ -170,7 +170,7 @@ non_exportable_declaration = "switch" "(" ")" "{" * ? "}" = - ( "(" ")" )? "->" + ( "|" "|" )? "->" = "->" diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index eddba4e..11ab366 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -121,6 +121,9 @@ namespace arti::lang { Expected parseIfStatement(); + Expected + parseElseStatement(); + Expected parseDeferStatement(); @@ -142,6 +145,9 @@ namespace arti::lang { Expected parseSwitchStatement(); + Expected + parseForLoopStatement(); + Expected parseCForStatement(); @@ -157,6 +163,12 @@ namespace arti::lang { Expected parseInfLoopStatement(); + Expected + parseExpressionStatement(); + + Expected + parseExpression(); + private: std::string unitName; std::string sourceCode; diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index c03cfc7..94039e0 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -20,3 +20,13 @@ // // //============================================================================// +#include + +namespace arti::lang { + + Expected + Parser::parseExpression() { + return {}; + } + +} // namespace arti::lang diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp index f418b64..dcaddf4 100644 --- a/lib/src/Parser/Statements.cpp +++ b/lib/src/Parser/Statements.cpp @@ -22,6 +22,8 @@ #include +#include + namespace arti::lang { Expected Parser::parseCodeBlock() { @@ -35,7 +37,10 @@ namespace arti::lang { } while (keepParsing) { - if (auto ok = parseStatement(); ok) { + if (auto ok = parseStatement(); ! ok) { + return Unexpected<>{ std::move(ok).error() }; + } + else { stmt = std::move(ok).value(); if (! stmt.has_value()) { @@ -45,26 +50,1239 @@ namespace arti::lang { node->statements.push_back(std::move(stmt).value()); } } - else { - return Unexpected<>{ std::move(ok).error() }; - } } if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) { return Unexpected<>{ std::move(rsquirly).error() }; - } + } return node; } Expected> Parser::parseStatement() { - /* TODO: Implement statement parsing logic. - * This is intentionally stubbed while the parser architecture - * is being in development. - * Currently, the compiler is in a 'Declarations-Only' state. */ + ast::Optional label; + + auto peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + if (peekToken->value == TokenV::tkIdentifier) { + if (auto isLabel = match(TokenV::opLabel, 1); ! isLabel) { + return Unexpected<>{ std::move(isLabel).error() }; + } + else if (isLabel.value()) { + auto labelName = consume(TokenV::tkIdentifier, "identifier").value(); + std::ignore = consume(TokenV::opLabel, "':='"); + + peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + label = labelName.strValue; + } + } + + if (peekToken->value == TokenV::kwLet || + peekToken->value == TokenV::kwDef) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseVariableStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwIf) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseIfStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwDefer) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseDeferStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwErrDefer) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseErrDeferStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwReturn) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseReturnStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwBreak) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseBreakStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwContinue) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseContinueStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwMatch) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseMatchStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwSwitch) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseSwitchStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwFor) { + if (auto stmt = parseForLoopStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + if (label.has_value()) { + std::visit( + OverloadSet( + [&label](ast::CForStmtNode &stmt) { + stmt->label = label.value(); + }, + [&label](ast::RangeForStmtNode &stmt) { + stmt->label = label.value(); + }, + [](auto &) {} + ), + stmt.value() + ); + } + + return std::move(stmt).value(); + } + } + else if (peekToken->value == TokenV::kwWhile) { + if (auto stmt = parseWhileStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + if (label.has_value()) { + stmt.value()->label = label.value(); + } + + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwDo) { + if (auto stmt = parseDoWhileStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + if (label.has_value()) { + stmt.value()->label = label.value(); + } + + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else if (peekToken->value == TokenV::kwLoop) { + if (auto stmt = parseInfLoopStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + if (label.has_value()) { + stmt.value()->label = label.value(); + } + + return ast::StatementNode{ std::move(stmt).value() }; + } + } + else { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseExpressionStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } + return std::nullopt; } - + + Expected + Parser::parseVariableStatement() { + auto node = ast::MakeNode(); + auto peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + node->location = { + .line = peekToken->line, + .column = peekToken->column, + }; + + if (peekToken->value == TokenV::kwLet) { + node->mutability = ast::Mutability::Mutable; + } + else if (peekToken->value == TokenV::kwDef) { + node->mutability = ast::Mutability::Constant; + } + else { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "variable declaration keyword, i.e. any of ( let, def )" + ); + } + std::ignore = tokenizer.consume(); + + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->name = ident->strValue; + } + + if (auto colon = matchAndConsume(TokenV::opColon); ! colon) { + return Unexpected<>{ std::move(colon).error() }; + } + else if (colon.value()) { + if (auto type = parseType(); ! type) { + return Unexpected<>{ std::move(type).error() }; + } + else { + node->type = std::move(type).value(); + } + } + + if (auto assign = matchAndConsume(TokenV::opAssign); ! assign) { + return Unexpected<>{ std::move(assign).error() }; + } + else if(assign.value()) { + if (auto expr = parseExpression(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + node->initializer = std::move(expr).value(); + } + } + + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } + + if (node->type == std::nullopt and node->initializer == std::nullopt) { + /* TODO: Is this the correct error code for this case? */ + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "variable declaration type or initializer" + ); + } + + return node; + } + + Expected + Parser::parseIfStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwIf, "'if' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column + }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto condition = parseExpression(); ! condition) { + return Unexpected<>{ std::move(condition).error() }; + } + else { + node->condition = std::move(condition).value(); + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + if (auto unwrap = matchAndConsume(TokenV::opOr); ! unwrap) { + return Unexpected<>{ std::move(unwrap).error() }; + } + else if (unwrap.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->unwrappedVar = ident->strValue; + } + + if (auto closeUnwrap = consume(TokenV::opOr, "'|'"); ! closeUnwrap) { + return Unexpected<>{ std::move(closeUnwrap).error() }; + } + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + if (auto hasElse = match(TokenV::kwElse); ! hasElse) { + return Unexpected<>{ std::move(hasElse).error() }; + } + else if (hasElse.value()) { + if (auto elseStmt = parseElseStatement(); ! elseStmt) { + return Unexpected<>{ std::move(elseStmt).error() }; + } + else { + node->elseBranch = std::move(elseStmt).value(); + } + } + + return node; + } + + Expected + Parser::parseElseStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwElse, "'else' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto tailIf = match(TokenV::kwIf); ! tailIf) { + return Unexpected<>{ std::move(tailIf).error() }; + } + else if (tailIf.value()) { + node.reset(); + + if (auto ifStmt = parseIfStatement(); ! ifStmt) { + return Unexpected<>{ std::move(ifStmt).error() }; + } + else { + return std::move(ifStmt).value(); + } + } + + if (auto unwrap = matchAndConsume(TokenV::opOr); ! unwrap) { + return Unexpected<>{ std::move(unwrap).error() }; + } + else if (unwrap.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->unwrappedVar = ident->strValue; + } + + if (auto closeUnwrap = consume(TokenV::opOr, "'|'"); ! closeUnwrap) { + return Unexpected<>{ std::move(closeUnwrap).error() }; + } + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + return node; + } + + Expected + Parser::parseDeferStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwDefer, "'defer' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto isBlock = match(TokenV::opLSquirly); ! isBlock) { + return Unexpected<>{ std::move(isBlock).error() }; + } + else if (isBlock.value()) { + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + } + else { + auto peekToken = tokenizer.peek(); + + if (not peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + if (auto stmt = parseExpressionStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + node->body = std::move(stmt).value(); + } + } + + return node; + } + + Expected + Parser::parseErrDeferStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwErrDefer, "'errdefer' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto isBlock = match(TokenV::opLSquirly); ! isBlock) { + return Unexpected<>{ std::move(isBlock).error() }; + } + else if (isBlock.value()) { + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + } + else { + auto peekToken = tokenizer.peek(); + + if (not peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + if (auto stmt = parseExpressionStatement(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + node->body = std::move(stmt).value(); + } + } + + return node; + } + + Expected + Parser::parseReturnStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwReturn, "'return' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto skipExpr = match(TokenV::opSemicolon); ! skipExpr) { + return Unexpected<>{ std::move(skipExpr).error() }; + } + else if (! skipExpr.value()) { + if (auto stmt = parseExpression(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + node->value = std::move(stmt).value(); + } + } + + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } + + return node; + } + + Expected + Parser::parseBreakStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwBreak, "'break' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto ident = match(TokenV::tkIdentifier); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else if (ident.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->label = ident->strValue; + } + } + + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } + + return node; + } + + Expected + Parser::parseContinueStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwContinue, "'continue' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto ident = match(TokenV::tkIdentifier); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else if (ident.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->label = ident->strValue; + } + } + + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } + + return node; + } + + Expected + Parser::parseMatchStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwMatch, "'match' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto condition = parseExpression(); ! condition) { + return Unexpected<>{ std::move(condition).error() }; + } + else { + node->value = std::move(condition).value(); + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + if (auto lSquirly = consume(TokenV::opLSquirly, "'{'"); ! lSquirly) { + return Unexpected<>{ std::move(lSquirly).error() }; + } + + bool keepParsing = true; + + while (keepParsing) { + if (auto isDefault = match(TokenV::kwUnderscore); ! isDefault) { + return Unexpected<>{ std::move(isDefault).error() }; + } + else if (isDefault.value()) { + keepParsing = false; + + if (auto under = consume(TokenV::kwUnderscore, "'_' keyword"); + ! under) { + return Unexpected<>{ std::move(under).error() }; + } + + if (auto arrow = consume(TokenV::opArrow, "'->'"); ! arrow) { + return Unexpected<>{ std::move(arrow).error() }; + } + + if (auto defCase = parseCodeBlock(); ! defCase) { + return Unexpected<>{ std::move(defCase).error() }; + } + else { + node->defaultCase = std::move(defCase).value(); + } + } + else { + auto curCase = ast::MakeNode(); + + if (auto type = parseType(); ! type) { + return Unexpected<>{ std::move(type).error() }; + } + else { + curCase->location = type.value()->location; + curCase->matchType = std::move(type).value(); + } + + if (auto unwrap = matchAndConsume(TokenV::opOr); ! unwrap) { + return Unexpected<>{ std::move(unwrap).error() }; + } + else if (unwrap.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); + ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + curCase->unwrappedVar = ident->strValue; + } + + if (auto closeUnwrap = consume(TokenV::opOr, "'|'"); ! closeUnwrap) { + return Unexpected<>{ std::move(closeUnwrap).error() }; + } + } + + if (auto arrow = consume(TokenV::opArrow, "'->'"); ! arrow) { + return Unexpected<>{ std::move(arrow).error() }; + } + + if (auto caseBody = parseCodeBlock(); ! caseBody) { + return Unexpected<>{ std::move(caseBody).error() }; + } + else { + curCase->body = std::move(caseBody).value(); + } + + node->matchCases.push_back(std::move(curCase)); + } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + keepParsing = false; + } + } + + if (auto rSquirly = consume(TokenV::opRSquirly, "'}'"); ! rSquirly) { + return Unexpected<>{ std::move(rSquirly).error() }; + } + + return node; + } + + Expected + Parser::parseSwitchStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwSwitch, "'switch' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto condition = parseExpression(); ! condition) { + return Unexpected<>{ std::move(condition).error() }; + } + else { + node->value = std::move(condition).value(); + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + if (auto lSquirly = consume(TokenV::opLSquirly, "'{'"); ! lSquirly) { + return Unexpected<>{ std::move(lSquirly).error() }; + } + + bool keepParsing = true; + + while (keepParsing) { + if (auto isDefault = match(TokenV::kwUnderscore); ! isDefault) { + return Unexpected<>{ std::move(isDefault).error() }; + } + else if (isDefault.value()) { + keepParsing = false; + + if (auto under = consume(TokenV::kwUnderscore, "'_' keyword"); + ! under) { + return Unexpected<>{ std::move(under).error() }; + } + + if (auto arrow = consume(TokenV::opArrow, "'->'"); ! arrow) { + return Unexpected<>{ std::move(arrow).error() }; + } + + if (auto defCase = parseCodeBlock(); ! defCase) { + return Unexpected<>{ std::move(defCase).error() }; + } + else { + node->defaultCase = std::move(defCase).value(); + } + } + else { + auto curCase = ast::MakeNode(); + + if (auto expr = parseExpression(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + curCase->location = std::visit( + [](const auto &exprNode) -> ast::SourceLocation { + return exprNode->location; + }, + expr.value() + ); + curCase->matchExpr = std::move(expr).value(); + } + + if (auto arrow = consume(TokenV::opArrow, "'->'"); ! arrow) { + return Unexpected<>{ std::move(arrow).error() }; + } + + if (auto caseBody = parseCodeBlock(); ! caseBody) { + return Unexpected<>{ std::move(caseBody).error() }; + } + else { + curCase->body = std::move(caseBody).value(); + } + + node->switchCases.push_back(std::move(curCase)); + } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + keepParsing = false; + } + } + + if (auto rSquirly = consume(TokenV::opRSquirly, "'}'"); ! rSquirly) { + return Unexpected<>{ std::move(rSquirly).error() }; + } + + return node; + } + + Expected + Parser::parseForLoopStatement() { + bool isRange = false; + + auto isLet = match(TokenV::kwLet, 2); + if (! isLet) { + return Unexpected<>{ std::move(isLet).error() }; + } + + auto isDef = match(TokenV::kwDef, 2); + if (! isDef) { + return Unexpected<>{ std::move(isDef).error() }; + } + + if (isLet.value() || isDef.value()) { + auto hasRangeOp = match(TokenV::opLabel, 4); + + if (! hasRangeOp) { + return Unexpected<>{ std::move(hasRangeOp).error() }; + } + + isRange = hasRangeOp.value(); + } + + if (isRange) { + return parseRangeForStatement().transform( + [](auto &&val) -> ast::StatementNode { + return std::forward(val); + } + ); + } + else { + return parseCForStatement().transform( + [](auto &&val) -> ast::StatementNode { + return std::forward(val); + } + ); + } + } + + Expected + Parser::parseCForStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwFor, "'for' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto skipPre = match(TokenV::opSemicolon); ! skipPre) { + return Unexpected<>{ std::move(skipPre).error() }; + } + else if (not skipPre.value()) { + auto isLet = match(TokenV::kwLet); + if (! isLet) { + return Unexpected<>{ std::move(isLet).error() }; + } + + auto isDef = match(TokenV::kwDef); + if (! isDef) { + return Unexpected<>{ std::move(isDef).error() }; + } + + if (isLet.value() || isDef.value()) { + if (auto preStmt = parseVariableStatement(); ! preStmt) { + return Unexpected<>{ std::move(preStmt).error() }; + } + else { + node->preLoop = std::move(preStmt).value(); + } + } + else { + if (auto preStmt = parseExpressionStatement(); ! preStmt) { + return Unexpected<>{ std::move(preStmt).error() }; + } + else { + node->preLoop = std::move(preStmt).value(); + } + } + } + + if (auto condition = parseExpression(); ! condition) { + return Unexpected<>{ std::move(condition).error() }; + } + else { + node->condition = std::move(condition).value(); + } + + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } + + if (auto skipPost = match(TokenV::opRParen); ! skipPost) { + return Unexpected<>{ std::move(skipPost).error() }; + } + else if (not skipPost.value()) { + if (auto postStmt = parseExpression(); ! postStmt) { + return Unexpected<>{ std::move(postStmt).error() }; + } + else { + node->postLoop = std::move(postStmt).value(); + } + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + return node; + } + + Expected + Parser::parseRangeForStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwFor, "'for' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto isLet = matchAndConsume(TokenV::kwLet); ! isLet) { + return Unexpected<>{ std::move(isLet).error() }; + } + else if (isLet.value()) { + node->varMutability = ast::Mutability::Mutable; + } + else if (auto isDef = matchAndConsume(TokenV::kwDef); ! isDef) { + return Unexpected<>{ std::move(isDef).error() }; + } + else if (isDef.value()) { + node->varMutability = ast::Mutability::Constant; + } + else { + auto peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "variable declarator, i.e. any of ( let, def )" + ); + } + + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->varName = ident->strValue; + } + + if (auto assign = consume(TokenV::opLabel, "':='"); ! assign) { + return Unexpected<>{ std::move(assign).error() }; + } + + if (auto range = parseExpression(); ! range) { + return Unexpected<>{ std::move(range).error() }; + } + else { + node->range = std::move(range).value(); + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + return node; + } + + Expected + Parser::parseWhileStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwWhile, "'while' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column + }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto condition = parseExpression(); ! condition) { + return Unexpected<>{ std::move(condition).error() }; + } + else { + node->condition = std::move(condition).value(); + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + if (auto unwrap = matchAndConsume(TokenV::opOr); ! unwrap) { + return Unexpected<>{ std::move(unwrap).error() }; + } + else if (unwrap.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + node->unwrappedVar = ident->strValue; + } + + if (auto closeUnwrap = consume(TokenV::opOr, "'|'"); ! closeUnwrap) { + return Unexpected<>{ std::move(closeUnwrap).error() }; + } + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + if (auto hasElse = match(TokenV::kwElse); ! hasElse) { + return Unexpected<>{ std::move(hasElse).error() }; + } + else if (hasElse.value()) { + if (auto elseStmt = parseElseStatement(); ! elseStmt) { + return Unexpected<>{ std::move(elseStmt).error() }; + } + else { + node->elseBranch = std::move(elseStmt).value(); + } + } + + return node; + } + + Expected + Parser::parseDoWhileStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwDo, "'do' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column + }; + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + if (auto kw = consume(TokenV::kwWhile, "'while' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + + if (auto lParen = consume(TokenV::opLParen, "'('"); ! lParen) { + return Unexpected<>{ std::move(lParen).error() }; + } + + if (auto condition = parseExpression(); ! condition) { + return Unexpected<>{ std::move(condition).error() }; + } + else { + node->condition = std::move(condition).value(); + } + + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + return node; + } + + Expected + Parser::parseInfLoopStatement() { + auto node = ast::MakeNode(); + + if (auto kw = consume(TokenV::kwLoop, "'loop' keyword"); ! kw) { + return Unexpected<>{ std::move(kw).error() }; + } + else { + node->location = { + .line = kw->line, + .column = kw->column, + }; + } + + if (auto body = parseCodeBlock(); ! body) { + return Unexpected<>{ std::move(body).error() }; + } + else { + node->body = std::move(body).value(); + } + + return node; + } + + Expected + Parser::parseExpressionStatement() { + auto node = ast::MakeNode(); + + if (auto expr = parseExpression(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + node->location = std::visit( + [](const auto &exprNode) -> ast::SourceLocation { + return exprNode->location; + }, + expr.value() + ); + node->expression = std::move(expr).value(); + } + + if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) { + return Unexpected<>{ std::move(semicolon).error() }; + } + + return node; + } } // namespace arti::lang -- 2.52.0 From 30d64d9b65a94bd351023d056b1398542f2f7367 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Fri, 26 Dec 2025 00:28:18 -0600 Subject: [PATCH 08/15] fix(parser): support empty blocks, support nested scoping, and refine loop lookahead Signed-off-by: erick-alcachofa This commit addresses several critical issues in the recursive descent parser, specifically regarding the handling of empty constructs, statement termination, and AST representation of nested scopes. These changes bring the implementation in line with the Artichoke EBNF specification. * **CodeBlock as Statement:** Added `CodeBlockStmtNode` to the `StatementNode` variant. This allows a bare `{}` to be treated as a valid statement, enabling manual scoping within functions. * **Visitor Support:** Updated `toDot.cpp` (Graphviz) and `toString.cpp` (Pretty-print) to support the new `CodeBlockStmtNode` during AST traversal. * **Empty Member Lists:** Implemented a pre-loop check for the closing brace `}` in `parseStruct` and `parseEnum`. This prevents the parser from attempting to parse members in empty declarations (e.g., `struct Empty {}`). * **Diagnostic Accuracy:** Enhanced the member-parsing loop to provide better error context. If a member is not followed by a comma or a closing brace, the parser now explicitly suggests `',' or '}'` as the expected tokens. * **Nested Scopes:** The parser now correctly identifies a `{` at the start of a statement and dispatches to `parseCodeBlock`. * **Empty Code Blocks:** Added a guard in the block-parsing loop to check for `}` immediately after `{`, allowing functions or nested scopes to be empty. * **C-Style For-Loops:** Replaced `match` with `matchAndConsume` for the initialization semicolon. This allows the parser to correctly handle loops where the initialization is omitted (e.g., `for (; 1; 1)`). * **Correctness:** Resolves parser hangs or errors when encountering empty blocks. * **Compliance:** Fully supports the EBNF definition of zero-or-more members/statements. * **Visuals:** AST diagrams now accurately reflect nested block structures. --- .../artichoke/Parser/AST/Statements.hpp | 3 +- lib/src/Parser/AST/toDot.cpp | 1 + lib/src/Parser/AST/toString.cpp | 3 + lib/src/Parser/Declarations.cpp | 60 ++++++++++++++----- lib/src/Parser/Statements.cpp | 37 +++++++++++- 5 files changed, 88 insertions(+), 16 deletions(-) diff --git a/lib/include/artichoke/Parser/AST/Statements.hpp b/lib/include/artichoke/Parser/AST/Statements.hpp index f0b50a4..adfc6d1 100644 --- a/lib/include/artichoke/Parser/AST/Statements.hpp +++ b/lib/include/artichoke/Parser/AST/Statements.hpp @@ -93,7 +93,8 @@ namespace arti::lang::ast { WhileStmtNode, DoWhileStmtNode, InfLoopStmtNode, - ExpressionStmtNode + ExpressionStmtNode, + CodeBlockStmtNode >; using ElseBranchNode = Variant< diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp index 811b923..9e5eb19 100644 --- a/lib/src/Parser/AST/toDot.cpp +++ b/lib/src/Parser/AST/toDot.cpp @@ -1002,6 +1002,7 @@ namespace arti::lang::ast { [&g](const DoWhileStmtNode &n) { return emit(n, g); }, [&g](const InfLoopStmtNode &n) { return emit(n, g); }, [&g](const ExpressionStmtNode &n) { return emit(n, g); }, + [&g](const CodeBlockStmtNode &n) { return emit(n, g); }, }; return std::visit(visitor, node); } diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp index c86b264..9617e30 100644 --- a/lib/src/Parser/AST/toString.cpp +++ b/lib/src/Parser/AST/toString.cpp @@ -1419,6 +1419,9 @@ namespace arti::lang::ast { [padding](const ExpressionStmtNode &node) -> std::string { return toString(node, padding); }, + [padding](const CodeBlockStmtNode &node) -> std::string { + return toString(node, padding); + } }; return std::visit(visitor, node); diff --git a/lib/src/Parser/Declarations.cpp b/lib/src/Parser/Declarations.cpp index 00b4e79..fa33f69 100644 --- a/lib/src/Parser/Declarations.cpp +++ b/lib/src/Parser/Declarations.cpp @@ -468,6 +468,13 @@ namespace arti::lang { 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() }; @@ -479,13 +486,22 @@ namespace arti::lang { 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::opRSquirly) { - keepParsing = false; + else if (! comma.value()) { + if (auto peekToken = tokenizer.peek(); ! peekToken) { + return Unexpected{ std::move(peekToken).error() }; + } + else { + if (peekToken->value != TokenV::opRSquirly) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "',' or '}'" + ); + } + else { + keepParsing = false; + } } } } @@ -533,6 +549,13 @@ namespace arti::lang { 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() }; @@ -544,13 +567,22 @@ namespace arti::lang { 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::opRSquirly) { - keepParsing = false; + else if (! comma.value()) { + if (auto peekToken = tokenizer.peek(); ! peekToken) { + return Unexpected{ std::move(peekToken).error() }; + } + else { + if (peekToken->value != TokenV::opRSquirly) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "',' or '}'" + ); + } + else { + keepParsing = false; + } } } } diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp index dcaddf4..eab6553 100644 --- a/lib/src/Parser/Statements.cpp +++ b/lib/src/Parser/Statements.cpp @@ -36,6 +36,13 @@ namespace arti::lang { return Unexpected<>{ std::move(lsquirly).error() }; } + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + keepParsing = false; + } + while (keepParsing) { if (auto ok = parseStatement(); ! ok) { return Unexpected<>{ std::move(ok).error() }; @@ -50,6 +57,13 @@ namespace arti::lang { node->statements.push_back(std::move(stmt).value()); } } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + keepParsing = false; + } } if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) { @@ -300,6 +314,23 @@ namespace arti::lang { return ast::StatementNode{ std::move(stmt).value() }; } } + else if (peekToken->value == TokenV::opLSquirly) { + if (label.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "loop keyword, i.e. any of ( for, while, do, loop )" + ); + } + + if (auto stmt = parseCodeBlock(); ! stmt) { + return Unexpected<>{ std::move(stmt).error() }; + } + else { + return ast::StatementNode{ std::move(stmt).value() }; + } + } else { if (label.has_value()) { return langException( @@ -981,7 +1012,7 @@ namespace arti::lang { return Unexpected<>{ std::move(lParen).error() }; } - if (auto skipPre = match(TokenV::opSemicolon); ! skipPre) { + if (auto skipPre = matchAndConsume(TokenV::opSemicolon); ! skipPre) { return Unexpected<>{ std::move(skipPre).error() }; } else if (not skipPre.value()) { @@ -1234,6 +1265,10 @@ namespace arti::lang { return Unexpected<>{ std::move(rParen).error() }; } + if (auto rParen = consume(TokenV::opSemicolon, "';'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + return node; } -- 2.52.0 From 5762497f56c824f321c3cbc43153c45aec7922ae Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Fri, 26 Dec 2025 23:32:49 -0600 Subject: [PATCH 09/15] feat(parser): implement Pratt expression parsing and refactor operator types Signed-off-by: erick-alcachofa Overhaul the expression parsing mechanism to utilize a Pratt (top-down operator precedence) parser. This change provides a more scalable and maintainable way to handle operator precedence and associativity compared to standard recursive descent. As part of this transition, the nomenclature for operators has been refined to reflect their position in the grammar (Prefix, Infix, Postfix) rather than their arity. * Renamed `UnaryOperator` and `UnaryExpression` to `PrefixOperator` and `PrefixExpression`. * Renamed `BinaryOperator` and `BinaryExpression` to `InfixOperator` and `InfixExpression`. * Renamed `ScopeAccessExpression` to `ModuleAccessExpression`. * Introduced `PostfixOperator` enum and associated logic for function calls, slicing, and reflection attributes. * Updated `toDot.cpp` and `toString.cpp` to support the new node types and renamed operators. * Added `Pratt.hpp` and `Pratt.cpp` to define `BindingPower` and map operators to their respective precedence levels. * Added `Operators.cpp` to handle token-to-operator mapping and classification (isPrefix, isInfix, isPostfix). * Refactored `Parser::parseExpression` to implement the core Pratt loop using binding power comparisons. * Moved literal parsing logic into a dedicated `Literals.cpp`. * Implemented explicit parsing methods for `Integer`, `Float`, `Char`, `String`, `Boolean`, and `Null` literals. * Added support for `this` and `_` (underscore) as identifier expressions. * **Prefix**: `!`, `-`, `~`, `&` (MemPtr), `*` (DerefPtr). * **Infix**: Arithmetic, Comparison, Bitwise, Logical, and all Compound Assignments. * **Postfix**: `()` (Call), `[]` (Slice/Access), `.#` (Slice length), `.*` (Slice pointer), and `.@` (Reflection). * **Missing Literals**: Struct literals and Array literals are not yet implemented in the new parsing flow. * **Node Specialization**: `MemberAccess`, `PointerMemberAccess`, and `ModuleAccess` currently use generic infix logic and need to be migrated to their specific AST node types. * **Error Handling**: Literal parsing (specifically `std::stold` and `std::stoul`) needs safety checks to prevent potential exceptions during conversion. * **Diagnostics**: Refine the error message for unexpected tokens in postfix expressions to explicitly list supported operators. * **Generic Ambiguity**: Generic type/function instantiation currently causes parsing conflicts with comparison operators (e.g., `Foo`). This is a known issue that will be resolved by transitioning the grammar to a turbofish-style `::<...>` syntax. --- lib/include/artichoke/Parser/AST/Common.hpp | 53 +- .../artichoke/Parser/AST/Expressions.hpp | 28 +- lib/include/artichoke/Parser/Parser.hpp | 35 +- lib/include/artichoke/Parser/Pratt.hpp | 48 ++ lib/src/Parser/AST/toDot.cpp | 88 +-- lib/src/Parser/AST/toString.cpp | 94 ++-- lib/src/Parser/Expressions.cpp | 522 +++++++++++++++++- lib/src/Parser/Literals.cpp | 156 ++++++ lib/src/Parser/Operators.cpp | 288 ++++++++++ lib/src/Parser/Pratt.cpp | 111 ++++ 10 files changed, 1316 insertions(+), 107 deletions(-) create mode 100644 lib/include/artichoke/Parser/Pratt.hpp create mode 100644 lib/src/Parser/Operators.cpp create mode 100644 lib/src/Parser/Pratt.cpp diff --git a/lib/include/artichoke/Parser/AST/Common.hpp b/lib/include/artichoke/Parser/AST/Common.hpp index 2d90531..1749405 100644 --- a/lib/include/artichoke/Parser/AST/Common.hpp +++ b/lib/include/artichoke/Parser/AST/Common.hpp @@ -50,35 +50,60 @@ namespace arti::lang::ast { Optional, }; - enum class UnaryOperator { + enum class PrefixOperator { Uninitialized, Not, Minus, BitNot, - Ampersand, - Star, + MemPtr, + DerefPtr, }; - enum class BinaryOperator { + enum class InfixOperator { Uninitialized, + Modulo, + Addition, + Substraction, + Division, + Multiplication, Equal, NotEqual, - GreaterThan, LessThan, - GreaterEqual, + GreaterThan, LessEqual, - BitAnd, - BitXor, - BitOr, + GreaterEqual, LeftShift, RightShift, - Adition, - Substraction, - Multiplication, - Division, - Modulo, BoolAnd, BoolOr, + BitAnd, + BitOr, + BitXor, + Assignment, + ModuleAccess, + MemberAccess, + PointerMemberAccess, + AdditionAssignment, + SubstractionAssignment, + MultiplicationAssignment, + DivisionAssignment, + ModuloAssignment, + BitAndAssignment, + BitOrAssignment, + LShiftAssignment, + RShiftAssignment, + BoolAndAssignment, + BoolOrAssignment, + }; + + enum class PostfixOperator { + Uninitialized, + FunctionCall, + SliceAccess, + SliceSize, + PtrToSlice, + SliceToPtr, + Reflect, }; enum class CompoundAssignOperator { diff --git a/lib/include/artichoke/Parser/AST/Expressions.hpp b/lib/include/artichoke/Parser/AST/Expressions.hpp index e210ea1..695b541 100644 --- a/lib/include/artichoke/Parser/AST/Expressions.hpp +++ b/lib/include/artichoke/Parser/AST/Expressions.hpp @@ -32,8 +32,8 @@ namespace arti::lang::ast { /* Main declaration node types */ struct IdentifierExpression; - struct UnaryExpression; - struct BinaryExpression; + struct PrefixExpression; + struct InfixExpression; struct AssignExpression; struct CompoundAssignExpression; struct FunctionCallExpression; @@ -41,7 +41,7 @@ namespace arti::lang::ast { struct SliceRangeExpression; struct MemberAccessExpression; struct PointerAccessExpression; - struct ScopeAccessExpression; + struct ModuleAccessExpression; struct ReflectionExpression; struct SliceCreationExpression; struct SliceLengthExpression; @@ -51,8 +51,8 @@ namespace arti::lang::ast { /* Public Aliases */ using IdentifierExprNode = Ptr; - using UnaryExprNode = Ptr; - using BinaryExprNode = Ptr; + using PrefixExprNode = Ptr; + using InfixExprNode = Ptr; using AssignExprNode = Ptr; using CompoundAssignExprNode = Ptr; using FunctionCallExprNode = Ptr; @@ -60,7 +60,7 @@ namespace arti::lang::ast { using SliceRangeExprNode = Ptr; using MemberAccessExprNode = Ptr; using PointerAccessExprNode = Ptr; - using ScopeAccessExprNode = Ptr; + using ModuleAccessExprNode = Ptr; using ReflectionExprNode = Ptr; using SliceCreationExprNode = Ptr; using SliceLengthExprNode = Ptr; @@ -77,8 +77,8 @@ namespace arti::lang::ast { StructLtrlNode, SliceLtrlNode, IdentifierExprNode, - UnaryExprNode, - BinaryExprNode, + PrefixExprNode, + InfixExprNode, AssignExprNode, CompoundAssignExprNode, FunctionCallExprNode, @@ -86,7 +86,7 @@ namespace arti::lang::ast { SliceRangeExprNode, MemberAccessExprNode, PointerAccessExprNode, - ScopeAccessExprNode, + ModuleAccessExprNode, SliceCreationExprNode, SliceLengthExprNode, SlicePtrExprNode, @@ -99,16 +99,16 @@ namespace arti::lang::ast { String identifierName; }; - struct nodes::UnaryExpression { + struct nodes::PrefixExpression { SourceLocation location; - UnaryOperator op; + PrefixOperator op; ExpressionNode right; }; - struct nodes::BinaryExpression { + struct nodes::InfixExpression { SourceLocation location; - BinaryOperator op; + InfixOperator op; ExpressionNode left; ExpressionNode right; }; @@ -164,7 +164,7 @@ namespace arti::lang::ast { ExpressionNode object; }; - struct nodes::ScopeAccessExpression { + struct nodes::ModuleAccessExpression { SourceLocation location; String memberName; diff --git a/lib/include/artichoke/Parser/Parser.hpp b/lib/include/artichoke/Parser/Parser.hpp index 11ab366..4a52bed 100644 --- a/lib/include/artichoke/Parser/Parser.hpp +++ b/lib/include/artichoke/Parser/Parser.hpp @@ -167,7 +167,40 @@ namespace arti::lang { parseExpressionStatement(); Expected - parseExpression(); + parseExpression(std::uint16_t p = 0); + + Expected> + parsePrimaryExpression(); + + Expected + parsePrefixExpression(); + + Expected + parseInfixExpression(ast::ExpressionNode lhs); + + Expected + parsePostfixExpression(ast::ExpressionNode lhs); + + Expected + parseIdentifierExpression(); + + Expected + parseCharLiteral(); + + Expected + parseNullLiteral(); + + Expected + parseStringLiteral(); + + Expected + parseFloatLiteral(); + + Expected + parseIntegerLiteral(); + + Expected + parseBooleanLiteral(); private: std::string unitName; diff --git a/lib/include/artichoke/Parser/Pratt.hpp b/lib/include/artichoke/Parser/Pratt.hpp new file mode 100644 index 0000000..d24d29f --- /dev/null +++ b/lib/include/artichoke/Parser/Pratt.hpp @@ -0,0 +1,48 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + +#include + +namespace arti::lang::pratt { + + struct BindingPower { + std::uint16_t left; + std::uint16_t right; + }; + + ast::PostfixOperator getPostfixOperator(TokenV tokenType); + ast::PrefixOperator getPrefixOperator(TokenV tokenType); + ast::InfixOperator getInfixOperator(TokenV tokenType); + + bool isPostfixOperator(TokenV tokenType); + bool isPrefixOperator(TokenV tokenType); + bool isInfixOperator(TokenV tokenType); + + std::uint16_t postfixBindingPower(ast::PostfixOperator op); + std::uint16_t prefixBindingPower(ast::PrefixOperator op); + BindingPower infixBindingPower(ast::InfixOperator op); + + bool isCompoundAssignOperator(ast::InfixOperator op); + + ast::CompoundAssignOperator getCompoundOperatorType(ast::InfixOperator op); + +} diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp index 9e5eb19..50042f9 100644 --- a/lib/src/Parser/AST/toDot.cpp +++ b/lib/src/Parser/AST/toDot.cpp @@ -81,41 +81,57 @@ namespace arti::lang::ast { }; // Operator label helpers (matching AST.cpp) - std::string toString(UnaryOperator op) { - using enum UnaryOperator; + std::string toString(PrefixOperator op) { + using enum PrefixOperator; switch (op) { case Not: return "Not (!)"; case Minus: return "Minus (-)"; case BitNot: return "BitNot (~)"; - case Ampersand: return "Ampersand (&)"; - case Star: return "Star (*)"; + case MemPtr: return "MemPtr (&)"; + case DerefPtr: return "DerefPtr (*)"; default: std::unreachable(); break; } std::unreachable(); } - std::string toString(BinaryOperator op) { - using enum BinaryOperator; + std::string toString(InfixOperator op) { + using enum InfixOperator; switch (op) { - case Equal: return "Equal (==)"; - case NotEqual: return "NotEqual (!=)"; - case GreaterThan: return "GreaterThan (>)"; - case LessThan: return "LessThan (<)"; - case GreaterEqual: return "GreaterEqual (>=)"; - case LessEqual: return "LessEqual (<=)"; - case BitAnd: return "BitAnd (&)"; - case BitXor: return "BitXor (^)"; - case BitOr: return "BitOr (|)"; - case LeftShift: return "LeftShift (<<)"; - case RightShift: return "RightShift (>>)"; - case Adition: return "Adition (+)"; - case Substraction: return "Substraction (-)"; - case Multiplication: return "Multiplication (*)"; - case Division: return "Division (/)"; - case Modulo: return "Modulo (%)"; - case BoolAnd: return "BoolAnd (&&)"; - case BoolOr: return "BoolOr (||)"; - default: std::unreachable(); break; + case Equal: return "Equal (==)"; + case NotEqual: return "NotEqual (!=)"; + case GreaterThan: return "GreaterThan (>)"; + case LessThan: return "LessThan (<)"; + case GreaterEqual: return "GreaterEqual (>=)"; + case LessEqual: return "LessEqual (<=)"; + case BitAnd: return "BitAnd (&)"; + case BitXor: return "BitXor (^)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case Addition: return "Addition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + case Assignment: return "Assignment (=)"; + case ModuleAccess: return "ModuleAccess (::)"; + case MemberAccess: return "MemberAccess (.)"; + case PointerMemberAccess: return "PointerMemberAccess (->)"; + case AdditionAssignment: return "AdditionAssignment (+=)"; + case SubstractionAssignment: return "SubstractionAssignment (-=)"; + case MultiplicationAssignment: return "MultiplicationAssignment (*=)"; + case DivisionAssignment: return "DivisionAssignment (/=)"; + case ModuloAssignment: return "ModuloAssignment (%=)"; + case BitAndAssignment: return "BitAndAssignment (&=)"; + case BitOrAssignment: return "BitOrAssignment (|=)"; + case BoolAndAssignment: return "BoolAndAssignment (&&=)"; + case BoolOrAssignment: return "BoolOrAssignment (||=)"; + case LShiftAssignment: return "LShiftAssignment (<<=)"; + case RShiftAssignment: return "RShiftAssignment (>>=)"; + + default: std::unreachable(); break; } std::unreachable(); } @@ -172,8 +188,8 @@ namespace arti::lang::ast { std::string emit(const StructLtrlPositionalInitializerNode&, GraphBuilder&); std::string emit(const StructLtrlInitializerNode &, GraphBuilder &); std::string emit(const IdentifierExprNode &, GraphBuilder &); - std::string emit(const UnaryExprNode &, GraphBuilder &); - std::string emit(const BinaryExprNode &, GraphBuilder &); + std::string emit(const PrefixExprNode &, GraphBuilder &); + std::string emit(const InfixExprNode &, GraphBuilder &); std::string emit(const AssignExprNode &, GraphBuilder &); std::string emit(const CompoundAssignExprNode &, GraphBuilder &); std::string emit(const FunctionCallExprNode &, GraphBuilder &); @@ -181,7 +197,7 @@ namespace arti::lang::ast { std::string emit(const SliceRangeExprNode &, GraphBuilder &); std::string emit(const MemberAccessExprNode &, GraphBuilder &); std::string emit(const PointerAccessExprNode &, GraphBuilder &); - std::string emit(const ScopeAccessExprNode &, GraphBuilder &); + std::string emit(const ModuleAccessExprNode &, GraphBuilder &); std::string emit(const ReflectionExprNode &, GraphBuilder &); std::string emit(const SliceCreationExprNode &, GraphBuilder &); std::string emit(const SliceLengthExprNode &, GraphBuilder &); @@ -629,8 +645,8 @@ namespace arti::lang::ast { return g.makeNode(std::format("Identifier `{}`", node->identifierName)); } - std::string emit(const UnaryExprNode &node, GraphBuilder &g) { - auto id = g.makeNode("UnaryExpression"); + std::string emit(const PrefixExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("PrefixExpression"); auto opLeaf = makeLeaf(g, toString(node->op)); g.addEdge(id, opLeaf, "Operator"); auto rhs = emit(node->right, g); @@ -638,8 +654,8 @@ namespace arti::lang::ast { return id; } - std::string emit(const BinaryExprNode &node, GraphBuilder &g) { - auto id = g.makeNode("BinaryExpression"); + std::string emit(const InfixExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("InfixExpression"); auto opLeaf = makeLeaf(g, toString(node->op)); g.addEdge(id, opLeaf, "Operator"); auto lhs = emit(node->left, g); @@ -709,7 +725,7 @@ namespace arti::lang::ast { return id; } - std::string emit(const ScopeAccessExprNode &node, GraphBuilder &g) { + std::string emit(const ModuleAccessExprNode &node, GraphBuilder &g) { auto id = g.makeNode("ScopeAccessExpression"); g.addEdge(id, emit(node->scope, g), "Object"); if (! node->genericParams.empty()) { @@ -764,8 +780,8 @@ namespace arti::lang::ast { [&g](const StructLtrlNode &n) { return emit(n, g); }, [&g](const SliceLtrlNode &n) { return emit(n, g); }, [&g](const IdentifierExprNode &n) { return emit(n, g); }, - [&g](const UnaryExprNode &n) { return emit(n, g); }, - [&g](const BinaryExprNode &n) { return emit(n, g); }, + [&g](const PrefixExprNode &n) { return emit(n, g); }, + [&g](const InfixExprNode &n) { return emit(n, g); }, [&g](const AssignExprNode &n) { return emit(n, g); }, [&g](const CompoundAssignExprNode &n) { return emit(n, g); }, [&g](const FunctionCallExprNode &n) { return emit(n, g); }, @@ -773,7 +789,7 @@ namespace arti::lang::ast { [&g](const SliceRangeExprNode &n) { return emit(n, g); }, [&g](const MemberAccessExprNode &n) { return emit(n, g); }, [&g](const PointerAccessExprNode &n) { return emit(n, g); }, - [&g](const ScopeAccessExprNode &n) { return emit(n, g); }, + [&g](const ModuleAccessExprNode &n) { return emit(n, g); }, [&g](const SliceCreationExprNode &n) { return emit(n, g); }, [&g](const SliceLengthExprNode &n) { return emit(n, g); }, [&g](const SlicePtrExprNode &n) { return emit(n, g); }, diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp index 9617e30..0b27951 100644 --- a/lib/src/Parser/AST/toString.cpp +++ b/lib/src/Parser/AST/toString.cpp @@ -62,8 +62,8 @@ namespace arti::lang::ast { std::string toString(const StructLtrlPositionalInitializerNode&, std::string); std::string toString(const StructLtrlInitializerNode &, std::string); std::string toString(const IdentifierExprNode &, std::string); - std::string toString(const UnaryExprNode &, std::string); - std::string toString(const BinaryExprNode &, std::string); + std::string toString(const PrefixExprNode &, std::string); + std::string toString(const InfixExprNode &, std::string); std::string toString(const AssignExprNode &, std::string); std::string toString(const CompoundAssignExprNode &, std::string); std::string toString(const FunctionCallExprNode &, std::string); @@ -71,7 +71,7 @@ namespace arti::lang::ast { std::string toString(const SliceRangeExprNode &, std::string); std::string toString(const MemberAccessExprNode &, std::string); std::string toString(const PointerAccessExprNode &, std::string); - std::string toString(const ScopeAccessExprNode &, std::string); + std::string toString(const ModuleAccessExprNode &, std::string); std::string toString(const ReflectionExprNode &, std::string); std::string toString(const SliceCreationExprNode &, std::string); std::string toString(const SliceLengthExprNode &, std::string); @@ -100,8 +100,8 @@ namespace arti::lang::ast { std::string toString(const ElseBranchNode &, std::string); std::string toString(const DeferableNode &, std::string); std::string toString(const PreLoopStmtNode &, std::string); - std::string toString(UnaryOperator op); - std::string toString(BinaryOperator op); + std::string toString(PrefixOperator op); + std::string toString(InfixOperator op); std::string toString(CompoundAssignOperator op); const auto StrTreeNoNode = "│ "; @@ -725,9 +725,9 @@ namespace arti::lang::ast { return std::format("Identifier `{}`", node->identifierName); } - std::string toString(const UnaryExprNode &node, std::string prefix) { + std::string toString(const PrefixExprNode &node, std::string prefix) { std::stringstream ss; - ss << "UnaryExpression"; + ss << "PrefixExpression"; int total = 2; int emitted = 0; appendGroupLeaf( @@ -741,9 +741,9 @@ namespace arti::lang::ast { return ss.str(); } - std::string toString(const BinaryExprNode &node, std::string prefix) { + std::string toString(const InfixExprNode &node, std::string prefix) { std::stringstream ss; - ss << "BinaryExpression"; + ss << "InfixExpression"; int total = 3; int emitted = 0; appendGroupLeaf( @@ -849,7 +849,7 @@ namespace arti::lang::ast { return ss.str(); } - std::string toString(const ScopeAccessExprNode &node, std::string prefix) { + std::string toString(const ModuleAccessExprNode &node, std::string prefix) { std::stringstream ss; ss << "ScopeAccessExpression"; int total = 2; @@ -943,10 +943,10 @@ namespace arti::lang::ast { [padding](const IdentifierExprNode &node) -> std::string { return toString(node, padding); }, - [padding](const UnaryExprNode &node) -> std::string { + [padding](const PrefixExprNode &node) -> std::string { return toString(node, padding); }, - [padding](const BinaryExprNode &node) -> std::string { + [padding](const InfixExprNode &node) -> std::string { return toString(node, padding); }, [padding](const AssignExprNode &node) -> std::string { @@ -970,7 +970,7 @@ namespace arti::lang::ast { [padding](const PointerAccessExprNode &node) -> std::string { return toString(node, padding); }, - [padding](const ScopeAccessExprNode &node) -> std::string { + [padding](const ModuleAccessExprNode &node) -> std::string { return toString(node, padding); }, [padding](const SliceCreationExprNode &node) -> std::string { @@ -1466,46 +1466,60 @@ namespace arti::lang::ast { return std::visit(visitor, node); } - std::string toString(UnaryOperator op) { - using enum UnaryOperator; + std::string toString(PrefixOperator op) { + using enum PrefixOperator; switch (op) { case Not: return "Not (!)"; case Minus: return "Minus (-)"; case BitNot: return "BitNot (~)"; - case Ampersand: return "Ampersand (&)"; - case Star: return "Star (*)"; + case MemPtr: return "MemPtr (&)"; + case DerefPtr: return "DerefPtr (*)"; default: std::unreachable(); break; } std::unreachable(); } - std::string toString(BinaryOperator op) { - using enum BinaryOperator; - + std::string toString(InfixOperator op) { + using enum InfixOperator; switch (op) { - case Equal: return "Equal (==)"; - case NotEqual: return "NotEqual (!=)"; - case GreaterThan: return "GreaterThan (>)"; - case LessThan: return "LessThan (<)"; - case GreaterEqual: return "GreaterEqual (>=)"; - case LessEqual: return "LessEqual (<=)"; - case BitAnd: return "BitAnd (&)"; - case BitXor: return "BitXor (^)"; - case BitOr: return "BitOr (|)"; - case LeftShift: return "LeftShift (<<)"; - case RightShift: return "RightShift (>>)"; - case Adition: return "Adition (+)"; - case Substraction: return "Substraction (-)"; - case Multiplication: return "Multiplication (*)"; - case Division: return "Division (/)"; - case Modulo: return "Modulo (%)"; - case BoolAnd: return "BoolAnd (&&)"; - case BoolOr: return "BoolOr (||)"; - default: std::unreachable(); break; - } + case Equal: return "Equal (==)"; + case NotEqual: return "NotEqual (!=)"; + case GreaterThan: return "GreaterThan (>)"; + case LessThan: return "LessThan (<)"; + case GreaterEqual: return "GreaterEqual (>=)"; + case LessEqual: return "LessEqual (<=)"; + case BitAnd: return "BitAnd (&)"; + case BitXor: return "BitXor (^)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case Addition: return "Addition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + case Assignment: return "Assignment (=)"; + case ModuleAccess: return "ModuleAccess (::)"; + case MemberAccess: return "MemberAccess (.)"; + case PointerMemberAccess: return "PointerMemberAccess (->)"; + case AdditionAssignment: return "AdditionAssignment (+=)"; + case SubstractionAssignment: return "SubstractionAssignment (-=)"; + case MultiplicationAssignment: return "MultiplicationAssignment (*=)"; + case DivisionAssignment: return "DivisionAssignment (/=)"; + case ModuloAssignment: return "ModuloAssignment (%=)"; + case BitAndAssignment: return "BitAndAssignment (&=)"; + case BitOrAssignment: return "BitOrAssignment (|=)"; + case BoolAndAssignment: return "BoolAndAssignment (&&=)"; + case BoolOrAssignment: return "BoolOrAssignment (||=)"; + case LShiftAssignment: return "LShiftAssignment (<<=)"; + case RShiftAssignment: return "RShiftAssignment (>>=)"; + default: std::unreachable(); break; + } std::unreachable(); } diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index 94039e0..c7ffb89 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -21,12 +21,530 @@ //============================================================================// #include +#include namespace arti::lang { Expected - Parser::parseExpression() { - return {}; + Parser::parseExpression(std::uint16_t minBindingPower) { + auto peekToken = tokenizer.peek(); + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + bool keepParsing = true; + ast::Optional lhs = std::nullopt; + + if (peekToken->value == TokenV::opLParen) { + std::ignore = tokenizer.consume(); + + if (auto lhsExpr = parseExpression(); ! lhsExpr) { + return Unexpected<>{ std::move(lhsExpr).error() }; + } + else { + if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) { + return Unexpected<>{ std::move(rParen).error() }; + } + + lhs = std::move(lhsExpr).value(); + } + } + else if (pratt::isPrefixOperator(peekToken->value)) { + if (auto newLhs = parsePrefixExpression(); ! newLhs) { + return Unexpected<>{ std::move(newLhs).error() }; + } + else { + lhs = std::move(newLhs).value(); + } + } + else { + if (auto expr = parsePrimaryExpression(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else if (not expr.value().has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "primary expression, i.e. " + "any of ( null, boolean, number, character, string, identifier )" + ); + } + else { + lhs = std::move(expr).value().value(); + } + } + + while (keepParsing) { + peekToken = tokenizer.peek(); + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + if (pratt::isPostfixOperator(peekToken->value)) { + auto op = pratt::getPostfixOperator(peekToken->value); + auto bindingPower = pratt::postfixBindingPower(op); + + if (bindingPower < minBindingPower) { + keepParsing = false; + } + else { + if (auto newLhs = parsePostfixExpression(std::move(lhs).value()); + ! newLhs) { + return Unexpected<>{ std::move(newLhs).error() }; + } + else { + lhs = std::move(newLhs).value(); + } + } + } + else if (pratt::isInfixOperator(peekToken->value)) { + auto op = pratt::getInfixOperator(peekToken->value); + auto [lbp, rbp] = pratt::infixBindingPower(op); + + if (lbp < minBindingPower) { + keepParsing = false; + } + else { + if (auto newLhs = parseInfixExpression(std::move(lhs).value()); + ! newLhs) { + return Unexpected<>{ std::move(newLhs).error() }; + } + else { + lhs = std::move(newLhs).value(); + } + } + } + else { + keepParsing = false; + } + } + + if (not lhs.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "primary expression, i.e. " + "any of ( null, boolean, number, character, string, identifier )" + ); + } + else { + return std::move(lhs).value(); + } + } + + Expected> + Parser::parsePrimaryExpression() { + auto peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + if (peekToken->value == TokenV::tkInteger) { + if (auto expr = parseIntegerLiteral(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::tkDecimal) { + if (auto expr = parseFloatLiteral(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::tkCharacter) { + if (auto expr = parseCharLiteral(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::tkString) { + if (auto expr = parseStringLiteral(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::kwTrue || + peekToken->value == TokenV::kwFalse) { + if (auto expr = parseBooleanLiteral(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::kwNull) { + if (auto expr = parseNullLiteral(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::tkIdentifier) { + if (auto expr = parseIdentifierExpression(); ! expr) { + return Unexpected<>{ std::move(expr).error() }; + } + else { + return std::move(expr).value(); + } + } + else if (peekToken->value == TokenV::kwThis) { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::kwThis, "'this' keyword"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + node->identifierName = ltrl->strValue; + } + + return node; + } + else if (peekToken->value == TokenV::kwUnderscore) { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::kwUnderscore, "'_' keyword"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + node->identifierName = ltrl->strValue; + } + + return node; + } + + return std::nullopt; } + Expected + Parser::parsePrefixExpression() { + auto peekToken = tokenizer.peek(); + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + auto op = pratt::getPrefixOperator(peekToken->value); + auto bindingPower = pratt::prefixBindingPower(op); + + std::ignore = tokenizer.consume(); + + auto rhs = parseExpression(bindingPower); + + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->op = op; + node->right = std::move(rhs).value(); + + return node; + } + + Expected + Parser::parseInfixExpression(ast::ExpressionNode lhs) { + auto peekToken = tokenizer.peek(); + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + std::ignore = tokenizer.consume(); + + auto op = pratt::getInfixOperator(peekToken->value); + auto [lbp, rbp] = pratt::infixBindingPower(op); + + auto rhs = parseExpression(rbp); + + if (! rhs) { + return Unexpected<>{ std::move(rhs).error() }; + } + + /* TODO: MemberAccess and PointerMemberAccess do not use their respective + * nodes types yet */ + /* TODO: ModuleAccess do not use its respective node type yet */ + if (op == ast::InfixOperator::Assignment) { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->left = std::move(lhs); + node->right = std::move(rhs).value(); + + return node; + } + else if (pratt::isCompoundAssignOperator(op)) { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->op = pratt::getCompoundOperatorType(op); + node->left = std::move(lhs); + node->right = std::move(rhs).value(); + + return node; + } + else { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->op = op; + node->left = std::move(lhs); + node->right = std::move(rhs).value(); + + return node; + } + } + + Expected + Parser::parsePostfixExpression(ast::ExpressionNode lhs) { + auto peekToken = tokenizer.peek(); + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + std::ignore = tokenizer.consume(); + + auto op = pratt::getPostfixOperator(peekToken->value); + auto bindingPower = pratt::postfixBindingPower(op); + + std::optional node = std::nullopt; + + if (op == ast::PostfixOperator::FunctionCall) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + bool stillParams = true; + + if (auto close = match(TokenV::opRParen); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + stillParams = false; + } + + while (stillParams) { + auto arg = parseExpression(); + + if (! arg) { + return Unexpected<>{ std::move(arg).error() }; + } + + newNode->arguments.emplace_back(std::move(arg).value()); + + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected{ std::move(comma).error() }; + } + else if (! comma.value()) { + if (auto ntok = tokenizer.peek(); ! ntok) { + return Unexpected{ std::move(ntok).error() }; + } + else { + if (ntok->value != TokenV::opRParen) { + return langException( + ntok->line, + ntok->column, + toString(*ntok), + "',' or ')'" + ); + } + else { + stillParams = false; + } + } + } + + if (auto close = match(TokenV::opRParen); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + stillParams = false; + } + } + + if (auto close = consume(TokenV::opRParen, "')'"); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + + newNode->callee = std::move(lhs); + + node = std::move(newNode); + } + else if (op == ast::PostfixOperator::SliceAccess) { + auto idx = parseExpression(); + + if (! idx) { + return Unexpected<>{ std::move(idx).error() }; + } + + if (auto range = matchAndConsume(TokenV::opColon); ! range) { + return Unexpected<>{ std::move(range).error() }; + } + else if (range.value()) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + newNode->start = std::move(idx).value(); + + auto endIdx = parseExpression(); + + if (! endIdx) { + return Unexpected<>{ std::move(endIdx).error() }; + } + + newNode->end = std::move(endIdx).value(); + + if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + + newNode->slice = std::move(lhs); + + node = std::move(newNode); + } + else { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + newNode->index = std::move(idx).value(); + + if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + + newNode->slice = std::move(lhs); + + node = std::move(newNode); + } + } + else if (op == ast::PostfixOperator::SliceSize) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + newNode->object = std::move(lhs); + + node = std::move(newNode); + } + else if (op == ast::PostfixOperator::PtrToSlice) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + auto len = parseExpression(); + + if (! len) { + return Unexpected<>{ std::move(len).error() }; + } + + newNode->length = std::move(len).value(); + + if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + + newNode->object = std::move(lhs); + + node = std::move(newNode); + } + else if (op == ast::PostfixOperator::SliceToPtr) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + newNode->object = std::move(lhs); + + node = std::move(newNode); + } + else if (op == ast::PostfixOperator::Reflect) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + if (auto ident = match(TokenV::tkIdentifier); ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else if (ident.value()) { + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); + ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + newNode->attribute = ident->strValue; + } + } + + newNode->object = std::move(lhs); + + node = std::move(newNode); + } + + if (not node.has_value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "postfix operator, i.e. " + "any of ( /*TODO*/ )" + ); + } + + return std::move(node).value(); + } + + } // namespace arti::lang diff --git a/lib/src/Parser/Literals.cpp b/lib/src/Parser/Literals.cpp index c03cfc7..ebb668a 100644 --- a/lib/src/Parser/Literals.cpp +++ b/lib/src/Parser/Literals.cpp @@ -20,3 +20,159 @@ // // //============================================================================// +#include + +namespace arti::lang { + + Expected + Parser::parseCharLiteral() { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::tkCharacter, "character literal"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + node->value = static_cast(ltrl->strValue[1]); + } + + return node; + } + + Expected + Parser::parseNullLiteral() { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::kwNull, "null keyword"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + } + + return node; + } + + Expected + Parser::parseStringLiteral() { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::tkString, "string literal"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + + ltrl->strValue.remove_suffix(1); + ltrl->strValue.remove_prefix(1); + + node->value = ltrl->strValue; + } + + return node; + } + + Expected + Parser::parseFloatLiteral() { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::tkDecimal, "number literal"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + /* TODO: This could throw? */ + std::string value{ ltrl->strValue }; + node->value = std::stold(value); + } + + return node; + } + + Expected + Parser::parseIntegerLiteral() { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::tkInteger, "integer literal"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + /* TODO: This could throw? */ + std::string value{ ltrl->strValue }; + node->value = std::stoul(value); + } + + return node; + } + + Expected + Parser::parseBooleanLiteral() { + auto node = ast::MakeNode(); + + auto peekToken = tokenizer.peek(); + + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; + } + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + if (peekToken->value == TokenV::kwTrue) { + node->value = true; + } + else if (peekToken->value == TokenV::kwFalse) { + node->value = false; + } + else { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "boolean literal, i.e. " + "any of ( true, false )" + ); + } + + std::ignore = tokenizer.consume(); + + return node; + } + + Expected + Parser::parseIdentifierExpression() { + auto node = ast::MakeNode(); + + if (auto ltrl = consume(TokenV::tkIdentifier, "identifier"); ! ltrl) { + return Unexpected<>{ std::move(ltrl).error() }; + } + else { + node->location = { + .line = ltrl->line, + .column = ltrl->column + }; + node->identifierName = ltrl->strValue; + } + + return node; + } + +} // namespace arti::lang diff --git a/lib/src/Parser/Operators.cpp b/lib/src/Parser/Operators.cpp new file mode 100644 index 0000000..ce9b9a9 --- /dev/null +++ b/lib/src/Parser/Operators.cpp @@ -0,0 +1,288 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + +#include + +namespace arti::lang::pratt { + + bool isPrefixOperator(TokenV tokenType) { + switch(tokenType) { + using enum TokenV; + + case opHyphen: + case opBang: + case opStar: + case opTilde: + case opAnd: + case opLParen: + case kwNot: + return true; + + default: + return false; + } + } + + bool isInfixOperator(TokenV tokenType) { + switch(tokenType) { + using enum TokenV; + + case opDot: + case opMod: + case opPlus: + case opHyphen: + case opSlash: + case opStar: + case opAssign: + case opAccess: + case opEq: + case opNeq: + case opLt: + case opGt: + case opLtEq: + case opGtEq: + case opLShift: + case opRShift: + case opBoolAnd: + case opBoolOr: + case kwAnd: + case kwOr: + case opAnd: + case opOr: + case opCaret: + case opArrow: + case opPlusAssign: + case opHyphenAssign: + case opStarAssign: + case opSlashAssign: + case opModAssign: + case opAndAssign: + case opOrAssign: + case opLShiftAssign: + case opRShiftAssign: + case opBoolAndAssign: + case opBoolOrAssign: + return true; + default: + return false; + } + } + + bool isPostfixOperator(TokenV tokenType) { + switch(tokenType) { + using enum TokenV; + + case opLParen: + case opLBracket: + case opSliceSize: + case opPtrSlice: + case opSlicePtr: + case opReflect: + return true; + default: + return false; + } + } + + + ast::PrefixOperator getPrefixOperator(TokenV tokenType) { + using enum ast::PrefixOperator; + + switch(tokenType) { + using enum TokenV; + + case opHyphen: + return Minus; + case kwNot: + case opBang: + return Not; + case opStar: + return DerefPtr; + case opTilde: + return BitNot; + case opAnd: + return MemPtr; + default: + return Uninitialized; + } + } + + ast::InfixOperator getInfixOperator(TokenV tokenType) { + using enum ast::InfixOperator; + + switch(tokenType) { + using enum TokenV; + + case opMod: + return Modulo; + case opPlus: + return Addition; + case opHyphen: + return Substraction; + case opSlash: + return Division; + case opStar: + return Multiplication; + case opEq: + return Equal; + case opNeq: + return NotEqual; + case opLt: + return LessThan; + case opGt: + return GreaterThan; + case opLtEq: + return LessEqual; + case opGtEq: + return GreaterEqual; + case opLShift: + return LeftShift; + case opRShift: + return RightShift; + case kwAnd: + case opBoolAnd: + return BoolAnd; + case kwOr: + case opBoolOr: + return BoolOr; + case opAnd: + return BitAnd; + case opOr: + return BitOr; + case opCaret: + return BitXor; + case opAssign: + return Assignment; + case opAccess: + return ModuleAccess; + case opDot: + return MemberAccess; + case opArrow: + return PointerMemberAccess; + case opPlusAssign: + return AdditionAssignment; + case opHyphenAssign: + return SubstractionAssignment; + case opStarAssign: + return MultiplicationAssignment; + case opSlashAssign: + return DivisionAssignment; + case opModAssign: + return ModuloAssignment; + case opAndAssign: + return BitAndAssignment; + case opOrAssign: + return BitOrAssignment; + case opLShiftAssign: + return LShiftAssignment; + case opRShiftAssign: + return RShiftAssignment; + case opBoolAndAssign: + return BoolAndAssignment; + case opBoolOrAssign: + return BoolOrAssignment; + default: + return Uninitialized; + } + } + + ast::PostfixOperator getPostfixOperator(TokenV tokenType) { + using enum ast::PostfixOperator; + + switch(tokenType) { + using enum TokenV; + + case opLParen: + return FunctionCall; + case opLBracket: + return SliceAccess; + case opSliceSize: + return SliceSize; + case opPtrSlice: + return PtrToSlice; + case opSlicePtr: + return SliceToPtr; + case opReflect: + return Reflect; + default: + return Uninitialized; + } + } + + bool isCompoundAssignOperator(ast::InfixOperator op) { + + switch(op) { + using enum ast::InfixOperator; + + case AdditionAssignment: + case SubstractionAssignment: + case MultiplicationAssignment: + case DivisionAssignment: + case ModuloAssignment: + case BitAndAssignment: + case BitOrAssignment: + case LShiftAssignment: + case RShiftAssignment: + case BoolAndAssignment: + case BoolOrAssignment: + return true; + + default: + return false; + } + + } + + ast::CompoundAssignOperator getCompoundOperatorType(ast::InfixOperator op) { + switch(op) { + using enum ast::InfixOperator; + + case AdditionAssignment: + return ast::CompoundAssignOperator::Addition; + case SubstractionAssignment: + return ast::CompoundAssignOperator::Substraction; + case MultiplicationAssignment: + return ast::CompoundAssignOperator::Multiplication; + case DivisionAssignment: + return ast::CompoundAssignOperator::Division; + case ModuloAssignment: + return ast::CompoundAssignOperator::Modulo; + case BitAndAssignment: + return ast::CompoundAssignOperator::BitAnd; + case BitOrAssignment: + return ast::CompoundAssignOperator::BitOr; + case LShiftAssignment: + return ast::CompoundAssignOperator::LeftShift; + case RShiftAssignment: + return ast::CompoundAssignOperator::RightShift; + case BoolAndAssignment: + return ast::CompoundAssignOperator::BoolAnd; + case BoolOrAssignment: + return ast::CompoundAssignOperator::BoolOr; + + default: + return ast::CompoundAssignOperator::Uninitialized; + } + } + +} + diff --git a/lib/src/Parser/Pratt.cpp b/lib/src/Parser/Pratt.cpp new file mode 100644 index 0000000..48516fb --- /dev/null +++ b/lib/src/Parser/Pratt.cpp @@ -0,0 +1,111 @@ +//============================================================================// +// // +// 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 . // +// // +//============================================================================// + +#include + +namespace arti::lang::pratt { + + std::uint16_t prefixBindingPower(ast::PrefixOperator op) { + switch (op) { + // Unary operators generally bind very tightly + case ast::PrefixOperator::Not: + case ast::PrefixOperator::Minus: + case ast::PrefixOperator::BitNot: + case ast::PrefixOperator::MemPtr: + case ast::PrefixOperator::DerefPtr: + return 17; // Should be higher than most infix but lower than postfix + default: return 0; + } + } + + BindingPower infixBindingPower(ast::InfixOperator op) { + switch (op) { + // Member Access (Highest) + case ast::InfixOperator::ModuleAccess: + case ast::InfixOperator::MemberAccess: + case ast::InfixOperator::PointerMemberAccess: return { 21, 22 }; + + // Multiplicative + case ast::InfixOperator::Multiplication: + case ast::InfixOperator::Division: + case ast::InfixOperator::Modulo: return { 15, 16 }; + + // Additive + case ast::InfixOperator::Addition: + case ast::InfixOperator::Substraction: return { 13, 14 }; + + // Shift + case ast::InfixOperator::LeftShift: + case ast::InfixOperator::RightShift: return { 11, 12 }; + + // Relational + case ast::InfixOperator::LessThan: + case ast::InfixOperator::GreaterThan: + case ast::InfixOperator::LessEqual: + case ast::InfixOperator::GreaterEqual: return { 9, 10 }; + + // Equality + case ast::InfixOperator::Equal: + case ast::InfixOperator::NotEqual: return { 7, 8 }; + + // Bitwise + case ast::InfixOperator::BitAnd: return { 6, 7 }; + case ast::InfixOperator::BitXor: return { 5, 6 }; + case ast::InfixOperator::BitOr: return { 4, 5 }; + + // Logical + case ast::InfixOperator::BoolAnd: return { 3, 4 }; + case ast::InfixOperator::BoolOr: return { 1, 2 }; + + // Assignment (Right-associative: left > right) + case ast::InfixOperator::Assignment: + case ast::InfixOperator::AdditionAssignment: + case ast::InfixOperator::SubstractionAssignment: + case ast::InfixOperator::MultiplicationAssignment: + case ast::InfixOperator::DivisionAssignment: + case ast::InfixOperator::ModuloAssignment: + case ast::InfixOperator::BitAndAssignment: + case ast::InfixOperator::BitOrAssignment: + case ast::InfixOperator::BoolAndAssignment: + case ast::InfixOperator::BoolOrAssignment: + case ast::InfixOperator::LShiftAssignment: + case ast::InfixOperator::RShiftAssignment: return { 2, 1 }; + + default: return { 0, 0 }; + } + } + + std::uint16_t postfixBindingPower(ast::PostfixOperator op) { + switch (op) { + // Postfix usually has the highest precedence (e.g., function calls, + // slicing) + case ast::PostfixOperator::FunctionCall: + case ast::PostfixOperator::SliceAccess: + case ast::PostfixOperator::SliceSize: + case ast::PostfixOperator::PtrToSlice: + case ast::PostfixOperator::SliceToPtr: + case ast::PostfixOperator::Reflect: return 19; + default: return 0; + } + } + +} // namespace arti::lang::pratt -- 2.52.0 From 09c44f3b67070a0911d7f49b039efddb5e40b0de Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Fri, 26 Dec 2025 23:54:17 -0600 Subject: [PATCH 10/15] fix(parser): resolve generic nesting ambiguity by splitting `>>` tokens Signed-off-by: erick-alcachofa Refactor the termination logic for generic parameter lists in type parsing to correctly handle nested generics. By replacing manual peeking with `peekExpect(TokenV::opGt)`, the parser now correctly handles cases where two closing angle brackets appear consecutively (e.g., `List>`). Previously, the parser manually checked for a literal `>` token. If the lexer encountered `>>` (a right-shift operator), the parser would fail to recognize it as two closing brackets. The transition to `peekExpect` allows the tokenizer to "split" the `>>` token into two individual `>` tokens when a single closing bracket is expected, resolving the classic nested template ambiguity. Key changes: - Replaced manual token validation and error reporting with `peekExpect`. - Enabled support for nested generic types without requiring spaces between closing brackets. - Simplified the `keepParsing` loop state in `lib/src/Parser/Types.cpp`. --- lib/src/Parser/Types.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp index 4d17bcf..650fe77 100644 --- a/lib/src/Parser/Types.cpp +++ b/lib/src/Parser/Types.cpp @@ -299,21 +299,11 @@ namespace arti::lang { return Unexpected{ std::move(comma).error() }; } else if (! comma.value()) { - if (peekToken = tokenizer.peek(); ! peekToken) { + if (peekToken = tokenizer.peekExpect(TokenV::opGt); ! peekToken) { return Unexpected{ std::move(peekToken).error() }; } else { - if (peekToken->value != TokenV::opGt) { - return langException( - peekToken->line, - peekToken->column, - toString(*peekToken), - "',' or '>'" - ); - } - else { - keepParsing = false; - } + keepParsing = false; } } } -- 2.52.0 From 8dd75e3b8ac4653c4e32af537ce1bdbc7b81d06d Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sat, 27 Dec 2025 23:08:26 -0600 Subject: [PATCH 11/15] feat(parser): support turbofish operator and specialize access expressions Signed-off-by: erick-alcachofa Overhaul the AST and parser logic to support explicit generic instantiation in expressions (e.g., `Result::::Ok(0)`). This is achieved by implementing the "turbofish" operator (`::<>`) and specializing how member and module access are handled. * Added `GenericExpression` to represent generic instantiations in expressions. * Updated the Pratt parser to look for `<` immediately following a `::` (ModuleAccess) operator. If found, it parses a `GenericExpression` containing the generic arguments. * This change resolves the ambiguity between generic lists and comparison operators in the expression parser. * Renamed `PointerAccessExpression` to `PointerMemberAccessExpression`. * Refactored `MemberAccessExpression` and `PointerMemberAccessExpression` to store the member as an `ExpressionNode`. This allows the right-hand side of a `.` or `->` to be a complex expression (like a generic call). * Simplified `ModuleAccessExpression` to a binary `left`/`right` structure, separating scope resolution from generic instantiation. * Flattened the `Type` AST: replaced recursive `baseType` structures with a `Vector` (`typeNodes`) to represent namespaced paths (e.g., `std::collections::Map`) more efficiently. * Removed redundant `NamespacedType` and `NamespacedIdentifier` nodes. * Simplified `GenericType` and `IdentifierType` to use direct `String` type names. * Refactored `parseType` to iterate through namespaced components and populate the new flattened `typeNodes` vector. * Updated the Pratt infix loop to correctly dispatch to `ModuleAccess`, `MemberAccess`, or `GenericExpression` based on the operator and lookahead tokens. * Adjusted `toDot` and `toString` visitors to match the new AST definitions. --- .../artichoke/Parser/AST/Expressions.hpp | 27 ++-- lib/include/artichoke/Parser/AST/Types.hpp | 16 +- lib/src/Parser/AST/toDot.cpp | 62 ++++---- lib/src/Parser/AST/toString.cpp | 83 ++++++---- lib/src/Parser/Expressions.cpp | 64 +++++++- lib/src/Parser/Types.cpp | 147 ++++++++---------- 6 files changed, 233 insertions(+), 166 deletions(-) diff --git a/lib/include/artichoke/Parser/AST/Expressions.hpp b/lib/include/artichoke/Parser/AST/Expressions.hpp index 695b541..e24679f 100644 --- a/lib/include/artichoke/Parser/AST/Expressions.hpp +++ b/lib/include/artichoke/Parser/AST/Expressions.hpp @@ -40,7 +40,8 @@ namespace arti::lang::ast { struct SliceAccessExpression; struct SliceRangeExpression; struct MemberAccessExpression; - struct PointerAccessExpression; + struct PointerMemberAccessExpression; + struct GenericExpression; struct ModuleAccessExpression; struct ReflectionExpression; struct SliceCreationExpression; @@ -59,7 +60,8 @@ namespace arti::lang::ast { using SliceAccessExprNode = Ptr; using SliceRangeExprNode = Ptr; using MemberAccessExprNode = Ptr; - using PointerAccessExprNode = Ptr; + using PointerMemberAccessExprNode = Ptr; + using GenericExprNode = Ptr; using ModuleAccessExprNode = Ptr; using ReflectionExprNode = Ptr; using SliceCreationExprNode = Ptr; @@ -85,7 +87,8 @@ namespace arti::lang::ast { SliceAccessExprNode, SliceRangeExprNode, MemberAccessExprNode, - PointerAccessExprNode, + PointerMemberAccessExprNode, + GenericExprNode, ModuleAccessExprNode, SliceCreationExprNode, SliceLengthExprNode, @@ -153,23 +156,29 @@ namespace arti::lang::ast { struct nodes::MemberAccessExpression { SourceLocation location; - String memberName; + ExpressionNode member; ExpressionNode object; }; - struct nodes::PointerAccessExpression { + struct nodes::PointerMemberAccessExpression { SourceLocation location; - String memberName; + ExpressionNode member; ExpressionNode object; }; struct nodes::ModuleAccessExpression { SourceLocation location; - String memberName; - ExpressionNode scope; - Vector genericParams; + ExpressionNode left; + ExpressionNode right; + }; + + struct nodes::GenericExpression { + SourceLocation location; + + ExpressionNode typeNode; + std::vector genericArgs; }; struct nodes::ReflectionExpression { diff --git a/lib/include/artichoke/Parser/AST/Types.hpp b/lib/include/artichoke/Parser/AST/Types.hpp index 040d76b..37b2143 100644 --- a/lib/include/artichoke/Parser/AST/Types.hpp +++ b/lib/include/artichoke/Parser/AST/Types.hpp @@ -33,7 +33,6 @@ namespace arti::lang::ast { struct Type; struct GenericType; struct IdentifierType; - struct NamespacedType; /* Helper type node types */ struct NamespacedIdentifier; @@ -44,14 +43,12 @@ namespace arti::lang::ast { using TypeNode = Ptr; using GenericTypeNode = Ptr; using IdentifierTypeNode = Ptr; - using NamespacedTypeNode = Ptr; using NamespacedIdentifierNode = Ptr; /* Variant nodes */ using TypeExpressionNode = Variant< GenericTypeNode, - IdentifierTypeNode, - NamespacedTypeNode + IdentifierTypeNode >; /* Node definitions */ @@ -60,27 +57,20 @@ namespace arti::lang::ast { SourceLocation location; Vector qualifiers; - TypeExpressionNode baseType; + Vector typeNodes; }; struct nodes::GenericType { SourceLocation location; - TypeExpressionNode baseType; + String typeName; Vector genericArgs; }; struct nodes::IdentifierType { SourceLocation location; - NamespacedIdentifierNode typeName; - }; - - struct nodes::NamespacedType { - SourceLocation location; - String typeName; - TypeExpressionNode baseType; }; struct nodes::NamespacedIdentifier { diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp index 50042f9..461e756 100644 --- a/lib/src/Parser/AST/toDot.cpp +++ b/lib/src/Parser/AST/toDot.cpp @@ -171,8 +171,6 @@ namespace arti::lang::ast { std::string emit(const TypeNode &, GraphBuilder &); std::string emit(const GenericTypeNode &, GraphBuilder &); std::string emit(const IdentifierTypeNode &, GraphBuilder &); - std::string emit(const NamespacedTypeNode &, GraphBuilder &); - std::string emit(const NamespacedIdentifierNode &, GraphBuilder &); std::string emit(const TypeExpressionNode &, GraphBuilder &); std::string emit(const CharLtrlNode &, GraphBuilder &); std::string emit(const NullLtrlNode &, GraphBuilder &); @@ -196,7 +194,8 @@ namespace arti::lang::ast { std::string emit(const SliceAccessExprNode &, GraphBuilder &); std::string emit(const SliceRangeExprNode &, GraphBuilder &); std::string emit(const MemberAccessExprNode &, GraphBuilder &); - std::string emit(const PointerAccessExprNode &, GraphBuilder &); + std::string emit(const PointerMemberAccessExprNode &, GraphBuilder &); + std::string emit(const GenericExprNode &, GraphBuilder &); std::string emit(const ModuleAccessExprNode &, GraphBuilder &); std::string emit(const ReflectionExprNode &, GraphBuilder &); std::string emit(const SliceCreationExprNode &, GraphBuilder &); @@ -494,15 +493,16 @@ namespace arti::lang::ast { }); } } - auto baseId = emit(node->baseType, g); - g.addEdge(id, baseId, "BaseType"); + emitGroupVec(g, id, "TypeNodes", node->typeNodes, [&](const auto &arg) { + return emit(arg, g); + }); return id; } std::string emit(const GenericTypeNode &node, GraphBuilder &g) { auto id = g.makeNode("GenericType"); - auto baseId = emit(node->baseType, g); - g.addEdge(id, baseId, "BaseType"); + auto typeId = g.makeNode(node->typeName); + g.addEdge(id, typeId, "TypeName"); if (! node->genericArgs.empty()) { emitGroupVec( g, @@ -516,30 +516,17 @@ namespace arti::lang::ast { } std::string emit(const IdentifierTypeNode &node, GraphBuilder &g) { + std::ignore = node; auto id = g.makeNode("IdentifierType"); - auto cid = emit(node->typeName, g); - g.addEdge(id, cid, "TypeName"); + auto typeId = g.makeNode(node->typeName); + g.addEdge(id, typeId, "TypeName"); return id; } - std::string emit(const NamespacedTypeNode &node, GraphBuilder &g) { - auto id = g.makeNode("NamespacedType"); - auto baseId = emit(node->baseType, g); - g.addEdge(id, baseId, "BaseType"); - auto leaf = makeLeaf(g, node->typeName); - g.addEdge(id, leaf, "TypeName"); - return id; - } - - std::string emit(const NamespacedIdentifierNode &node, GraphBuilder &g) { - return g.makeNode(namespacedIdentToString(node)); - } - std::string emit(const TypeExpressionNode &node, GraphBuilder &g) { auto visitor = OverloadSet{ [&g](const GenericTypeNode &n) { return emit(n, g); }, [&g](const IdentifierTypeNode &n) { return emit(n, g); }, - [&g](const NamespacedTypeNode &n) { return emit(n, g); }, }; return std::visit(visitor, node); } @@ -714,30 +701,36 @@ namespace arti::lang::ast { std::string emit(const MemberAccessExprNode &node, GraphBuilder &g) { auto id = g.makeNode("MemberAccessExpression"); g.addEdge(id, emit(node->object, g), "Object"); - g.addEdge(id, makeLeaf(g, node->memberName), "Member"); + g.addEdge(id, emit(node->member, g), "Member"); return id; } - std::string emit(const PointerAccessExprNode &node, GraphBuilder &g) { + std::string emit(const PointerMemberAccessExprNode &node, GraphBuilder &g) { auto id = g.makeNode("PointerAccessExpression"); g.addEdge(id, emit(node->object, g), "Object"); - g.addEdge(id, makeLeaf(g, node->memberName), "Member"); + g.addEdge(id, emit(node->member, g), "Member"); return id; } std::string emit(const ModuleAccessExprNode &node, GraphBuilder &g) { - auto id = g.makeNode("ScopeAccessExpression"); - g.addEdge(id, emit(node->scope, g), "Object"); - if (! node->genericParams.empty()) { + auto id = g.makeNode("ModuleAccessExpression"); + g.addEdge(id, emit(node->left, g), "Scope"); + g.addEdge(id, emit(node->right, g), "Member"); + return id; + } + + std::string emit(const GenericExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("GenericExpression"); + g.addEdge(id, emit(node->typeNode, g), "TypeNode"); + if (! node->genericArgs.empty()) { emitGroupVec( g, id, - "GenericParams", - node->genericParams, - [&](const auto &p) { return emit(p, g); } + "GenericArguments", + node->genericArgs, + [&](const auto &arg) { return emit(arg, g); } ); } - g.addEdge(id, makeLeaf(g, node->memberName), "Member"); return id; } @@ -788,12 +781,13 @@ namespace arti::lang::ast { [&g](const SliceAccessExprNode &n) { return emit(n, g); }, [&g](const SliceRangeExprNode &n) { return emit(n, g); }, [&g](const MemberAccessExprNode &n) { return emit(n, g); }, - [&g](const PointerAccessExprNode &n) { return emit(n, g); }, + [&g](const PointerMemberAccessExprNode &n) { return emit(n, g); }, [&g](const ModuleAccessExprNode &n) { return emit(n, g); }, [&g](const SliceCreationExprNode &n) { return emit(n, g); }, [&g](const SliceLengthExprNode &n) { return emit(n, g); }, [&g](const SlicePtrExprNode &n) { return emit(n, g); }, [&g](const ReflectionExprNode &n) { return emit(n, g); }, + [&g](const GenericExprNode &n) { return emit(n, g); }, }; return std::visit(visitor, node); } diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp index 0b27951..6ec8b31 100644 --- a/lib/src/Parser/AST/toString.cpp +++ b/lib/src/Parser/AST/toString.cpp @@ -45,7 +45,6 @@ namespace arti::lang::ast { std::string toString(const TypeNode &, std::string); std::string toString(const GenericTypeNode &, std::string); std::string toString(const IdentifierTypeNode &, std::string); - std::string toString(const NamespacedTypeNode &, std::string); std::string toString(const NamespacedIdentifierNode &, std::string); std::string toString(const TypeExpressionNode &, std::string); std::string toString(const CharLtrlNode &, std::string); @@ -70,7 +69,8 @@ namespace arti::lang::ast { std::string toString(const SliceAccessExprNode &, std::string); std::string toString(const SliceRangeExprNode &, std::string); std::string toString(const MemberAccessExprNode &, std::string); - std::string toString(const PointerAccessExprNode &, std::string); + std::string toString(const PointerMemberAccessExprNode &, std::string); + std::string toString(const GenericExprNode &, std::string); std::string toString(const ModuleAccessExprNode &, std::string); std::string toString(const ReflectionExprNode &, std::string); std::string toString(const SliceCreationExprNode &, std::string); @@ -126,6 +126,17 @@ namespace arti::lang::ast { << toString(item, nextPrefix(prefix, isLastChild)); } + void appendItemString( + std::stringstream &ss, + const std::string &prefix, + const std::string &item, + bool isLastChild + ) { + ss << "\n" + << prefix << (isLastChild ? StrTreeLast : StrTreeChilds) << " " + << item; + } + template void appendGroupVec( std::stringstream &ss, @@ -529,12 +540,22 @@ namespace arti::lang::ast { if (! qls.empty()) { ++total; } - ++total; // BaseType is always present + if (! node->typeNodes.empty()) { + ++total; + } int emitted = 0; if (! qls.empty()) { appendGroupLeafList(ss, prefix, "Qualifiers", qls, ++emitted == total); } - appendGroupOne(ss, prefix, "BaseType", node->baseType, ++emitted == total); + if (! node->typeNodes.empty()) { + appendGroupVec( + ss, + prefix, + "TypeNodes", + node->typeNodes, + ++emitted == total + ); + } return ss.str(); } @@ -546,7 +567,9 @@ namespace arti::lang::ast { ++total; } int emitted = 0; - appendGroupOne(ss, prefix, "BaseType", node->baseType, ++emitted == total); + + appendItemString(ss, prefix, std::format("TypeName `{}`", node->typeName), ++emitted == total); + if (! node->genericArgs.empty()) { appendGroupVec( ss, @@ -560,17 +583,10 @@ namespace arti::lang::ast { } std::string toString(const IdentifierTypeNode &node, std::string prefix) { + std::ignore = node; + std::ignore = prefix; std::stringstream ss; - ss << "IdentifierType"; - appendGroupOne(ss, prefix, "TypeName", node->typeName, true); - return ss.str(); - } - - std::string toString(const NamespacedTypeNode &node, std::string prefix) { - std::stringstream ss; - ss << "NamespacedType"; - appendGroupOne(ss, prefix, "BaseType", node->baseType, false); - appendGroupLeaf(ss, prefix, "TypeName", node->typeName, true); + ss << "TypeName `" << node->typeName << "`"; return ss.str(); } @@ -591,9 +607,6 @@ namespace arti::lang::ast { [padding](const IdentifierTypeNode &node) -> std::string { return toString(node, padding); }, - [padding](const NamespacedTypeNode &node) -> std::string { - return toString(node, padding); - }, }; return std::visit(visitor, node); @@ -837,37 +850,46 @@ namespace arti::lang::ast { std::stringstream ss; ss << "MemberAccessExpression"; appendGroupOne(ss, prefix, "Object", node->object, false); - appendGroupLeaf(ss, prefix, "Member", node->memberName, true); + appendGroupOne(ss, prefix, "Member", node->member, true); return ss.str(); } - std::string toString(const PointerAccessExprNode &node, std::string prefix) { + std::string toString(const PointerMemberAccessExprNode &node, std::string prefix) { std::stringstream ss; ss << "PointerAccessExpression"; appendGroupOne(ss, prefix, "Object", node->object, false); - appendGroupLeaf(ss, prefix, "Member", node->memberName, true); + appendGroupOne(ss, prefix, "Member", node->member, true); return ss.str(); } std::string toString(const ModuleAccessExprNode &node, std::string prefix) { std::stringstream ss; - ss << "ScopeAccessExpression"; + ss << "ModuleAccessExpression"; int total = 2; - if (! node->genericParams.empty()) { + int emitted = 0; + appendGroupOne(ss, prefix, "Scope", node->left, ++emitted == total); + appendGroupOne(ss, prefix, "Member", node->right, ++emitted == total); + return ss.str(); + } + + std::string toString(const GenericExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "GenericExpression"; + int total = 1; + if (! node->genericArgs.empty()) { ++total; } int emitted = 0; - appendGroupOne(ss, prefix, "Object", node->scope, ++emitted == total); - if (! node->genericParams.empty()) { + appendGroupOne(ss, prefix, "TypeNode", node->typeNode, ++emitted == total); + if (! node->genericArgs.empty()) { appendGroupVec( ss, prefix, - "GenericParams", - node->genericParams, + "GenericArguments", + node->genericArgs, ++emitted == total ); } - appendGroupLeaf(ss, prefix, "Member", node->memberName, ++emitted == total); return ss.str(); } @@ -967,7 +989,7 @@ namespace arti::lang::ast { [padding](const MemberAccessExprNode &node) -> std::string { return toString(node, padding); }, - [padding](const PointerAccessExprNode &node) -> std::string { + [padding](const PointerMemberAccessExprNode &node) -> std::string { return toString(node, padding); }, [padding](const ModuleAccessExprNode &node) -> std::string { @@ -985,6 +1007,9 @@ namespace arti::lang::ast { [padding](const ReflectionExprNode &node) -> std::string { return toString(node, padding); }, + [padding](const GenericExprNode &node) -> std::string { + return toString(node, padding); + }, }; return std::visit(visitor, node); diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index c7ffb89..a55fae1 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -274,6 +274,30 @@ namespace arti::lang { auto op = pratt::getInfixOperator(peekToken->value); auto [lbp, rbp] = pratt::infixBindingPower(op); + if (op == ast::InfixOperator::ModuleAccess) { + if (auto isGeneric = match(TokenV::opLt); ! isGeneric) { + return Unexpected<>{ std::move(isGeneric).error() }; + } + else if (isGeneric.value()) { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + if (auto args = parseGenericArgumentsList(); ! args) { + return Unexpected<>{ std::move(args).error() }; + } + else { + node->typeNode = std::move(lhs); + node->genericArgs = std::move(args).value(); + } + + return node; + } + } + auto rhs = parseExpression(rbp); if (! rhs) { @@ -282,7 +306,6 @@ namespace arti::lang { /* TODO: MemberAccess and PointerMemberAccess do not use their respective * nodes types yet */ - /* TODO: ModuleAccess do not use its respective node type yet */ if (op == ast::InfixOperator::Assignment) { auto node = ast::MakeNode(); @@ -296,6 +319,45 @@ namespace arti::lang { return node; } + else if (op == ast::InfixOperator::ModuleAccess) { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->left = std::move(lhs); + node->right = std::move(rhs).value(); + + return node; + } + else if (op == ast::InfixOperator::MemberAccess) { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->object = std::move(lhs); + node->member = std::move(rhs).value(); + + return node; + } + else if (op == ast::InfixOperator::PointerMemberAccess) { + auto node = ast::MakeNode(); + + node->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + node->object = std::move(lhs); + node->member = std::move(rhs).value(); + + return node; + } else if (pratt::isCompoundAssignOperator(op)) { auto node = ast::MakeNode(); diff --git a/lib/src/Parser/Types.cpp b/lib/src/Parser/Types.cpp index 650fe77..ec18e98 100644 --- a/lib/src/Parser/Types.cpp +++ b/lib/src/Parser/Types.cpp @@ -72,7 +72,6 @@ namespace arti::lang { Expected Parser::parseType() { auto node = ast::MakeNode(); - auto currentNode = ast::TypeExpressionNode{}; if (auto nextToken = tokenizer.peek(); ! nextToken) { return Unexpected<>{ std::move(nextToken).error() }; @@ -93,105 +92,91 @@ namespace arti::lang { } } - if (auto identType = parseNamespacedIdentifier(); ! identType) { - return Unexpected<>{ std::move(identType).error() }; + if (auto ident = match(TokenV::tkIdentifier); ! ident) { + return Unexpected<>{ std::move(ident).error() }; } - else { - currentNode = ast::MakeNode(); + else if (not ident.value()) { + auto peekToken = tokenizer.peek(); - std::get(currentNode)->location = - (*identType)->location; - - std::get(currentNode)->typeName = - std::move(identType).value(); - } - - if (auto lt = matchAndConsume(TokenV::opLt); ! lt) { - return Unexpected<>{ std::move(lt).error() }; - } - else if (lt.value()) { - if (auto genericArgs = parseGenericArgumentsList(); ! genericArgs) { - return Unexpected<>{ std::move(genericArgs).error() }; + if (! peekToken) { + return Unexpected<>{ std::move(peekToken).error() }; } - else { - auto genParamsNode = ast::MakeNode(); - genParamsNode->location = std::visit( - [](const auto &node) { return node->location; }, - currentNode - ); - - genParamsNode->baseType = std::move(currentNode); - genParamsNode->genericArgs = std::move(genericArgs).value(); - currentNode = std::move(genParamsNode); - - if (auto gt = consume(TokenV::opGt, "'>'"); ! gt) { - return Unexpected<>{ std::move(gt).error() }; - } - } + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "identifier type name" + ); } - bool keepParsing = false; - - if (auto access = matchAndConsume(TokenV::opAccess); ! access) { - return Unexpected<>{ std::move(access).error() }; - } - else if (access.value()) { - keepParsing = true; - } + bool keepParsing = true; while (keepParsing) { + auto currentNode = ast::TypeExpressionNode{}; + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) { return Unexpected<>{ std::move(ident).error() }; } else { - auto newNode = ast::MakeNode(); - - newNode->location = std::visit( - [](const auto &node) { return node->location; }, - currentNode - ); + auto newNode = ast::MakeNode(); + newNode->location = { + .line = ident->line, + .column = ident->column + }; newNode->typeName = ident->strValue; - newNode->baseType = std::move(currentNode); - currentNode = std::move(newNode); - } - if (auto lt = matchAndConsume(TokenV::opLt); ! lt) { - return Unexpected<>{ std::move(lt).error() }; - } - else if (lt.value()) { - if (auto genericArgs = parseGenericArgumentsList(); ! genericArgs) { - return Unexpected<>{ std::move(genericArgs).error() }; + if (auto access = match(TokenV::opAccess); ! access) { + return Unexpected<>{ std::move(access).error() }; + } + else if (not access.value()) { + currentNode = std::move(newNode); + keepParsing = false; } else { - auto genParamsNode = ast::MakeNode(); + std::ignore = tokenizer.consume(); - genParamsNode->location = std::visit( - [](const auto &node) { return node->location; }, - currentNode - ); + if (auto access = match(TokenV::opLt); ! access) { + return Unexpected<>{ std::move(access).error() }; + } + else if (not access.value()) { + currentNode = std::move(newNode); + } + else { + auto newNode = ast::MakeNode(); - genParamsNode->baseType = std::move(currentNode); - genParamsNode->genericArgs = std::move(genericArgs).value(); - currentNode = std::move(genParamsNode); + newNode->location = { + .line = ident->line, + .column = ident->column + }; + newNode->typeName = ident->strValue; - if (auto gt = consume(TokenV::opGt, "'>'"); ! gt) { - return Unexpected<>{ std::move(gt).error() }; + if (auto args = parseGenericArgumentsList(); ! args) { + return Unexpected<>{ std::move(args).error() }; + } + else { + newNode->genericArgs = std::move(args).value(); + } + + if (auto access = match(TokenV::opAccess); ! access) { + return Unexpected<>{ std::move(access).error() }; + } + else if (not access.value()) { + keepParsing = false; + } + else { + std::ignore = tokenizer.consume(); + } + + currentNode = std::move(newNode); } } } - if (auto access = matchAndConsume(TokenV::opAccess); ! access) { - return Unexpected<>{ std::move(access).error() }; - } - else { - keepParsing = access.value(); - } + node->typeNodes.emplace_back(std::move(currentNode)); } - node->baseType = std::move(currentNode); - return node; } @@ -268,11 +253,9 @@ namespace arti::lang { Expected> Parser::parseGenericArgumentsList() { auto args = ast::Vector{}; - - auto peekToken = tokenizer.peek(); - - if (! peekToken) { - return Unexpected{ std::move(peekToken).error() }; + + if (auto lt = consume(TokenV::opLt, "'<'"); ! lt) { + return Unexpected<>{ std::move(lt).error() }; } bool keepParsing = true; @@ -299,7 +282,7 @@ namespace arti::lang { return Unexpected{ std::move(comma).error() }; } else if (! comma.value()) { - if (peekToken = tokenizer.peekExpect(TokenV::opGt); ! peekToken) { + if (auto peekToken = tokenizer.peekExpect(TokenV::opGt); ! peekToken) { return Unexpected{ std::move(peekToken).error() }; } else { @@ -308,6 +291,10 @@ namespace arti::lang { } } + if (auto gt = consume(TokenV::opGt, "'>'"); ! gt) { + return Unexpected<>{ std::move(gt).error() }; + } + return args; } -- 2.52.0 From 3180ca4662128e325169bb6cc1856713ad0c7190 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sun, 28 Dec 2025 00:12:43 -0600 Subject: [PATCH 12/15] feat(parser): implement object literals to unify struct and slice syntax Signed-off-by: erick-alcachofa 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. --- lib/include/artichoke/Parser/AST/Common.hpp | 1 + .../artichoke/Parser/AST/Expressions.hpp | 20 +-- lib/include/artichoke/Parser/AST/Literals.hpp | 47 ++---- lib/src/Parser/AST/toDot.cpp | 50 ++---- lib/src/Parser/AST/toString.cpp | 62 ++----- lib/src/Parser/Declarations.cpp | 14 ++ lib/src/Parser/Expressions.cpp | 156 +++++++++++++++++- lib/src/Parser/Operators.cpp | 3 + lib/src/Parser/Pratt.cpp | 14 +- 9 files changed, 228 insertions(+), 139 deletions(-) diff --git a/lib/include/artichoke/Parser/AST/Common.hpp b/lib/include/artichoke/Parser/AST/Common.hpp index 1749405..8fbd275 100644 --- a/lib/include/artichoke/Parser/AST/Common.hpp +++ b/lib/include/artichoke/Parser/AST/Common.hpp @@ -104,6 +104,7 @@ namespace arti::lang::ast { PtrToSlice, SliceToPtr, Reflect, + ObjectLiteral, }; enum class CompoundAssignOperator { diff --git a/lib/include/artichoke/Parser/AST/Expressions.hpp b/lib/include/artichoke/Parser/AST/Expressions.hpp index e24679f..4fcfb1f 100644 --- a/lib/include/artichoke/Parser/AST/Expressions.hpp +++ b/lib/include/artichoke/Parser/AST/Expressions.hpp @@ -76,8 +76,7 @@ namespace arti::lang::ast { FloatLtrlNode, IntegerLtrlNode, BooleanLtrlNode, - StructLtrlNode, - SliceLtrlNode, + ObjectLtrlNode, IdentifierExprNode, PrefixExprNode, InfixExprNode, @@ -207,30 +206,31 @@ namespace arti::lang::ast { ExpressionNode object; }; - struct nodes::StructLiteralNamedFieldInit { + struct nodes::ObjectLiteralNamedFieldInit { SourceLocation location; String fieldName; ExpressionNode fieldValue; }; - struct nodes::StructLiteralPositionalInit { + struct nodes::ObjectLiteralNamedInitializer { SourceLocation location; - ExpressionNode fieldValue; + Vector fields; }; - struct nodes::StructLiteralNamedInitializer { + struct nodes::ObjectLiteralPositionalInitializer { SourceLocation location; - Vector fields; + Vector fields; }; - struct nodes::StructLiteralPositionalInitializer { + struct nodes::ObjectLiteral { SourceLocation location; - Vector fields; - }; + ExpressionNode type; + Optional initializer; + }; } // namespace arti::lang::ast diff --git a/lib/include/artichoke/Parser/AST/Literals.hpp b/lib/include/artichoke/Parser/AST/Literals.hpp index e6871b7..74a6aef 100644 --- a/lib/include/artichoke/Parser/AST/Literals.hpp +++ b/lib/include/artichoke/Parser/AST/Literals.hpp @@ -37,14 +37,12 @@ namespace arti::lang::ast { struct FloatLiteral; struct IntegerLiteral; struct BooleanLiteral; - struct StructLiteral; - struct SliceLiteral; + struct ObjectLiteral; /* Helper declaration node types */ - struct StructLiteralNamedFieldInit; - struct StructLiteralPositionalInit; - struct StructLiteralNamedInitializer; - struct StructLiteralPositionalInitializer; + struct ObjectLiteralNamedFieldInit; + struct ObjectLiteralNamedInitializer; + struct ObjectLiteralPositionalInitializer; } // namespace nodes @@ -55,22 +53,19 @@ namespace arti::lang::ast { using FloatLtrlNode = Ptr; using IntegerLtrlNode = Ptr; using BooleanLtrlNode = Ptr; - using StructLtrlNode = Ptr; - using SliceLtrlNode = Ptr; + using ObjectLtrlNode = Ptr; - using StructLtrlNamedFieldInitNode = - Ptr; - using StructLtrlPositionalInitNode = - Ptr; - using StructLtrlNamedInitializerNode = - Ptr; - using StructLtrlPositionalInitializerNode = - Ptr; + using ObjectLtrlNamedFieldInitNode = + Ptr; + using ObjectLtrlNamedInitializerNode = + Ptr; + using ObjectLtrlPositionalInitializerNode = + Ptr; /* Variant nodes */ - using StructLtrlInitializerNode = Variant< - StructLtrlNamedInitializerNode, - StructLtrlPositionalInitializerNode + using ObjectLtrlInitializerNode = Variant< + ObjectLtrlNamedInitializerNode, + ObjectLtrlPositionalInitializerNode >; /* Node definitions */ @@ -109,20 +104,6 @@ namespace arti::lang::ast { Boolean value; }; - struct nodes::StructLiteral { - SourceLocation location; - - TypeNode type; - Optional initializer; - }; - - struct nodes::SliceLiteral { - SourceLocation location; - - TypeNode type; - Optional initializer; - }; - /* INFO: Helper types definitions are on Expressions.hpp * due to dependency in ExpressionNode variant. */ diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp index 461e756..dccdbd3 100644 --- a/lib/src/Parser/AST/toDot.cpp +++ b/lib/src/Parser/AST/toDot.cpp @@ -178,13 +178,11 @@ namespace arti::lang::ast { std::string emit(const FloatLtrlNode &, GraphBuilder &); std::string emit(const IntegerLtrlNode &, GraphBuilder &); std::string emit(const BooleanLtrlNode &, GraphBuilder &); - std::string emit(const StructLtrlNode &, GraphBuilder &); - std::string emit(const SliceLtrlNode &, GraphBuilder &); - std::string emit(const StructLtrlNamedFieldInitNode &, GraphBuilder &); - std::string emit(const StructLtrlPositionalInitNode &, GraphBuilder &); - std::string emit(const StructLtrlNamedInitializerNode &, GraphBuilder &); - std::string emit(const StructLtrlPositionalInitializerNode&, GraphBuilder&); - std::string emit(const StructLtrlInitializerNode &, GraphBuilder &); + std::string emit(const ObjectLtrlNode &, GraphBuilder &); + std::string emit(const ObjectLtrlNamedFieldInitNode &, GraphBuilder &); + std::string emit(const ObjectLtrlNamedInitializerNode &, GraphBuilder &); + std::string emit(const ObjectLtrlPositionalInitializerNode&, GraphBuilder&); + std::string emit(const ObjectLtrlInitializerNode &, GraphBuilder &); std::string emit(const IdentifierExprNode &, GraphBuilder &); std::string emit(const PrefixExprNode &, GraphBuilder &); std::string emit(const InfixExprNode &, GraphBuilder &); @@ -555,19 +553,8 @@ namespace arti::lang::ast { } // Struct/Slice literals and initializers - std::string emit(const StructLtrlNode &node, GraphBuilder &g) { - auto id = g.makeNode("StructLiteral"); - auto t = emit(node->type, g); - g.addEdge(id, t, "Type"); - if (node->initializer) { - auto cid = emit(*node->initializer, g); - g.addEdge(id, cid, "Elements"); - } - return id; - } - - std::string emit(const SliceLtrlNode &node, GraphBuilder &g) { - auto id = g.makeNode("SliceLiteral"); + std::string emit(const ObjectLtrlNode &node, GraphBuilder &g) { + auto id = g.makeNode("ObjectLiteral"); auto t = emit(node->type, g); g.addEdge(id, t, "Type"); if (node->initializer) { @@ -578,7 +565,7 @@ namespace arti::lang::ast { } std::string - emit(const StructLtrlNamedFieldInitNode &node, GraphBuilder &g) { + emit(const ObjectLtrlNamedFieldInitNode &node, GraphBuilder &g) { auto id = g.makeNode("FieldInitializer"); auto leaf = makeLeaf(g, node->fieldName); g.addEdge(id, leaf, "Field"); @@ -588,15 +575,7 @@ namespace arti::lang::ast { } std::string - emit(const StructLtrlPositionalInitNode &node, GraphBuilder &g) { - auto id = g.makeNode("PositionalInitializer"); - auto val = emit(node->fieldValue, g); - g.addEdge(id, val, "Value"); - return id; - } - - std::string - emit(const StructLtrlNamedInitializerNode &node, GraphBuilder &g) { + emit(const ObjectLtrlNamedInitializerNode &node, GraphBuilder &g) { auto id = g.makeNode("InitializerList"); if (! node->fields.empty()) { emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) { @@ -607,7 +586,7 @@ namespace arti::lang::ast { } std::string - emit(const StructLtrlPositionalInitializerNode &node, GraphBuilder &g) { + emit(const ObjectLtrlPositionalInitializerNode &node, GraphBuilder &g) { auto id = g.makeNode("InitializerList"); if (! node->fields.empty()) { emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) { @@ -617,10 +596,10 @@ namespace arti::lang::ast { return id; } - std::string emit(const StructLtrlInitializerNode &node, GraphBuilder &g) { + std::string emit(const ObjectLtrlInitializerNode &node, GraphBuilder &g) { auto visitor = OverloadSet{ - [&g](const StructLtrlNamedInitializerNode &n) { return emit(n, g); }, - [&g](const StructLtrlPositionalInitializerNode &n) { + [&g](const ObjectLtrlNamedInitializerNode &n) { return emit(n, g); }, + [&g](const ObjectLtrlPositionalInitializerNode &n) { return emit(n, g); }, }; @@ -770,8 +749,7 @@ namespace arti::lang::ast { [&g](const FloatLtrlNode &n) { return emit(n, g); }, [&g](const IntegerLtrlNode &n) { return emit(n, g); }, [&g](const BooleanLtrlNode &n) { return emit(n, g); }, - [&g](const StructLtrlNode &n) { return emit(n, g); }, - [&g](const SliceLtrlNode &n) { return emit(n, g); }, + [&g](const ObjectLtrlNode &n) { return emit(n, g); }, [&g](const IdentifierExprNode &n) { return emit(n, g); }, [&g](const PrefixExprNode &n) { return emit(n, g); }, [&g](const InfixExprNode &n) { return emit(n, g); }, diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp index 6ec8b31..d39020f 100644 --- a/lib/src/Parser/AST/toString.cpp +++ b/lib/src/Parser/AST/toString.cpp @@ -53,13 +53,11 @@ namespace arti::lang::ast { std::string toString(const FloatLtrlNode &, std::string); std::string toString(const IntegerLtrlNode &, std::string); std::string toString(const BooleanLtrlNode &, std::string); - std::string toString(const StructLtrlNode &, std::string); - std::string toString(const SliceLtrlNode &, std::string); - std::string toString(const StructLtrlNamedFieldInitNode &, std::string); - std::string toString(const StructLtrlPositionalInitNode &, std::string); - std::string toString(const StructLtrlNamedInitializerNode &, std::string); - std::string toString(const StructLtrlPositionalInitializerNode&, std::string); - std::string toString(const StructLtrlInitializerNode &, std::string); + std::string toString(const ObjectLtrlNode &, std::string); + std::string toString(const ObjectLtrlNamedFieldInitNode &, std::string); + std::string toString(const ObjectLtrlNamedInitializerNode &, std::string); + std::string toString(const ObjectLtrlPositionalInitializerNode&, std::string); + std::string toString(const ObjectLtrlInitializerNode &, std::string); std::string toString(const IdentifierExprNode &, std::string); std::string toString(const PrefixExprNode &, std::string); std::string toString(const InfixExprNode &, std::string); @@ -643,30 +641,9 @@ namespace arti::lang::ast { return std::format("BooleanLiteral {}", node->value ? "true" : "false"); } - std::string toString(const StructLtrlNode &node, std::string prefix) { + std::string toString(const ObjectLtrlNode &node, std::string prefix) { std::stringstream ss; - ss << "StructLiteral"; - int total = 1; - if (node->initializer) { - ++total; - } - int emitted = 0; - appendGroupOne(ss, prefix, "Type", node->type, ++emitted == total); - if (node->initializer) { - appendGroupOne( - ss, - prefix, - "Elements", - *node->initializer, - ++emitted == total - ); - } - return ss.str(); - } - - std::string toString(const SliceLtrlNode &node, std::string prefix) { - std::stringstream ss; - ss << "SliceLiteral"; + ss << "ObjectLiteral"; int total = 1; if (node->initializer) { ++total; @@ -686,7 +663,7 @@ namespace arti::lang::ast { } std::string - toString(const StructLtrlNamedFieldInitNode &node, std::string prefix) { + toString(const ObjectLtrlNamedFieldInitNode &node, std::string prefix) { std::stringstream ss; ss << "FieldInitializer"; appendGroupLeaf(ss, prefix, "Field", node->fieldName, false); @@ -695,15 +672,7 @@ namespace arti::lang::ast { } std::string - toString(const StructLtrlPositionalInitNode &node, std::string prefix) { - std::stringstream ss; - ss << "PositionalInitializer"; - appendGroupOne(ss, prefix, "Value", node->fieldValue, true); - return ss.str(); - } - - std::string - toString(const StructLtrlNamedInitializerNode &node, std::string prefix) { + toString(const ObjectLtrlNamedInitializerNode &node, std::string prefix) { std::stringstream ss; ss << "InitializerList"; appendGroupVec(ss, prefix, "Elements", node->fields, true); @@ -711,7 +680,7 @@ namespace arti::lang::ast { } std::string toString( - const StructLtrlPositionalInitializerNode &node, + const ObjectLtrlPositionalInitializerNode &node, std::string prefix ) { std::stringstream ss; @@ -721,12 +690,12 @@ namespace arti::lang::ast { } std::string - toString(const StructLtrlInitializerNode &node, std::string padding) { + toString(const ObjectLtrlInitializerNode &node, std::string padding) { auto visitor = OverloadSet{ - [padding](const StructLtrlNamedInitializerNode &node) -> std::string { + [padding](const ObjectLtrlNamedInitializerNode &node) -> std::string { return toString(node, padding); }, - [padding](const StructLtrlPositionalInitializerNode &node) + [padding](const ObjectLtrlPositionalInitializerNode &node) -> std::string { return toString(node, padding); }, }; @@ -956,10 +925,7 @@ namespace arti::lang::ast { [padding](const BooleanLtrlNode &node) -> std::string { return toString(node, padding); }, - [padding](const StructLtrlNode &node) -> std::string { - return toString(node, padding); - }, - [padding](const SliceLtrlNode &node) -> std::string { + [padding](const ObjectLtrlNode &node) -> std::string { return toString(node, padding); }, [padding](const IdentifierExprNode &node) -> std::string { diff --git a/lib/src/Parser/Declarations.cpp b/lib/src/Parser/Declarations.cpp index fa33f69..ba20620 100644 --- a/lib/src/Parser/Declarations.cpp +++ b/lib/src/Parser/Declarations.cpp @@ -504,6 +504,13 @@ namespace arti::lang { } } } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + keepParsing = false; + } } return membersList; @@ -585,6 +592,13 @@ namespace arti::lang { } } } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + keepParsing = false; + } } return membersList; diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index a55fae1..61c6848 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -449,13 +449,6 @@ namespace arti::lang { } } } - - if (auto close = match(TokenV::opRParen); ! close) { - return Unexpected<>{ std::move(close).error() }; - } - else if (close.value()) { - stillParams = false; - } } if (auto close = consume(TokenV::opRParen, "')'"); ! close) { @@ -466,6 +459,155 @@ namespace arti::lang { node = std::move(newNode); } + else if (op == ast::PostfixOperator::ObjectLiteral) { + auto newNode = ast::MakeNode(); + + newNode->location = { + .line = peekToken->line, + .column = peekToken->column + }; + + bool stillParams = true; + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + stillParams = false; + } + + if (auto isNamed = match(TokenV::opDot); ! isNamed) { + return Unexpected<>{ std::move(isNamed).error() }; + } + else if (isNamed.value()) { + auto initializerNode = + ast::MakeNode(); + + initializerNode->location = newNode->location; + + while (stillParams) { + auto currLocation = ast::SourceLocation{}; + + if (auto dot = consume(TokenV::opDot, "'.'"); ! dot) { + return Unexpected<>{ std::move(dot).error() }; + } + else { + currLocation.line = dot->line; + currLocation.column = dot->column; + } + + if (auto ident = consume(TokenV::tkIdentifier, "identifier"); + ! ident) { + return Unexpected<>{ std::move(ident).error() }; + } + else { + if (auto eq = consume(TokenV::opAssign, "'='"); ! eq) { + return Unexpected<>{ std::move(eq).error() }; + } + + auto value = parseExpression(); + + if (! value) { + return Unexpected<>{ std::move(value).error() }; + } + + auto init = ast::MakeNode(); + + init->location = currLocation; + init->fieldName = ident->strValue; + init->fieldValue = std::move(value).value(); + + initializerNode->fields.push_back(std::move(init)); + + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected{ std::move(comma).error() }; + } + else if (! comma.value()) { + if (auto ntok = tokenizer.peek(); ! ntok) { + return Unexpected{ std::move(ntok).error() }; + } + else { + if (ntok->value != TokenV::opRSquirly) { + return langException( + ntok->line, + ntok->column, + toString(*ntok), + "',' or '}'" + ); + } + else { + stillParams = false; + } + } + } + } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + stillParams = false; + } + } + + newNode->initializer = std::move(initializerNode); + } + else { + auto initializerNode = + ast::MakeNode(); + + initializerNode->location = newNode->location; + + while (stillParams) { + auto arg = parseExpression(); + + if (! arg) { + return Unexpected<>{ std::move(arg).error() }; + } + + initializerNode->fields.push_back(std::move(arg).value()); + + if (auto comma = matchAndConsume(TokenV::opComma); ! comma) { + return Unexpected{ std::move(comma).error() }; + } + else if (! comma.value()) { + if (auto ntok = tokenizer.peek(); ! ntok) { + return Unexpected{ std::move(ntok).error() }; + } + else { + if (ntok->value != TokenV::opRSquirly) { + return langException( + ntok->line, + ntok->column, + toString(*ntok), + "',' or '}'" + ); + } + else { + stillParams = false; + } + } + } + + if (auto close = match(TokenV::opRSquirly); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + stillParams = false; + } + } + + newNode->initializer = std::move(initializerNode); + } + + if (auto close = consume(TokenV::opRSquirly, "'}'"); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + + newNode->type = std::move(lhs); + + node = std::move(newNode); + } else if (op == ast::PostfixOperator::SliceAccess) { auto idx = parseExpression(); diff --git a/lib/src/Parser/Operators.cpp b/lib/src/Parser/Operators.cpp index ce9b9a9..92b1992 100644 --- a/lib/src/Parser/Operators.cpp +++ b/lib/src/Parser/Operators.cpp @@ -97,6 +97,7 @@ namespace arti::lang::pratt { case opPtrSlice: case opSlicePtr: case opReflect: + case opLSquirly: return true; default: return false; @@ -223,6 +224,8 @@ namespace arti::lang::pratt { return SliceToPtr; case opReflect: return Reflect; + case opLSquirly: + return ObjectLiteral; default: return Uninitialized; } diff --git a/lib/src/Parser/Pratt.cpp b/lib/src/Parser/Pratt.cpp index 48516fb..2c43b02 100644 --- a/lib/src/Parser/Pratt.cpp +++ b/lib/src/Parser/Pratt.cpp @@ -88,9 +88,10 @@ namespace arti::lang::pratt { case ast::InfixOperator::BoolAndAssignment: case ast::InfixOperator::BoolOrAssignment: case ast::InfixOperator::LShiftAssignment: - case ast::InfixOperator::RShiftAssignment: return { 2, 1 }; - - default: return { 0, 0 }; + case ast::InfixOperator::RShiftAssignment: + return { 2, 1 }; + default: + return { 0, 0 }; } } @@ -103,8 +104,11 @@ namespace arti::lang::pratt { case ast::PostfixOperator::SliceSize: case ast::PostfixOperator::PtrToSlice: case ast::PostfixOperator::SliceToPtr: - case ast::PostfixOperator::Reflect: return 19; - default: return 0; + case ast::PostfixOperator::Reflect: + case ast::PostfixOperator::ObjectLiteral: + return 19; + default: + return 0; } } -- 2.52.0 From f024334da5105198ab7cd82cac7540ce6f26f082 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sun, 28 Dec 2025 00:44:02 -0600 Subject: [PATCH 13/15] fix(parser): resolve expression ambiguity in switch/match cases Signed-off-by: erick-alcachofa Implement precedence capping in `parseExpression` for switch cases to prevent the parser from misinterpreting the case arrow (`->`) as a pointer member access operator. Additionally, increased the binding power of `ModuleAccess` (::) to ensure namespaced identifiers are correctly resolved within case patterns before hitting the precedence limit. - Use `PointerMemberAccess.right` as the precedence floor for cases. - Update `ModuleAccess` binding power to {23, 24}. --- lib/src/Parser/Pratt.cpp | 2 +- lib/src/Parser/Statements.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/Parser/Pratt.cpp b/lib/src/Parser/Pratt.cpp index 2c43b02..b71bcf7 100644 --- a/lib/src/Parser/Pratt.cpp +++ b/lib/src/Parser/Pratt.cpp @@ -40,7 +40,7 @@ namespace arti::lang::pratt { BindingPower infixBindingPower(ast::InfixOperator op) { switch (op) { // Member Access (Highest) - case ast::InfixOperator::ModuleAccess: + case ast::InfixOperator::ModuleAccess: return { 23, 24 }; case ast::InfixOperator::MemberAccess: case ast::InfixOperator::PointerMemberAccess: return { 21, 22 }; diff --git a/lib/src/Parser/Statements.cpp b/lib/src/Parser/Statements.cpp index eab6553..b1405e2 100644 --- a/lib/src/Parser/Statements.cpp +++ b/lib/src/Parser/Statements.cpp @@ -21,6 +21,7 @@ //============================================================================// #include +#include #include @@ -884,6 +885,9 @@ namespace arti::lang { return Unexpected<>{ std::move(lSquirly).error() }; } + uint16_t limit = + pratt::infixBindingPower(ast::InfixOperator::PointerMemberAccess).right; + bool keepParsing = true; while (keepParsing) { @@ -912,7 +916,7 @@ namespace arti::lang { else { auto curCase = ast::MakeNode(); - if (auto expr = parseExpression(); ! expr) { + if (auto expr = parseExpression(limit); ! expr) { return Unexpected<>{ std::move(expr).error() }; } else { -- 2.52.0 From c2f37d57023c306933db2befd54333ee6182ee69 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sun, 28 Dec 2025 10:21:24 -0600 Subject: [PATCH 14/15] feat(parser): add support for type-initiated expressions Signed-off-by: erick-alcachofa Implement TypeExpression AST node to allow types to be used within expressions, enabling the parsing of anonymous slice and array initializers like `[]Type { ... }`. - Register `[` as a prefix-style token (NUD) in the Pratt parser. - Add `TypeExpression` node to AST and expression variants. - Update `toDot` and `toString` visitors for AST visualization. - Update frontend to open source files directly to fix issues at opening paths. --- frontend/src/main.cpp | 2 +- .../artichoke/Parser/AST/Expressions.hpp | 11 ++++++++- lib/src/Parser/AST/toDot.cpp | 8 +++++++ lib/src/Parser/AST/toString.cpp | 11 +++++++++ lib/src/Parser/Expressions.cpp | 23 +++++++++++++++++++ lib/src/Parser/Operators.cpp | 1 + 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/frontend/src/main.cpp b/frontend/src/main.cpp index 67adcc8..a5536c5 100644 --- a/frontend/src/main.cpp +++ b/frontend/src/main.cpp @@ -41,7 +41,7 @@ int main(int argc, char **argv) { } std::ifstream file; - file.open(sanitizePath(argv[1])); + file.open(argv[1]); if (! file.is_open()) { std::println("Failed to open file {}", argv[1]); diff --git a/lib/include/artichoke/Parser/AST/Expressions.hpp b/lib/include/artichoke/Parser/AST/Expressions.hpp index 4fcfb1f..79ff4dd 100644 --- a/lib/include/artichoke/Parser/AST/Expressions.hpp +++ b/lib/include/artichoke/Parser/AST/Expressions.hpp @@ -47,6 +47,7 @@ namespace arti::lang::ast { struct SliceCreationExpression; struct SliceLengthExpression; struct SlicePtrExpression; + struct TypeExpression; } // namespace nodes @@ -67,6 +68,7 @@ namespace arti::lang::ast { using SliceCreationExprNode = Ptr; using SliceLengthExprNode = Ptr; using SlicePtrExprNode = Ptr; + using TypeExprNode = Ptr; /* Variant nodes */ using ExpressionNode = Variant< @@ -92,7 +94,8 @@ namespace arti::lang::ast { SliceCreationExprNode, SliceLengthExprNode, SlicePtrExprNode, - ReflectionExprNode + ReflectionExprNode, + TypeExprNode >; /* Node definitions */ @@ -232,5 +235,11 @@ namespace arti::lang::ast { Optional initializer; }; + struct nodes::TypeExpression { + SourceLocation location; + + TypeNode type; + }; + } // namespace arti::lang::ast diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp index dccdbd3..ed8dfd8 100644 --- a/lib/src/Parser/AST/toDot.cpp +++ b/lib/src/Parser/AST/toDot.cpp @@ -223,6 +223,7 @@ namespace arti::lang::ast { std::string emit(const ElseBranchNode &, GraphBuilder &); std::string emit(const DeferableNode &, GraphBuilder &); std::string emit(const PreLoopStmtNode &, GraphBuilder &); + std::string emit(const TypeExprNode &, GraphBuilder &); // Helpers for making leaf nodes with backticked values inline std::string makeLeaf(GraphBuilder &g, std::string_view value) { @@ -766,6 +767,7 @@ namespace arti::lang::ast { [&g](const SlicePtrExprNode &n) { return emit(n, g); }, [&g](const ReflectionExprNode &n) { return emit(n, g); }, [&g](const GenericExprNode &n) { return emit(n, g); }, + [&g](const TypeExprNode &n) { return emit(n, g); }, }; return std::visit(visitor, node); } @@ -1019,6 +1021,12 @@ namespace arti::lang::ast { return std::visit(visitor, node); } + std::string emit(const TypeExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("TypeExpression"); + g.addEdge(id, emit(node->type, g), "Type"); + return id; + } + } // namespace // Public API diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp index d39020f..3874db7 100644 --- a/lib/src/Parser/AST/toString.cpp +++ b/lib/src/Parser/AST/toString.cpp @@ -98,6 +98,7 @@ namespace arti::lang::ast { std::string toString(const ElseBranchNode &, std::string); std::string toString(const DeferableNode &, std::string); std::string toString(const PreLoopStmtNode &, std::string); + std::string toString(const TypeExprNode &, std::string); std::string toString(PrefixOperator op); std::string toString(InfixOperator op); std::string toString(CompoundAssignOperator op); @@ -976,6 +977,9 @@ namespace arti::lang::ast { [padding](const GenericExprNode &node) -> std::string { return toString(node, padding); }, + [padding](const TypeExprNode &node) -> std::string { + return toString(node, padding); + }, }; return std::visit(visitor, node); @@ -1457,6 +1461,13 @@ namespace arti::lang::ast { return std::visit(visitor, node); } + std::string toString(const TypeExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "TypeÉxpression"; + appendItem(ss, prefix, node->type, true); + return ss.str(); + } + std::string toString(PrefixOperator op) { using enum PrefixOperator; diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index 61c6848..83afed0 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -49,6 +49,29 @@ namespace arti::lang { lhs = std::move(lhsExpr).value(); } } + else if (peekToken->value == TokenV::opLBracket) { + if (auto close = match(TokenV::opRBracket, 1); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (! close.value()) { + return langException( + peekToken->line, + peekToken->column, + toString(*peekToken), + "']'" + ); + } + + if (auto type = parseType(); ! type) { + return Unexpected<>{ std::move(type).error() }; + } + else { + auto node = ast::MakeNode(); + node->location = type.value()->location; + node->type = std::move(type).value(); + lhs = std::move(node); + } + } else if (pratt::isPrefixOperator(peekToken->value)) { if (auto newLhs = parsePrefixExpression(); ! newLhs) { return Unexpected<>{ std::move(newLhs).error() }; diff --git a/lib/src/Parser/Operators.cpp b/lib/src/Parser/Operators.cpp index 92b1992..ab1416c 100644 --- a/lib/src/Parser/Operators.cpp +++ b/lib/src/Parser/Operators.cpp @@ -34,6 +34,7 @@ namespace arti::lang::pratt { case opTilde: case opAnd: case opLParen: + case opLBracket: case kwNot: return true; -- 2.52.0 From 25486fbacef82eb5b73b72c1dfd715f0898dcadf Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sun, 28 Dec 2025 11:26:27 -0600 Subject: [PATCH 15/15] fix(parser): support optional start and end indices in slice ranges Signed-off-by: erick-alcachofa Update the SliceAccess postfix operator logic to handle the full variety of slice range syntaxes. This allows for open-ended slices by making the start and end expressions optional within the brackets. - Add logic to detect a leading colon for `[:end]` and `[:]` forms. - Support trailing colons for `[start:]` forms. - Differentiate between a single index access and a slice range based on the presence of the colon operator. - Update SliceRangeExprNode construction to handle optional boundaries. --- lib/src/Parser/Expressions.cpp | 72 ++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index 83afed0..e696667 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -632,16 +632,52 @@ namespace arti::lang { node = std::move(newNode); } else if (op == ast::PostfixOperator::SliceAccess) { - auto idx = parseExpression(); + bool isSlice = false; + bool skipSliceEnd = false; + bool skipSliceStart = false; - if (! idx) { - return Unexpected<>{ std::move(idx).error() }; + if (auto skipLeft = matchAndConsume(TokenV::opColon); ! skipLeft) { + return Unexpected<>{ std::move(skipLeft).error() }; + } + else if (skipLeft.value()) { + isSlice = true; + skipSliceStart = true; + + if (auto close = matchAndConsume(TokenV::opRBracket); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + skipSliceEnd = true; + } } - if (auto range = matchAndConsume(TokenV::opColon); ! range) { - return Unexpected<>{ std::move(range).error() }; + auto idxExpr = ast::Optional{}; + + if (! skipSliceStart) { + auto idx = parseExpression(); + + if (! idx) { + return Unexpected<>{ std::move(idx).error() }; + } + + if (auto range = matchAndConsume(TokenV::opColon); ! range) { + return Unexpected<>{ std::move(range).error() }; + } + else if (range.value()) { + isSlice = true; + + if (auto close = matchAndConsume(TokenV::opRBracket); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + skipSliceEnd = true; + } + } + + idxExpr = std::move(idx).value(); } - else if (range.value()) { + + if (isSlice) { auto newNode = ast::MakeNode(); newNode->location = { @@ -649,18 +685,22 @@ namespace arti::lang { .column = peekToken->column }; - newNode->start = std::move(idx).value(); - - auto endIdx = parseExpression(); - - if (! endIdx) { - return Unexpected<>{ std::move(endIdx).error() }; + if (! skipSliceStart) { + newNode->start = std::move(idxExpr).value(); } - newNode->end = std::move(endIdx).value(); + if (! skipSliceEnd) { + auto endIdx = parseExpression(); - if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { - return Unexpected<>{ std::move(close).error() }; + if (! endIdx) { + return Unexpected<>{ std::move(endIdx).error() }; + } + + newNode->end = std::move(endIdx).value(); + + if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { + return Unexpected<>{ std::move(close).error() }; + } } newNode->slice = std::move(lhs); @@ -675,7 +715,7 @@ namespace arti::lang { .column = peekToken->column }; - newNode->index = std::move(idx).value(); + newNode->index = std::move(idxExpr).value(); if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { return Unexpected<>{ std::move(close).error() }; -- 2.52.0