Initial Parser Implementation and Feature Completion #1
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,4 +3,6 @@
|
||||
build/**
|
||||
install/**
|
||||
|
||||
cpm-package-lock.cmake
|
||||
|
||||
TODO.md
|
||||
|
||||
27
add-notice.sh
Executable file
27
add-notice.sh
Executable 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!"
|
||||
@ -170,7 +170,7 @@ non_exportable_declaration =
|
||||
"switch" "(" <expression> ")" "{" <switch_case>* <default_case>? "}"
|
||||
|
||||
<match_case> =
|
||||
<type_name> ( "(" <identifier> ")" )? "->" <code_block>
|
||||
<type_name> ( "|" <identifier> "|" )? "->" <code_block>
|
||||
|
||||
<switch_case> =
|
||||
<expression> "->" <code_block>
|
||||
|
||||
@ -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 **) {
|
||||
std::println("[LOG] Hello world");
|
||||
#include <print>
|
||||
#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));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
#include <string_view>
|
||||
|
||||
@ -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
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -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
|
||||
|
||||
#define yield co_yield
|
||||
|
||||
@ -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
|
||||
|
||||
#include <artichoke/Parser/AST/Common.hpp>
|
||||
|
||||
@ -1,3 +1,25 @@
|
||||
//============================================================================//
|
||||
// //
|
||||
// artichoke programming language //
|
||||
// //
|
||||
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
||||
// //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as published //
|
||||
// by the Free Software Foundation, either version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
// //
|
||||
//============================================================================//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
@ -28,35 +50,61 @@ namespace arti::lang::ast {
|
||||
Optional,
|
||||
};
|
||||
|
||||
enum class UnaryOperator {
|
||||
enum class PrefixOperator {
|
||||
Uninitialized,
|
||||
Not,
|
||||
Minus,
|
||||
BitNot,
|
||||
Ampersand,
|
||||
Star,
|
||||
MemPtr,
|
||||
DerefPtr,
|
||||
};
|
||||
|
||||
enum class BinaryOperator {
|
||||
enum class InfixOperator {
|
||||
Uninitialized,
|
||||
Modulo,
|
||||
Addition,
|
||||
Substraction,
|
||||
Division,
|
||||
Multiplication,
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
GreaterEqual,
|
||||
GreaterThan,
|
||||
LessEqual,
|
||||
BitAnd,
|
||||
BitXor,
|
||||
BitOr,
|
||||
GreaterEqual,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
Adition,
|
||||
Substraction,
|
||||
Multiplication,
|
||||
Division,
|
||||
Modulo,
|
||||
BoolAnd,
|
||||
BoolOr,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
Assignment,
|
||||
ModuleAccess,
|
||||
MemberAccess,
|
||||
PointerMemberAccess,
|
||||
AdditionAssignment,
|
||||
SubstractionAssignment,
|
||||
MultiplicationAssignment,
|
||||
DivisionAssignment,
|
||||
ModuloAssignment,
|
||||
BitAndAssignment,
|
||||
BitOrAssignment,
|
||||
LShiftAssignment,
|
||||
RShiftAssignment,
|
||||
BoolAndAssignment,
|
||||
BoolOrAssignment,
|
||||
};
|
||||
|
||||
enum class PostfixOperator {
|
||||
Uninitialized,
|
||||
FunctionCall,
|
||||
SliceAccess,
|
||||
SliceSize,
|
||||
PtrToSlice,
|
||||
SliceToPtr,
|
||||
Reflect,
|
||||
ObjectLiteral,
|
||||
};
|
||||
|
||||
enum class CompoundAssignOperator {
|
||||
|
||||
@ -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
|
||||
|
||||
#include <artichoke/Parser/AST/Common.hpp>
|
||||
|
||||
@ -1,3 +1,25 @@
|
||||
//============================================================================//
|
||||
// //
|
||||
// artichoke programming language //
|
||||
// //
|
||||
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
||||
// //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as published //
|
||||
// by the Free Software Foundation, either version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
// //
|
||||
//============================================================================//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <artichoke/Parser/AST/Common.hpp>
|
||||
@ -10,39 +32,43 @@ namespace arti::lang::ast {
|
||||
|
||||
/* Main declaration node types */
|
||||
struct IdentifierExpression;
|
||||
struct UnaryExpression;
|
||||
struct BinaryExpression;
|
||||
struct PrefixExpression;
|
||||
struct InfixExpression;
|
||||
struct AssignExpression;
|
||||
struct CompoundAssignExpression;
|
||||
struct FunctionCallExpression;
|
||||
struct SliceAccessExpression;
|
||||
struct SliceRangeExpression;
|
||||
struct MemberAccessExpression;
|
||||
struct PointerAccessExpression;
|
||||
struct ScopeAccessExpression;
|
||||
struct PointerMemberAccessExpression;
|
||||
struct GenericExpression;
|
||||
struct ModuleAccessExpression;
|
||||
struct ReflectionExpression;
|
||||
struct SliceCreationExpression;
|
||||
struct SliceLengthExpression;
|
||||
struct SlicePtrExpression;
|
||||
struct TypeExpression;
|
||||
|
||||
} // namespace nodes
|
||||
|
||||
/* Public Aliases */
|
||||
using IdentifierExprNode = Ptr<nodes::IdentifierExpression>;
|
||||
using UnaryExprNode = Ptr<nodes::UnaryExpression>;
|
||||
using BinaryExprNode = Ptr<nodes::BinaryExpression>;
|
||||
using PrefixExprNode = Ptr<nodes::PrefixExpression>;
|
||||
using InfixExprNode = Ptr<nodes::InfixExpression>;
|
||||
using AssignExprNode = Ptr<nodes::AssignExpression>;
|
||||
using CompoundAssignExprNode = Ptr<nodes::CompoundAssignExpression>;
|
||||
using FunctionCallExprNode = Ptr<nodes::FunctionCallExpression>;
|
||||
using SliceAccessExprNode = Ptr<nodes::SliceAccessExpression>;
|
||||
using SliceRangeExprNode = Ptr<nodes::SliceRangeExpression>;
|
||||
using MemberAccessExprNode = Ptr<nodes::MemberAccessExpression>;
|
||||
using PointerAccessExprNode = Ptr<nodes::PointerAccessExpression>;
|
||||
using ScopeAccessExprNode = Ptr<nodes::ScopeAccessExpression>;
|
||||
using PointerMemberAccessExprNode = Ptr<nodes::PointerMemberAccessExpression>;
|
||||
using GenericExprNode = Ptr<nodes::GenericExpression>;
|
||||
using ModuleAccessExprNode = Ptr<nodes::ModuleAccessExpression>;
|
||||
using ReflectionExprNode = Ptr<nodes::ReflectionExpression>;
|
||||
using SliceCreationExprNode = Ptr<nodes::SliceCreationExpression>;
|
||||
using SliceLengthExprNode = Ptr<nodes::SliceLengthExpression>;
|
||||
using SlicePtrExprNode = Ptr<nodes::SlicePtrExpression>;
|
||||
using TypeExprNode = Ptr<nodes::TypeExpression>;
|
||||
|
||||
/* Variant nodes */
|
||||
using ExpressionNode = Variant<
|
||||
@ -52,23 +78,24 @@ namespace arti::lang::ast {
|
||||
FloatLtrlNode,
|
||||
IntegerLtrlNode,
|
||||
BooleanLtrlNode,
|
||||
StructLtrlNode,
|
||||
SliceLtrlNode,
|
||||
ObjectLtrlNode,
|
||||
IdentifierExprNode,
|
||||
UnaryExprNode,
|
||||
BinaryExprNode,
|
||||
PrefixExprNode,
|
||||
InfixExprNode,
|
||||
AssignExprNode,
|
||||
CompoundAssignExprNode,
|
||||
FunctionCallExprNode,
|
||||
SliceAccessExprNode,
|
||||
SliceRangeExprNode,
|
||||
MemberAccessExprNode,
|
||||
PointerAccessExprNode,
|
||||
ScopeAccessExprNode,
|
||||
PointerMemberAccessExprNode,
|
||||
GenericExprNode,
|
||||
ModuleAccessExprNode,
|
||||
SliceCreationExprNode,
|
||||
SliceLengthExprNode,
|
||||
SlicePtrExprNode,
|
||||
ReflectionExprNode
|
||||
ReflectionExprNode,
|
||||
TypeExprNode
|
||||
>;
|
||||
|
||||
/* Node definitions */
|
||||
@ -77,16 +104,16 @@ namespace arti::lang::ast {
|
||||
String identifierName;
|
||||
};
|
||||
|
||||
struct nodes::UnaryExpression {
|
||||
struct nodes::PrefixExpression {
|
||||
SourceLocation location;
|
||||
UnaryOperator op;
|
||||
PrefixOperator op;
|
||||
ExpressionNode right;
|
||||
};
|
||||
|
||||
struct nodes::BinaryExpression {
|
||||
struct nodes::InfixExpression {
|
||||
SourceLocation location;
|
||||
|
||||
BinaryOperator op;
|
||||
InfixOperator op;
|
||||
ExpressionNode left;
|
||||
ExpressionNode right;
|
||||
};
|
||||
@ -131,23 +158,29 @@ namespace arti::lang::ast {
|
||||
struct nodes::MemberAccessExpression {
|
||||
SourceLocation location;
|
||||
|
||||
String memberName;
|
||||
ExpressionNode member;
|
||||
ExpressionNode object;
|
||||
};
|
||||
|
||||
struct nodes::PointerAccessExpression {
|
||||
struct nodes::PointerMemberAccessExpression {
|
||||
SourceLocation location;
|
||||
|
||||
String memberName;
|
||||
ExpressionNode member;
|
||||
ExpressionNode object;
|
||||
};
|
||||
|
||||
struct nodes::ScopeAccessExpression {
|
||||
struct nodes::ModuleAccessExpression {
|
||||
SourceLocation location;
|
||||
|
||||
String memberName;
|
||||
ExpressionNode scope;
|
||||
Vector<TypeNode> genericParams;
|
||||
ExpressionNode left;
|
||||
ExpressionNode right;
|
||||
};
|
||||
|
||||
struct nodes::GenericExpression {
|
||||
SourceLocation location;
|
||||
|
||||
ExpressionNode typeNode;
|
||||
std::vector<TypeNode> genericArgs;
|
||||
};
|
||||
|
||||
struct nodes::ReflectionExpression {
|
||||
@ -176,30 +209,37 @@ namespace arti::lang::ast {
|
||||
ExpressionNode object;
|
||||
};
|
||||
|
||||
struct nodes::StructLiteralNamedFieldInit {
|
||||
struct nodes::ObjectLiteralNamedFieldInit {
|
||||
SourceLocation location;
|
||||
|
||||
String fieldName;
|
||||
ExpressionNode fieldValue;
|
||||
};
|
||||
|
||||
struct nodes::StructLiteralPositionalInit {
|
||||
struct nodes::ObjectLiteralNamedInitializer {
|
||||
SourceLocation location;
|
||||
|
||||
ExpressionNode fieldValue;
|
||||
Vector<ObjectLtrlNamedFieldInitNode> fields;
|
||||
};
|
||||
|
||||
struct nodes::StructLiteralNamedInitializer {
|
||||
struct nodes::ObjectLiteralPositionalInitializer {
|
||||
SourceLocation location;
|
||||
|
||||
Vector<StructLtrlNamedFieldInitNode> fields;
|
||||
Vector<ExpressionNode> fields;
|
||||
};
|
||||
|
||||
struct nodes::StructLiteralPositionalInitializer {
|
||||
struct nodes::ObjectLiteral {
|
||||
SourceLocation location;
|
||||
|
||||
Vector<StructLtrlPositionalInitNode> fields;
|
||||
};
|
||||
ExpressionNode type;
|
||||
Optional<ObjectLtrlInitializerNode> initializer;
|
||||
};
|
||||
|
||||
struct nodes::TypeExpression {
|
||||
SourceLocation location;
|
||||
|
||||
TypeNode type;
|
||||
};
|
||||
|
||||
} // namespace arti::lang::ast
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
#include <artichoke/Parser/AST/Common.hpp>
|
||||
@ -15,14 +37,12 @@ namespace arti::lang::ast {
|
||||
struct FloatLiteral;
|
||||
struct IntegerLiteral;
|
||||
struct BooleanLiteral;
|
||||
struct StructLiteral;
|
||||
struct SliceLiteral;
|
||||
struct ObjectLiteral;
|
||||
|
||||
/* Helper declaration node types */
|
||||
struct StructLiteralNamedFieldInit;
|
||||
struct StructLiteralPositionalInit;
|
||||
struct StructLiteralNamedInitializer;
|
||||
struct StructLiteralPositionalInitializer;
|
||||
struct ObjectLiteralNamedFieldInit;
|
||||
struct ObjectLiteralNamedInitializer;
|
||||
struct ObjectLiteralPositionalInitializer;
|
||||
|
||||
} // namespace nodes
|
||||
|
||||
@ -33,22 +53,19 @@ namespace arti::lang::ast {
|
||||
using FloatLtrlNode = Ptr<nodes::FloatLiteral>;
|
||||
using IntegerLtrlNode = Ptr<nodes::IntegerLiteral>;
|
||||
using BooleanLtrlNode = Ptr<nodes::BooleanLiteral>;
|
||||
using StructLtrlNode = Ptr<nodes::StructLiteral>;
|
||||
using SliceLtrlNode = Ptr<nodes::SliceLiteral>;
|
||||
using ObjectLtrlNode = Ptr<nodes::ObjectLiteral>;
|
||||
|
||||
using StructLtrlNamedFieldInitNode =
|
||||
Ptr<nodes::StructLiteralNamedFieldInit>;
|
||||
using StructLtrlPositionalInitNode =
|
||||
Ptr<nodes::StructLiteralPositionalInit>;
|
||||
using StructLtrlNamedInitializerNode =
|
||||
Ptr<nodes::StructLiteralNamedInitializer>;
|
||||
using StructLtrlPositionalInitializerNode =
|
||||
Ptr<nodes::StructLiteralPositionalInitializer>;
|
||||
using ObjectLtrlNamedFieldInitNode =
|
||||
Ptr<nodes::ObjectLiteralNamedFieldInit>;
|
||||
using ObjectLtrlNamedInitializerNode =
|
||||
Ptr<nodes::ObjectLiteralNamedInitializer>;
|
||||
using ObjectLtrlPositionalInitializerNode =
|
||||
Ptr<nodes::ObjectLiteralPositionalInitializer>;
|
||||
|
||||
/* Variant nodes */
|
||||
using StructLtrlInitializerNode = Variant<
|
||||
StructLtrlNamedInitializerNode,
|
||||
StructLtrlPositionalInitializerNode
|
||||
using ObjectLtrlInitializerNode = Variant<
|
||||
ObjectLtrlNamedInitializerNode,
|
||||
ObjectLtrlPositionalInitializerNode
|
||||
>;
|
||||
|
||||
/* Node definitions */
|
||||
@ -87,20 +104,6 @@ namespace arti::lang::ast {
|
||||
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
|
||||
* due to dependency in ExpressionNode variant. */
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
#include <artichoke/Parser/AST/Common.hpp>
|
||||
@ -71,7 +93,8 @@ namespace arti::lang::ast {
|
||||
WhileStmtNode,
|
||||
DoWhileStmtNode,
|
||||
InfLoopStmtNode,
|
||||
ExpressionStmtNode
|
||||
ExpressionStmtNode,
|
||||
CodeBlockStmtNode
|
||||
>;
|
||||
|
||||
using ElseBranchNode = Variant<
|
||||
|
||||
@ -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
|
||||
|
||||
#include <artichoke/Parser/AST/Common.hpp>
|
||||
@ -11,7 +33,6 @@ namespace arti::lang::ast {
|
||||
struct Type;
|
||||
struct GenericType;
|
||||
struct IdentifierType;
|
||||
struct NamespacedType;
|
||||
|
||||
/* Helper type node types */
|
||||
struct NamespacedIdentifier;
|
||||
@ -22,14 +43,12 @@ namespace arti::lang::ast {
|
||||
using TypeNode = Ptr<nodes::Type>;
|
||||
using GenericTypeNode = Ptr<nodes::GenericType>;
|
||||
using IdentifierTypeNode = Ptr<nodes::IdentifierType>;
|
||||
using NamespacedTypeNode = Ptr<nodes::NamespacedType>;
|
||||
using NamespacedIdentifierNode = Ptr<nodes::NamespacedIdentifier>;
|
||||
|
||||
/* Variant nodes */
|
||||
using TypeExpressionNode = Variant<
|
||||
GenericTypeNode,
|
||||
IdentifierTypeNode,
|
||||
NamespacedTypeNode
|
||||
IdentifierTypeNode
|
||||
>;
|
||||
|
||||
/* Node definitions */
|
||||
@ -38,27 +57,20 @@ namespace arti::lang::ast {
|
||||
SourceLocation location;
|
||||
|
||||
Vector<TypeQualifier> qualifiers;
|
||||
TypeExpressionNode baseType;
|
||||
Vector<TypeExpressionNode> typeNodes;
|
||||
};
|
||||
|
||||
struct nodes::GenericType {
|
||||
SourceLocation location;
|
||||
|
||||
TypeExpressionNode baseType;
|
||||
String typeName;
|
||||
Vector<TypeNode> genericArgs;
|
||||
};
|
||||
|
||||
struct nodes::IdentifierType {
|
||||
SourceLocation location;
|
||||
|
||||
NamespacedIdentifierNode typeName;
|
||||
};
|
||||
|
||||
struct nodes::NamespacedType {
|
||||
SourceLocation location;
|
||||
|
||||
String typeName;
|
||||
TypeExpressionNode baseType;
|
||||
};
|
||||
|
||||
struct nodes::NamespacedIdentifier {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
48
lib/include/artichoke/Parser/Pratt.hpp
Normal file
48
lib/include/artichoke/Parser/Pratt.hpp
Normal 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);
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -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
|
||||
|
||||
#include <deque>
|
||||
@ -25,7 +47,11 @@ namespace arti::lang {
|
||||
|
||||
Expected<void> consume(std::size_t n = 1) 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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
#include <artichoke/Util/Expected.hpp>
|
||||
|
||||
@ -1,3 +1,25 @@
|
||||
//============================================================================//
|
||||
// //
|
||||
// artichoke programming language //
|
||||
// //
|
||||
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
||||
// //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as published //
|
||||
// by the Free Software Foundation, either version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
// //
|
||||
//============================================================================//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <artichoke/Util/Expected.hpp>
|
||||
|
||||
@ -1,3 +1,25 @@
|
||||
//============================================================================//
|
||||
// //
|
||||
// artichoke programming language //
|
||||
// //
|
||||
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
||||
// //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as published //
|
||||
// by the Free Software Foundation, either version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
||||
// //
|
||||
//============================================================================//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
@ -17,6 +39,10 @@ namespace arti::lang {
|
||||
ecInvalidCharacter,
|
||||
ecInvalidIndex,
|
||||
ecInvalidComment,
|
||||
ecUnexpectedToken,
|
||||
ecExpectedSemicolon,
|
||||
ecImportInsideModule,
|
||||
ecUnimplemented,
|
||||
};
|
||||
|
||||
struct Exception {
|
||||
@ -71,6 +97,24 @@ namespace arti::lang {
|
||||
else if constexpr (code == ecInvalidComment) {
|
||||
return "Invalid comment found, missing '*/' end of comment";
|
||||
}
|
||||
else if constexpr (code == ecUnexpectedToken) {
|
||||
return std::format(
|
||||
"Found unexpected token '{}', expected {}",
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
else if constexpr (code == ecExpectedSemicolon) {
|
||||
return std::format(
|
||||
"Expected ';', got '{}'",
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
else if constexpr (code == ecImportInsideModule) {
|
||||
return "Cannot use import statements inside a module declaration";
|
||||
}
|
||||
else if constexpr (code == ecUnimplemented) {
|
||||
return "Unimplemented";
|
||||
}
|
||||
else {
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
namespace arti::lang {
|
||||
|
||||
@ -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
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@ -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
|
||||
|
||||
#include <map>
|
||||
|
||||
@ -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 <sstream>
|
||||
@ -59,41 +81,57 @@ namespace arti::lang::ast {
|
||||
};
|
||||
|
||||
// Operator label helpers (matching AST.cpp)
|
||||
std::string toString(UnaryOperator op) {
|
||||
using enum UnaryOperator;
|
||||
std::string toString(PrefixOperator op) {
|
||||
using enum PrefixOperator;
|
||||
switch (op) {
|
||||
case Not: return "Not (!)";
|
||||
case Minus: return "Minus (-)";
|
||||
case BitNot: return "BitNot (~)";
|
||||
case Ampersand: return "Ampersand (&)";
|
||||
case Star: return "Star (*)";
|
||||
case MemPtr: return "MemPtr (&)";
|
||||
case DerefPtr: return "DerefPtr (*)";
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
std::string toString(BinaryOperator op) {
|
||||
using enum BinaryOperator;
|
||||
std::string toString(InfixOperator op) {
|
||||
using enum InfixOperator;
|
||||
switch (op) {
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Adition: return "Adition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
default: std::unreachable(); break;
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Addition: return "Addition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
case Assignment: return "Assignment (=)";
|
||||
case ModuleAccess: return "ModuleAccess (::)";
|
||||
case MemberAccess: return "MemberAccess (.)";
|
||||
case PointerMemberAccess: return "PointerMemberAccess (->)";
|
||||
case AdditionAssignment: return "AdditionAssignment (+=)";
|
||||
case SubstractionAssignment: return "SubstractionAssignment (-=)";
|
||||
case MultiplicationAssignment: return "MultiplicationAssignment (*=)";
|
||||
case DivisionAssignment: return "DivisionAssignment (/=)";
|
||||
case ModuloAssignment: return "ModuloAssignment (%=)";
|
||||
case BitAndAssignment: return "BitAndAssignment (&=)";
|
||||
case BitOrAssignment: return "BitOrAssignment (|=)";
|
||||
case BoolAndAssignment: return "BoolAndAssignment (&&=)";
|
||||
case BoolOrAssignment: return "BoolOrAssignment (||=)";
|
||||
case LShiftAssignment: return "LShiftAssignment (<<=)";
|
||||
case RShiftAssignment: return "RShiftAssignment (>>=)";
|
||||
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
@ -133,8 +171,6 @@ namespace arti::lang::ast {
|
||||
std::string emit(const TypeNode &, GraphBuilder &);
|
||||
std::string emit(const GenericTypeNode &, GraphBuilder &);
|
||||
std::string emit(const IdentifierTypeNode &, GraphBuilder &);
|
||||
std::string emit(const NamespacedTypeNode &, GraphBuilder &);
|
||||
std::string emit(const NamespacedIdentifierNode &, GraphBuilder &);
|
||||
std::string emit(const TypeExpressionNode &, GraphBuilder &);
|
||||
std::string emit(const CharLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const NullLtrlNode &, GraphBuilder &);
|
||||
@ -142,24 +178,23 @@ namespace arti::lang::ast {
|
||||
std::string emit(const FloatLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const IntegerLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const BooleanLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const StructLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const SliceLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const StructLtrlNamedFieldInitNode &, GraphBuilder &);
|
||||
std::string emit(const StructLtrlPositionalInitNode &, GraphBuilder &);
|
||||
std::string emit(const StructLtrlNamedInitializerNode &, GraphBuilder &);
|
||||
std::string emit(const StructLtrlPositionalInitializerNode&, GraphBuilder&);
|
||||
std::string emit(const StructLtrlInitializerNode &, GraphBuilder &);
|
||||
std::string emit(const ObjectLtrlNode &, GraphBuilder &);
|
||||
std::string emit(const ObjectLtrlNamedFieldInitNode &, GraphBuilder &);
|
||||
std::string emit(const ObjectLtrlNamedInitializerNode &, GraphBuilder &);
|
||||
std::string emit(const ObjectLtrlPositionalInitializerNode&, GraphBuilder&);
|
||||
std::string emit(const ObjectLtrlInitializerNode &, GraphBuilder &);
|
||||
std::string emit(const IdentifierExprNode &, GraphBuilder &);
|
||||
std::string emit(const UnaryExprNode &, GraphBuilder &);
|
||||
std::string emit(const BinaryExprNode &, GraphBuilder &);
|
||||
std::string emit(const PrefixExprNode &, GraphBuilder &);
|
||||
std::string emit(const InfixExprNode &, GraphBuilder &);
|
||||
std::string emit(const AssignExprNode &, GraphBuilder &);
|
||||
std::string emit(const CompoundAssignExprNode &, GraphBuilder &);
|
||||
std::string emit(const FunctionCallExprNode &, GraphBuilder &);
|
||||
std::string emit(const SliceAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const SliceRangeExprNode &, GraphBuilder &);
|
||||
std::string emit(const MemberAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const PointerAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const ScopeAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const PointerMemberAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const GenericExprNode &, GraphBuilder &);
|
||||
std::string emit(const ModuleAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const ReflectionExprNode &, GraphBuilder &);
|
||||
std::string emit(const SliceCreationExprNode &, GraphBuilder &);
|
||||
std::string emit(const SliceLengthExprNode &, GraphBuilder &);
|
||||
@ -188,6 +223,7 @@ namespace arti::lang::ast {
|
||||
std::string emit(const ElseBranchNode &, GraphBuilder &);
|
||||
std::string emit(const DeferableNode &, GraphBuilder &);
|
||||
std::string emit(const PreLoopStmtNode &, GraphBuilder &);
|
||||
std::string emit(const TypeExprNode &, GraphBuilder &);
|
||||
|
||||
// Helpers for making leaf nodes with backticked values
|
||||
inline std::string makeLeaf(GraphBuilder &g, std::string_view value) {
|
||||
@ -456,15 +492,16 @@ namespace arti::lang::ast {
|
||||
});
|
||||
}
|
||||
}
|
||||
auto baseId = emit(node->baseType, g);
|
||||
g.addEdge(id, baseId, "BaseType");
|
||||
emitGroupVec(g, id, "TypeNodes", node->typeNodes, [&](const auto &arg) {
|
||||
return emit(arg, g);
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const GenericTypeNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("GenericType");
|
||||
auto baseId = emit(node->baseType, g);
|
||||
g.addEdge(id, baseId, "BaseType");
|
||||
auto typeId = g.makeNode(node->typeName);
|
||||
g.addEdge(id, typeId, "TypeName");
|
||||
if (! node->genericArgs.empty()) {
|
||||
emitGroupVec(
|
||||
g,
|
||||
@ -478,30 +515,17 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string emit(const IdentifierTypeNode &node, GraphBuilder &g) {
|
||||
std::ignore = node;
|
||||
auto id = g.makeNode("IdentifierType");
|
||||
auto cid = emit(node->typeName, g);
|
||||
g.addEdge(id, cid, "TypeName");
|
||||
auto typeId = g.makeNode(node->typeName);
|
||||
g.addEdge(id, typeId, "TypeName");
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const NamespacedTypeNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("NamespacedType");
|
||||
auto baseId = emit(node->baseType, g);
|
||||
g.addEdge(id, baseId, "BaseType");
|
||||
auto leaf = makeLeaf(g, node->typeName);
|
||||
g.addEdge(id, leaf, "TypeName");
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const NamespacedIdentifierNode &node, GraphBuilder &g) {
|
||||
return g.makeNode(namespacedIdentToString(node));
|
||||
}
|
||||
|
||||
std::string emit(const TypeExpressionNode &node, GraphBuilder &g) {
|
||||
auto visitor = OverloadSet{
|
||||
[&g](const GenericTypeNode &n) { return emit(n, g); },
|
||||
[&g](const IdentifierTypeNode &n) { return emit(n, g); },
|
||||
[&g](const NamespacedTypeNode &n) { return emit(n, g); },
|
||||
};
|
||||
return std::visit(visitor, node);
|
||||
}
|
||||
@ -530,19 +554,8 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
// Struct/Slice literals and initializers
|
||||
std::string emit(const StructLtrlNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("StructLiteral");
|
||||
auto t = emit(node->type, g);
|
||||
g.addEdge(id, t, "Type");
|
||||
if (node->initializer) {
|
||||
auto cid = emit(*node->initializer, g);
|
||||
g.addEdge(id, cid, "Elements");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const SliceLtrlNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("SliceLiteral");
|
||||
std::string emit(const ObjectLtrlNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("ObjectLiteral");
|
||||
auto t = emit(node->type, g);
|
||||
g.addEdge(id, t, "Type");
|
||||
if (node->initializer) {
|
||||
@ -553,7 +566,7 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string
|
||||
emit(const StructLtrlNamedFieldInitNode &node, GraphBuilder &g) {
|
||||
emit(const ObjectLtrlNamedFieldInitNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("FieldInitializer");
|
||||
auto leaf = makeLeaf(g, node->fieldName);
|
||||
g.addEdge(id, leaf, "Field");
|
||||
@ -563,15 +576,7 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string
|
||||
emit(const StructLtrlPositionalInitNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("PositionalInitializer");
|
||||
auto val = emit(node->fieldValue, g);
|
||||
g.addEdge(id, val, "Value");
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string
|
||||
emit(const StructLtrlNamedInitializerNode &node, GraphBuilder &g) {
|
||||
emit(const ObjectLtrlNamedInitializerNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("InitializerList");
|
||||
if (! node->fields.empty()) {
|
||||
emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) {
|
||||
@ -582,7 +587,7 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string
|
||||
emit(const StructLtrlPositionalInitializerNode &node, GraphBuilder &g) {
|
||||
emit(const ObjectLtrlPositionalInitializerNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("InitializerList");
|
||||
if (! node->fields.empty()) {
|
||||
emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) {
|
||||
@ -592,10 +597,10 @@ namespace arti::lang::ast {
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const StructLtrlInitializerNode &node, GraphBuilder &g) {
|
||||
std::string emit(const ObjectLtrlInitializerNode &node, GraphBuilder &g) {
|
||||
auto visitor = OverloadSet{
|
||||
[&g](const StructLtrlNamedInitializerNode &n) { return emit(n, g); },
|
||||
[&g](const StructLtrlPositionalInitializerNode &n) {
|
||||
[&g](const ObjectLtrlNamedInitializerNode &n) { return emit(n, g); },
|
||||
[&g](const ObjectLtrlPositionalInitializerNode &n) {
|
||||
return emit(n, g);
|
||||
},
|
||||
};
|
||||
@ -607,8 +612,8 @@ namespace arti::lang::ast {
|
||||
return g.makeNode(std::format("Identifier `{}`", node->identifierName));
|
||||
}
|
||||
|
||||
std::string emit(const UnaryExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("UnaryExpression");
|
||||
std::string emit(const PrefixExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("PrefixExpression");
|
||||
auto opLeaf = makeLeaf(g, toString(node->op));
|
||||
g.addEdge(id, opLeaf, "Operator");
|
||||
auto rhs = emit(node->right, g);
|
||||
@ -616,8 +621,8 @@ namespace arti::lang::ast {
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const BinaryExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("BinaryExpression");
|
||||
std::string emit(const InfixExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("InfixExpression");
|
||||
auto opLeaf = makeLeaf(g, toString(node->op));
|
||||
g.addEdge(id, opLeaf, "Operator");
|
||||
auto lhs = emit(node->left, g);
|
||||
@ -676,30 +681,36 @@ namespace arti::lang::ast {
|
||||
std::string emit(const MemberAccessExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("MemberAccessExpression");
|
||||
g.addEdge(id, emit(node->object, g), "Object");
|
||||
g.addEdge(id, makeLeaf(g, node->memberName), "Member");
|
||||
g.addEdge(id, emit(node->member, g), "Member");
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const PointerAccessExprNode &node, GraphBuilder &g) {
|
||||
std::string emit(const PointerMemberAccessExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("PointerAccessExpression");
|
||||
g.addEdge(id, emit(node->object, g), "Object");
|
||||
g.addEdge(id, makeLeaf(g, node->memberName), "Member");
|
||||
g.addEdge(id, emit(node->member, g), "Member");
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const ScopeAccessExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("ScopeAccessExpression");
|
||||
g.addEdge(id, emit(node->scope, g), "Object");
|
||||
if (! node->genericParams.empty()) {
|
||||
std::string emit(const ModuleAccessExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("ModuleAccessExpression");
|
||||
g.addEdge(id, emit(node->left, g), "Scope");
|
||||
g.addEdge(id, emit(node->right, g), "Member");
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const GenericExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("GenericExpression");
|
||||
g.addEdge(id, emit(node->typeNode, g), "TypeNode");
|
||||
if (! node->genericArgs.empty()) {
|
||||
emitGroupVec(
|
||||
g,
|
||||
id,
|
||||
"GenericParams",
|
||||
node->genericParams,
|
||||
[&](const auto &p) { return emit(p, g); }
|
||||
"GenericArguments",
|
||||
node->genericArgs,
|
||||
[&](const auto &arg) { return emit(arg, g); }
|
||||
);
|
||||
}
|
||||
g.addEdge(id, makeLeaf(g, node->memberName), "Member");
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -739,23 +750,24 @@ namespace arti::lang::ast {
|
||||
[&g](const FloatLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const IntegerLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const BooleanLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const StructLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const SliceLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const ObjectLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const IdentifierExprNode &n) { return emit(n, g); },
|
||||
[&g](const UnaryExprNode &n) { return emit(n, g); },
|
||||
[&g](const BinaryExprNode &n) { return emit(n, g); },
|
||||
[&g](const PrefixExprNode &n) { return emit(n, g); },
|
||||
[&g](const InfixExprNode &n) { return emit(n, g); },
|
||||
[&g](const AssignExprNode &n) { return emit(n, g); },
|
||||
[&g](const CompoundAssignExprNode &n) { return emit(n, g); },
|
||||
[&g](const FunctionCallExprNode &n) { return emit(n, g); },
|
||||
[&g](const SliceAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const SliceRangeExprNode &n) { return emit(n, g); },
|
||||
[&g](const MemberAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const PointerAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const ScopeAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const PointerMemberAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const ModuleAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const SliceCreationExprNode &n) { return emit(n, g); },
|
||||
[&g](const SliceLengthExprNode &n) { return emit(n, g); },
|
||||
[&g](const SlicePtrExprNode &n) { return emit(n, g); },
|
||||
[&g](const ReflectionExprNode &n) { return emit(n, g); },
|
||||
[&g](const GenericExprNode &n) { return emit(n, g); },
|
||||
[&g](const TypeExprNode &n) { return emit(n, g); },
|
||||
};
|
||||
return std::visit(visitor, node);
|
||||
}
|
||||
@ -980,6 +992,7 @@ namespace arti::lang::ast {
|
||||
[&g](const DoWhileStmtNode &n) { return emit(n, g); },
|
||||
[&g](const InfLoopStmtNode &n) { return emit(n, g); },
|
||||
[&g](const ExpressionStmtNode &n) { return emit(n, g); },
|
||||
[&g](const CodeBlockStmtNode &n) { return emit(n, g); },
|
||||
};
|
||||
return std::visit(visitor, node);
|
||||
}
|
||||
@ -1008,6 +1021,12 @@ namespace arti::lang::ast {
|
||||
return std::visit(visitor, node);
|
||||
}
|
||||
|
||||
std::string emit(const TypeExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("TypeExpression");
|
||||
g.addEdge(id, emit(node->type, g), "Type");
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Public API
|
||||
|
||||
@ -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 <sstream>
|
||||
@ -23,7 +45,6 @@ namespace arti::lang::ast {
|
||||
std::string toString(const TypeNode &, std::string);
|
||||
std::string toString(const GenericTypeNode &, std::string);
|
||||
std::string toString(const IdentifierTypeNode &, std::string);
|
||||
std::string toString(const NamespacedTypeNode &, std::string);
|
||||
std::string toString(const NamespacedIdentifierNode &, std::string);
|
||||
std::string toString(const TypeExpressionNode &, std::string);
|
||||
std::string toString(const CharLtrlNode &, std::string);
|
||||
@ -32,24 +53,23 @@ namespace arti::lang::ast {
|
||||
std::string toString(const FloatLtrlNode &, std::string);
|
||||
std::string toString(const IntegerLtrlNode &, std::string);
|
||||
std::string toString(const BooleanLtrlNode &, std::string);
|
||||
std::string toString(const StructLtrlNode &, std::string);
|
||||
std::string toString(const SliceLtrlNode &, std::string);
|
||||
std::string toString(const StructLtrlNamedFieldInitNode &, std::string);
|
||||
std::string toString(const StructLtrlPositionalInitNode &, std::string);
|
||||
std::string toString(const StructLtrlNamedInitializerNode &, std::string);
|
||||
std::string toString(const StructLtrlPositionalInitializerNode&, std::string);
|
||||
std::string toString(const StructLtrlInitializerNode &, std::string);
|
||||
std::string toString(const ObjectLtrlNode &, std::string);
|
||||
std::string toString(const ObjectLtrlNamedFieldInitNode &, std::string);
|
||||
std::string toString(const ObjectLtrlNamedInitializerNode &, std::string);
|
||||
std::string toString(const ObjectLtrlPositionalInitializerNode&, std::string);
|
||||
std::string toString(const ObjectLtrlInitializerNode &, std::string);
|
||||
std::string toString(const IdentifierExprNode &, std::string);
|
||||
std::string toString(const UnaryExprNode &, std::string);
|
||||
std::string toString(const BinaryExprNode &, std::string);
|
||||
std::string toString(const PrefixExprNode &, std::string);
|
||||
std::string toString(const InfixExprNode &, std::string);
|
||||
std::string toString(const AssignExprNode &, std::string);
|
||||
std::string toString(const CompoundAssignExprNode &, std::string);
|
||||
std::string toString(const FunctionCallExprNode &, std::string);
|
||||
std::string toString(const SliceAccessExprNode &, std::string);
|
||||
std::string toString(const SliceRangeExprNode &, std::string);
|
||||
std::string toString(const MemberAccessExprNode &, std::string);
|
||||
std::string toString(const PointerAccessExprNode &, std::string);
|
||||
std::string toString(const ScopeAccessExprNode &, std::string);
|
||||
std::string toString(const PointerMemberAccessExprNode &, std::string);
|
||||
std::string toString(const GenericExprNode &, std::string);
|
||||
std::string toString(const ModuleAccessExprNode &, std::string);
|
||||
std::string toString(const ReflectionExprNode &, std::string);
|
||||
std::string toString(const SliceCreationExprNode &, std::string);
|
||||
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 DeferableNode &, std::string);
|
||||
std::string toString(const PreLoopStmtNode &, std::string);
|
||||
std::string toString(UnaryOperator op);
|
||||
std::string toString(BinaryOperator op);
|
||||
std::string toString(const TypeExprNode &, std::string);
|
||||
std::string toString(PrefixOperator op);
|
||||
std::string toString(InfixOperator op);
|
||||
std::string toString(CompoundAssignOperator op);
|
||||
|
||||
const auto StrTreeNoNode = "│ ";
|
||||
@ -104,6 +125,17 @@ namespace arti::lang::ast {
|
||||
<< toString(item, nextPrefix(prefix, isLastChild));
|
||||
}
|
||||
|
||||
void appendItemString(
|
||||
std::stringstream &ss,
|
||||
const std::string &prefix,
|
||||
const std::string &item,
|
||||
bool isLastChild
|
||||
) {
|
||||
ss << "\n"
|
||||
<< prefix << (isLastChild ? StrTreeLast : StrTreeChilds) << " "
|
||||
<< item;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void appendGroupVec(
|
||||
std::stringstream &ss,
|
||||
@ -507,12 +539,22 @@ namespace arti::lang::ast {
|
||||
if (! qls.empty()) {
|
||||
++total;
|
||||
}
|
||||
++total; // BaseType is always present
|
||||
if (! node->typeNodes.empty()) {
|
||||
++total;
|
||||
}
|
||||
int emitted = 0;
|
||||
if (! qls.empty()) {
|
||||
appendGroupLeafList(ss, prefix, "Qualifiers", qls, ++emitted == total);
|
||||
}
|
||||
appendGroupOne(ss, prefix, "BaseType", node->baseType, ++emitted == total);
|
||||
if (! node->typeNodes.empty()) {
|
||||
appendGroupVec(
|
||||
ss,
|
||||
prefix,
|
||||
"TypeNodes",
|
||||
node->typeNodes,
|
||||
++emitted == total
|
||||
);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -524,7 +566,9 @@ namespace arti::lang::ast {
|
||||
++total;
|
||||
}
|
||||
int emitted = 0;
|
||||
appendGroupOne(ss, prefix, "BaseType", node->baseType, ++emitted == total);
|
||||
|
||||
appendItemString(ss, prefix, std::format("TypeName `{}`", node->typeName), ++emitted == total);
|
||||
|
||||
if (! node->genericArgs.empty()) {
|
||||
appendGroupVec(
|
||||
ss,
|
||||
@ -538,17 +582,10 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string toString(const IdentifierTypeNode &node, std::string prefix) {
|
||||
std::ignore = node;
|
||||
std::ignore = prefix;
|
||||
std::stringstream ss;
|
||||
ss << "IdentifierType";
|
||||
appendGroupOne(ss, prefix, "TypeName", node->typeName, true);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const NamespacedTypeNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "NamespacedType";
|
||||
appendGroupOne(ss, prefix, "BaseType", node->baseType, false);
|
||||
appendGroupLeaf(ss, prefix, "TypeName", node->typeName, true);
|
||||
ss << "TypeName `" << node->typeName << "`";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -569,9 +606,6 @@ namespace arti::lang::ast {
|
||||
[padding](const IdentifierTypeNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const NamespacedTypeNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
};
|
||||
|
||||
return std::visit(visitor, node);
|
||||
@ -608,30 +642,9 @@ namespace arti::lang::ast {
|
||||
return std::format("BooleanLiteral {}", node->value ? "true" : "false");
|
||||
}
|
||||
|
||||
std::string toString(const StructLtrlNode &node, std::string prefix) {
|
||||
std::string toString(const ObjectLtrlNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "StructLiteral";
|
||||
int total = 1;
|
||||
if (node->initializer) {
|
||||
++total;
|
||||
}
|
||||
int emitted = 0;
|
||||
appendGroupOne(ss, prefix, "Type", node->type, ++emitted == total);
|
||||
if (node->initializer) {
|
||||
appendGroupOne(
|
||||
ss,
|
||||
prefix,
|
||||
"Elements",
|
||||
*node->initializer,
|
||||
++emitted == total
|
||||
);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const SliceLtrlNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "SliceLiteral";
|
||||
ss << "ObjectLiteral";
|
||||
int total = 1;
|
||||
if (node->initializer) {
|
||||
++total;
|
||||
@ -651,7 +664,7 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string
|
||||
toString(const StructLtrlNamedFieldInitNode &node, std::string prefix) {
|
||||
toString(const ObjectLtrlNamedFieldInitNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "FieldInitializer";
|
||||
appendGroupLeaf(ss, prefix, "Field", node->fieldName, false);
|
||||
@ -660,15 +673,7 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string
|
||||
toString(const StructLtrlPositionalInitNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "PositionalInitializer";
|
||||
appendGroupOne(ss, prefix, "Value", node->fieldValue, true);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
toString(const StructLtrlNamedInitializerNode &node, std::string prefix) {
|
||||
toString(const ObjectLtrlNamedInitializerNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "InitializerList";
|
||||
appendGroupVec(ss, prefix, "Elements", node->fields, true);
|
||||
@ -676,7 +681,7 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string toString(
|
||||
const StructLtrlPositionalInitializerNode &node,
|
||||
const ObjectLtrlPositionalInitializerNode &node,
|
||||
std::string prefix
|
||||
) {
|
||||
std::stringstream ss;
|
||||
@ -686,12 +691,12 @@ namespace arti::lang::ast {
|
||||
}
|
||||
|
||||
std::string
|
||||
toString(const StructLtrlInitializerNode &node, std::string padding) {
|
||||
toString(const ObjectLtrlInitializerNode &node, std::string padding) {
|
||||
auto visitor = OverloadSet{
|
||||
[padding](const StructLtrlNamedInitializerNode &node) -> std::string {
|
||||
[padding](const ObjectLtrlNamedInitializerNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const StructLtrlPositionalInitializerNode &node)
|
||||
[padding](const ObjectLtrlPositionalInitializerNode &node)
|
||||
-> std::string { return toString(node, padding); },
|
||||
};
|
||||
|
||||
@ -703,9 +708,9 @@ namespace arti::lang::ast {
|
||||
return std::format("Identifier `{}`", node->identifierName);
|
||||
}
|
||||
|
||||
std::string toString(const UnaryExprNode &node, std::string prefix) {
|
||||
std::string toString(const PrefixExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "UnaryExpression";
|
||||
ss << "PrefixExpression";
|
||||
int total = 2;
|
||||
int emitted = 0;
|
||||
appendGroupLeaf(
|
||||
@ -719,9 +724,9 @@ namespace arti::lang::ast {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const BinaryExprNode &node, std::string prefix) {
|
||||
std::string toString(const InfixExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "BinaryExpression";
|
||||
ss << "InfixExpression";
|
||||
int total = 3;
|
||||
int emitted = 0;
|
||||
appendGroupLeaf(
|
||||
@ -815,37 +820,46 @@ namespace arti::lang::ast {
|
||||
std::stringstream ss;
|
||||
ss << "MemberAccessExpression";
|
||||
appendGroupOne(ss, prefix, "Object", node->object, false);
|
||||
appendGroupLeaf(ss, prefix, "Member", node->memberName, true);
|
||||
appendGroupOne(ss, prefix, "Member", node->member, true);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const PointerAccessExprNode &node, std::string prefix) {
|
||||
std::string toString(const PointerMemberAccessExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "PointerAccessExpression";
|
||||
appendGroupOne(ss, prefix, "Object", node->object, false);
|
||||
appendGroupLeaf(ss, prefix, "Member", node->memberName, true);
|
||||
appendGroupOne(ss, prefix, "Member", node->member, true);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const ScopeAccessExprNode &node, std::string prefix) {
|
||||
std::string toString(const ModuleAccessExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "ScopeAccessExpression";
|
||||
ss << "ModuleAccessExpression";
|
||||
int total = 2;
|
||||
if (! node->genericParams.empty()) {
|
||||
int emitted = 0;
|
||||
appendGroupOne(ss, prefix, "Scope", node->left, ++emitted == total);
|
||||
appendGroupOne(ss, prefix, "Member", node->right, ++emitted == total);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const GenericExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "GenericExpression";
|
||||
int total = 1;
|
||||
if (! node->genericArgs.empty()) {
|
||||
++total;
|
||||
}
|
||||
int emitted = 0;
|
||||
appendGroupOne(ss, prefix, "Object", node->scope, ++emitted == total);
|
||||
if (! node->genericParams.empty()) {
|
||||
appendGroupOne(ss, prefix, "TypeNode", node->typeNode, ++emitted == total);
|
||||
if (! node->genericArgs.empty()) {
|
||||
appendGroupVec(
|
||||
ss,
|
||||
prefix,
|
||||
"GenericParams",
|
||||
node->genericParams,
|
||||
"GenericArguments",
|
||||
node->genericArgs,
|
||||
++emitted == total
|
||||
);
|
||||
}
|
||||
appendGroupLeaf(ss, prefix, "Member", node->memberName, ++emitted == total);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -912,19 +926,16 @@ namespace arti::lang::ast {
|
||||
[padding](const BooleanLtrlNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const StructLtrlNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const SliceLtrlNode &node) -> std::string {
|
||||
[padding](const ObjectLtrlNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const IdentifierExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const UnaryExprNode &node) -> std::string {
|
||||
[padding](const PrefixExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const BinaryExprNode &node) -> std::string {
|
||||
[padding](const InfixExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const AssignExprNode &node) -> std::string {
|
||||
@ -945,10 +956,10 @@ namespace arti::lang::ast {
|
||||
[padding](const MemberAccessExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const PointerAccessExprNode &node) -> std::string {
|
||||
[padding](const PointerMemberAccessExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const ScopeAccessExprNode &node) -> std::string {
|
||||
[padding](const ModuleAccessExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const SliceCreationExprNode &node) -> std::string {
|
||||
@ -963,6 +974,12 @@ namespace arti::lang::ast {
|
||||
[padding](const ReflectionExprNode &node) -> std::string {
|
||||
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);
|
||||
@ -1397,6 +1414,9 @@ namespace arti::lang::ast {
|
||||
[padding](const ExpressionStmtNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const CodeBlockStmtNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
}
|
||||
};
|
||||
|
||||
return std::visit(visitor, node);
|
||||
@ -1441,46 +1461,67 @@ namespace arti::lang::ast {
|
||||
return std::visit(visitor, node);
|
||||
}
|
||||
|
||||
std::string toString(UnaryOperator op) {
|
||||
using enum UnaryOperator;
|
||||
std::string toString(const TypeExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "TypeÉxpression";
|
||||
appendItem(ss, prefix, node->type, true);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(PrefixOperator op) {
|
||||
using enum PrefixOperator;
|
||||
|
||||
switch (op) {
|
||||
case Not: return "Not (!)";
|
||||
case Minus: return "Minus (-)";
|
||||
case BitNot: return "BitNot (~)";
|
||||
case Ampersand: return "Ampersand (&)";
|
||||
case Star: return "Star (*)";
|
||||
case MemPtr: return "MemPtr (&)";
|
||||
case DerefPtr: return "DerefPtr (*)";
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
std::string toString(BinaryOperator op) {
|
||||
using enum BinaryOperator;
|
||||
|
||||
std::string toString(InfixOperator op) {
|
||||
using enum InfixOperator;
|
||||
switch (op) {
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Adition: return "Adition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Addition: return "Addition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
case Assignment: return "Assignment (=)";
|
||||
case ModuleAccess: return "ModuleAccess (::)";
|
||||
case MemberAccess: return "MemberAccess (.)";
|
||||
case PointerMemberAccess: return "PointerMemberAccess (->)";
|
||||
case AdditionAssignment: return "AdditionAssignment (+=)";
|
||||
case SubstractionAssignment: return "SubstractionAssignment (-=)";
|
||||
case MultiplicationAssignment: return "MultiplicationAssignment (*=)";
|
||||
case DivisionAssignment: return "DivisionAssignment (/=)";
|
||||
case ModuloAssignment: return "ModuloAssignment (%=)";
|
||||
case BitAndAssignment: return "BitAndAssignment (&=)";
|
||||
case BitOrAssignment: return "BitOrAssignment (|=)";
|
||||
case BoolAndAssignment: return "BoolAndAssignment (&&=)";
|
||||
case BoolOrAssignment: return "BoolOrAssignment (||=)";
|
||||
case LShiftAssignment: return "LShiftAssignment (<<=)";
|
||||
case RShiftAssignment: return "RShiftAssignment (>>=)";
|
||||
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
|
||||
822
lib/src/Parser/Declarations.cpp
Normal file
822
lib/src/Parser/Declarations.cpp
Normal 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
|
||||
817
lib/src/Parser/Expressions.cpp
Normal file
817
lib/src/Parser/Expressions.cpp
Normal 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
178
lib/src/Parser/Literals.cpp
Normal 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
|
||||
292
lib/src/Parser/Operators.cpp
Normal file
292
lib/src/Parser/Operators.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
115
lib/src/Parser/Pratt.cpp
Normal 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
|
||||
1327
lib/src/Parser/Statements.cpp
Normal file
1327
lib/src/Parser/Statements.cpp
Normal file
File diff suppressed because it is too large
Load Diff
301
lib/src/Parser/Types.cpp
Normal file
301
lib/src/Parser/Types.cpp
Normal 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
|
||||
@ -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 <format>
|
||||
|
||||
@ -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 <utility>
|
||||
@ -106,22 +128,25 @@ namespace arti::lang {
|
||||
return tokensBuffer.at(n);
|
||||
}
|
||||
|
||||
Expected<Token>
|
||||
Tokenizer::peekExpect(std::size_t n, TokenV tokenType) noexcept {
|
||||
Expected<Token> Tokenizer::peekExpect(
|
||||
TokenV tokenType,
|
||||
std::string_view message,
|
||||
std::size_t n
|
||||
) noexcept {
|
||||
if (message.empty()) {
|
||||
message = toString(tokenType);
|
||||
}
|
||||
|
||||
if (tokensBuffer.size() > (n + 1)) {
|
||||
auto tokenAt = tokensBuffer.at(n);
|
||||
|
||||
if (tokenAt.value != tokenType) {
|
||||
return Unexpected<> {
|
||||
Exception{
|
||||
.line = tokenAt.line,
|
||||
.column = tokenAt.column,
|
||||
.message = std::format(
|
||||
"Expected token of type {}, got {}",
|
||||
toString(tokenType), toString(tokenAt)
|
||||
)
|
||||
}
|
||||
};
|
||||
return langException<ExceptCode::ecUnexpectedToken>(
|
||||
tokenAt.line,
|
||||
tokenAt.column,
|
||||
toString(tokenAt),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
return tokenAt;
|
||||
@ -129,7 +154,7 @@ namespace arti::lang {
|
||||
|
||||
auto token = peek(n);
|
||||
|
||||
if (!token) {
|
||||
if (! token) {
|
||||
return token;
|
||||
}
|
||||
|
||||
@ -141,22 +166,18 @@ namespace arti::lang {
|
||||
tokensBuffer.pop_back();
|
||||
tokensBuffer.push_back(*token);
|
||||
token->column += 1;
|
||||
token->strValue = std::string_view{token->strValue.begin() + 1, 1};
|
||||
token->strValue = std::string_view{ token->strValue.begin() + 1, 1 };
|
||||
tokensBuffer.push_back(*token);
|
||||
token = peekTok;
|
||||
}
|
||||
|
||||
if (token->value != tokenType) {
|
||||
return Unexpected<> {
|
||||
Exception{
|
||||
.line = token->line,
|
||||
.column = token->column,
|
||||
.message = std::format(
|
||||
"Expected token of type {}, got {}",
|
||||
toString(tokenType), toString(*token)
|
||||
)
|
||||
}
|
||||
};
|
||||
return langException<ExceptCode::ecUnexpectedToken>(
|
||||
token->line,
|
||||
token->column,
|
||||
toString(*token),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
return *token;
|
||||
|
||||
@ -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 <utility>
|
||||
|
||||
@ -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 <utility>
|
||||
|
||||
@ -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 <string>
|
||||
|
||||
@ -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 <string>
|
||||
|
||||
@ -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 <array>
|
||||
|
||||
@ -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 <array>
|
||||
|
||||
@ -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 <array>
|
||||
|
||||
@ -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 <array>
|
||||
|
||||
@ -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 <array>
|
||||
|
||||
@ -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
|
||||
|
||||
#include <random>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user