Initial Parser Implementation and Feature Completion #1

Merged
me merged 16 commits from parser-dev into main 2025-12-28 11:54:12 -06:00
46 changed files with 5426 additions and 347 deletions

2
.gitignore vendored
View File

@ -3,4 +3,6 @@
build/** build/**
install/** install/**
cpm-package-lock.cmake
TODO.md TODO.md

27
add-notice.sh Executable file
View File

@ -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!"

View File

@ -170,7 +170,7 @@ non_exportable_declaration =
"switch" "(" <expression> ")" "{" <switch_case>* <default_case>? "}" "switch" "(" <expression> ")" "{" <switch_case>* <default_case>? "}"
<match_case> = <match_case> =
<type_name> ( "(" <identifier> ")" )? "->" <code_block> <type_name> ( "|" <identifier> "|" )? "->" <code_block>
<switch_case> = <switch_case> =
<expression> "->" <code_block> <expression> "->" <code_block>

View File

@ -1,5 +1,78 @@
#include <print> //============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
int main(int, char **) { #include <print>
std::println("[LOG] Hello world"); #include <fstream>
#include <filesystem>
#include <artichoke/Parser/Parser.hpp>
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;
if (argc < 2) {
std::println("Usage:\n {} <filename>", 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<char>(file),
std::istreambuf_iterator<char>() };
auto parser = Parser{ sanitizePath(argv[1]), 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");
std::println("```markdown");
std::println("{}", ast::toString(ast));
std::println("```");
// std::println("{}", ast::toDot(ast));
} }

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <string_view> #include <string_view>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <utility> #include <utility>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#define yield co_yield #define yield co_yield

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Parser/AST/Common.hpp> #include <artichoke/Parser/AST/Common.hpp>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <cstddef> #include <cstddef>
@ -28,35 +50,61 @@ namespace arti::lang::ast {
Optional, Optional,
}; };
enum class UnaryOperator { enum class PrefixOperator {
Uninitialized, Uninitialized,
Not, Not,
Minus, Minus,
BitNot, BitNot,
Ampersand, MemPtr,
Star, DerefPtr,
}; };
enum class BinaryOperator { enum class InfixOperator {
Uninitialized, Uninitialized,
Modulo,
Addition,
Substraction,
Division,
Multiplication,
Equal, Equal,
NotEqual, NotEqual,
GreaterThan,
LessThan, LessThan,
GreaterEqual, GreaterThan,
LessEqual, LessEqual,
BitAnd, GreaterEqual,
BitXor,
BitOr,
LeftShift, LeftShift,
RightShift, RightShift,
Adition,
Substraction,
Multiplication,
Division,
Modulo,
BoolAnd, BoolAnd,
BoolOr, 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,
ObjectLiteral,
}; };
enum class CompoundAssignOperator { enum class CompoundAssignOperator {

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Parser/AST/Common.hpp> #include <artichoke/Parser/AST/Common.hpp>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Parser/AST/Common.hpp> #include <artichoke/Parser/AST/Common.hpp>
@ -10,39 +32,43 @@ namespace arti::lang::ast {
/* Main declaration node types */ /* Main declaration node types */
struct IdentifierExpression; struct IdentifierExpression;
struct UnaryExpression; struct PrefixExpression;
struct BinaryExpression; struct InfixExpression;
struct AssignExpression; struct AssignExpression;
struct CompoundAssignExpression; struct CompoundAssignExpression;
struct FunctionCallExpression; struct FunctionCallExpression;
struct SliceAccessExpression; struct SliceAccessExpression;
struct SliceRangeExpression; struct SliceRangeExpression;
struct MemberAccessExpression; struct MemberAccessExpression;
struct PointerAccessExpression; struct PointerMemberAccessExpression;
struct ScopeAccessExpression; struct GenericExpression;
struct ModuleAccessExpression;
struct ReflectionExpression; struct ReflectionExpression;
struct SliceCreationExpression; struct SliceCreationExpression;
struct SliceLengthExpression; struct SliceLengthExpression;
struct SlicePtrExpression; struct SlicePtrExpression;
struct TypeExpression;
} // namespace nodes } // namespace nodes
/* Public Aliases */ /* Public Aliases */
using IdentifierExprNode = Ptr<nodes::IdentifierExpression>; using IdentifierExprNode = Ptr<nodes::IdentifierExpression>;
using UnaryExprNode = Ptr<nodes::UnaryExpression>; using PrefixExprNode = Ptr<nodes::PrefixExpression>;
using BinaryExprNode = Ptr<nodes::BinaryExpression>; using InfixExprNode = Ptr<nodes::InfixExpression>;
using AssignExprNode = Ptr<nodes::AssignExpression>; using AssignExprNode = Ptr<nodes::AssignExpression>;
using CompoundAssignExprNode = Ptr<nodes::CompoundAssignExpression>; using CompoundAssignExprNode = Ptr<nodes::CompoundAssignExpression>;
using FunctionCallExprNode = Ptr<nodes::FunctionCallExpression>; using FunctionCallExprNode = Ptr<nodes::FunctionCallExpression>;
using SliceAccessExprNode = Ptr<nodes::SliceAccessExpression>; using SliceAccessExprNode = Ptr<nodes::SliceAccessExpression>;
using SliceRangeExprNode = Ptr<nodes::SliceRangeExpression>; using SliceRangeExprNode = Ptr<nodes::SliceRangeExpression>;
using MemberAccessExprNode = Ptr<nodes::MemberAccessExpression>; using MemberAccessExprNode = Ptr<nodes::MemberAccessExpression>;
using PointerAccessExprNode = Ptr<nodes::PointerAccessExpression>; using PointerMemberAccessExprNode = Ptr<nodes::PointerMemberAccessExpression>;
using ScopeAccessExprNode = Ptr<nodes::ScopeAccessExpression>; using GenericExprNode = Ptr<nodes::GenericExpression>;
using ModuleAccessExprNode = Ptr<nodes::ModuleAccessExpression>;
using ReflectionExprNode = Ptr<nodes::ReflectionExpression>; using ReflectionExprNode = Ptr<nodes::ReflectionExpression>;
using SliceCreationExprNode = Ptr<nodes::SliceCreationExpression>; using SliceCreationExprNode = Ptr<nodes::SliceCreationExpression>;
using SliceLengthExprNode = Ptr<nodes::SliceLengthExpression>; using SliceLengthExprNode = Ptr<nodes::SliceLengthExpression>;
using SlicePtrExprNode = Ptr<nodes::SlicePtrExpression>; using SlicePtrExprNode = Ptr<nodes::SlicePtrExpression>;
using TypeExprNode = Ptr<nodes::TypeExpression>;
/* Variant nodes */ /* Variant nodes */
using ExpressionNode = Variant< using ExpressionNode = Variant<
@ -52,23 +78,24 @@ namespace arti::lang::ast {
FloatLtrlNode, FloatLtrlNode,
IntegerLtrlNode, IntegerLtrlNode,
BooleanLtrlNode, BooleanLtrlNode,
StructLtrlNode, ObjectLtrlNode,
SliceLtrlNode,
IdentifierExprNode, IdentifierExprNode,
UnaryExprNode, PrefixExprNode,
BinaryExprNode, InfixExprNode,
AssignExprNode, AssignExprNode,
CompoundAssignExprNode, CompoundAssignExprNode,
FunctionCallExprNode, FunctionCallExprNode,
SliceAccessExprNode, SliceAccessExprNode,
SliceRangeExprNode, SliceRangeExprNode,
MemberAccessExprNode, MemberAccessExprNode,
PointerAccessExprNode, PointerMemberAccessExprNode,
ScopeAccessExprNode, GenericExprNode,
ModuleAccessExprNode,
SliceCreationExprNode, SliceCreationExprNode,
SliceLengthExprNode, SliceLengthExprNode,
SlicePtrExprNode, SlicePtrExprNode,
ReflectionExprNode ReflectionExprNode,
TypeExprNode
>; >;
/* Node definitions */ /* Node definitions */
@ -77,16 +104,16 @@ namespace arti::lang::ast {
String identifierName; String identifierName;
}; };
struct nodes::UnaryExpression { struct nodes::PrefixExpression {
SourceLocation location; SourceLocation location;
UnaryOperator op; PrefixOperator op;
ExpressionNode right; ExpressionNode right;
}; };
struct nodes::BinaryExpression { struct nodes::InfixExpression {
SourceLocation location; SourceLocation location;
BinaryOperator op; InfixOperator op;
ExpressionNode left; ExpressionNode left;
ExpressionNode right; ExpressionNode right;
}; };
@ -131,23 +158,29 @@ namespace arti::lang::ast {
struct nodes::MemberAccessExpression { struct nodes::MemberAccessExpression {
SourceLocation location; SourceLocation location;
String memberName; ExpressionNode member;
ExpressionNode object; ExpressionNode object;
}; };
struct nodes::PointerAccessExpression { struct nodes::PointerMemberAccessExpression {
SourceLocation location; SourceLocation location;
String memberName; ExpressionNode member;
ExpressionNode object; ExpressionNode object;
}; };
struct nodes::ScopeAccessExpression { struct nodes::ModuleAccessExpression {
SourceLocation location; SourceLocation location;
String memberName; ExpressionNode left;
ExpressionNode scope; ExpressionNode right;
Vector<TypeNode> genericParams; };
struct nodes::GenericExpression {
SourceLocation location;
ExpressionNode typeNode;
std::vector<TypeNode> genericArgs;
}; };
struct nodes::ReflectionExpression { struct nodes::ReflectionExpression {
@ -176,30 +209,37 @@ namespace arti::lang::ast {
ExpressionNode object; ExpressionNode object;
}; };
struct nodes::StructLiteralNamedFieldInit { struct nodes::ObjectLiteralNamedFieldInit {
SourceLocation location; SourceLocation location;
String fieldName; String fieldName;
ExpressionNode fieldValue; ExpressionNode fieldValue;
}; };
struct nodes::StructLiteralPositionalInit { struct nodes::ObjectLiteralNamedInitializer {
SourceLocation location; SourceLocation location;
ExpressionNode fieldValue; Vector<ObjectLtrlNamedFieldInitNode> fields;
}; };
struct nodes::StructLiteralNamedInitializer { struct nodes::ObjectLiteralPositionalInitializer {
SourceLocation location; SourceLocation location;
Vector<StructLtrlNamedFieldInitNode> fields; Vector<ExpressionNode> fields;
}; };
struct nodes::StructLiteralPositionalInitializer { struct nodes::ObjectLiteral {
SourceLocation location; SourceLocation location;
Vector<StructLtrlPositionalInitNode> fields; ExpressionNode type;
}; Optional<ObjectLtrlInitializerNode> initializer;
};
struct nodes::TypeExpression {
SourceLocation location;
TypeNode type;
};
} // namespace arti::lang::ast } // namespace arti::lang::ast

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Parser/AST/Common.hpp> #include <artichoke/Parser/AST/Common.hpp>
@ -15,14 +37,12 @@ namespace arti::lang::ast {
struct FloatLiteral; struct FloatLiteral;
struct IntegerLiteral; struct IntegerLiteral;
struct BooleanLiteral; struct BooleanLiteral;
struct StructLiteral; struct ObjectLiteral;
struct SliceLiteral;
/* Helper declaration node types */ /* Helper declaration node types */
struct StructLiteralNamedFieldInit; struct ObjectLiteralNamedFieldInit;
struct StructLiteralPositionalInit; struct ObjectLiteralNamedInitializer;
struct StructLiteralNamedInitializer; struct ObjectLiteralPositionalInitializer;
struct StructLiteralPositionalInitializer;
} // namespace nodes } // namespace nodes
@ -33,22 +53,19 @@ namespace arti::lang::ast {
using FloatLtrlNode = Ptr<nodes::FloatLiteral>; using FloatLtrlNode = Ptr<nodes::FloatLiteral>;
using IntegerLtrlNode = Ptr<nodes::IntegerLiteral>; using IntegerLtrlNode = Ptr<nodes::IntegerLiteral>;
using BooleanLtrlNode = Ptr<nodes::BooleanLiteral>; using BooleanLtrlNode = Ptr<nodes::BooleanLiteral>;
using StructLtrlNode = Ptr<nodes::StructLiteral>; using ObjectLtrlNode = Ptr<nodes::ObjectLiteral>;
using SliceLtrlNode = Ptr<nodes::SliceLiteral>;
using StructLtrlNamedFieldInitNode = using ObjectLtrlNamedFieldInitNode =
Ptr<nodes::StructLiteralNamedFieldInit>; Ptr<nodes::ObjectLiteralNamedFieldInit>;
using StructLtrlPositionalInitNode = using ObjectLtrlNamedInitializerNode =
Ptr<nodes::StructLiteralPositionalInit>; Ptr<nodes::ObjectLiteralNamedInitializer>;
using StructLtrlNamedInitializerNode = using ObjectLtrlPositionalInitializerNode =
Ptr<nodes::StructLiteralNamedInitializer>; Ptr<nodes::ObjectLiteralPositionalInitializer>;
using StructLtrlPositionalInitializerNode =
Ptr<nodes::StructLiteralPositionalInitializer>;
/* Variant nodes */ /* Variant nodes */
using StructLtrlInitializerNode = Variant< using ObjectLtrlInitializerNode = Variant<
StructLtrlNamedInitializerNode, ObjectLtrlNamedInitializerNode,
StructLtrlPositionalInitializerNode ObjectLtrlPositionalInitializerNode
>; >;
/* Node definitions */ /* Node definitions */
@ -87,20 +104,6 @@ namespace arti::lang::ast {
Boolean value; Boolean value;
}; };
struct nodes::StructLiteral {
SourceLocation location;
TypeNode type;
Optional<StructLtrlInitializerNode> initializer;
};
struct nodes::SliceLiteral {
SourceLocation location;
TypeNode type;
Optional<StructLtrlPositionalInitializerNode> initializer;
};
/* INFO: Helper types definitions are on Expressions.hpp /* INFO: Helper types definitions are on Expressions.hpp
* due to dependency in ExpressionNode variant. */ * due to dependency in ExpressionNode variant. */

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Parser/AST/Common.hpp> #include <artichoke/Parser/AST/Common.hpp>
@ -71,7 +93,8 @@ namespace arti::lang::ast {
WhileStmtNode, WhileStmtNode,
DoWhileStmtNode, DoWhileStmtNode,
InfLoopStmtNode, InfLoopStmtNode,
ExpressionStmtNode ExpressionStmtNode,
CodeBlockStmtNode
>; >;
using ElseBranchNode = Variant< using ElseBranchNode = Variant<

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Parser/AST/Common.hpp> #include <artichoke/Parser/AST/Common.hpp>
@ -11,7 +33,6 @@ namespace arti::lang::ast {
struct Type; struct Type;
struct GenericType; struct GenericType;
struct IdentifierType; struct IdentifierType;
struct NamespacedType;
/* Helper type node types */ /* Helper type node types */
struct NamespacedIdentifier; struct NamespacedIdentifier;
@ -22,14 +43,12 @@ namespace arti::lang::ast {
using TypeNode = Ptr<nodes::Type>; using TypeNode = Ptr<nodes::Type>;
using GenericTypeNode = Ptr<nodes::GenericType>; using GenericTypeNode = Ptr<nodes::GenericType>;
using IdentifierTypeNode = Ptr<nodes::IdentifierType>; using IdentifierTypeNode = Ptr<nodes::IdentifierType>;
using NamespacedTypeNode = Ptr<nodes::NamespacedType>;
using NamespacedIdentifierNode = Ptr<nodes::NamespacedIdentifier>; using NamespacedIdentifierNode = Ptr<nodes::NamespacedIdentifier>;
/* Variant nodes */ /* Variant nodes */
using TypeExpressionNode = Variant< using TypeExpressionNode = Variant<
GenericTypeNode, GenericTypeNode,
IdentifierTypeNode, IdentifierTypeNode
NamespacedTypeNode
>; >;
/* Node definitions */ /* Node definitions */
@ -38,27 +57,20 @@ namespace arti::lang::ast {
SourceLocation location; SourceLocation location;
Vector<TypeQualifier> qualifiers; Vector<TypeQualifier> qualifiers;
TypeExpressionNode baseType; Vector<TypeExpressionNode> typeNodes;
}; };
struct nodes::GenericType { struct nodes::GenericType {
SourceLocation location; SourceLocation location;
TypeExpressionNode baseType; String typeName;
Vector<TypeNode> genericArgs; Vector<TypeNode> genericArgs;
}; };
struct nodes::IdentifierType { struct nodes::IdentifierType {
SourceLocation location; SourceLocation location;
NamespacedIdentifierNode typeName;
};
struct nodes::NamespacedType {
SourceLocation location;
String typeName; String typeName;
TypeExpressionNode baseType;
}; };
struct nodes::NamespacedIdentifier { struct nodes::NamespacedIdentifier {

View File

@ -0,0 +1,211 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once
#include <artichoke/Parser/AST/AST.hpp>
#include <artichoke/Tokenizer/Tokenizer.hpp>
namespace arti::lang {
struct Parser {
Parser(std::string source) noexcept;
Parser(std::string unitName, std::string source) noexcept;
Parser(Parser &&) noexcept;
Parser &operator=(Parser &&) noexcept;
Parser(const Parser &) noexcept = delete;
Parser &operator=(const Parser &) noexcept = delete;
Expected<ast::AST> parse();
Expected<Token>
consume(TokenV type, std::string_view message);
Expected<bool>
matchAndConsume(TokenV type);
Expected<bool>
match(TokenV type, std::size_t offset = 0);
Expected<ast::Optional<ast::TopLevelDeclNode>>
parseTopLevelDeclaration();
Expected<ast::ImportDeclNode>
parseImportDeclaration();
Expected<ast::AliasDeclNode>
parseAliasDeclaration();
Expected<ast::ModuleDeclNode>
parseModuleDeclaration();
Expected<ast::StructDeclNode>
parseStructDeclaration();
Expected<ast::EnumDeclNode>
parseEnumDeclaration();
Expected<ast::Vector<ast::GenericParamNode>>
parseGenericParamsList();
Expected<ast::GenericParamNode>
parseGenericParam();
Expected<ast::Vector<ast::StructMemberNode>>
parseStructMembersList();
Expected<ast::StructMemberNode>
parseStructMember();
Expected<ast::Vector<ast::EnumMemberNode>>
parseEnumMembersList();
Expected<ast::EnumMemberNode>
parseEnumMember();
Expected<ast::FunctionDeclNode>
parseFunctionDeclaration();
Expected<ast::NamespacedIdentifierNode>
parseNamespacedIdentifier();
Expected<ast::TypeNode>
parseType();
Expected<ast::Vector<ast::TypeQualifier>>
parseTypeQualifiers();
Expected<ast::Vector<ast::TypeNode>>
parseGenericArgumentsList();
Expected<ast::Vector<ast::FunctionParamNode>>
parseFunctionParamsList();
Expected<ast::FunctionParamNode>
parseFunctionParam();
Expected<ast::FunctionParamNode>
parseFunctionParamThis();
Expected<ast::CodeBlockStmtNode>
parseCodeBlock();
Expected<ast::Optional<ast::StatementNode>>
parseStatement();
Expected<ast::VariableStmtNode>
parseVariableStatement();
Expected<ast::IfStmtNode>
parseIfStatement();
Expected<ast::ElseBranchNode>
parseElseStatement();
Expected<ast::DeferStmtNode>
parseDeferStatement();
Expected<ast::ErrDeferStmtNode>
parseErrDeferStatement();
Expected<ast::ReturnStmtNode>
parseReturnStatement();
Expected<ast::BreakStmtNode>
parseBreakStatement();
Expected<ast::ContinueStmtNode>
parseContinueStatement();
Expected<ast::MatchStmtNode>
parseMatchStatement();
Expected<ast::SwitchStmtNode>
parseSwitchStatement();
Expected<ast::StatementNode>
parseForLoopStatement();
Expected<ast::CForStmtNode>
parseCForStatement();
Expected<ast::RangeForStmtNode>
parseRangeForStatement();
Expected<ast::WhileStmtNode>
parseWhileStatement();
Expected<ast::DoWhileStmtNode>
parseDoWhileStatement();
Expected<ast::InfLoopStmtNode>
parseInfLoopStatement();
Expected<ast::ExpressionStmtNode>
parseExpressionStatement();
Expected<ast::ExpressionNode>
parseExpression(std::uint16_t p = 0);
Expected<ast::Optional<ast::ExpressionNode>>
parsePrimaryExpression();
Expected<ast::ExpressionNode>
parsePrefixExpression();
Expected<ast::ExpressionNode>
parseInfixExpression(ast::ExpressionNode lhs);
Expected<ast::ExpressionNode>
parsePostfixExpression(ast::ExpressionNode lhs);
Expected<ast::IdentifierExprNode>
parseIdentifierExpression();
Expected<ast::CharLtrlNode>
parseCharLiteral();
Expected<ast::NullLtrlNode>
parseNullLiteral();
Expected<ast::StringLtrlNode>
parseStringLiteral();
Expected<ast::FloatLtrlNode>
parseFloatLiteral();
Expected<ast::IntegerLtrlNode>
parseIntegerLiteral();
Expected<ast::BooleanLtrlNode>
parseBooleanLiteral();
private:
std::string unitName;
std::string sourceCode;
Tokenizer tokenizer;
};
}

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Parser.hpp>
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);
}

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <string> #include <string>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <deque> #include <deque>
@ -25,7 +47,11 @@ namespace arti::lang {
Expected<void> consume(std::size_t n = 1) noexcept; Expected<void> consume(std::size_t n = 1) noexcept;
Expected<Token> peek(std::size_t n = 0) noexcept; Expected<Token> peek(std::size_t n = 0) noexcept;
Expected<Token> peekExpect(std::size_t n, TokenV tokenType) noexcept; Expected<Token> peekExpect(
TokenV tokenType,
std::string_view message = "",
std::size_t n = 0
) noexcept;
bool finished() const noexcept; bool finished() const noexcept;

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Util/Expected.hpp> #include <artichoke/Util/Expected.hpp>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <artichoke/Util/Expected.hpp> #include <artichoke/Util/Expected.hpp>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <cstddef> #include <cstddef>
@ -17,6 +39,10 @@ namespace arti::lang {
ecInvalidCharacter, ecInvalidCharacter,
ecInvalidIndex, ecInvalidIndex,
ecInvalidComment, ecInvalidComment,
ecUnexpectedToken,
ecExpectedSemicolon,
ecImportInsideModule,
ecUnimplemented,
}; };
struct Exception { struct Exception {
@ -71,6 +97,24 @@ namespace arti::lang {
else if constexpr (code == ecInvalidComment) { else if constexpr (code == ecInvalidComment) {
return "Invalid comment found, missing '*/' end of comment"; return "Invalid comment found, missing '*/' end of comment";
} }
else if constexpr (code == ecUnexpectedToken) {
return std::format(
"Found unexpected token '{}', expected {}",
std::forward<Args>(args)...
);
}
else if constexpr (code == ecExpectedSemicolon) {
return std::format(
"Expected ';', got '{}'",
std::forward<Args>(args)...
);
}
else if constexpr (code == ecImportInsideModule) {
return "Cannot use import statements inside a module declaration";
}
else if constexpr (code == ecUnimplemented) {
return "Unimplemented";
}
else { else {
return "Unknown error"; return "Unknown error";
} }

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
namespace arti::lang { namespace arti::lang {

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <cstddef> #include <cstddef>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <map> #include <map>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/AST/AST.hpp> #include <artichoke/Parser/AST/AST.hpp>
#include <sstream> #include <sstream>
@ -59,41 +81,57 @@ namespace arti::lang::ast {
}; };
// Operator label helpers (matching AST.cpp) // Operator label helpers (matching AST.cpp)
std::string toString(UnaryOperator op) { std::string toString(PrefixOperator op) {
using enum UnaryOperator; using enum PrefixOperator;
switch (op) { switch (op) {
case Not: return "Not (!)"; case Not: return "Not (!)";
case Minus: return "Minus (-)"; case Minus: return "Minus (-)";
case BitNot: return "BitNot (~)"; case BitNot: return "BitNot (~)";
case Ampersand: return "Ampersand (&)"; case MemPtr: return "MemPtr (&)";
case Star: return "Star (*)"; case DerefPtr: return "DerefPtr (*)";
default: std::unreachable(); break; default: std::unreachable(); break;
} }
std::unreachable(); std::unreachable();
} }
std::string toString(BinaryOperator op) { std::string toString(InfixOperator op) {
using enum BinaryOperator; using enum InfixOperator;
switch (op) { switch (op) {
case Equal: return "Equal (==)"; case Equal: return "Equal (==)";
case NotEqual: return "NotEqual (!=)"; case NotEqual: return "NotEqual (!=)";
case GreaterThan: return "GreaterThan (>)"; case GreaterThan: return "GreaterThan (>)";
case LessThan: return "LessThan (<)"; case LessThan: return "LessThan (<)";
case GreaterEqual: return "GreaterEqual (>=)"; case GreaterEqual: return "GreaterEqual (>=)";
case LessEqual: return "LessEqual (<=)"; case LessEqual: return "LessEqual (<=)";
case BitAnd: return "BitAnd (&)"; case BitAnd: return "BitAnd (&)";
case BitXor: return "BitXor (^)"; case BitXor: return "BitXor (^)";
case BitOr: return "BitOr (|)"; case BitOr: return "BitOr (|)";
case LeftShift: return "LeftShift (<<)"; case LeftShift: return "LeftShift (<<)";
case RightShift: return "RightShift (>>)"; case RightShift: return "RightShift (>>)";
case Adition: return "Adition (+)"; case Addition: return "Addition (+)";
case Substraction: return "Substraction (-)"; case Substraction: return "Substraction (-)";
case Multiplication: return "Multiplication (*)"; case Multiplication: return "Multiplication (*)";
case Division: return "Division (/)"; case Division: return "Division (/)";
case Modulo: return "Modulo (%)"; case Modulo: return "Modulo (%)";
case BoolAnd: return "BoolAnd (&&)"; case BoolAnd: return "BoolAnd (&&)";
case BoolOr: return "BoolOr (||)"; case BoolOr: return "BoolOr (||)";
default: std::unreachable(); break; 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(); std::unreachable();
} }
@ -133,8 +171,6 @@ namespace arti::lang::ast {
std::string emit(const TypeNode &, GraphBuilder &); std::string emit(const TypeNode &, GraphBuilder &);
std::string emit(const GenericTypeNode &, GraphBuilder &); std::string emit(const GenericTypeNode &, GraphBuilder &);
std::string emit(const IdentifierTypeNode &, 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 TypeExpressionNode &, GraphBuilder &);
std::string emit(const CharLtrlNode &, GraphBuilder &); std::string emit(const CharLtrlNode &, GraphBuilder &);
std::string emit(const NullLtrlNode &, GraphBuilder &); std::string emit(const NullLtrlNode &, GraphBuilder &);
@ -142,24 +178,23 @@ namespace arti::lang::ast {
std::string emit(const FloatLtrlNode &, GraphBuilder &); std::string emit(const FloatLtrlNode &, GraphBuilder &);
std::string emit(const IntegerLtrlNode &, GraphBuilder &); std::string emit(const IntegerLtrlNode &, GraphBuilder &);
std::string emit(const BooleanLtrlNode &, GraphBuilder &); std::string emit(const BooleanLtrlNode &, GraphBuilder &);
std::string emit(const StructLtrlNode &, GraphBuilder &); std::string emit(const ObjectLtrlNode &, GraphBuilder &);
std::string emit(const SliceLtrlNode &, GraphBuilder &); std::string emit(const ObjectLtrlNamedFieldInitNode &, GraphBuilder &);
std::string emit(const StructLtrlNamedFieldInitNode &, GraphBuilder &); std::string emit(const ObjectLtrlNamedInitializerNode &, GraphBuilder &);
std::string emit(const StructLtrlPositionalInitNode &, GraphBuilder &); std::string emit(const ObjectLtrlPositionalInitializerNode&, GraphBuilder&);
std::string emit(const StructLtrlNamedInitializerNode &, GraphBuilder &); std::string emit(const ObjectLtrlInitializerNode &, GraphBuilder &);
std::string emit(const StructLtrlPositionalInitializerNode&, GraphBuilder&);
std::string emit(const StructLtrlInitializerNode &, GraphBuilder &);
std::string emit(const IdentifierExprNode &, GraphBuilder &); std::string emit(const IdentifierExprNode &, GraphBuilder &);
std::string emit(const UnaryExprNode &, GraphBuilder &); std::string emit(const PrefixExprNode &, GraphBuilder &);
std::string emit(const BinaryExprNode &, GraphBuilder &); std::string emit(const InfixExprNode &, GraphBuilder &);
std::string emit(const AssignExprNode &, GraphBuilder &); std::string emit(const AssignExprNode &, GraphBuilder &);
std::string emit(const CompoundAssignExprNode &, GraphBuilder &); std::string emit(const CompoundAssignExprNode &, GraphBuilder &);
std::string emit(const FunctionCallExprNode &, GraphBuilder &); std::string emit(const FunctionCallExprNode &, GraphBuilder &);
std::string emit(const SliceAccessExprNode &, GraphBuilder &); std::string emit(const SliceAccessExprNode &, GraphBuilder &);
std::string emit(const SliceRangeExprNode &, GraphBuilder &); std::string emit(const SliceRangeExprNode &, GraphBuilder &);
std::string emit(const MemberAccessExprNode &, GraphBuilder &); std::string emit(const MemberAccessExprNode &, GraphBuilder &);
std::string emit(const PointerAccessExprNode &, GraphBuilder &); std::string emit(const PointerMemberAccessExprNode &, GraphBuilder &);
std::string emit(const ScopeAccessExprNode &, GraphBuilder &); std::string emit(const GenericExprNode &, GraphBuilder &);
std::string emit(const ModuleAccessExprNode &, GraphBuilder &);
std::string emit(const ReflectionExprNode &, GraphBuilder &); std::string emit(const ReflectionExprNode &, GraphBuilder &);
std::string emit(const SliceCreationExprNode &, GraphBuilder &); std::string emit(const SliceCreationExprNode &, GraphBuilder &);
std::string emit(const SliceLengthExprNode &, GraphBuilder &); std::string emit(const SliceLengthExprNode &, GraphBuilder &);
@ -188,6 +223,7 @@ namespace arti::lang::ast {
std::string emit(const ElseBranchNode &, GraphBuilder &); std::string emit(const ElseBranchNode &, GraphBuilder &);
std::string emit(const DeferableNode &, GraphBuilder &); std::string emit(const DeferableNode &, GraphBuilder &);
std::string emit(const PreLoopStmtNode &, GraphBuilder &); std::string emit(const PreLoopStmtNode &, GraphBuilder &);
std::string emit(const TypeExprNode &, GraphBuilder &);
// Helpers for making leaf nodes with backticked values // Helpers for making leaf nodes with backticked values
inline std::string makeLeaf(GraphBuilder &g, std::string_view value) { inline std::string makeLeaf(GraphBuilder &g, std::string_view value) {
@ -456,15 +492,16 @@ namespace arti::lang::ast {
}); });
} }
} }
auto baseId = emit(node->baseType, g); emitGroupVec(g, id, "TypeNodes", node->typeNodes, [&](const auto &arg) {
g.addEdge(id, baseId, "BaseType"); return emit(arg, g);
});
return id; return id;
} }
std::string emit(const GenericTypeNode &node, GraphBuilder &g) { std::string emit(const GenericTypeNode &node, GraphBuilder &g) {
auto id = g.makeNode("GenericType"); auto id = g.makeNode("GenericType");
auto baseId = emit(node->baseType, g); auto typeId = g.makeNode(node->typeName);
g.addEdge(id, baseId, "BaseType"); g.addEdge(id, typeId, "TypeName");
if (! node->genericArgs.empty()) { if (! node->genericArgs.empty()) {
emitGroupVec( emitGroupVec(
g, g,
@ -478,30 +515,17 @@ namespace arti::lang::ast {
} }
std::string emit(const IdentifierTypeNode &node, GraphBuilder &g) { std::string emit(const IdentifierTypeNode &node, GraphBuilder &g) {
std::ignore = node;
auto id = g.makeNode("IdentifierType"); auto id = g.makeNode("IdentifierType");
auto cid = emit(node->typeName, g); auto typeId = g.makeNode(node->typeName);
g.addEdge(id, cid, "TypeName"); g.addEdge(id, typeId, "TypeName");
return id; 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) { std::string emit(const TypeExpressionNode &node, GraphBuilder &g) {
auto visitor = OverloadSet{ auto visitor = OverloadSet{
[&g](const GenericTypeNode &n) { return emit(n, g); }, [&g](const GenericTypeNode &n) { return emit(n, g); },
[&g](const IdentifierTypeNode &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); return std::visit(visitor, node);
} }
@ -530,19 +554,8 @@ namespace arti::lang::ast {
} }
// Struct/Slice literals and initializers // Struct/Slice literals and initializers
std::string emit(const StructLtrlNode &node, GraphBuilder &g) { std::string emit(const ObjectLtrlNode &node, GraphBuilder &g) {
auto id = g.makeNode("StructLiteral"); auto id = g.makeNode("ObjectLiteral");
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");
auto t = emit(node->type, g); auto t = emit(node->type, g);
g.addEdge(id, t, "Type"); g.addEdge(id, t, "Type");
if (node->initializer) { if (node->initializer) {
@ -553,7 +566,7 @@ namespace arti::lang::ast {
} }
std::string std::string
emit(const StructLtrlNamedFieldInitNode &node, GraphBuilder &g) { emit(const ObjectLtrlNamedFieldInitNode &node, GraphBuilder &g) {
auto id = g.makeNode("FieldInitializer"); auto id = g.makeNode("FieldInitializer");
auto leaf = makeLeaf(g, node->fieldName); auto leaf = makeLeaf(g, node->fieldName);
g.addEdge(id, leaf, "Field"); g.addEdge(id, leaf, "Field");
@ -563,15 +576,7 @@ namespace arti::lang::ast {
} }
std::string std::string
emit(const StructLtrlPositionalInitNode &node, GraphBuilder &g) { emit(const ObjectLtrlNamedInitializerNode &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) {
auto id = g.makeNode("InitializerList"); auto id = g.makeNode("InitializerList");
if (! node->fields.empty()) { if (! node->fields.empty()) {
emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) { emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) {
@ -582,7 +587,7 @@ namespace arti::lang::ast {
} }
std::string std::string
emit(const StructLtrlPositionalInitializerNode &node, GraphBuilder &g) { emit(const ObjectLtrlPositionalInitializerNode &node, GraphBuilder &g) {
auto id = g.makeNode("InitializerList"); auto id = g.makeNode("InitializerList");
if (! node->fields.empty()) { if (! node->fields.empty()) {
emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) { emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) {
@ -592,10 +597,10 @@ namespace arti::lang::ast {
return id; return id;
} }
std::string emit(const StructLtrlInitializerNode &node, GraphBuilder &g) { std::string emit(const ObjectLtrlInitializerNode &node, GraphBuilder &g) {
auto visitor = OverloadSet{ auto visitor = OverloadSet{
[&g](const StructLtrlNamedInitializerNode &n) { return emit(n, g); }, [&g](const ObjectLtrlNamedInitializerNode &n) { return emit(n, g); },
[&g](const StructLtrlPositionalInitializerNode &n) { [&g](const ObjectLtrlPositionalInitializerNode &n) {
return emit(n, g); return emit(n, g);
}, },
}; };
@ -607,8 +612,8 @@ namespace arti::lang::ast {
return g.makeNode(std::format("Identifier `{}`", node->identifierName)); return g.makeNode(std::format("Identifier `{}`", node->identifierName));
} }
std::string emit(const UnaryExprNode &node, GraphBuilder &g) { std::string emit(const PrefixExprNode &node, GraphBuilder &g) {
auto id = g.makeNode("UnaryExpression"); auto id = g.makeNode("PrefixExpression");
auto opLeaf = makeLeaf(g, toString(node->op)); auto opLeaf = makeLeaf(g, toString(node->op));
g.addEdge(id, opLeaf, "Operator"); g.addEdge(id, opLeaf, "Operator");
auto rhs = emit(node->right, g); auto rhs = emit(node->right, g);
@ -616,8 +621,8 @@ namespace arti::lang::ast {
return id; return id;
} }
std::string emit(const BinaryExprNode &node, GraphBuilder &g) { std::string emit(const InfixExprNode &node, GraphBuilder &g) {
auto id = g.makeNode("BinaryExpression"); auto id = g.makeNode("InfixExpression");
auto opLeaf = makeLeaf(g, toString(node->op)); auto opLeaf = makeLeaf(g, toString(node->op));
g.addEdge(id, opLeaf, "Operator"); g.addEdge(id, opLeaf, "Operator");
auto lhs = emit(node->left, g); auto lhs = emit(node->left, g);
@ -676,30 +681,36 @@ namespace arti::lang::ast {
std::string emit(const MemberAccessExprNode &node, GraphBuilder &g) { std::string emit(const MemberAccessExprNode &node, GraphBuilder &g) {
auto id = g.makeNode("MemberAccessExpression"); auto id = g.makeNode("MemberAccessExpression");
g.addEdge(id, emit(node->object, g), "Object"); 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; return id;
} }
std::string emit(const PointerAccessExprNode &node, GraphBuilder &g) { std::string emit(const PointerMemberAccessExprNode &node, GraphBuilder &g) {
auto id = g.makeNode("PointerAccessExpression"); auto id = g.makeNode("PointerAccessExpression");
g.addEdge(id, emit(node->object, g), "Object"); 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; return id;
} }
std::string emit(const ScopeAccessExprNode &node, GraphBuilder &g) { std::string emit(const ModuleAccessExprNode &node, GraphBuilder &g) {
auto id = g.makeNode("ScopeAccessExpression"); auto id = g.makeNode("ModuleAccessExpression");
g.addEdge(id, emit(node->scope, g), "Object"); g.addEdge(id, emit(node->left, g), "Scope");
if (! node->genericParams.empty()) { 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( emitGroupVec(
g, g,
id, id,
"GenericParams", "GenericArguments",
node->genericParams, node->genericArgs,
[&](const auto &p) { return emit(p, g); } [&](const auto &arg) { return emit(arg, g); }
); );
} }
g.addEdge(id, makeLeaf(g, node->memberName), "Member");
return id; return id;
} }
@ -739,23 +750,24 @@ namespace arti::lang::ast {
[&g](const FloatLtrlNode &n) { return emit(n, g); }, [&g](const FloatLtrlNode &n) { return emit(n, g); },
[&g](const IntegerLtrlNode &n) { return emit(n, g); }, [&g](const IntegerLtrlNode &n) { return emit(n, g); },
[&g](const BooleanLtrlNode &n) { return emit(n, g); }, [&g](const BooleanLtrlNode &n) { return emit(n, g); },
[&g](const StructLtrlNode &n) { return emit(n, g); }, [&g](const ObjectLtrlNode &n) { return emit(n, g); },
[&g](const SliceLtrlNode &n) { return emit(n, g); },
[&g](const IdentifierExprNode &n) { return emit(n, g); }, [&g](const IdentifierExprNode &n) { return emit(n, g); },
[&g](const UnaryExprNode &n) { return emit(n, g); }, [&g](const PrefixExprNode &n) { return emit(n, g); },
[&g](const BinaryExprNode &n) { return emit(n, g); }, [&g](const InfixExprNode &n) { return emit(n, g); },
[&g](const AssignExprNode &n) { return emit(n, g); }, [&g](const AssignExprNode &n) { return emit(n, g); },
[&g](const CompoundAssignExprNode &n) { return emit(n, g); }, [&g](const CompoundAssignExprNode &n) { return emit(n, g); },
[&g](const FunctionCallExprNode &n) { return emit(n, g); }, [&g](const FunctionCallExprNode &n) { return emit(n, g); },
[&g](const SliceAccessExprNode &n) { return emit(n, g); }, [&g](const SliceAccessExprNode &n) { return emit(n, g); },
[&g](const SliceRangeExprNode &n) { return emit(n, g); }, [&g](const SliceRangeExprNode &n) { return emit(n, g); },
[&g](const MemberAccessExprNode &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 ScopeAccessExprNode &n) { return emit(n, g); }, [&g](const ModuleAccessExprNode &n) { return emit(n, g); },
[&g](const SliceCreationExprNode &n) { return emit(n, g); }, [&g](const SliceCreationExprNode &n) { return emit(n, g); },
[&g](const SliceLengthExprNode &n) { return emit(n, g); }, [&g](const SliceLengthExprNode &n) { return emit(n, g); },
[&g](const SlicePtrExprNode &n) { return emit(n, g); }, [&g](const SlicePtrExprNode &n) { return emit(n, g); },
[&g](const ReflectionExprNode &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); return std::visit(visitor, node);
} }
@ -980,6 +992,7 @@ namespace arti::lang::ast {
[&g](const DoWhileStmtNode &n) { return emit(n, g); }, [&g](const DoWhileStmtNode &n) { return emit(n, g); },
[&g](const InfLoopStmtNode &n) { return emit(n, g); }, [&g](const InfLoopStmtNode &n) { return emit(n, g); },
[&g](const ExpressionStmtNode &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); return std::visit(visitor, node);
} }
@ -1008,6 +1021,12 @@ namespace arti::lang::ast {
return std::visit(visitor, node); 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 } // namespace
// Public API // Public API

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/AST/AST.hpp> #include <artichoke/Parser/AST/AST.hpp>
#include <sstream> #include <sstream>
@ -23,7 +45,6 @@ namespace arti::lang::ast {
std::string toString(const TypeNode &, std::string); std::string toString(const TypeNode &, std::string);
std::string toString(const GenericTypeNode &, std::string); std::string toString(const GenericTypeNode &, std::string);
std::string toString(const IdentifierTypeNode &, 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 NamespacedIdentifierNode &, std::string);
std::string toString(const TypeExpressionNode &, std::string); std::string toString(const TypeExpressionNode &, std::string);
std::string toString(const CharLtrlNode &, std::string); std::string toString(const CharLtrlNode &, std::string);
@ -32,24 +53,23 @@ namespace arti::lang::ast {
std::string toString(const FloatLtrlNode &, std::string); std::string toString(const FloatLtrlNode &, std::string);
std::string toString(const IntegerLtrlNode &, std::string); std::string toString(const IntegerLtrlNode &, std::string);
std::string toString(const BooleanLtrlNode &, std::string); std::string toString(const BooleanLtrlNode &, std::string);
std::string toString(const StructLtrlNode &, std::string); std::string toString(const ObjectLtrlNode &, std::string);
std::string toString(const SliceLtrlNode &, std::string); std::string toString(const ObjectLtrlNamedFieldInitNode &, std::string);
std::string toString(const StructLtrlNamedFieldInitNode &, std::string); std::string toString(const ObjectLtrlNamedInitializerNode &, std::string);
std::string toString(const StructLtrlPositionalInitNode &, std::string); std::string toString(const ObjectLtrlPositionalInitializerNode&, std::string);
std::string toString(const StructLtrlNamedInitializerNode &, std::string); std::string toString(const ObjectLtrlInitializerNode &, std::string);
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 IdentifierExprNode &, std::string);
std::string toString(const UnaryExprNode &, std::string); std::string toString(const PrefixExprNode &, std::string);
std::string toString(const BinaryExprNode &, std::string); std::string toString(const InfixExprNode &, std::string);
std::string toString(const AssignExprNode &, std::string); std::string toString(const AssignExprNode &, std::string);
std::string toString(const CompoundAssignExprNode &, std::string); std::string toString(const CompoundAssignExprNode &, std::string);
std::string toString(const FunctionCallExprNode &, std::string); std::string toString(const FunctionCallExprNode &, std::string);
std::string toString(const SliceAccessExprNode &, std::string); std::string toString(const SliceAccessExprNode &, std::string);
std::string toString(const SliceRangeExprNode &, std::string); std::string toString(const SliceRangeExprNode &, std::string);
std::string toString(const MemberAccessExprNode &, 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 ScopeAccessExprNode &, 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 ReflectionExprNode &, std::string);
std::string toString(const SliceCreationExprNode &, std::string); std::string toString(const SliceCreationExprNode &, std::string);
std::string toString(const SliceLengthExprNode &, std::string); std::string toString(const SliceLengthExprNode &, std::string);
@ -78,8 +98,9 @@ namespace arti::lang::ast {
std::string toString(const ElseBranchNode &, std::string); std::string toString(const ElseBranchNode &, std::string);
std::string toString(const DeferableNode &, std::string); std::string toString(const DeferableNode &, std::string);
std::string toString(const PreLoopStmtNode &, std::string); std::string toString(const PreLoopStmtNode &, std::string);
std::string toString(UnaryOperator op); std::string toString(const TypeExprNode &, std::string);
std::string toString(BinaryOperator op); std::string toString(PrefixOperator op);
std::string toString(InfixOperator op);
std::string toString(CompoundAssignOperator op); std::string toString(CompoundAssignOperator op);
const auto StrTreeNoNode = ""; const auto StrTreeNoNode = "";
@ -104,6 +125,17 @@ namespace arti::lang::ast {
<< toString(item, nextPrefix(prefix, isLastChild)); << 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 <typename T> template <typename T>
void appendGroupVec( void appendGroupVec(
std::stringstream &ss, std::stringstream &ss,
@ -507,12 +539,22 @@ namespace arti::lang::ast {
if (! qls.empty()) { if (! qls.empty()) {
++total; ++total;
} }
++total; // BaseType is always present if (! node->typeNodes.empty()) {
++total;
}
int emitted = 0; int emitted = 0;
if (! qls.empty()) { if (! qls.empty()) {
appendGroupLeafList(ss, prefix, "Qualifiers", qls, ++emitted == total); 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(); return ss.str();
} }
@ -524,7 +566,9 @@ namespace arti::lang::ast {
++total; ++total;
} }
int emitted = 0; 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()) { if (! node->genericArgs.empty()) {
appendGroupVec( appendGroupVec(
ss, ss,
@ -538,17 +582,10 @@ namespace arti::lang::ast {
} }
std::string toString(const IdentifierTypeNode &node, std::string prefix) { std::string toString(const IdentifierTypeNode &node, std::string prefix) {
std::ignore = node;
std::ignore = prefix;
std::stringstream ss; std::stringstream ss;
ss << "IdentifierType"; ss << "TypeName `" << node->typeName << "`";
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);
return ss.str(); return ss.str();
} }
@ -569,9 +606,6 @@ namespace arti::lang::ast {
[padding](const IdentifierTypeNode &node) -> std::string { [padding](const IdentifierTypeNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const NamespacedTypeNode &node) -> std::string {
return toString(node, padding);
},
}; };
return std::visit(visitor, node); return std::visit(visitor, node);
@ -608,30 +642,9 @@ namespace arti::lang::ast {
return std::format("BooleanLiteral {}", node->value ? "true" : "false"); 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; std::stringstream ss;
ss << "StructLiteral"; ss << "ObjectLiteral";
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";
int total = 1; int total = 1;
if (node->initializer) { if (node->initializer) {
++total; ++total;
@ -651,7 +664,7 @@ namespace arti::lang::ast {
} }
std::string std::string
toString(const StructLtrlNamedFieldInitNode &node, std::string prefix) { toString(const ObjectLtrlNamedFieldInitNode &node, std::string prefix) {
std::stringstream ss; std::stringstream ss;
ss << "FieldInitializer"; ss << "FieldInitializer";
appendGroupLeaf(ss, prefix, "Field", node->fieldName, false); appendGroupLeaf(ss, prefix, "Field", node->fieldName, false);
@ -660,15 +673,7 @@ namespace arti::lang::ast {
} }
std::string std::string
toString(const StructLtrlPositionalInitNode &node, std::string prefix) { toString(const ObjectLtrlNamedInitializerNode &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) {
std::stringstream ss; std::stringstream ss;
ss << "InitializerList"; ss << "InitializerList";
appendGroupVec(ss, prefix, "Elements", node->fields, true); appendGroupVec(ss, prefix, "Elements", node->fields, true);
@ -676,7 +681,7 @@ namespace arti::lang::ast {
} }
std::string toString( std::string toString(
const StructLtrlPositionalInitializerNode &node, const ObjectLtrlPositionalInitializerNode &node,
std::string prefix std::string prefix
) { ) {
std::stringstream ss; std::stringstream ss;
@ -686,12 +691,12 @@ namespace arti::lang::ast {
} }
std::string std::string
toString(const StructLtrlInitializerNode &node, std::string padding) { toString(const ObjectLtrlInitializerNode &node, std::string padding) {
auto visitor = OverloadSet{ auto visitor = OverloadSet{
[padding](const StructLtrlNamedInitializerNode &node) -> std::string { [padding](const ObjectLtrlNamedInitializerNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const StructLtrlPositionalInitializerNode &node) [padding](const ObjectLtrlPositionalInitializerNode &node)
-> std::string { return toString(node, padding); }, -> std::string { return toString(node, padding); },
}; };
@ -703,9 +708,9 @@ namespace arti::lang::ast {
return std::format("Identifier `{}`", node->identifierName); 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; std::stringstream ss;
ss << "UnaryExpression"; ss << "PrefixExpression";
int total = 2; int total = 2;
int emitted = 0; int emitted = 0;
appendGroupLeaf( appendGroupLeaf(
@ -719,9 +724,9 @@ namespace arti::lang::ast {
return ss.str(); return ss.str();
} }
std::string toString(const BinaryExprNode &node, std::string prefix) { std::string toString(const InfixExprNode &node, std::string prefix) {
std::stringstream ss; std::stringstream ss;
ss << "BinaryExpression"; ss << "InfixExpression";
int total = 3; int total = 3;
int emitted = 0; int emitted = 0;
appendGroupLeaf( appendGroupLeaf(
@ -815,37 +820,46 @@ namespace arti::lang::ast {
std::stringstream ss; std::stringstream ss;
ss << "MemberAccessExpression"; ss << "MemberAccessExpression";
appendGroupOne(ss, prefix, "Object", node->object, false); appendGroupOne(ss, prefix, "Object", node->object, false);
appendGroupLeaf(ss, prefix, "Member", node->memberName, true); appendGroupOne(ss, prefix, "Member", node->member, true);
return ss.str(); return ss.str();
} }
std::string toString(const PointerAccessExprNode &node, std::string prefix) { std::string toString(const PointerMemberAccessExprNode &node, std::string prefix) {
std::stringstream ss; std::stringstream ss;
ss << "PointerAccessExpression"; ss << "PointerAccessExpression";
appendGroupOne(ss, prefix, "Object", node->object, false); appendGroupOne(ss, prefix, "Object", node->object, false);
appendGroupLeaf(ss, prefix, "Member", node->memberName, true); appendGroupOne(ss, prefix, "Member", node->member, true);
return ss.str(); return ss.str();
} }
std::string toString(const ScopeAccessExprNode &node, std::string prefix) { std::string toString(const ModuleAccessExprNode &node, std::string prefix) {
std::stringstream ss; std::stringstream ss;
ss << "ScopeAccessExpression"; ss << "ModuleAccessExpression";
int total = 2; 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; ++total;
} }
int emitted = 0; int emitted = 0;
appendGroupOne(ss, prefix, "Object", node->scope, ++emitted == total); appendGroupOne(ss, prefix, "TypeNode", node->typeNode, ++emitted == total);
if (! node->genericParams.empty()) { if (! node->genericArgs.empty()) {
appendGroupVec( appendGroupVec(
ss, ss,
prefix, prefix,
"GenericParams", "GenericArguments",
node->genericParams, node->genericArgs,
++emitted == total ++emitted == total
); );
} }
appendGroupLeaf(ss, prefix, "Member", node->memberName, ++emitted == total);
return ss.str(); return ss.str();
} }
@ -912,19 +926,16 @@ namespace arti::lang::ast {
[padding](const BooleanLtrlNode &node) -> std::string { [padding](const BooleanLtrlNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const StructLtrlNode &node) -> std::string { [padding](const ObjectLtrlNode &node) -> std::string {
return toString(node, padding);
},
[padding](const SliceLtrlNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const IdentifierExprNode &node) -> std::string { [padding](const IdentifierExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const UnaryExprNode &node) -> std::string { [padding](const PrefixExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const BinaryExprNode &node) -> std::string { [padding](const InfixExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const AssignExprNode &node) -> std::string { [padding](const AssignExprNode &node) -> std::string {
@ -945,10 +956,10 @@ namespace arti::lang::ast {
[padding](const MemberAccessExprNode &node) -> std::string { [padding](const MemberAccessExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const PointerAccessExprNode &node) -> std::string { [padding](const PointerMemberAccessExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const ScopeAccessExprNode &node) -> std::string { [padding](const ModuleAccessExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const SliceCreationExprNode &node) -> std::string { [padding](const SliceCreationExprNode &node) -> std::string {
@ -963,6 +974,12 @@ namespace arti::lang::ast {
[padding](const ReflectionExprNode &node) -> std::string { [padding](const ReflectionExprNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[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); return std::visit(visitor, node);
@ -1397,6 +1414,9 @@ namespace arti::lang::ast {
[padding](const ExpressionStmtNode &node) -> std::string { [padding](const ExpressionStmtNode &node) -> std::string {
return toString(node, padding); return toString(node, padding);
}, },
[padding](const CodeBlockStmtNode &node) -> std::string {
return toString(node, padding);
}
}; };
return std::visit(visitor, node); return std::visit(visitor, node);
@ -1441,46 +1461,67 @@ namespace arti::lang::ast {
return std::visit(visitor, node); return std::visit(visitor, node);
} }
std::string toString(UnaryOperator op) { std::string toString(const TypeExprNode &node, std::string prefix) {
using enum UnaryOperator; std::stringstream ss;
ss << "TypeÉxpression";
appendItem(ss, prefix, node->type, true);
return ss.str();
}
std::string toString(PrefixOperator op) {
using enum PrefixOperator;
switch (op) { switch (op) {
case Not: return "Not (!)"; case Not: return "Not (!)";
case Minus: return "Minus (-)"; case Minus: return "Minus (-)";
case BitNot: return "BitNot (~)"; case BitNot: return "BitNot (~)";
case Ampersand: return "Ampersand (&)"; case MemPtr: return "MemPtr (&)";
case Star: return "Star (*)"; case DerefPtr: return "DerefPtr (*)";
default: std::unreachable(); break; default: std::unreachable(); break;
} }
std::unreachable(); std::unreachable();
} }
std::string toString(BinaryOperator op) { std::string toString(InfixOperator op) {
using enum BinaryOperator; using enum InfixOperator;
switch (op) { switch (op) {
case Equal: return "Equal (==)"; case Equal: return "Equal (==)";
case NotEqual: return "NotEqual (!=)"; case NotEqual: return "NotEqual (!=)";
case GreaterThan: return "GreaterThan (>)"; case GreaterThan: return "GreaterThan (>)";
case LessThan: return "LessThan (<)"; case LessThan: return "LessThan (<)";
case GreaterEqual: return "GreaterEqual (>=)"; case GreaterEqual: return "GreaterEqual (>=)";
case LessEqual: return "LessEqual (<=)"; case LessEqual: return "LessEqual (<=)";
case BitAnd: return "BitAnd (&)"; case BitAnd: return "BitAnd (&)";
case BitXor: return "BitXor (^)"; case BitXor: return "BitXor (^)";
case BitOr: return "BitOr (|)"; case BitOr: return "BitOr (|)";
case LeftShift: return "LeftShift (<<)"; case LeftShift: return "LeftShift (<<)";
case RightShift: return "RightShift (>>)"; case RightShift: return "RightShift (>>)";
case Adition: return "Adition (+)"; case Addition: return "Addition (+)";
case Substraction: return "Substraction (-)"; case Substraction: return "Substraction (-)";
case Multiplication: return "Multiplication (*)"; case Multiplication: return "Multiplication (*)";
case Division: return "Division (/)"; case Division: return "Division (/)";
case Modulo: return "Modulo (%)"; case Modulo: return "Modulo (%)";
case BoolAnd: return "BoolAnd (&&)"; case BoolAnd: return "BoolAnd (&&)";
case BoolOr: return "BoolOr (||)"; case BoolOr: return "BoolOr (||)";
default: std::unreachable(); break; 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(); std::unreachable();
} }

View File

@ -0,0 +1,822 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Expected<ast::Optional<ast::TopLevelDeclNode>>
Parser::parseTopLevelDeclaration() {
bool exportable = false;
if (auto exported = matchAndConsume(TokenV::kwExport); ! exported) {
return Unexpected<>{ std::move(exported).error() };
}
else if (exported.value()) {
exportable = true;
}
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
if (peekToken->value == TokenV::kwImport) {
if (exportable) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"exportable declaration, ie. Struct, Enum, Function, Module"
);
}
if (auto node = parseImportDeclaration(); ! node) {
return Unexpected<>{ std::move(node).error() };
}
else {
return ast::TopLevelDeclNode{ std::move(node).value() };
}
}
else if (peekToken->value == TokenV::kwUsing) {
if (exportable) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"exportable declaration, ie. Struct, Enum, Function, Module"
);
}
if (auto node = parseAliasDeclaration(); ! node) {
return Unexpected<>{ std::move(node).error() };
}
else {
return ast::TopLevelDeclNode{ std::move(node).value() };
}
}
else if (peekToken->value == TokenV::kwModule) {
if (auto node = parseModuleDeclaration(); ! node) {
return Unexpected<>{ std::move(node).error() };
}
else {
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
}
else if (peekToken->value == TokenV::kwStruct) {
if (auto node = parseStructDeclaration(); ! node) {
return Unexpected<>{ std::move(node).error() };
}
else {
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
}
else if (peekToken->value == TokenV::kwEnum) {
if (auto node = parseEnumDeclaration(); ! node) {
return Unexpected<>{ std::move(node).error() };
}
else {
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
}
else if (peekToken->value == TokenV::kwFn) {
if (auto node = parseFunctionDeclaration(); ! node) {
return Unexpected<>{ std::move(node).error() };
}
else {
(*node)->isExported = exportable;
return ast::TopLevelDeclNode{ std::move(node).value() };
}
}
return std::nullopt;
}
Expected<ast::ImportDeclNode> Parser::parseImportDeclaration() {
auto node = ast::MakeNode<ast::ImportDeclNode>();
if (auto kw = consume(TokenV::kwImport, "'import' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto target = parseNamespacedIdentifier(); ! target) {
return Unexpected<>{ std::move(target).error() };
}
else {
node->importTarget = std::move(target).value();
}
if (auto acc = matchAndConsume(TokenV::opAccess); ! acc) {
return Unexpected<>{ std::move(acc).error() };
}
else if (acc.value()) {
if (auto star = matchAndConsume(TokenV::opStar); ! star) {
return Unexpected<>{ std::move(star).error() };
}
else if (star.value()) {
node->importAll = true;
}
else {
auto star = tokenizer.peek();
return langException<ExceptCode::ecUnexpectedToken>(
star->line,
star->column,
toString(*star),
"identifier or '*'"
);
}
}
if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) {
return Unexpected<>{ std::move(semicolon).error() };
}
return node;
}
Expected<ast::AliasDeclNode> Parser::parseAliasDeclaration() {
auto node = ast::MakeNode<ast::AliasDeclNode>();
if (auto kw = consume(TokenV::kwUsing, "'using' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
node->alias = ident->strValue;
}
if (auto eq = consume(TokenV::opAssign, "'='"); ! eq) {
return Unexpected{ std::move(eq).error() };
}
if (auto type = parseType(); ! type) {
return Unexpected{ std::move(type).error() };
}
else {
node->target = std::move(type).value();
}
if (auto semicolon = consume(TokenV::opSemicolon, "';'"); ! semicolon) {
return Unexpected<>{ std::move(semicolon).error() };
}
return node;
}
Expected<ast::ModuleDeclNode> Parser::parseModuleDeclaration() {
auto node = ast::MakeNode<ast::ModuleDeclNode>();
auto decl = ast::Optional<ast::TopLevelDeclNode>{};
bool keepParsing = true;
if (auto kw = consume(TokenV::kwModule, "'module' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto name = parseNamespacedIdentifier(); ! name) {
return Unexpected<>{ std::move(name).error() };
}
else {
node->name = std::move(name).value();
}
if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) {
return Unexpected<>{ std::move(lsquirly).error() };
}
while (keepParsing) {
if (auto ok = parseTopLevelDeclaration(); ! ok) {
return Unexpected<>{ std::move(ok).error() };
}
else {
decl = std::move(ok).value();
if (! decl.has_value()) {
keepParsing = false;
continue;
}
if (std::holds_alternative<ast::ModuleDeclNode>(*decl)) {
node->childModules.push_back(
std::get<ast::ModuleDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::StructDeclNode>(*decl)) {
node->innerDeclarations.push_back(
std::get<ast::StructDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::EnumDeclNode>(*decl)) {
node->innerDeclarations.push_back(
std::get<ast::EnumDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::FunctionDeclNode>(*decl)) {
node->innerDeclarations.push_back(
std::get<ast::FunctionDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::AliasDeclNode>(*decl)) {
node->aliasDeclarations.push_back(
std::get<ast::AliasDeclNode>(std::move(*decl))
);
}
else if (std::holds_alternative<ast::ImportDeclNode>(*decl)) {
auto importDecl = std::get<ast::ImportDeclNode>(std::move(*decl));
return langException<ExceptCode::ecImportInsideModule>(
importDecl->location.line,
importDecl->location.column
);
}
}
}
if (auto rsquirly = consume(TokenV::opRSquirly, "'{'"); ! rsquirly) {
return Unexpected<>{ std::move(rsquirly).error() };
}
return node;
}
Expected<ast::StructDeclNode> Parser::parseStructDeclaration() {
auto node = ast::MakeNode<ast::StructDeclNode>();
if (auto kw = consume(TokenV::kwStruct, "'struct' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) {
return Unexpected<>{ std::move(name).error() };
}
else {
node->name = name->strValue;
}
if (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) {
return Unexpected<>{ std::move(hasLt).error() };
}
else if (hasLt.value()) {
if (auto params = parseGenericParamsList(); ! params) {
return Unexpected<>{ std::move(params).error() };
}
else {
node->genericParams = std::move(params).value();
if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) {
return Unexpected<>{ std::move(hasGt ).error() };
}
}
}
if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) {
return Unexpected<>{ std::move(lsquirly).error() };
}
if (auto members = parseStructMembersList(); ! members) {
return Unexpected<>{ std::move(members).error() };
}
else {
node->structMembers = std::move(members).value();
}
if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) {
return Unexpected<>{ std::move(rsquirly).error() };
}
return node;
}
Expected<ast::EnumDeclNode> Parser::parseEnumDeclaration() {
auto node = ast::MakeNode<ast::EnumDeclNode>();
if (auto kw = consume(TokenV::kwEnum, "'enum' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) {
return Unexpected<>{ std::move(name).error() };
}
else {
node->name = name->strValue;
}
if (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) {
return Unexpected<>{ std::move(hasLt).error() };
}
else if (hasLt.value()) {
if (auto params = parseGenericParamsList(); ! params) {
return Unexpected<>{ std::move(params).error() };
}
else {
node->genericParams = std::move(params).value();
if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) {
return Unexpected<>{ std::move(hasGt ).error() };
}
}
}
if (auto lsquirly = consume(TokenV::opLSquirly, "'{'"); ! lsquirly) {
return Unexpected<>{ std::move(lsquirly).error() };
}
if (auto members = parseEnumMembersList(); ! members) {
return Unexpected<>{ std::move(members).error() };
}
else {
node->enumMembers = std::move(members).value();
}
if (auto rsquirly = consume(TokenV::opRSquirly, "'}'"); ! rsquirly) {
return Unexpected<>{ std::move(rsquirly).error() };
}
return node;
}
Expected<ast::Vector<ast::GenericParamNode>>
Parser::parseGenericParamsList() {
auto paramsList = ast::Vector<ast::GenericParamNode>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
bool keepParsing = true;
if (auto comma = tokenizer.peek();
comma and comma->value == TokenV::opComma) {
return langException<ExceptCode::ecUnexpectedToken>(
comma->line,
comma->column,
toString(*comma),
"'typename' keyword"
);
}
while (keepParsing) {
if (auto param = parseGenericParam(); ! param) {
return Unexpected{ std::move(param).error() };
}
else {
paramsList.push_back(std::move(param).value());
}
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
return Unexpected{ std::move(comma).error() };
}
else if (! comma.value()) {
if (peekToken = tokenizer.peek(); ! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
else {
if (peekToken->value != TokenV::opGt) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"',' or '>'"
);
}
else {
keepParsing = false;
}
}
}
}
return paramsList;
}
Expected<ast::GenericParamNode> Parser::parseGenericParam() {
auto node = ast::MakeNode<ast::GenericParamNode>();
if (auto kw = consume(TokenV::kwTypename, "'typename' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) {
return Unexpected{ std::move(ident).error() };
}
else {
node->name = ident->strValue;
}
return node;
}
Expected<ast::Vector<ast::StructMemberNode>>
Parser::parseStructMembersList() {
auto membersList = ast::Vector<ast::StructMemberNode>{};
if (auto comma = tokenizer.peek();
comma and comma->value == TokenV::opComma) {
return langException<ExceptCode::ecUnexpectedToken>(
comma->line,
comma->column,
toString(*comma),
"identifier or '}'"
);
}
bool keepParsing = true;
if (auto close = match(TokenV::opRSquirly); ! close) {
return Unexpected{ std::move(close).error() };
}
else if (close.value()) {
keepParsing = false;
}
while (keepParsing) {
if (auto member = parseStructMember(); ! member) {
return Unexpected{ std::move(member).error() };
}
else {
membersList.push_back(std::move(member).value());
}
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
return Unexpected<>{ std::move(comma).error() };
}
else if (! comma.value()) {
if (auto peekToken = tokenizer.peek(); ! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
else {
if (peekToken->value != TokenV::opRSquirly) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"',' or '}'"
);
}
else {
keepParsing = false;
}
}
}
if (auto close = match(TokenV::opRSquirly); ! close) {
return Unexpected<>{ std::move(close).error() };
}
else if (close.value()) {
keepParsing = false;
}
}
return membersList;
}
Expected<ast::StructMemberNode> Parser::parseStructMember() {
auto node = ast::MakeNode<ast::StructMemberNode>();
if (auto ident = consume(TokenV::tkIdentifier, "'identifier'"); ! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
node->location = { ident->line, ident->column };
node->name = ident->strValue;
}
if (auto colon = consume(TokenV::opColon, "':'"); ! colon) {
return Unexpected{ std::move(colon).error() };
}
if (auto type = parseType(); ! type) {
return Unexpected{ std::move(type).error() };
}
else {
node->type = std::move(type).value();
}
return node;
}
Expected<ast::Vector<ast::EnumMemberNode>> Parser::parseEnumMembersList() {
auto membersList = ast::Vector<ast::EnumMemberNode>{};
if (auto comma = tokenizer.peek();
comma and comma->value == TokenV::opComma) {
return langException<ExceptCode::ecUnexpectedToken>(
comma->line,
comma->column,
toString(*comma),
"identifier or '}'"
);
}
bool keepParsing = true;
if (auto close = match(TokenV::opRSquirly); ! close) {
return Unexpected{ std::move(close).error() };
}
else if (close.value()) {
keepParsing = false;
}
while (keepParsing) {
if (auto member = parseEnumMember(); ! member) {
return Unexpected{ std::move(member).error() };
}
else {
membersList.push_back(std::move(member).value());
}
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
return Unexpected<>{ std::move(comma).error() };
}
else if (! comma.value()) {
if (auto peekToken = tokenizer.peek(); ! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
else {
if (peekToken->value != TokenV::opRSquirly) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"',' or '}'"
);
}
else {
keepParsing = false;
}
}
}
if (auto close = match(TokenV::opRSquirly); ! close) {
return Unexpected<>{ std::move(close).error() };
}
else if (close.value()) {
keepParsing = false;
}
}
return membersList;
}
Expected<ast::EnumMemberNode> Parser::parseEnumMember() {
auto node = ast::MakeNode<ast::EnumMemberNode>();
if (auto ident = consume(TokenV::tkIdentifier, "'identifier'"); ! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
node->location = { ident->line, ident->column };
node->name = ident->strValue;
}
if (auto hasLParen = matchAndConsume(TokenV::opLParen); ! hasLParen) {
return Unexpected{ std::move(hasLParen).error() };
}
else if (hasLParen.value()) {
if (auto type = parseType(); ! type) {
return Unexpected{ std::move(type).error() };
}
else {
node->type = std::move(type).value();
if (auto hasRParen = consume(TokenV::opRParen, "')'"); ! hasRParen) {
return Unexpected{ std::move(hasRParen).error() };
}
}
}
return node;
}
Expected<ast::FunctionDeclNode> Parser::parseFunctionDeclaration() {
auto node = ast::MakeNode<ast::FunctionDeclNode>();
if (auto kw = consume(TokenV::kwFn, "'fn' keyword"); ! kw) {
return Unexpected<>{ std::move(kw).error() };
}
else {
node->location = { kw->line, kw->column };
}
if (auto name = consume(TokenV::tkIdentifier, "identifier"); ! name) {
return Unexpected<>{ std::move(name).error() };
}
else {
node->name = name->strValue;
}
if (auto hasLt = matchAndConsume(TokenV::opLt); ! hasLt) {
return Unexpected<>{ std::move(hasLt).error() };
}
else if (hasLt.value()) {
if (auto params = parseGenericParamsList(); ! params) {
return Unexpected<>{ std::move(params).error() };
}
else {
node->genericParams = std::move(params).value();
if (auto hasGt = consume(TokenV::opGt, "'>'"); ! hasGt) {
return Unexpected<>{ std::move(hasGt ).error() };
}
}
}
if (auto lparen = consume(TokenV::opLParen, "'('"); ! lparen) {
return Unexpected<>{ std::move(lparen).error() };
}
if (auto rparen = tokenizer.peek(); ! rparen) {
return Unexpected<>{ std::move(rparen).error() };
}
else if (rparen->value != TokenV::opRParen) {
if (auto params = parseFunctionParamsList(); ! params) {
return Unexpected<>{ std::move(params).error() };
}
else {
node->functionParams = std::move(params).value();
}
}
if (auto rparen = consume(TokenV::opRParen, "')'"); ! rparen) {
return Unexpected<>{ std::move(rparen).error() };
}
if (auto hasArrow = matchAndConsume(TokenV::opArrow); ! hasArrow) {
return Unexpected<>{ std::move(hasArrow).error() };
}
else if (hasArrow.value()) {
if (auto type = parseType(); ! type) {
return Unexpected<>{ std::move(type).error() };
}
else {
node->returnType = std::move(type).value();
}
}
if (auto body = parseCodeBlock(); ! body) {
return Unexpected<>{ std::move(body).error() };
}
else {
node->functionBody = std::move(body).value();
}
return node;
}
Expected<ast::Vector<ast::FunctionParamNode>>
Parser::parseFunctionParamsList() {
auto params = ast::Vector<ast::FunctionParamNode>{};
if (auto comma = tokenizer.peek();
comma and comma->value == TokenV::opComma) {
return langException<ExceptCode::ecUnexpectedToken>(
comma->line,
comma->column,
toString(*comma),
"identifier, 'this' keyword or ')'"
);
}
if (auto hasThis = match(TokenV::kwThis); ! hasThis) {
return Unexpected<>{ std::move(hasThis).value() };
}
else if (hasThis.value()) {
auto thisParam = parseFunctionParamThis();
if (auto thisṔaram = parseFunctionParamThis(); ! thisParam) {
return Unexpected<>{ std::move(thisParam).error() };
}
else {
params.push_back(std::move(thisParam).value());
}
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
return Unexpected<>{ std::move(comma).error() };
}
}
bool keepParsing = true;
while (keepParsing) {
if (auto param = parseFunctionParam(); ! param) {
return Unexpected<>{ std::move(param).error() };
}
else {
params.push_back(std::move(param).value());
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
return Unexpected<>{ std::move(comma).error() };
}
if (auto peekToken = tokenizer.peek(); ! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
else {
if (peekToken->value == TokenV::opRParen) {
keepParsing = false;
}
}
}
}
return params;
}
Expected<ast::FunctionParamNode> Parser::parseFunctionParamThis() {
auto node = ast::MakeNode<ast::FunctionParamNode>();
if (auto thisToken = consume(TokenV::kwThis, "'this' keyword");
! thisToken) {
return Unexpected<>{ std::move(thisToken).error() };
}
else {
node->isThis = true;
node->location.line = thisToken->line;
node->location.column = thisToken->column;
node->name = "this";
if (auto type = parseType(); ! type) {
return Unexpected<>{ std::move(type).error() };
}
else {
node->type = std::move(type).value();
}
}
return node;
}
Expected<ast::FunctionParamNode> Parser::parseFunctionParam() {
auto node = ast::MakeNode<ast::FunctionParamNode>();
node->isThis = false;
if (auto ident = consume(TokenV::tkIdentifier, "identifier"); ! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else {
node->name = ident->strValue;
node->location.line = ident->line;
node->location.column = ident->column;
}
if (auto colon = consume(TokenV::opColon, "':'"); ! colon) {
return Unexpected{ std::move(colon).error() };
}
if (auto type = parseType(); ! type) {
return Unexpected{ std::move(type).error() };
}
else {
node->type = std::move(type).value();
}
return node;
}
} // namespace arti::lang

View File

@ -0,0 +1,817 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Parser.hpp>
#include <artichoke/Parser/Pratt.hpp>
namespace arti::lang {
Expected<ast::ExpressionNode>
Parser::parseExpression(std::uint16_t minBindingPower) {
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
bool keepParsing = true;
ast::Optional<ast::ExpressionNode> 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 (peekToken->value == TokenV::opLBracket) {
if (auto close = match(TokenV::opRBracket, 1); ! close) {
return Unexpected<>{ std::move(close).error() };
}
else if (! close.value()) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"']'"
);
}
if (auto type = parseType(); ! type) {
return Unexpected<>{ std::move(type).error() };
}
else {
auto node = ast::MakeNode<ast::TypeExprNode>();
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() };
}
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<ExceptCode::ecUnexpectedToken>(
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<ExceptCode::ecUnexpectedToken>(
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<ast::Optional<ast::ExpressionNode>>
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<ast::IdentifierExprNode>();
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<ast::IdentifierExprNode>();
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<ast::ExpressionNode>
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<ast::PrefixExprNode>();
node->location = {
.line = peekToken->line,
.column = peekToken->column
};
node->op = op;
node->right = std::move(rhs).value();
return node;
}
Expected<ast::ExpressionNode>
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);
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<ast::GenericExprNode>();
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) {
return Unexpected<>{ std::move(rhs).error() };
}
/* TODO: MemberAccess and PointerMemberAccess do not use their respective
* nodes types yet */
if (op == ast::InfixOperator::Assignment) {
auto node = ast::MakeNode<ast::AssignExprNode>();
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::ModuleAccess) {
auto node = ast::MakeNode<ast::ModuleAccessExprNode>();
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<ast::MemberAccessExprNode>();
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<ast::PointerMemberAccessExprNode>();
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<ast::CompoundAssignExprNode>();
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<ast::InfixExprNode>();
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<ast::ExpressionNode>
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<ast::ExpressionNode> node = std::nullopt;
if (op == ast::PostfixOperator::FunctionCall) {
auto newNode = ast::MakeNode<ast::FunctionCallExprNode>();
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<ExceptCode::ecUnexpectedToken>(
ntok->line,
ntok->column,
toString(*ntok),
"',' or ')'"
);
}
else {
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::ObjectLiteral) {
auto newNode = ast::MakeNode<ast::ObjectLtrlNode>();
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<ast::ObjectLtrlNamedInitializerNode>();
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<ast::ObjectLtrlNamedFieldInitNode>();
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<ExceptCode::ecUnexpectedToken>(
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<ast::ObjectLtrlPositionalInitializerNode>();
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<ExceptCode::ecUnexpectedToken>(
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) {
bool isSlice = false;
bool skipSliceEnd = false;
bool skipSliceStart = false;
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;
}
}
auto idxExpr = ast::Optional<ast::ExpressionNode>{};
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();
}
if (isSlice) {
auto newNode = ast::MakeNode<ast::SliceRangeExprNode>();
newNode->location = {
.line = peekToken->line,
.column = peekToken->column
};
if (! skipSliceStart) {
newNode->start = std::move(idxExpr).value();
}
if (! skipSliceEnd) {
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<ast::SliceAccessExprNode>();
newNode->location = {
.line = peekToken->line,
.column = peekToken->column
};
newNode->index = std::move(idxExpr).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<ast::SliceLengthExprNode>();
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<ast::SliceCreationExprNode>();
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<ast::SlicePtrExprNode>();
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<ast::ReflectionExprNode>();
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<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"postfix operator, i.e. "
"any of ( /*TODO*/ )"
);
}
return std::move(node).value();
}
} // namespace arti::lang

178
lib/src/Parser/Literals.cpp Normal file
View File

@ -0,0 +1,178 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Expected<ast::CharLtrlNode>
Parser::parseCharLiteral() {
auto node = ast::MakeNode<ast::CharLtrlNode>();
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<uint8_t>(ltrl->strValue[1]);
}
return node;
}
Expected<ast::NullLtrlNode>
Parser::parseNullLiteral() {
auto node = ast::MakeNode<ast::NullLtrlNode>();
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<ast::StringLtrlNode>
Parser::parseStringLiteral() {
auto node = ast::MakeNode<ast::StringLtrlNode>();
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<ast::FloatLtrlNode>
Parser::parseFloatLiteral() {
auto node = ast::MakeNode<ast::FloatLtrlNode>();
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<ast::IntegerLtrlNode>
Parser::parseIntegerLiteral() {
auto node = ast::MakeNode<ast::IntegerLtrlNode>();
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<ast::BooleanLtrlNode>
Parser::parseBooleanLiteral() {
auto node = ast::MakeNode<ast::BooleanLtrlNode>();
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<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"boolean literal, i.e. "
"any of ( true, false )"
);
}
std::ignore = tokenizer.consume();
return node;
}
Expected<ast::IdentifierExprNode>
Parser::parseIdentifierExpression() {
auto node = ast::MakeNode<ast::IdentifierExprNode>();
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

View File

@ -0,0 +1,292 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Pratt.hpp>
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 opLBracket:
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:
case opLSquirly:
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;
case opLSquirly:
return ObjectLiteral;
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;
}
}
}

View File

@ -0,0 +1,105 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Parser::Parser(std::string source) noexcept
: unitName{}
, sourceCode{ source }
, tokenizer{ source } { }
Parser::Parser(std::string unitName, std::string source) noexcept
: unitName{ unitName }
, sourceCode{ source }
, tokenizer{ source } { }
Expected<ast::AST> Parser::parse() {
auto unit = ast::MakeNode<ast::AST>();
auto decl = ast::Optional<ast::TopLevelDeclNode>{};
bool keepParsing = true;
unit->unitName = this->unitName;
while (keepParsing) {
if (auto ok = parseTopLevelDeclaration(); ! ok) {
return Unexpected<>{ std::move(ok).error() };
}
else {
decl = std::move(ok).value();
if (! decl.has_value()) {
keepParsing = false;
}
else {
unit->declarations.push_back(std::move(decl).value());
}
}
}
if (auto eof = consume(TokenV::tkEOF, "end of compilation unit"); ! eof) {
return Unexpected<>{ std::move(eof).error() };
}
return unit;
}
Expected<Token> 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<bool> 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<bool> 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

115
lib/src/Parser/Pratt.cpp Normal file
View File

@ -0,0 +1,115 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Pratt.hpp>
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: return { 23, 24 };
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:
case ast::PostfixOperator::ObjectLiteral:
return 19;
default:
return 0;
}
}
} // namespace arti::lang::pratt

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,301 @@
//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Parser/Parser.hpp>
namespace arti::lang {
Expected<ast::NamespacedIdentifierNode> Parser::parseNamespacedIdentifier() {
auto node = ast::MakeNode<ast::NamespacedIdentifierNode>();
bool keepParsing = true;
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 {
return node;
}
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<ast::TypeNode> Parser::parseType() {
auto node = ast::MakeNode<ast::TypeNode>();
if (auto nextToken = tokenizer.peek(); ! nextToken) {
return Unexpected<>{ std::move(nextToken).error() };
}
else {
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();
}
}
if (auto ident = match(TokenV::tkIdentifier); ! ident) {
return Unexpected<>{ std::move(ident).error() };
}
else if (not ident.value()) {
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"identifier type name"
);
}
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<ast::IdentifierTypeNode>();
newNode->location = {
.line = ident->line,
.column = ident->column
};
newNode->typeName = ident->strValue;
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 {
std::ignore = tokenizer.consume();
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<ast::GenericTypeNode>();
newNode->location = {
.line = ident->line,
.column = ident->column
};
newNode->typeName = ident->strValue;
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);
}
}
}
node->typeNodes.emplace_back(std::move(currentNode));
}
return node;
}
Expected<ast::Vector<ast::TypeQualifier>> Parser::parseTypeQualifiers() {
auto qualifs = ast::Vector<ast::TypeQualifier>{};
auto peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
enum { None, AfterOptional, AfterMutable } state = None;
while (true) {
switch (peekToken->value) {
using enum TokenV;
case opStar:
qualifs.push_back(ast::TypeQualifier::Pointer);
state = None;
break;
case opMut:
if (state == AfterMutable) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"non mutable type qualifier, i.e. any of ( *, ?, [] )"
);
}
qualifs.push_back(ast::TypeQualifier::Mutable);
state = AfterMutable;
break;
case opOpt:
if (state == AfterOptional) {
return langException<ExceptCode::ecUnexpectedToken>(
peekToken->line,
peekToken->column,
toString(*peekToken),
"non optional type qualifier, i.e. any of ( *, $, [] )"
);
}
qualifs.push_back(ast::TypeQualifier::Optional);
state = AfterOptional;
break;
case opLBracket:
std::ignore = tokenizer.consume();
peekToken = tokenizer.peekExpect(opRBracket);
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
qualifs.push_back(ast::TypeQualifier::Slice);
state = None;
break;
default:
return qualifs;
}
std::ignore = tokenizer.consume();
peekToken = tokenizer.peek();
if (! peekToken) {
return Unexpected<>{ std::move(peekToken).error() };
}
}
return qualifs;
}
Expected<ast::Vector<ast::TypeNode>> Parser::parseGenericArgumentsList() {
auto args = ast::Vector<ast::TypeNode>{};
if (auto lt = consume(TokenV::opLt, "'<'"); ! lt) {
return Unexpected<>{ std::move(lt).error() };
}
bool keepParsing = true;
if (auto comma = tokenizer.peek();
comma and comma->value == TokenV::opComma) {
return langException<ExceptCode::ecUnexpectedToken>(
comma->line,
comma->column,
toString(*comma),
"type"
);
}
while (keepParsing) {
if (auto type = parseType(); ! type) {
return Unexpected<>{ std::move(type).error() };
}
else {
args.push_back(std::move(type).value());
}
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
return Unexpected{ std::move(comma).error() };
}
else if (! comma.value()) {
if (auto peekToken = tokenizer.peekExpect(TokenV::opGt); ! peekToken) {
return Unexpected{ std::move(peekToken).error() };
}
else {
keepParsing = false;
}
}
}
if (auto gt = consume(TokenV::opGt, "'>'"); ! gt) {
return Unexpected<>{ std::move(gt).error() };
}
return args;
}
} // namespace arti::lang

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Tokenizer/Token.hpp> #include <artichoke/Tokenizer/Token.hpp>
#include <format> #include <format>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Tokenizer/Tokenizer.hpp> #include <artichoke/Tokenizer/Tokenizer.hpp>
#include <utility> #include <utility>
@ -106,22 +128,25 @@ namespace arti::lang {
return tokensBuffer.at(n); return tokensBuffer.at(n);
} }
Expected<Token> Expected<Token> Tokenizer::peekExpect(
Tokenizer::peekExpect(std::size_t n, TokenV tokenType) noexcept { TokenV tokenType,
std::string_view message,
std::size_t n
) noexcept {
if (message.empty()) {
message = toString(tokenType);
}
if (tokensBuffer.size() > (n + 1)) { if (tokensBuffer.size() > (n + 1)) {
auto tokenAt = tokensBuffer.at(n); auto tokenAt = tokensBuffer.at(n);
if (tokenAt.value != tokenType) { if (tokenAt.value != tokenType) {
return Unexpected<> { return langException<ExceptCode::ecUnexpectedToken>(
Exception{ tokenAt.line,
.line = tokenAt.line, tokenAt.column,
.column = tokenAt.column, toString(tokenAt),
.message = std::format( message
"Expected token of type {}, got {}", );
toString(tokenType), toString(tokenAt)
)
}
};
} }
return tokenAt; return tokenAt;
@ -129,7 +154,7 @@ namespace arti::lang {
auto token = peek(n); auto token = peek(n);
if (!token) { if (! token) {
return token; return token;
} }
@ -141,22 +166,18 @@ namespace arti::lang {
tokensBuffer.pop_back(); tokensBuffer.pop_back();
tokensBuffer.push_back(*token); tokensBuffer.push_back(*token);
token->column += 1; 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); tokensBuffer.push_back(*token);
token = peekTok; token = peekTok;
} }
if (token->value != tokenType) { if (token->value != tokenType) {
return Unexpected<> { return langException<ExceptCode::ecUnexpectedToken>(
Exception{ token->line,
.line = token->line, token->column,
.column = token->column, toString(*token),
.message = std::format( message
"Expected token of type {}, got {}", );
toString(tokenType), toString(*token)
)
}
};
} }
return *token; return *token;

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Tokenizer/TokenizerRange.hpp> #include <artichoke/Tokenizer/TokenizerRange.hpp>
#include <utility> #include <utility>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <artichoke/Util/Demangle.hpp> #include <artichoke/Util/Demangle.hpp>
#include <utility> #include <utility>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <string> #include <string>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <string> #include <string>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <array> #include <array>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <array> #include <array>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <array> #include <array>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <array> #include <array>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <array> #include <array>

View File

@ -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 <https://www.gnu.org/licenses/>. //
// //
//============================================================================//
#pragma once #pragma once
#include <random> #include <random>