feat(parser): implement Pratt expression parsing and refactor operator types
Signed-off-by: erick-alcachofa <erick@artichoke.dev> Overhaul the expression parsing mechanism to utilize a Pratt (top-down operator precedence) parser. This change provides a more scalable and maintainable way to handle operator precedence and associativity compared to standard recursive descent. As part of this transition, the nomenclature for operators has been refined to reflect their position in the grammar (Prefix, Infix, Postfix) rather than their arity. * Renamed `UnaryOperator` and `UnaryExpression` to `PrefixOperator` and `PrefixExpression`. * Renamed `BinaryOperator` and `BinaryExpression` to `InfixOperator` and `InfixExpression`. * Renamed `ScopeAccessExpression` to `ModuleAccessExpression`. * Introduced `PostfixOperator` enum and associated logic for function calls, slicing, and reflection attributes. * Updated `toDot.cpp` and `toString.cpp` to support the new node types and renamed operators. * Added `Pratt.hpp` and `Pratt.cpp` to define `BindingPower` and map operators to their respective precedence levels. * Added `Operators.cpp` to handle token-to-operator mapping and classification (isPrefix, isInfix, isPostfix). * Refactored `Parser::parseExpression` to implement the core Pratt loop using binding power comparisons. * Moved literal parsing logic into a dedicated `Literals.cpp`. * Implemented explicit parsing methods for `Integer`, `Float`, `Char`, `String`, `Boolean`, and `Null` literals. * Added support for `this` and `_` (underscore) as identifier expressions. * **Prefix**: `!`, `-`, `~`, `&` (MemPtr), `*` (DerefPtr). * **Infix**: Arithmetic, Comparison, Bitwise, Logical, and all Compound Assignments. * **Postfix**: `()` (Call), `[]` (Slice/Access), `.#` (Slice length), `.*` (Slice pointer), and `.@` (Reflection). * **Missing Literals**: Struct literals and Array literals are not yet implemented in the new parsing flow. * **Node Specialization**: `MemberAccess`, `PointerMemberAccess`, and `ModuleAccess` currently use generic infix logic and need to be migrated to their specific AST node types. * **Error Handling**: Literal parsing (specifically `std::stold` and `std::stoul`) needs safety checks to prevent potential exceptions during conversion. * **Diagnostics**: Refine the error message for unexpected tokens in postfix expressions to explicitly list supported operators. * **Generic Ambiguity**: Generic type/function instantiation currently causes parsing conflicts with comparison operators (e.g., `Foo<T>`). This is a known issue that will be resolved by transitioning the grammar to a turbofish-style `::<...>` syntax.
This commit is contained in:
parent
30d64d9b65
commit
5762497f56
@ -50,35 +50,60 @@ namespace arti::lang::ast {
|
||||
Optional,
|
||||
};
|
||||
|
||||
enum class UnaryOperator {
|
||||
enum class PrefixOperator {
|
||||
Uninitialized,
|
||||
Not,
|
||||
Minus,
|
||||
BitNot,
|
||||
Ampersand,
|
||||
Star,
|
||||
MemPtr,
|
||||
DerefPtr,
|
||||
};
|
||||
|
||||
enum class BinaryOperator {
|
||||
enum class InfixOperator {
|
||||
Uninitialized,
|
||||
Modulo,
|
||||
Addition,
|
||||
Substraction,
|
||||
Division,
|
||||
Multiplication,
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
GreaterEqual,
|
||||
GreaterThan,
|
||||
LessEqual,
|
||||
BitAnd,
|
||||
BitXor,
|
||||
BitOr,
|
||||
GreaterEqual,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
Adition,
|
||||
Substraction,
|
||||
Multiplication,
|
||||
Division,
|
||||
Modulo,
|
||||
BoolAnd,
|
||||
BoolOr,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
Assignment,
|
||||
ModuleAccess,
|
||||
MemberAccess,
|
||||
PointerMemberAccess,
|
||||
AdditionAssignment,
|
||||
SubstractionAssignment,
|
||||
MultiplicationAssignment,
|
||||
DivisionAssignment,
|
||||
ModuloAssignment,
|
||||
BitAndAssignment,
|
||||
BitOrAssignment,
|
||||
LShiftAssignment,
|
||||
RShiftAssignment,
|
||||
BoolAndAssignment,
|
||||
BoolOrAssignment,
|
||||
};
|
||||
|
||||
enum class PostfixOperator {
|
||||
Uninitialized,
|
||||
FunctionCall,
|
||||
SliceAccess,
|
||||
SliceSize,
|
||||
PtrToSlice,
|
||||
SliceToPtr,
|
||||
Reflect,
|
||||
};
|
||||
|
||||
enum class CompoundAssignOperator {
|
||||
|
||||
@ -32,8 +32,8 @@ namespace arti::lang::ast {
|
||||
|
||||
/* Main declaration node types */
|
||||
struct IdentifierExpression;
|
||||
struct UnaryExpression;
|
||||
struct BinaryExpression;
|
||||
struct PrefixExpression;
|
||||
struct InfixExpression;
|
||||
struct AssignExpression;
|
||||
struct CompoundAssignExpression;
|
||||
struct FunctionCallExpression;
|
||||
@ -41,7 +41,7 @@ namespace arti::lang::ast {
|
||||
struct SliceRangeExpression;
|
||||
struct MemberAccessExpression;
|
||||
struct PointerAccessExpression;
|
||||
struct ScopeAccessExpression;
|
||||
struct ModuleAccessExpression;
|
||||
struct ReflectionExpression;
|
||||
struct SliceCreationExpression;
|
||||
struct SliceLengthExpression;
|
||||
@ -51,8 +51,8 @@ namespace arti::lang::ast {
|
||||
|
||||
/* Public Aliases */
|
||||
using IdentifierExprNode = Ptr<nodes::IdentifierExpression>;
|
||||
using UnaryExprNode = Ptr<nodes::UnaryExpression>;
|
||||
using BinaryExprNode = Ptr<nodes::BinaryExpression>;
|
||||
using PrefixExprNode = Ptr<nodes::PrefixExpression>;
|
||||
using InfixExprNode = Ptr<nodes::InfixExpression>;
|
||||
using AssignExprNode = Ptr<nodes::AssignExpression>;
|
||||
using CompoundAssignExprNode = Ptr<nodes::CompoundAssignExpression>;
|
||||
using FunctionCallExprNode = Ptr<nodes::FunctionCallExpression>;
|
||||
@ -60,7 +60,7 @@ namespace arti::lang::ast {
|
||||
using SliceRangeExprNode = Ptr<nodes::SliceRangeExpression>;
|
||||
using MemberAccessExprNode = Ptr<nodes::MemberAccessExpression>;
|
||||
using PointerAccessExprNode = Ptr<nodes::PointerAccessExpression>;
|
||||
using ScopeAccessExprNode = Ptr<nodes::ScopeAccessExpression>;
|
||||
using ModuleAccessExprNode = Ptr<nodes::ModuleAccessExpression>;
|
||||
using ReflectionExprNode = Ptr<nodes::ReflectionExpression>;
|
||||
using SliceCreationExprNode = Ptr<nodes::SliceCreationExpression>;
|
||||
using SliceLengthExprNode = Ptr<nodes::SliceLengthExpression>;
|
||||
@ -77,8 +77,8 @@ namespace arti::lang::ast {
|
||||
StructLtrlNode,
|
||||
SliceLtrlNode,
|
||||
IdentifierExprNode,
|
||||
UnaryExprNode,
|
||||
BinaryExprNode,
|
||||
PrefixExprNode,
|
||||
InfixExprNode,
|
||||
AssignExprNode,
|
||||
CompoundAssignExprNode,
|
||||
FunctionCallExprNode,
|
||||
@ -86,7 +86,7 @@ namespace arti::lang::ast {
|
||||
SliceRangeExprNode,
|
||||
MemberAccessExprNode,
|
||||
PointerAccessExprNode,
|
||||
ScopeAccessExprNode,
|
||||
ModuleAccessExprNode,
|
||||
SliceCreationExprNode,
|
||||
SliceLengthExprNode,
|
||||
SlicePtrExprNode,
|
||||
@ -99,16 +99,16 @@ namespace arti::lang::ast {
|
||||
String identifierName;
|
||||
};
|
||||
|
||||
struct nodes::UnaryExpression {
|
||||
struct nodes::PrefixExpression {
|
||||
SourceLocation location;
|
||||
UnaryOperator op;
|
||||
PrefixOperator op;
|
||||
ExpressionNode right;
|
||||
};
|
||||
|
||||
struct nodes::BinaryExpression {
|
||||
struct nodes::InfixExpression {
|
||||
SourceLocation location;
|
||||
|
||||
BinaryOperator op;
|
||||
InfixOperator op;
|
||||
ExpressionNode left;
|
||||
ExpressionNode right;
|
||||
};
|
||||
@ -164,7 +164,7 @@ namespace arti::lang::ast {
|
||||
ExpressionNode object;
|
||||
};
|
||||
|
||||
struct nodes::ScopeAccessExpression {
|
||||
struct nodes::ModuleAccessExpression {
|
||||
SourceLocation location;
|
||||
|
||||
String memberName;
|
||||
|
||||
@ -167,7 +167,40 @@ namespace arti::lang {
|
||||
parseExpressionStatement();
|
||||
|
||||
Expected<ast::ExpressionNode>
|
||||
parseExpression();
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@ -81,41 +81,57 @@ namespace arti::lang::ast {
|
||||
};
|
||||
|
||||
// Operator label helpers (matching AST.cpp)
|
||||
std::string toString(UnaryOperator op) {
|
||||
using enum UnaryOperator;
|
||||
std::string toString(PrefixOperator op) {
|
||||
using enum PrefixOperator;
|
||||
switch (op) {
|
||||
case Not: return "Not (!)";
|
||||
case Minus: return "Minus (-)";
|
||||
case BitNot: return "BitNot (~)";
|
||||
case Ampersand: return "Ampersand (&)";
|
||||
case Star: return "Star (*)";
|
||||
case MemPtr: return "MemPtr (&)";
|
||||
case DerefPtr: return "DerefPtr (*)";
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
std::string toString(BinaryOperator op) {
|
||||
using enum BinaryOperator;
|
||||
std::string toString(InfixOperator op) {
|
||||
using enum InfixOperator;
|
||||
switch (op) {
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Adition: return "Adition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
default: std::unreachable(); break;
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Addition: return "Addition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
case Assignment: return "Assignment (=)";
|
||||
case ModuleAccess: return "ModuleAccess (::)";
|
||||
case MemberAccess: return "MemberAccess (.)";
|
||||
case PointerMemberAccess: return "PointerMemberAccess (->)";
|
||||
case AdditionAssignment: return "AdditionAssignment (+=)";
|
||||
case SubstractionAssignment: return "SubstractionAssignment (-=)";
|
||||
case MultiplicationAssignment: return "MultiplicationAssignment (*=)";
|
||||
case DivisionAssignment: return "DivisionAssignment (/=)";
|
||||
case ModuloAssignment: return "ModuloAssignment (%=)";
|
||||
case BitAndAssignment: return "BitAndAssignment (&=)";
|
||||
case BitOrAssignment: return "BitOrAssignment (|=)";
|
||||
case BoolAndAssignment: return "BoolAndAssignment (&&=)";
|
||||
case BoolOrAssignment: return "BoolOrAssignment (||=)";
|
||||
case LShiftAssignment: return "LShiftAssignment (<<=)";
|
||||
case RShiftAssignment: return "RShiftAssignment (>>=)";
|
||||
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
@ -172,8 +188,8 @@ namespace arti::lang::ast {
|
||||
std::string emit(const StructLtrlPositionalInitializerNode&, GraphBuilder&);
|
||||
std::string emit(const StructLtrlInitializerNode &, GraphBuilder &);
|
||||
std::string emit(const IdentifierExprNode &, GraphBuilder &);
|
||||
std::string emit(const UnaryExprNode &, GraphBuilder &);
|
||||
std::string emit(const BinaryExprNode &, GraphBuilder &);
|
||||
std::string emit(const PrefixExprNode &, GraphBuilder &);
|
||||
std::string emit(const InfixExprNode &, GraphBuilder &);
|
||||
std::string emit(const AssignExprNode &, GraphBuilder &);
|
||||
std::string emit(const CompoundAssignExprNode &, GraphBuilder &);
|
||||
std::string emit(const FunctionCallExprNode &, GraphBuilder &);
|
||||
@ -181,7 +197,7 @@ namespace arti::lang::ast {
|
||||
std::string emit(const SliceRangeExprNode &, GraphBuilder &);
|
||||
std::string emit(const MemberAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const PointerAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const ScopeAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const ModuleAccessExprNode &, GraphBuilder &);
|
||||
std::string emit(const ReflectionExprNode &, GraphBuilder &);
|
||||
std::string emit(const SliceCreationExprNode &, GraphBuilder &);
|
||||
std::string emit(const SliceLengthExprNode &, GraphBuilder &);
|
||||
@ -629,8 +645,8 @@ namespace arti::lang::ast {
|
||||
return g.makeNode(std::format("Identifier `{}`", node->identifierName));
|
||||
}
|
||||
|
||||
std::string emit(const UnaryExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("UnaryExpression");
|
||||
std::string emit(const PrefixExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("PrefixExpression");
|
||||
auto opLeaf = makeLeaf(g, toString(node->op));
|
||||
g.addEdge(id, opLeaf, "Operator");
|
||||
auto rhs = emit(node->right, g);
|
||||
@ -638,8 +654,8 @@ namespace arti::lang::ast {
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const BinaryExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("BinaryExpression");
|
||||
std::string emit(const InfixExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("InfixExpression");
|
||||
auto opLeaf = makeLeaf(g, toString(node->op));
|
||||
g.addEdge(id, opLeaf, "Operator");
|
||||
auto lhs = emit(node->left, g);
|
||||
@ -709,7 +725,7 @@ namespace arti::lang::ast {
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string emit(const ScopeAccessExprNode &node, GraphBuilder &g) {
|
||||
std::string emit(const ModuleAccessExprNode &node, GraphBuilder &g) {
|
||||
auto id = g.makeNode("ScopeAccessExpression");
|
||||
g.addEdge(id, emit(node->scope, g), "Object");
|
||||
if (! node->genericParams.empty()) {
|
||||
@ -764,8 +780,8 @@ namespace arti::lang::ast {
|
||||
[&g](const StructLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const SliceLtrlNode &n) { return emit(n, g); },
|
||||
[&g](const IdentifierExprNode &n) { return emit(n, g); },
|
||||
[&g](const UnaryExprNode &n) { return emit(n, g); },
|
||||
[&g](const BinaryExprNode &n) { return emit(n, g); },
|
||||
[&g](const PrefixExprNode &n) { return emit(n, g); },
|
||||
[&g](const InfixExprNode &n) { return emit(n, g); },
|
||||
[&g](const AssignExprNode &n) { return emit(n, g); },
|
||||
[&g](const CompoundAssignExprNode &n) { return emit(n, g); },
|
||||
[&g](const FunctionCallExprNode &n) { return emit(n, g); },
|
||||
@ -773,7 +789,7 @@ namespace arti::lang::ast {
|
||||
[&g](const SliceRangeExprNode &n) { return emit(n, g); },
|
||||
[&g](const MemberAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const PointerAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const ScopeAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const ModuleAccessExprNode &n) { return emit(n, g); },
|
||||
[&g](const SliceCreationExprNode &n) { return emit(n, g); },
|
||||
[&g](const SliceLengthExprNode &n) { return emit(n, g); },
|
||||
[&g](const SlicePtrExprNode &n) { return emit(n, g); },
|
||||
|
||||
@ -62,8 +62,8 @@ namespace arti::lang::ast {
|
||||
std::string toString(const StructLtrlPositionalInitializerNode&, std::string);
|
||||
std::string toString(const StructLtrlInitializerNode &, std::string);
|
||||
std::string toString(const IdentifierExprNode &, std::string);
|
||||
std::string toString(const UnaryExprNode &, std::string);
|
||||
std::string toString(const BinaryExprNode &, std::string);
|
||||
std::string toString(const PrefixExprNode &, std::string);
|
||||
std::string toString(const InfixExprNode &, std::string);
|
||||
std::string toString(const AssignExprNode &, std::string);
|
||||
std::string toString(const CompoundAssignExprNode &, std::string);
|
||||
std::string toString(const FunctionCallExprNode &, std::string);
|
||||
@ -71,7 +71,7 @@ namespace arti::lang::ast {
|
||||
std::string toString(const SliceRangeExprNode &, std::string);
|
||||
std::string toString(const MemberAccessExprNode &, std::string);
|
||||
std::string toString(const PointerAccessExprNode &, std::string);
|
||||
std::string toString(const ScopeAccessExprNode &, std::string);
|
||||
std::string toString(const ModuleAccessExprNode &, std::string);
|
||||
std::string toString(const ReflectionExprNode &, std::string);
|
||||
std::string toString(const SliceCreationExprNode &, std::string);
|
||||
std::string toString(const SliceLengthExprNode &, std::string);
|
||||
@ -100,8 +100,8 @@ namespace arti::lang::ast {
|
||||
std::string toString(const ElseBranchNode &, std::string);
|
||||
std::string toString(const DeferableNode &, std::string);
|
||||
std::string toString(const PreLoopStmtNode &, std::string);
|
||||
std::string toString(UnaryOperator op);
|
||||
std::string toString(BinaryOperator op);
|
||||
std::string toString(PrefixOperator op);
|
||||
std::string toString(InfixOperator op);
|
||||
std::string toString(CompoundAssignOperator op);
|
||||
|
||||
const auto StrTreeNoNode = "│ ";
|
||||
@ -725,9 +725,9 @@ namespace arti::lang::ast {
|
||||
return std::format("Identifier `{}`", node->identifierName);
|
||||
}
|
||||
|
||||
std::string toString(const UnaryExprNode &node, std::string prefix) {
|
||||
std::string toString(const PrefixExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "UnaryExpression";
|
||||
ss << "PrefixExpression";
|
||||
int total = 2;
|
||||
int emitted = 0;
|
||||
appendGroupLeaf(
|
||||
@ -741,9 +741,9 @@ namespace arti::lang::ast {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const BinaryExprNode &node, std::string prefix) {
|
||||
std::string toString(const InfixExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "BinaryExpression";
|
||||
ss << "InfixExpression";
|
||||
int total = 3;
|
||||
int emitted = 0;
|
||||
appendGroupLeaf(
|
||||
@ -849,7 +849,7 @@ namespace arti::lang::ast {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string toString(const ScopeAccessExprNode &node, std::string prefix) {
|
||||
std::string toString(const ModuleAccessExprNode &node, std::string prefix) {
|
||||
std::stringstream ss;
|
||||
ss << "ScopeAccessExpression";
|
||||
int total = 2;
|
||||
@ -943,10 +943,10 @@ namespace arti::lang::ast {
|
||||
[padding](const IdentifierExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const UnaryExprNode &node) -> std::string {
|
||||
[padding](const PrefixExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const BinaryExprNode &node) -> std::string {
|
||||
[padding](const InfixExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const AssignExprNode &node) -> std::string {
|
||||
@ -970,7 +970,7 @@ namespace arti::lang::ast {
|
||||
[padding](const PointerAccessExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const ScopeAccessExprNode &node) -> std::string {
|
||||
[padding](const ModuleAccessExprNode &node) -> std::string {
|
||||
return toString(node, padding);
|
||||
},
|
||||
[padding](const SliceCreationExprNode &node) -> std::string {
|
||||
@ -1466,46 +1466,60 @@ namespace arti::lang::ast {
|
||||
return std::visit(visitor, node);
|
||||
}
|
||||
|
||||
std::string toString(UnaryOperator op) {
|
||||
using enum UnaryOperator;
|
||||
std::string toString(PrefixOperator op) {
|
||||
using enum PrefixOperator;
|
||||
|
||||
switch (op) {
|
||||
case Not: return "Not (!)";
|
||||
case Minus: return "Minus (-)";
|
||||
case BitNot: return "BitNot (~)";
|
||||
case Ampersand: return "Ampersand (&)";
|
||||
case Star: return "Star (*)";
|
||||
case MemPtr: return "MemPtr (&)";
|
||||
case DerefPtr: return "DerefPtr (*)";
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
std::string toString(BinaryOperator op) {
|
||||
using enum BinaryOperator;
|
||||
|
||||
std::string toString(InfixOperator op) {
|
||||
using enum InfixOperator;
|
||||
switch (op) {
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Adition: return "Adition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
case Equal: return "Equal (==)";
|
||||
case NotEqual: return "NotEqual (!=)";
|
||||
case GreaterThan: return "GreaterThan (>)";
|
||||
case LessThan: return "LessThan (<)";
|
||||
case GreaterEqual: return "GreaterEqual (>=)";
|
||||
case LessEqual: return "LessEqual (<=)";
|
||||
case BitAnd: return "BitAnd (&)";
|
||||
case BitXor: return "BitXor (^)";
|
||||
case BitOr: return "BitOr (|)";
|
||||
case LeftShift: return "LeftShift (<<)";
|
||||
case RightShift: return "RightShift (>>)";
|
||||
case Addition: return "Addition (+)";
|
||||
case Substraction: return "Substraction (-)";
|
||||
case Multiplication: return "Multiplication (*)";
|
||||
case Division: return "Division (/)";
|
||||
case Modulo: return "Modulo (%)";
|
||||
case BoolAnd: return "BoolAnd (&&)";
|
||||
case BoolOr: return "BoolOr (||)";
|
||||
case Assignment: return "Assignment (=)";
|
||||
case ModuleAccess: return "ModuleAccess (::)";
|
||||
case MemberAccess: return "MemberAccess (.)";
|
||||
case PointerMemberAccess: return "PointerMemberAccess (->)";
|
||||
case AdditionAssignment: return "AdditionAssignment (+=)";
|
||||
case SubstractionAssignment: return "SubstractionAssignment (-=)";
|
||||
case MultiplicationAssignment: return "MultiplicationAssignment (*=)";
|
||||
case DivisionAssignment: return "DivisionAssignment (/=)";
|
||||
case ModuloAssignment: return "ModuloAssignment (%=)";
|
||||
case BitAndAssignment: return "BitAndAssignment (&=)";
|
||||
case BitOrAssignment: return "BitOrAssignment (|=)";
|
||||
case BoolAndAssignment: return "BoolAndAssignment (&&=)";
|
||||
case BoolOrAssignment: return "BoolOrAssignment (||=)";
|
||||
case LShiftAssignment: return "LShiftAssignment (<<=)";
|
||||
case RShiftAssignment: return "RShiftAssignment (>>=)";
|
||||
|
||||
default: std::unreachable(); break;
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
|
||||
@ -21,12 +21,530 @@
|
||||
//============================================================================//
|
||||
|
||||
#include <artichoke/Parser/Parser.hpp>
|
||||
#include <artichoke/Parser/Pratt.hpp>
|
||||
|
||||
namespace arti::lang {
|
||||
|
||||
Expected<ast::ExpressionNode>
|
||||
Parser::parseExpression() {
|
||||
return {};
|
||||
Parser::parseExpression(std::uint16_t minBindingPower) {
|
||||
auto peekToken = tokenizer.peek();
|
||||
if (! peekToken) {
|
||||
return Unexpected<>{ std::move(peekToken).error() };
|
||||
}
|
||||
|
||||
bool keepParsing = true;
|
||||
ast::Optional<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 (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);
|
||||
|
||||
auto rhs = parseExpression(rbp);
|
||||
|
||||
if (! rhs) {
|
||||
return Unexpected<>{ std::move(rhs).error() };
|
||||
}
|
||||
|
||||
/* TODO: MemberAccess and PointerMemberAccess do not use their respective
|
||||
* nodes types yet */
|
||||
/* TODO: ModuleAccess do not use its respective node type yet */
|
||||
if (op == ast::InfixOperator::Assignment) {
|
||||
auto node = ast::MakeNode<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 (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 = match(TokenV::opRParen); ! close) {
|
||||
return Unexpected<>{ std::move(close).error() };
|
||||
}
|
||||
else if (close.value()) {
|
||||
stillParams = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto close = consume(TokenV::opRParen, "')'"); ! close) {
|
||||
return Unexpected<>{ std::move(close).error() };
|
||||
}
|
||||
|
||||
newNode->callee = std::move(lhs);
|
||||
|
||||
node = std::move(newNode);
|
||||
}
|
||||
else if (op == ast::PostfixOperator::SliceAccess) {
|
||||
auto idx = parseExpression();
|
||||
|
||||
if (! idx) {
|
||||
return Unexpected<>{ std::move(idx).error() };
|
||||
}
|
||||
|
||||
if (auto range = matchAndConsume(TokenV::opColon); ! range) {
|
||||
return Unexpected<>{ std::move(range).error() };
|
||||
}
|
||||
else if (range.value()) {
|
||||
auto newNode = ast::MakeNode<ast::SliceRangeExprNode>();
|
||||
|
||||
newNode->location = {
|
||||
.line = peekToken->line,
|
||||
.column = peekToken->column
|
||||
};
|
||||
|
||||
newNode->start = std::move(idx).value();
|
||||
|
||||
auto endIdx = parseExpression();
|
||||
|
||||
if (! endIdx) {
|
||||
return Unexpected<>{ std::move(endIdx).error() };
|
||||
}
|
||||
|
||||
newNode->end = std::move(endIdx).value();
|
||||
|
||||
if (auto close = consume(TokenV::opRBracket, "']'"); ! close) {
|
||||
return Unexpected<>{ std::move(close).error() };
|
||||
}
|
||||
|
||||
newNode->slice = std::move(lhs);
|
||||
|
||||
node = std::move(newNode);
|
||||
}
|
||||
else {
|
||||
auto newNode = ast::MakeNode<ast::SliceAccessExprNode>();
|
||||
|
||||
newNode->location = {
|
||||
.line = peekToken->line,
|
||||
.column = peekToken->column
|
||||
};
|
||||
|
||||
newNode->index = std::move(idx).value();
|
||||
|
||||
if (auto close = consume(TokenV::opRBracket, "']'"); ! close) {
|
||||
return Unexpected<>{ std::move(close).error() };
|
||||
}
|
||||
|
||||
newNode->slice = std::move(lhs);
|
||||
|
||||
node = std::move(newNode);
|
||||
}
|
||||
}
|
||||
else if (op == ast::PostfixOperator::SliceSize) {
|
||||
auto newNode = ast::MakeNode<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
|
||||
|
||||
@ -20,3 +20,159 @@
|
||||
// //
|
||||
//============================================================================//
|
||||
|
||||
#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
|
||||
|
||||
288
lib/src/Parser/Operators.cpp
Normal file
288
lib/src/Parser/Operators.cpp
Normal file
@ -0,0 +1,288 @@
|
||||
//============================================================================//
|
||||
// //
|
||||
// artichoke programming language //
|
||||
// //
|
||||
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
||||
// //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as published //
|
||||
// by the Free Software Foundation, either version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <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 kwNot:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isInfixOperator(TokenV tokenType) {
|
||||
switch(tokenType) {
|
||||
using enum TokenV;
|
||||
|
||||
case opDot:
|
||||
case opMod:
|
||||
case opPlus:
|
||||
case opHyphen:
|
||||
case opSlash:
|
||||
case opStar:
|
||||
case opAssign:
|
||||
case opAccess:
|
||||
case opEq:
|
||||
case opNeq:
|
||||
case opLt:
|
||||
case opGt:
|
||||
case opLtEq:
|
||||
case opGtEq:
|
||||
case opLShift:
|
||||
case opRShift:
|
||||
case opBoolAnd:
|
||||
case opBoolOr:
|
||||
case kwAnd:
|
||||
case kwOr:
|
||||
case opAnd:
|
||||
case opOr:
|
||||
case opCaret:
|
||||
case opArrow:
|
||||
case opPlusAssign:
|
||||
case opHyphenAssign:
|
||||
case opStarAssign:
|
||||
case opSlashAssign:
|
||||
case opModAssign:
|
||||
case opAndAssign:
|
||||
case opOrAssign:
|
||||
case opLShiftAssign:
|
||||
case opRShiftAssign:
|
||||
case opBoolAndAssign:
|
||||
case opBoolOrAssign:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isPostfixOperator(TokenV tokenType) {
|
||||
switch(tokenType) {
|
||||
using enum TokenV;
|
||||
|
||||
case opLParen:
|
||||
case opLBracket:
|
||||
case opSliceSize:
|
||||
case opPtrSlice:
|
||||
case opSlicePtr:
|
||||
case opReflect:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ast::PrefixOperator getPrefixOperator(TokenV tokenType) {
|
||||
using enum ast::PrefixOperator;
|
||||
|
||||
switch(tokenType) {
|
||||
using enum TokenV;
|
||||
|
||||
case opHyphen:
|
||||
return Minus;
|
||||
case kwNot:
|
||||
case opBang:
|
||||
return Not;
|
||||
case opStar:
|
||||
return DerefPtr;
|
||||
case opTilde:
|
||||
return BitNot;
|
||||
case opAnd:
|
||||
return MemPtr;
|
||||
default:
|
||||
return Uninitialized;
|
||||
}
|
||||
}
|
||||
|
||||
ast::InfixOperator getInfixOperator(TokenV tokenType) {
|
||||
using enum ast::InfixOperator;
|
||||
|
||||
switch(tokenType) {
|
||||
using enum TokenV;
|
||||
|
||||
case opMod:
|
||||
return Modulo;
|
||||
case opPlus:
|
||||
return Addition;
|
||||
case opHyphen:
|
||||
return Substraction;
|
||||
case opSlash:
|
||||
return Division;
|
||||
case opStar:
|
||||
return Multiplication;
|
||||
case opEq:
|
||||
return Equal;
|
||||
case opNeq:
|
||||
return NotEqual;
|
||||
case opLt:
|
||||
return LessThan;
|
||||
case opGt:
|
||||
return GreaterThan;
|
||||
case opLtEq:
|
||||
return LessEqual;
|
||||
case opGtEq:
|
||||
return GreaterEqual;
|
||||
case opLShift:
|
||||
return LeftShift;
|
||||
case opRShift:
|
||||
return RightShift;
|
||||
case kwAnd:
|
||||
case opBoolAnd:
|
||||
return BoolAnd;
|
||||
case kwOr:
|
||||
case opBoolOr:
|
||||
return BoolOr;
|
||||
case opAnd:
|
||||
return BitAnd;
|
||||
case opOr:
|
||||
return BitOr;
|
||||
case opCaret:
|
||||
return BitXor;
|
||||
case opAssign:
|
||||
return Assignment;
|
||||
case opAccess:
|
||||
return ModuleAccess;
|
||||
case opDot:
|
||||
return MemberAccess;
|
||||
case opArrow:
|
||||
return PointerMemberAccess;
|
||||
case opPlusAssign:
|
||||
return AdditionAssignment;
|
||||
case opHyphenAssign:
|
||||
return SubstractionAssignment;
|
||||
case opStarAssign:
|
||||
return MultiplicationAssignment;
|
||||
case opSlashAssign:
|
||||
return DivisionAssignment;
|
||||
case opModAssign:
|
||||
return ModuloAssignment;
|
||||
case opAndAssign:
|
||||
return BitAndAssignment;
|
||||
case opOrAssign:
|
||||
return BitOrAssignment;
|
||||
case opLShiftAssign:
|
||||
return LShiftAssignment;
|
||||
case opRShiftAssign:
|
||||
return RShiftAssignment;
|
||||
case opBoolAndAssign:
|
||||
return BoolAndAssignment;
|
||||
case opBoolOrAssign:
|
||||
return BoolOrAssignment;
|
||||
default:
|
||||
return Uninitialized;
|
||||
}
|
||||
}
|
||||
|
||||
ast::PostfixOperator getPostfixOperator(TokenV tokenType) {
|
||||
using enum ast::PostfixOperator;
|
||||
|
||||
switch(tokenType) {
|
||||
using enum TokenV;
|
||||
|
||||
case opLParen:
|
||||
return FunctionCall;
|
||||
case opLBracket:
|
||||
return SliceAccess;
|
||||
case opSliceSize:
|
||||
return SliceSize;
|
||||
case opPtrSlice:
|
||||
return PtrToSlice;
|
||||
case opSlicePtr:
|
||||
return SliceToPtr;
|
||||
case opReflect:
|
||||
return Reflect;
|
||||
default:
|
||||
return Uninitialized;
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompoundAssignOperator(ast::InfixOperator op) {
|
||||
|
||||
switch(op) {
|
||||
using enum ast::InfixOperator;
|
||||
|
||||
case AdditionAssignment:
|
||||
case SubstractionAssignment:
|
||||
case MultiplicationAssignment:
|
||||
case DivisionAssignment:
|
||||
case ModuloAssignment:
|
||||
case BitAndAssignment:
|
||||
case BitOrAssignment:
|
||||
case LShiftAssignment:
|
||||
case RShiftAssignment:
|
||||
case BoolAndAssignment:
|
||||
case BoolOrAssignment:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ast::CompoundAssignOperator getCompoundOperatorType(ast::InfixOperator op) {
|
||||
switch(op) {
|
||||
using enum ast::InfixOperator;
|
||||
|
||||
case AdditionAssignment:
|
||||
return ast::CompoundAssignOperator::Addition;
|
||||
case SubstractionAssignment:
|
||||
return ast::CompoundAssignOperator::Substraction;
|
||||
case MultiplicationAssignment:
|
||||
return ast::CompoundAssignOperator::Multiplication;
|
||||
case DivisionAssignment:
|
||||
return ast::CompoundAssignOperator::Division;
|
||||
case ModuloAssignment:
|
||||
return ast::CompoundAssignOperator::Modulo;
|
||||
case BitAndAssignment:
|
||||
return ast::CompoundAssignOperator::BitAnd;
|
||||
case BitOrAssignment:
|
||||
return ast::CompoundAssignOperator::BitOr;
|
||||
case LShiftAssignment:
|
||||
return ast::CompoundAssignOperator::LeftShift;
|
||||
case RShiftAssignment:
|
||||
return ast::CompoundAssignOperator::RightShift;
|
||||
case BoolAndAssignment:
|
||||
return ast::CompoundAssignOperator::BoolAnd;
|
||||
case BoolOrAssignment:
|
||||
return ast::CompoundAssignOperator::BoolOr;
|
||||
|
||||
default:
|
||||
return ast::CompoundAssignOperator::Uninitialized;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
111
lib/src/Parser/Pratt.cpp
Normal file
111
lib/src/Parser/Pratt.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
//============================================================================//
|
||||
// //
|
||||
// artichoke programming language //
|
||||
// //
|
||||
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
||||
// //
|
||||
// //
|
||||
// This program is free software: you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU Affero General Public License as published //
|
||||
// by the Free Software Foundation, either version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU Affero General Public License for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU Affero General Public License //
|
||||
// along with this program. If not, see <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:
|
||||
case ast::InfixOperator::MemberAccess:
|
||||
case ast::InfixOperator::PointerMemberAccess: return { 21, 22 };
|
||||
|
||||
// Multiplicative
|
||||
case ast::InfixOperator::Multiplication:
|
||||
case ast::InfixOperator::Division:
|
||||
case ast::InfixOperator::Modulo: return { 15, 16 };
|
||||
|
||||
// Additive
|
||||
case ast::InfixOperator::Addition:
|
||||
case ast::InfixOperator::Substraction: return { 13, 14 };
|
||||
|
||||
// Shift
|
||||
case ast::InfixOperator::LeftShift:
|
||||
case ast::InfixOperator::RightShift: return { 11, 12 };
|
||||
|
||||
// Relational
|
||||
case ast::InfixOperator::LessThan:
|
||||
case ast::InfixOperator::GreaterThan:
|
||||
case ast::InfixOperator::LessEqual:
|
||||
case ast::InfixOperator::GreaterEqual: return { 9, 10 };
|
||||
|
||||
// Equality
|
||||
case ast::InfixOperator::Equal:
|
||||
case ast::InfixOperator::NotEqual: return { 7, 8 };
|
||||
|
||||
// Bitwise
|
||||
case ast::InfixOperator::BitAnd: return { 6, 7 };
|
||||
case ast::InfixOperator::BitXor: return { 5, 6 };
|
||||
case ast::InfixOperator::BitOr: return { 4, 5 };
|
||||
|
||||
// Logical
|
||||
case ast::InfixOperator::BoolAnd: return { 3, 4 };
|
||||
case ast::InfixOperator::BoolOr: return { 1, 2 };
|
||||
|
||||
// Assignment (Right-associative: left > right)
|
||||
case ast::InfixOperator::Assignment:
|
||||
case ast::InfixOperator::AdditionAssignment:
|
||||
case ast::InfixOperator::SubstractionAssignment:
|
||||
case ast::InfixOperator::MultiplicationAssignment:
|
||||
case ast::InfixOperator::DivisionAssignment:
|
||||
case ast::InfixOperator::ModuloAssignment:
|
||||
case ast::InfixOperator::BitAndAssignment:
|
||||
case ast::InfixOperator::BitOrAssignment:
|
||||
case ast::InfixOperator::BoolAndAssignment:
|
||||
case ast::InfixOperator::BoolOrAssignment:
|
||||
case ast::InfixOperator::LShiftAssignment:
|
||||
case ast::InfixOperator::RShiftAssignment: return { 2, 1 };
|
||||
|
||||
default: return { 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
std::uint16_t postfixBindingPower(ast::PostfixOperator op) {
|
||||
switch (op) {
|
||||
// Postfix usually has the highest precedence (e.g., function calls,
|
||||
// slicing)
|
||||
case ast::PostfixOperator::FunctionCall:
|
||||
case ast::PostfixOperator::SliceAccess:
|
||||
case ast::PostfixOperator::SliceSize:
|
||||
case ast::PostfixOperator::PtrToSlice:
|
||||
case ast::PostfixOperator::SliceToPtr:
|
||||
case ast::PostfixOperator::Reflect: return 19;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace arti::lang::pratt
|
||||
Loading…
x
Reference in New Issue
Block a user