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