From 0a3971f18e74c7cb556f5aefb5b6e1da0edd9875 Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sun, 19 Oct 2025 21:54:24 -0600 Subject: [PATCH] refactor(ast): refactor AST printing to support DOT graph and improved string output Signed-off-by: erick-alcachofa This commit refactors the AST printing functionality by moving the human-readable `toString` implementation into its own file (`lib/src/Parser/AST/toString.cpp`) and introducing a new `toDot` function in `lib/src/Parser/AST/toDot.cpp` for generating Graphviz DOT format output. The `AST.hpp` header is updated to declare both the new `toDot` function and the modified `toString` function, which now uses an optional `prefix` parameter for prettier tree output. The `Token.hpp`/`Token.cpp` files are also adjusted to have `toString(const TokenV &)` return a `std::string_view`, and `toString(const Token &)` provides a cleaner string representation using only the token's value. --- lib/include/artichoke/Parser/AST/AST.hpp | 3 + lib/include/artichoke/Tokenizer/Token.hpp | 2 +- lib/src/Parser/AST/toDot.cpp | 1048 ++++++++++++++ lib/src/Parser/AST/toString.cpp | 1508 +++++++++++++++++++++ lib/src/Tokenizer/Token.cpp | 188 +-- 5 files changed, 2654 insertions(+), 95 deletions(-) create mode 100644 lib/src/Parser/AST/toDot.cpp create mode 100644 lib/src/Parser/AST/toString.cpp diff --git a/lib/include/artichoke/Parser/AST/AST.hpp b/lib/include/artichoke/Parser/AST/AST.hpp index c44d640..d45cc33 100644 --- a/lib/include/artichoke/Parser/AST/AST.hpp +++ b/lib/include/artichoke/Parser/AST/AST.hpp @@ -28,4 +28,7 @@ namespace arti::lang::ast { return std::make_unique(); } + std::string toDot(const AST &tree); + std::string toString(const AST &tree, std::string prefix = ""); + } // namespace arti::lang::ast diff --git a/lib/include/artichoke/Tokenizer/Token.hpp b/lib/include/artichoke/Tokenizer/Token.hpp index 207f3b8..5192d8a 100644 --- a/lib/include/artichoke/Tokenizer/Token.hpp +++ b/lib/include/artichoke/Tokenizer/Token.hpp @@ -111,6 +111,6 @@ namespace arti::lang { }; std::string toString(const Token &value); - std::string toString(const TokenV &value); + std::string_view toString(const TokenV &value); } // namespace arti::lang diff --git a/lib/src/Parser/AST/toDot.cpp b/lib/src/Parser/AST/toDot.cpp new file mode 100644 index 0000000..4be334a --- /dev/null +++ b/lib/src/Parser/AST/toDot.cpp @@ -0,0 +1,1048 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace arti::lang::ast { + + namespace { + // Small DOT graph builder + struct GraphBuilder { + std::size_t nextId{ 0 }; + std::stringstream nodes; + std::stringstream edges; + + static std::string escape(std::string_view s) { + std::string out; + out.reserve(s.size() + 16); + for (char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: out += c; break; + } + } + return out; + } + + std::string + makeNode(const std::string &label, std::string_view attrs = {}) { + const std::string id = std::format("n{}", nextId++); + nodes << " " << id << " [label=\"" << escape(label) << "\""; + if (! attrs.empty()) { + nodes << " " << attrs; + } + nodes << "];\n"; + return id; + } + + void addEdge( + const std::string &from, + const std::string &to, + std::string_view label + ) { + edges << " " << from << " -> " << to; + if (! label.empty()) { + edges << " [label=\"" << escape(label) << "\"]"; + } + edges << ";\n"; + } + }; + + // Operator label helpers (matching AST.cpp) + std::string toString(UnaryOperator op) { + using enum UnaryOperator; + switch (op) { + case Not: return "Not (!)"; + case Minus: return "Minus (-)"; + case BitNot: return "BitNot (~)"; + case Ampersand: return "Ampersand (&)"; + case Star: return "Star (*)"; + default: std::unreachable(); break; + } + std::unreachable(); + } + + std::string toString(BinaryOperator op) { + using enum BinaryOperator; + switch (op) { + case Equal: return "Equal (==)"; + case NotEqual: return "NotEqual (!=)"; + case GreaterThan: return "GreaterThan (>)"; + case LessThan: return "LessThan (<)"; + case GreaterEqual: return "GreaterEqual (>=)"; + case LessEqual: return "LessEqual (<=)"; + case BitAnd: return "BitAnd (&)"; + case BitXor: return "BitXor (^)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case Adition: return "Adition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + default: std::unreachable(); break; + } + std::unreachable(); + } + + std::string toString(CompoundAssignOperator op) { + using enum CompoundAssignOperator; + switch (op) { + case Addition: return "Addition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BitAnd: return "BitAnd (&)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + default: std::unreachable(); break; + } + std::unreachable(); + } + + // Forward declarations: one emitter per node kind, returning the node id. + std::string emit(const TopLevelDeclNode &, GraphBuilder &); + std::string emit(const ModuleDeclNode &, GraphBuilder &); + std::string emit(const StructDeclNode &, GraphBuilder &); + std::string emit(const EnumDeclNode &, GraphBuilder &); + std::string emit(const FunctionDeclNode &, GraphBuilder &); + std::string emit(const ImportDeclNode &, GraphBuilder &); + std::string emit(const AliasDeclNode &, GraphBuilder &); + std::string emit(const EnumMemberNode &, GraphBuilder &); + std::string emit(const StructMemberNode &, GraphBuilder &); + std::string emit(const GenericParamNode &, GraphBuilder &); + std::string emit(const FunctionParamNode &, GraphBuilder &); + std::string emit(const ModuleInnerDeclNode &, GraphBuilder &); + std::string emit(const TypeNode &, GraphBuilder &); + std::string emit(const GenericTypeNode &, GraphBuilder &); + std::string emit(const IdentifierTypeNode &, GraphBuilder &); + std::string emit(const NamespacedTypeNode &, GraphBuilder &); + std::string emit(const NamespacedIdentifierNode &, GraphBuilder &); + std::string emit(const TypeExpressionNode &, GraphBuilder &); + std::string emit(const CharLtrlNode &, GraphBuilder &); + std::string emit(const NullLtrlNode &, GraphBuilder &); + std::string emit(const StringLtrlNode &, GraphBuilder &); + std::string emit(const FloatLtrlNode &, GraphBuilder &); + std::string emit(const IntegerLtrlNode &, GraphBuilder &); + std::string emit(const BooleanLtrlNode &, GraphBuilder &); + std::string emit(const StructLtrlNode &, GraphBuilder &); + std::string emit(const SliceLtrlNode &, GraphBuilder &); + std::string emit(const StructLtrlNamedFieldInitNode &, GraphBuilder &); + std::string emit(const StructLtrlPositionalInitNode &, GraphBuilder &); + std::string emit(const StructLtrlNamedInitializerNode &, GraphBuilder &); + std::string emit(const StructLtrlPositionalInitializerNode&, GraphBuilder&); + std::string emit(const StructLtrlInitializerNode &, GraphBuilder &); + std::string emit(const IdentifierExprNode &, GraphBuilder &); + std::string emit(const UnaryExprNode &, GraphBuilder &); + std::string emit(const BinaryExprNode &, GraphBuilder &); + std::string emit(const AssignExprNode &, GraphBuilder &); + std::string emit(const CompoundAssignExprNode &, GraphBuilder &); + std::string emit(const FunctionCallExprNode &, GraphBuilder &); + std::string emit(const SliceAccessExprNode &, GraphBuilder &); + std::string emit(const SliceRangeExprNode &, GraphBuilder &); + std::string emit(const MemberAccessExprNode &, GraphBuilder &); + std::string emit(const PointerAccessExprNode &, GraphBuilder &); + std::string emit(const ScopeAccessExprNode &, GraphBuilder &); + std::string emit(const ReflectionExprNode &, GraphBuilder &); + std::string emit(const SliceCreationExprNode &, GraphBuilder &); + std::string emit(const SliceLengthExprNode &, GraphBuilder &); + std::string emit(const SlicePtrExprNode &, GraphBuilder &); + std::string emit(const ExpressionNode &, GraphBuilder &); + std::string emit(const CodeBlockStmtNode &, GraphBuilder &); + std::string emit(const VariableStmtNode &, GraphBuilder &); + std::string emit(const IfStmtNode &, GraphBuilder &); + std::string emit(const ElseStmtNode &, GraphBuilder &); + std::string emit(const DeferStmtNode &, GraphBuilder &); + std::string emit(const ErrDeferStmtNode &, GraphBuilder &); + std::string emit(const ReturnStmtNode &, GraphBuilder &); + std::string emit(const BreakStmtNode &, GraphBuilder &); + std::string emit(const ContinueStmtNode &, GraphBuilder &); + std::string emit(const MatchStmtNode &, GraphBuilder &); + std::string emit(const SwitchStmtNode &, GraphBuilder &); + std::string emit(const CForStmtNode &, GraphBuilder &); + std::string emit(const RangeForStmtNode &, GraphBuilder &); + std::string emit(const WhileStmtNode &, GraphBuilder &); + std::string emit(const DoWhileStmtNode &, GraphBuilder &); + std::string emit(const InfLoopStmtNode &, GraphBuilder &); + std::string emit(const ExpressionStmtNode &, GraphBuilder &); + std::string emit(const MatchCaseNode &, GraphBuilder &); + std::string emit(const SwitchCaseNode &, GraphBuilder &); + std::string emit(const StatementNode &, GraphBuilder &); + std::string emit(const ElseBranchNode &, GraphBuilder &); + std::string emit(const DeferableNode &, GraphBuilder &); + std::string emit(const PreLoopStmtNode &, GraphBuilder &); + + // Helpers for making leaf nodes with backticked values + inline std::string makeLeaf(GraphBuilder &g, std::string_view value) { + return g.makeNode(std::format("`{}`", value)); + } + + // Group helper: Parent -> Group(title) -> [i] -> child(i) + template + inline void emitGroupVec( + GraphBuilder &g, + const std::string &parentId, + std::string_view title, + const VecT &items, + EmitFn emitFn + ) { + if (items.empty()) { + return; + } + auto groupId = g.makeNode(std::string(title)); + g.addEdge(parentId, groupId, ""); + for (std::size_t i = 0; i < items.size(); ++i) { + auto childId = emitFn(items[i]); + g.addEdge(groupId, childId, std::format("[{}]", i)); + } + } + + std::string namespacedIdentToString(const NamespacedIdentifierNode &node) { + auto joined = node->identParts | + std::views::join_with(std::string_view{ "::" }) | + std::ranges::to(); + return std::format("`{}`", joined); + } + + // Emitters + + // Top-level AST -> root and children + // (public entry point declared below, here we only emit children) + // Declarations + std::string emit(const TopLevelDeclNode &tlDecl, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const ModuleDeclNode &node) { return emit(node, g); }, + [&g](const StructDeclNode &node) { return emit(node, g); }, + [&g](const EnumDeclNode &node) { return emit(node, g); }, + [&g](const FunctionDeclNode &node) { return emit(node, g); }, + [&g](const ImportDeclNode &node) { return emit(node, g); }, + [&g](const AliasDeclNode &node) { return emit(node, g); }, + }; + return std::visit(visitor, tlDecl); + } + + std::string emit(const ModuleDeclNode &node, GraphBuilder &g) { + auto label = std::format( + "Module {} {}", + namespacedIdentToString(node->name), + node->isExported ? "[exported]" : "" + ); + auto id = g.makeNode(label); + + if (! node->aliasDeclarations.empty()) { + emitGroupVec( + g, + id, + "AliasedTypes", + node->aliasDeclarations, + [&](const auto &a) { return emit(a, g); } + ); + } + if (! node->innerDeclarations.empty()) { + emitGroupVec( + g, + id, + "InnerDeclarations", + node->innerDeclarations, + [&](const auto &d) { return emit(d, g); } + ); + } + if (! node->childModules.empty()) { + emitGroupVec( + g, + id, + "ChildModules", + node->childModules, + [&](const auto &m) { return emit(m, g); } + ); + } + return id; + } + + std::string emit(const StructDeclNode &node, GraphBuilder &g) { + auto id = g.makeNode( + std::format( + "Struct `{}` {}", + node->name, + node->isExported ? "[exported]" : "" + ) + ); + if (! node->genericParams.empty()) { + emitGroupVec( + g, + id, + "GenericParams", + node->genericParams, + [&](const auto &p) { return emit(p, g); } + ); + } + if (! node->structMembers.empty()) { + emitGroupVec( + g, + id, + "FieldMembers", + node->structMembers, + [&](const auto &m) { return emit(m, g); } + ); + } + return id; + } + + std::string emit(const EnumDeclNode &node, GraphBuilder &g) { + auto id = g.makeNode( + std::format( + "Enum `{}` {}", + node->name, + node->isExported ? "[exported]" : "" + ) + ); + if (! node->genericParams.empty()) { + emitGroupVec( + g, + id, + "GenericParams", + node->genericParams, + [&](const auto &p) { return emit(p, g); } + ); + } + if (! node->enumMembers.empty()) { + emitGroupVec( + g, + id, + "EnumValues", + node->enumMembers, + [&](const auto &m) { return emit(m, g); } + ); + } + return id; + } + + std::string emit(const FunctionDeclNode &node, GraphBuilder &g) { + auto id = g.makeNode( + std::format( + "Function `{}` {}", + node->name, + node->isExported ? "[exported]" : "" + ) + ); + if (node->returnType) { + auto cid = emit(*node->returnType, g); + g.addEdge(id, cid, "ReturnType"); + } + if (! node->genericParams.empty()) { + emitGroupVec( + g, + id, + "GenericParams", + node->genericParams, + [&](const auto &p) { return emit(p, g); } + ); + } + if (! node->functionParams.empty()) { + emitGroupVec( + g, + id, + "FunctionParams", + node->functionParams, + [&](const auto &p) { return emit(p, g); } + ); + } + { + auto cid = emit(node->functionBody, g); + g.addEdge(id, cid, "FunctionBody"); + } + return id; + } + + std::string emit(const ImportDeclNode &node, GraphBuilder &g) { + auto id = g.makeNode( + std::format( + "Import {}{}", + namespacedIdentToString(node->importTarget), + node->importAll ? " [*]" : "" + ) + ); + return id; + } + + std::string emit(const AliasDeclNode &node, GraphBuilder &g) { + auto id = g.makeNode(std::format("Alias `{}`", node->alias)); + auto cid = emit(node->target, g); + g.addEdge(id, cid, "AliasedType"); + return id; + } + + std::string emit(const EnumMemberNode &node, GraphBuilder &g) { + auto id = g.makeNode(std::format("EnumMember `{}`", node->name)); + if (node->type) { + auto cid = emit(*node->type, g); + g.addEdge(id, cid, "StorageType"); + } + return id; + } + + std::string emit(const StructMemberNode &node, GraphBuilder &g) { + auto id = g.makeNode(std::format("StructMember `{}`", node->name)); + auto cid = emit(node->type, g); + g.addEdge(id, cid, "Type"); + return id; + } + + std::string emit(const GenericParamNode &node, GraphBuilder &g) { + return g.makeNode(std::format("typename `{}`", node->name)); + } + + std::string emit(const FunctionParamNode &node, GraphBuilder &g) { + std::stringstream ls; + if (node->isThis) { + ls << "`This`"; + } + else { + ls << std::format("Param `{}`", node->name); + } + auto id = g.makeNode(ls.str()); + auto cid = emit(node->type, g); + g.addEdge(id, cid, "Type"); + return id; + } + + std::string emit(const ModuleInnerDeclNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const StructDeclNode &n) { return emit(n, g); }, + [&g](const EnumDeclNode &n) { return emit(n, g); }, + [&g](const FunctionDeclNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + // Types + std::string emit(const TypeNode &node, GraphBuilder &g) { + auto id = g.makeNode("Type"); + // Qualifiers + { + std::vector qls; + qls.reserve(node->qualifiers.size()); + for (const auto &q : node->qualifiers) { + switch (q) { + case TypeQualifier::Pointer: qls.emplace_back("Pointer (*)"); break; + case TypeQualifier::Slice: qls.emplace_back("Slice ([])"); break; + case TypeQualifier::Mutable: qls.emplace_back("Mutable ($)"); break; + case TypeQualifier::Optional: + qls.emplace_back("Optional (?)"); + break; + default: break; + } + } + if (! qls.empty()) { + emitGroupVec(g, id, "Qualifiers", qls, [&](const auto &s) { + return makeLeaf(g, s); + }); + } + } + auto baseId = emit(node->baseType, g); + g.addEdge(id, baseId, "BaseType"); + return id; + } + + std::string emit(const GenericTypeNode &node, GraphBuilder &g) { + auto id = g.makeNode("GenericType"); + auto baseId = emit(node->baseType, g); + g.addEdge(id, baseId, "BaseType"); + if (! node->genericArgs.empty()) { + emitGroupVec( + g, + id, + "GenericArgs", + node->genericArgs, + [&](const auto &arg) { return emit(arg, g); } + ); + } + return id; + } + + std::string emit(const IdentifierTypeNode &node, GraphBuilder &g) { + auto id = g.makeNode("IdentifierType"); + auto cid = emit(node->typeName, g); + g.addEdge(id, cid, "TypeName"); + return id; + } + + std::string emit(const NamespacedTypeNode &node, GraphBuilder &g) { + auto id = g.makeNode("NamespacedType"); + auto baseId = emit(node->baseType, g); + g.addEdge(id, baseId, "BaseType"); + auto leaf = makeLeaf(g, node->typeName); + g.addEdge(id, leaf, "TypeName"); + return id; + } + + std::string emit(const NamespacedIdentifierNode &node, GraphBuilder &g) { + return g.makeNode(namespacedIdentToString(node)); + } + + std::string emit(const TypeExpressionNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const GenericTypeNode &n) { return emit(n, g); }, + [&g](const IdentifierTypeNode &n) { return emit(n, g); }, + [&g](const NamespacedTypeNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + // Literals + std::string emit(const CharLtrlNode &node, GraphBuilder &g) { + return g.makeNode(std::format("CharLiteral '{}'", node->value)); + } + std::string emit(const NullLtrlNode &node, GraphBuilder &g) { + std::ignore = node; + return g.makeNode("NullLiteral 'null'"); + } + std::string emit(const StringLtrlNode &node, GraphBuilder &g) { + return g.makeNode(std::format("StringLiteral \"{}\"", node->value)); + } + std::string emit(const FloatLtrlNode &node, GraphBuilder &g) { + return g.makeNode(std::format("FloatLiteral {}", node->value)); + } + std::string emit(const IntegerLtrlNode &node, GraphBuilder &g) { + return g.makeNode(std::format("IntegerLiteral {}", node->value)); + } + std::string emit(const BooleanLtrlNode &node, GraphBuilder &g) { + return g.makeNode( + std::format("BooleanLiteral {}", node->value ? "true" : "false") + ); + } + + // Struct/Slice literals and initializers + std::string emit(const StructLtrlNode &node, GraphBuilder &g) { + auto id = g.makeNode("StructLiteral"); + auto t = emit(node->type, g); + g.addEdge(id, t, "Type"); + if (node->initializer) { + auto cid = emit(*node->initializer, g); + g.addEdge(id, cid, "Elements"); + } + return id; + } + + std::string emit(const SliceLtrlNode &node, GraphBuilder &g) { + auto id = g.makeNode("SliceLiteral"); + 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 StructLtrlNamedFieldInitNode &node, GraphBuilder &g) { + auto id = g.makeNode("FieldInitializer"); + auto leaf = makeLeaf(g, node->fieldName); + g.addEdge(id, leaf, "Field"); + auto val = emit(node->fieldValue, g); + g.addEdge(id, val, "Value"); + return id; + } + + std::string + emit(const StructLtrlPositionalInitNode &node, GraphBuilder &g) { + auto id = g.makeNode("PositionalInitializer"); + auto val = emit(node->fieldValue, g); + g.addEdge(id, val, "Value"); + return id; + } + + std::string + emit(const StructLtrlNamedInitializerNode &node, GraphBuilder &g) { + auto id = g.makeNode("InitializerList"); + if (! node->fields.empty()) { + emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) { + return emit(f, g); + }); + } + return id; + } + + std::string + emit(const StructLtrlPositionalInitializerNode &node, GraphBuilder &g) { + auto id = g.makeNode("InitializerList"); + if (! node->fields.empty()) { + emitGroupVec(g, id, "Elements", node->fields, [&](const auto &f) { + return emit(f, g); + }); + } + return id; + } + + std::string emit(const StructLtrlInitializerNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const StructLtrlNamedInitializerNode &n) { return emit(n, g); }, + [&g](const StructLtrlPositionalInitializerNode &n) { + return emit(n, g); + }, + }; + return std::visit(visitor, node); + } + + // Expressions + std::string emit(const IdentifierExprNode &node, GraphBuilder &g) { + return g.makeNode(std::format("Identifier `{}`", node->identifierName)); + } + + std::string emit(const UnaryExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("UnaryExpression"); + auto opLeaf = makeLeaf(g, toString(node->op)); + g.addEdge(id, opLeaf, "Operator"); + auto rhs = emit(node->right, g); + g.addEdge(id, rhs, "Operand"); + return id; + } + + std::string emit(const BinaryExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("BinaryExpression"); + auto opLeaf = makeLeaf(g, toString(node->op)); + g.addEdge(id, opLeaf, "Operator"); + auto lhs = emit(node->left, g); + g.addEdge(id, lhs, "Left"); + auto rhs = emit(node->right, g); + g.addEdge(id, rhs, "Right"); + return id; + } + + std::string emit(const AssignExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("AssignExpression"); + g.addEdge(id, emit(node->left, g), "Left"); + g.addEdge(id, emit(node->right, g), "Right"); + return id; + } + + std::string emit(const CompoundAssignExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("CompoundAssignExpression"); + auto opLeaf = makeLeaf(g, toString(node->op)); + g.addEdge(id, opLeaf, "Operator"); + g.addEdge(id, emit(node->left, g), "Left"); + g.addEdge(id, emit(node->right, g), "Right"); + return id; + } + + std::string emit(const FunctionCallExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("FunctionCallExpression"); + g.addEdge(id, emit(node->callee, g), "Callee"); + if (! node->arguments.empty()) { + emitGroupVec(g, id, "Arguments", node->arguments, [&](const auto &arg) { + return emit(arg, g); + }); + } + return id; + } + + std::string emit(const SliceAccessExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("SliceAccessExpression"); + g.addEdge(id, emit(node->slice, g), "Slice"); + g.addEdge(id, emit(node->index, g), "Index"); + return id; + } + + std::string emit(const SliceRangeExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("SliceRangeExpression"); + g.addEdge(id, emit(node->slice, g), "Slice"); + if (node->start) { + g.addEdge(id, emit(*node->start, g), "Start"); + } + if (node->end) { + g.addEdge(id, emit(*node->end, g), "End"); + } + return id; + } + + std::string emit(const MemberAccessExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("MemberAccessExpression"); + g.addEdge(id, emit(node->object, g), "Object"); + g.addEdge(id, makeLeaf(g, node->memberName), "Member"); + return id; + } + + std::string emit(const PointerAccessExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("PointerAccessExpression"); + g.addEdge(id, emit(node->object, g), "Object"); + g.addEdge(id, makeLeaf(g, node->memberName), "Member"); + return id; + } + + std::string emit(const ScopeAccessExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("ScopeAccessExpression"); + g.addEdge(id, emit(node->scope, g), "Object"); + if (! node->genericParams.empty()) { + emitGroupVec( + g, + id, + "GenericParams", + node->genericParams, + [&](const auto &p) { return emit(p, g); } + ); + } + g.addEdge(id, makeLeaf(g, node->memberName), "Member"); + return id; + } + + std::string emit(const ReflectionExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("ReflectionExpression"); + g.addEdge(id, emit(node->object, g), "Object"); + if (node->attribute) { + g.addEdge(id, makeLeaf(g, *node->attribute), "Attribute"); + } + return id; + } + + std::string emit(const SliceCreationExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("SliceCreationExpression"); + g.addEdge(id, emit(node->object, g), "Object"); + g.addEdge(id, emit(node->length, g), "Length"); + return id; + } + + std::string emit(const SliceLengthExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("SliceLengthExpression"); + g.addEdge(id, emit(node->object, g), "Object"); + return id; + } + + std::string emit(const SlicePtrExprNode &node, GraphBuilder &g) { + auto id = g.makeNode("SlicePtrExpression"); + g.addEdge(id, emit(node->object, g), "Object"); + return id; + } + + std::string emit(const ExpressionNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const CharLtrlNode &n) { return emit(n, g); }, + [&g](const NullLtrlNode &n) { return emit(n, g); }, + [&g](const StringLtrlNode &n) { return emit(n, g); }, + [&g](const FloatLtrlNode &n) { return emit(n, g); }, + [&g](const IntegerLtrlNode &n) { return emit(n, g); }, + [&g](const BooleanLtrlNode &n) { return emit(n, g); }, + [&g](const StructLtrlNode &n) { return emit(n, g); }, + [&g](const SliceLtrlNode &n) { return emit(n, g); }, + [&g](const IdentifierExprNode &n) { return emit(n, g); }, + [&g](const UnaryExprNode &n) { return emit(n, g); }, + [&g](const BinaryExprNode &n) { return emit(n, g); }, + [&g](const AssignExprNode &n) { return emit(n, g); }, + [&g](const CompoundAssignExprNode &n) { return emit(n, g); }, + [&g](const FunctionCallExprNode &n) { return emit(n, g); }, + [&g](const SliceAccessExprNode &n) { return emit(n, g); }, + [&g](const SliceRangeExprNode &n) { return emit(n, g); }, + [&g](const MemberAccessExprNode &n) { return emit(n, g); }, + [&g](const PointerAccessExprNode &n) { return emit(n, g); }, + [&g](const ScopeAccessExprNode &n) { return emit(n, g); }, + [&g](const SliceCreationExprNode &n) { return emit(n, g); }, + [&g](const SliceLengthExprNode &n) { return emit(n, g); }, + [&g](const SlicePtrExprNode &n) { return emit(n, g); }, + [&g](const ReflectionExprNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + // Statements + std::string emit(const CodeBlockStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("CodeBlock"); + if (! node->statements.empty()) { + emitGroupVec(g, id, "Statements", node->statements, [&](const auto &s) { + return emit(s, g); + }); + } + return id; + } + + std::string emit(const VariableStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("VariableDeclaration"); + g.addEdge(id, makeLeaf(g, node->name), "Name"); + g.addEdge( + id, + makeLeaf(g, node->mutability == Mutability::Mutable ? "let" : "def"), + "Mutability" + ); + if (node->type) { + g.addEdge(id, emit(*node->type, g), "Type"); + } + if (node->initializer) { + g.addEdge(id, emit(*node->initializer, g), "Initializer"); + } + return id; + } + + std::string emit(const IfStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("IfStatement"); + if (node->unwrappedVar) { + g.addEdge(id, makeLeaf(g, *node->unwrappedVar), "UnwrappedVar"); + } + g.addEdge(id, emit(node->condition, g), "Condition"); + g.addEdge(id, emit(node->body, g), "Body"); + if (node->elseBranch) { + g.addEdge(id, emit(*node->elseBranch, g), "ElseBranch"); + } + return id; + } + + std::string emit(const ElseStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("ElseStatement"); + if (node->unwrappedVar) { + g.addEdge(id, makeLeaf(g, *node->unwrappedVar), "UnwrappedVar"); + } + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const DeferStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("DeferStatement"); + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const ErrDeferStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("ErrDeferStatement"); + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const ReturnStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("ReturnStatement"); + if (node->value) { + g.addEdge(id, emit(*node->value, g), "Expression"); + } + return id; + } + + std::string emit(const BreakStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("BreakStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + return id; + } + + std::string emit(const ContinueStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("ContinueStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + return id; + } + + std::string emit(const MatchStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("MatchStatement"); + g.addEdge(id, emit(node->value, g), "Value"); + if (! node->matchCases.empty()) { + emitGroupVec(g, id, "Cases", node->matchCases, [&](const auto &c) { + return emit(c, g); + }); + } + if (node->defaultCase) { + g.addEdge(id, emit(*node->defaultCase, g), "DefaultCase"); + } + return id; + } + + std::string emit(const SwitchStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("SwitchStatement"); + g.addEdge(id, emit(node->value, g), "Value"); + if (! node->switchCases.empty()) { + emitGroupVec(g, id, "Cases", node->switchCases, [&](const auto &c) { + return emit(c, g); + }); + } + if (node->defaultCase) { + g.addEdge(id, emit(*node->defaultCase, g), "DefaultCase"); + } + return id; + } + + std::string emit(const CForStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("CForStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + if (node->preLoop) { + g.addEdge(id, emit(*node->preLoop, g), "PreLoop"); + } + g.addEdge(id, emit(node->condition, g), "Condition"); + if (node->postLoop) { + g.addEdge(id, emit(*node->postLoop, g), "PostLoop"); + } + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const RangeForStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("ForRangeStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + std::string varLabel = node->varName; + if (node->varMutability == Mutability::Mutable) { + varLabel += " [mut]"; + } + g.addEdge(id, makeLeaf(g, varLabel), "Variable"); + g.addEdge(id, emit(node->range, g), "Range"); + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const WhileStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("WhileStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + if (node->unwrappedVar) { + g.addEdge(id, makeLeaf(g, *node->unwrappedVar), "UnwrappedVar"); + } + g.addEdge(id, emit(node->condition, g), "Condition"); + g.addEdge(id, emit(node->body, g), "Body"); + if (node->elseBranch) { + g.addEdge(id, emit(*node->elseBranch, g), "ElseBranch"); + } + return id; + } + + std::string emit(const DoWhileStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("DoWhileStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + g.addEdge(id, emit(node->condition, g), "Condition"); + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const InfLoopStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("InfLoopStatement"); + if (node->label) { + g.addEdge(id, makeLeaf(g, *node->label), "Label"); + } + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const ExpressionStmtNode &node, GraphBuilder &g) { + auto id = g.makeNode("ExpressionStatement"); + g.addEdge(id, emit(node->expression, g), "Expression"); + return id; + } + + std::string emit(const MatchCaseNode &node, GraphBuilder &g) { + auto id = g.makeNode("MatchCase"); + if (node->unwrappedVar) { + g.addEdge(id, makeLeaf(g, *node->unwrappedVar), "UnwrappedVar"); + } + g.addEdge(id, emit(node->matchType, g), "Type"); + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const SwitchCaseNode &node, GraphBuilder &g) { + auto id = g.makeNode("SwitchCase"); + g.addEdge(id, emit(node->matchExpr, g), "Matcher"); + g.addEdge(id, emit(node->body, g), "Body"); + return id; + } + + std::string emit(const StatementNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const VariableStmtNode &n) { return emit(n, g); }, + [&g](const IfStmtNode &n) { return emit(n, g); }, + [&g](const DeferStmtNode &n) { return emit(n, g); }, + [&g](const ErrDeferStmtNode &n) { return emit(n, g); }, + [&g](const ReturnStmtNode &n) { return emit(n, g); }, + [&g](const BreakStmtNode &n) { return emit(n, g); }, + [&g](const ContinueStmtNode &n) { return emit(n, g); }, + [&g](const MatchStmtNode &n) { return emit(n, g); }, + [&g](const SwitchStmtNode &n) { return emit(n, g); }, + [&g](const CForStmtNode &n) { return emit(n, g); }, + [&g](const RangeForStmtNode &n) { return emit(n, g); }, + [&g](const WhileStmtNode &n) { return emit(n, g); }, + [&g](const DoWhileStmtNode &n) { return emit(n, g); }, + [&g](const InfLoopStmtNode &n) { return emit(n, g); }, + [&g](const ExpressionStmtNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + std::string emit(const ElseBranchNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const ElseStmtNode &n) { return emit(n, g); }, + [&g](const IfStmtNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + std::string emit(const DeferableNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const ExpressionStmtNode &n) { return emit(n, g); }, + [&g](const CodeBlockStmtNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + std::string emit(const PreLoopStmtNode &node, GraphBuilder &g) { + auto visitor = OverloadSet{ + [&g](const VariableStmtNode &n) { return emit(n, g); }, + [&g](const ExpressionStmtNode &n) { return emit(n, g); }, + }; + return std::visit(visitor, node); + } + + } // namespace + + // Public API + std::string toDot(const AST &tree) { + GraphBuilder g; + + // Root node + auto root = g.makeNode(std::format("CompilationUnit `{}`", tree->unitName)); + + // Declarations + if (! tree->declarations.empty()) { + emitGroupVec( + g, + root, + "Declarations", + tree->declarations, + [&](const auto &decl) { return emit(decl, g); } + ); + } + + // Assemble final DOT + std::stringstream out; + out << "digraph AST {\n"; + // out << " rankdir=LR;\n"; + out << " graph [fontname=\"monospace\", bgcolor=\"white\", " + "ranksep=0.6, nodesep=0.4, pad=0.2];\n"; + out << " node [shape=box, style=\"rounded,filled\", " + "fillcolor=\"#f8fafc\", color=\"#cbd5e1\", " + "fontname=\"monospace\", fontsize=10, penwidth=1.2];\n"; + out << " edge [fontname=\"monospace\", fontsize=9, " + "color=\"#94a3b8\", arrowsize=0.7];\n"; + out << g.nodes.str(); + out << g.edges.str(); + out << "}\n"; + return out.str(); + } + +} // namespace arti::lang::ast diff --git a/lib/src/Parser/AST/toString.cpp b/lib/src/Parser/AST/toString.cpp new file mode 100644 index 0000000..c66949e --- /dev/null +++ b/lib/src/Parser/AST/toString.cpp @@ -0,0 +1,1508 @@ +#include + +#include +#include +#include + +#include + +namespace arti::lang::ast { + + std::string toString(const ModuleDeclNode &, std::string); + std::string toString(const StructDeclNode &, std::string); + std::string toString(const EnumDeclNode &, std::string); + std::string toString(const FunctionDeclNode &, std::string); + std::string toString(const ImportDeclNode &, std::string); + std::string toString(const AliasDeclNode &, std::string); + std::string toString(const EnumMemberNode &, std::string); + std::string toString(const StructMemberNode &, std::string); + std::string toString(const GenericParamNode &, std::string); + std::string toString(const FunctionParamNode &, std::string); + std::string toString(const TopLevelDeclNode &, std::string); + std::string toString(const ModuleInnerDeclNode &, std::string); + std::string toString(const TypeNode &, std::string); + std::string toString(const GenericTypeNode &, std::string); + std::string toString(const IdentifierTypeNode &, std::string); + std::string toString(const NamespacedTypeNode &, std::string); + std::string toString(const NamespacedIdentifierNode &, std::string); + std::string toString(const TypeExpressionNode &, std::string); + std::string toString(const CharLtrlNode &, std::string); + std::string toString(const NullLtrlNode &, std::string); + std::string toString(const StringLtrlNode &, std::string); + std::string toString(const FloatLtrlNode &, std::string); + std::string toString(const IntegerLtrlNode &, std::string); + std::string toString(const BooleanLtrlNode &, std::string); + std::string toString(const StructLtrlNode &, std::string); + std::string toString(const SliceLtrlNode &, std::string); + std::string toString(const StructLtrlNamedFieldInitNode &, std::string); + std::string toString(const StructLtrlPositionalInitNode &, std::string); + std::string toString(const StructLtrlNamedInitializerNode &, std::string); + std::string toString(const StructLtrlPositionalInitializerNode&, std::string); + std::string toString(const StructLtrlInitializerNode &, std::string); + std::string toString(const IdentifierExprNode &, std::string); + std::string toString(const UnaryExprNode &, std::string); + std::string toString(const BinaryExprNode &, std::string); + std::string toString(const AssignExprNode &, std::string); + std::string toString(const CompoundAssignExprNode &, std::string); + std::string toString(const FunctionCallExprNode &, std::string); + std::string toString(const SliceAccessExprNode &, std::string); + std::string toString(const SliceRangeExprNode &, std::string); + std::string toString(const MemberAccessExprNode &, std::string); + std::string toString(const PointerAccessExprNode &, std::string); + std::string toString(const ScopeAccessExprNode &, std::string); + std::string toString(const ReflectionExprNode &, std::string); + std::string toString(const SliceCreationExprNode &, std::string); + std::string toString(const SliceLengthExprNode &, std::string); + std::string toString(const SlicePtrExprNode &, std::string); + std::string toString(const ExpressionNode &, std::string); + std::string toString(const CodeBlockStmtNode &, std::string); + std::string toString(const VariableStmtNode &, std::string); + std::string toString(const IfStmtNode &, std::string); + std::string toString(const ElseStmtNode &, std::string); + std::string toString(const DeferStmtNode &, std::string); + std::string toString(const ErrDeferStmtNode &, std::string); + std::string toString(const ReturnStmtNode &, std::string); + std::string toString(const BreakStmtNode &, std::string); + std::string toString(const ContinueStmtNode &, std::string); + std::string toString(const MatchStmtNode &, std::string); + std::string toString(const SwitchStmtNode &, std::string); + std::string toString(const CForStmtNode &, std::string); + std::string toString(const RangeForStmtNode &, std::string); + std::string toString(const WhileStmtNode &, std::string); + std::string toString(const DoWhileStmtNode &, std::string); + std::string toString(const InfLoopStmtNode &, std::string); + std::string toString(const ExpressionStmtNode &, std::string); + std::string toString(const MatchCaseNode &, std::string); + std::string toString(const SwitchCaseNode &, std::string); + std::string toString(const StatementNode &, std::string); + 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(CompoundAssignOperator op); + + const auto StrTreeNoNode = "│ "; + const auto StrTreeChilds = "├─"; + const auto StrTreeLast = "└─"; + const auto StrTreeSpace = " "; + + // Unicode tree helpers (pure; used by all printers) + inline std::string nextPrefix(const std::string &prefix, bool isLast) { + return prefix + (isLast ? StrTreeSpace : StrTreeNoNode); + } + + template + void appendItem( + std::stringstream &ss, + const std::string &prefix, + const T &item, + bool isLastChild + ) { + ss << "\n" + << prefix << (isLastChild ? StrTreeLast : StrTreeChilds) << " " + << toString(item, nextPrefix(prefix, isLastChild)); + } + + template + void appendGroupVec( + std::stringstream &ss, + const std::string &prefix, + std::string_view title, + const std::vector &items, + bool isLastGroup + ) { + if (items.empty()) { + return; + } + ss << "\n" + << prefix << (isLastGroup ? StrTreeLast : StrTreeChilds) << " " << title; + for (size_t i = 0; i < items.size(); ++i) { + appendItem( + ss, + nextPrefix(prefix, isLastGroup), + items[i], + i == items.size() - 1 + ); + } + } + + template + void appendGroupVecIndexed( + std::stringstream &ss, + const std::string &prefix, + std::string_view title, + const std::vector &items, + bool isLastGroup + ) { + if (items.empty()) { + return; + } + ss << "\n" + << prefix << (isLastGroup ? StrTreeLast : StrTreeChilds) << " " << title; + for (size_t i = 0; i < items.size(); ++i) { + ss << "\n" + << nextPrefix(prefix, isLastGroup) + << (i == items.size() - 1 ? StrTreeLast : StrTreeChilds) << " [" << i + << "] " + << toString( + items[i], + nextPrefix(nextPrefix(prefix, isLastGroup), i == items.size() - 1) + ); + } + } + + template + void appendGroupOne( + std::stringstream &ss, + const std::string &prefix, + std::string_view title, + const T &item, + bool isLastGroup + ) { + ss << "\n" + << prefix << (isLastGroup ? StrTreeLast : StrTreeChilds) << " " << title; + appendItem(ss, nextPrefix(prefix, isLastGroup), item, true); + } + + template + void appendGroupOpt( + std::stringstream &ss, + const std::string &prefix, + std::string_view title, + const std::optional &item, + bool isLastGroup + ) { + if (item) { + appendGroupOne(ss, prefix, title, *item, isLastGroup); + } + } + + inline void appendGroupLeaf( + std::stringstream &ss, + const std::string &prefix, + std::string_view title, + std::string_view value, + bool isLastGroup + ) { + ss << "\n" + << prefix << (isLastGroup ? StrTreeLast : StrTreeChilds) << " " << title; + ss << "\n" + << nextPrefix(prefix, isLastGroup) << StrTreeLast << " `" << value + << "`"; + } + + inline void appendGroupLeafList( + std::stringstream &ss, + const std::string &prefix, + std::string_view title, + const std::vector &values, + bool isLastGroup + ) { + if (values.empty()) { + return; + } + ss << "\n" + << prefix << (isLastGroup ? StrTreeLast : StrTreeChilds) << " " << title; + for (size_t i = 0; i < values.size(); ++i) { + ss << "\n" + << nextPrefix(prefix, isLastGroup) + << (i == values.size() - 1 ? StrTreeLast : StrTreeChilds) << " `" + << values[i] << "`"; + } + } + + std::string toString(const AST &tree, std::string prefix) { + std::stringstream ss; + ss << std::format("{}CompilationUnit `{}`", prefix, tree->unitName); + if (! tree->declarations.empty()) { + appendGroupVec(ss, prefix, "Declarations", tree->declarations, true); + } + return ss.str(); + } + + std::string toString(const TopLevelDeclNode &tlDecl, std::string padding) { + auto visitor = OverloadSet{ + [padding](const ModuleDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StructDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const EnumDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FunctionDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ImportDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const AliasDeclNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, tlDecl); + } + + std::string toString(const ModuleDeclNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format( + "Module {} {}", + toString(node->name, prefix), + node->isExported ? "[exported]" : "" + ); + // Only non-empty groups, using correct last-group logic + int total = 0; + if (! node->aliasDeclarations.empty()) { + ++total; + } + if (! node->innerDeclarations.empty()) { + ++total; + } + if (! node->childModules.empty()) { + ++total; + } + int emitted = 0; + if (! node->aliasDeclarations.empty()) { + appendGroupVec( + ss, + prefix, + "AliasedTypes", + node->aliasDeclarations, + ++emitted == total + ); + } + if (! node->innerDeclarations.empty()) { + appendGroupVec( + ss, + prefix, + "InnerDeclarations", + node->innerDeclarations, + ++emitted == total + ); + } + if (! node->childModules.empty()) { + appendGroupVec( + ss, + prefix, + "ChildModules", + node->childModules, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const StructDeclNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format( + "Struct `{}` {}", + node->name, + node->isExported ? "[exported]" : "" + ); + // Collect how many groups non-empty to calculate last-ness + int total = 0; + if (! node->genericParams.empty()) { + ++total; + } + if (! node->structMembers.empty()) { + ++total; + } + int emitted = 0; + if (! node->genericParams.empty()) { + appendGroupVec( + ss, + prefix, + "GenericParams", + node->genericParams, + ++emitted == total + ); + } + if (! node->structMembers.empty()) { + appendGroupVec( + ss, + prefix, + "FieldMembers", + node->structMembers, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const EnumDeclNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format( + "Enum `{}` {}", + node->name, + node->isExported ? "[exported]" : "" + ); + int total = 0; + if (! node->genericParams.empty()) { + ++total; + } + if (! node->enumMembers.empty()) { + ++total; + } + int emitted = 0; + if (! node->genericParams.empty()) { + appendGroupVec( + ss, + prefix, + "GenericParams", + node->genericParams, + ++emitted == total + ); + } + if (! node->enumMembers.empty()) { + appendGroupVec( + ss, + prefix, + "EnumValues", + node->enumMembers, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const FunctionDeclNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format( + "Function `{}` {}", + node->name, + node->isExported ? "[exported]" : "" + ); + int total = 0; + if (node->returnType) { + ++total; + } + if (! node->genericParams.empty()) { + ++total; + } + if (! node->functionParams.empty()) { + ++total; + } + ++total; // FunctionBody is always present + + int emitted = 0; + if (node->returnType) { + appendGroupOne( + ss, + prefix, + "ReturnType", + *node->returnType, + ++emitted == total + ); + } + if (! node->genericParams.empty()) { + appendGroupVec( + ss, + prefix, + "GenericParams", + node->genericParams, + ++emitted == total + ); + } + if (! node->functionParams.empty()) { + appendGroupVec( + ss, + prefix, + "FunctionParams", + node->functionParams, + ++emitted == total + ); + } + appendGroupOne( + ss, + prefix, + "FunctionBody", + node->functionBody, + ++emitted == total + ); + return ss.str(); + } + + std::string toString(const ImportDeclNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format( + "Import {}{}", + toString(node->importTarget, prefix), + node->importAll ? " [*]" : "" + ); + return ss.str(); + } + + std::string toString(const AliasDeclNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format("Alias `{}`", node->alias); + appendGroupOne(ss, prefix, "AliasedType", node->target, true); + return ss.str(); + } + + std::string toString(const EnumMemberNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format("EnumMember `{}`", node->name); + appendGroupOpt(ss, prefix, "StorageType", node->type, true); + return ss.str(); + } + + std::string toString(const StructMemberNode &node, std::string prefix) { + std::stringstream ss; + ss << std::format("StructMember `{}`", node->name); + appendGroupOne(ss, prefix, "Type", node->type, true); + return ss.str(); + } + + std::string toString(const GenericParamNode &node, std::string padding) { + std::ignore = padding; + return std::format("typename `{}`", node->name); + } + + std::string toString(const FunctionParamNode &node, std::string prefix) { + std::stringstream ss; + if (node->isThis) { + ss << "`This`"; + } + else { + ss << std::format("Param `{}`", node->name); + } + appendGroupOne(ss, prefix, "Type", node->type, true); + return ss.str(); + } + + std::string toString(const ModuleInnerDeclNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const StructDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const EnumDeclNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FunctionDeclNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const TypeNode &node, std::string prefix) { + std::stringstream ss; + ss << "Type"; + // Collect qualifiers as strings for leaf-list output + std::vector qls; + for (const auto &q : node->qualifiers) { + switch (q) { + case TypeQualifier::Pointer: qls.emplace_back("Pointer (*)"); break; + case TypeQualifier::Slice: qls.emplace_back("Slice ([])"); break; + case TypeQualifier::Mutable: qls.emplace_back("Mutable ($)"); break; + case TypeQualifier::Optional: qls.emplace_back("Optional (?)"); break; + default: break; + } + } + int total = 0; + if (! qls.empty()) { + ++total; + } + ++total; // BaseType is always present + int emitted = 0; + if (! qls.empty()) { + appendGroupLeafList(ss, prefix, "Qualifiers", qls, ++emitted == total); + } + appendGroupOne(ss, prefix, "BaseType", node->baseType, ++emitted == total); + return ss.str(); + } + + std::string toString(const GenericTypeNode &node, std::string prefix) { + std::stringstream ss; + ss << "GenericType"; + int total = 1; + if (! node->genericArgs.empty()) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "BaseType", node->baseType, ++emitted == total); + if (! node->genericArgs.empty()) { + appendGroupVec( + ss, + prefix, + "GenericArgs", + node->genericArgs, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const IdentifierTypeNode &node, std::string prefix) { + std::stringstream ss; + ss << "IdentifierType"; + appendGroupOne(ss, prefix, "TypeName", node->typeName, true); + return ss.str(); + } + + std::string toString(const NamespacedTypeNode &node, std::string prefix) { + std::stringstream ss; + ss << "NamespacedType"; + appendGroupOne(ss, prefix, "BaseType", node->baseType, false); + appendGroupLeaf(ss, prefix, "TypeName", node->typeName, true); + return ss.str(); + } + + std::string + toString(const NamespacedIdentifierNode &node, std::string padding) { + std::ignore = padding; + auto joined = node->identParts | + std::views::join_with(std::string_view{ "::" }) | + std::ranges::to(); + return std::format("`{}`", joined); + } + + std::string toString(const TypeExpressionNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const GenericTypeNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IdentifierTypeNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const NamespacedTypeNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const CharLtrlNode &node, std::string padding) { + std::ignore = padding; + return std::format("CharLiteral '{}'", node->value); + } + + std::string toString(const NullLtrlNode &node, std::string padding) { + std::ignore = node; + std::ignore = padding; + return std::format("NullLiteral 'null'"); + } + + std::string toString(const StringLtrlNode &node, std::string padding) { + std::ignore = padding; + return std::format("StringLiteral \"{}\"", node->value); + } + + std::string toString(const FloatLtrlNode &node, std::string padding) { + std::ignore = padding; + return std::format("FloatLiteral {}", node->value); + } + + std::string toString(const IntegerLtrlNode &node, std::string padding) { + std::ignore = padding; + return std::format("IntegerLiteral {}", node->value); + } + + std::string toString(const BooleanLtrlNode &node, std::string padding) { + std::ignore = padding; + return std::format("BooleanLiteral {}", node->value ? "true" : "false"); + } + + std::string toString(const StructLtrlNode &node, std::string prefix) { + std::stringstream ss; + ss << "StructLiteral"; + int total = 1; + if (node->initializer) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Type", node->type, ++emitted == total); + if (node->initializer) { + appendGroupOne( + ss, + prefix, + "Elements", + *node->initializer, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const SliceLtrlNode &node, std::string prefix) { + std::stringstream ss; + ss << "SliceLiteral"; + 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 StructLtrlNamedFieldInitNode &node, std::string prefix) { + std::stringstream ss; + ss << "FieldInitializer"; + appendGroupLeaf(ss, prefix, "Field", node->fieldName, false); + appendGroupOne(ss, prefix, "Value", node->fieldValue, true); + return ss.str(); + } + + std::string + toString(const StructLtrlPositionalInitNode &node, std::string prefix) { + std::stringstream ss; + ss << "PositionalInitializer"; + appendGroupOne(ss, prefix, "Value", node->fieldValue, true); + return ss.str(); + } + + std::string + toString(const StructLtrlNamedInitializerNode &node, std::string prefix) { + std::stringstream ss; + ss << "InitializerList"; + appendGroupVec(ss, prefix, "Elements", node->fields, true); + return ss.str(); + } + + std::string toString( + const StructLtrlPositionalInitializerNode &node, + std::string prefix + ) { + std::stringstream ss; + ss << "InitializerList"; + appendGroupVecIndexed(ss, prefix, "Elements", node->fields, true); + return ss.str(); + } + + std::string + toString(const StructLtrlInitializerNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const StructLtrlNamedInitializerNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StructLtrlPositionalInitializerNode &node) + -> std::string { return toString(node, padding); }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const IdentifierExprNode &node, std::string padding) { + std::ignore = padding; + return std::format("Identifier `{}`", node->identifierName); + } + + std::string toString(const UnaryExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "UnaryExpression"; + int total = 2; + int emitted = 0; + appendGroupLeaf( + ss, + prefix, + "Operator", + toString(node->op), + ++emitted == total + ); + appendGroupOne(ss, prefix, "Operand", node->right, ++emitted == total); + return ss.str(); + } + + std::string toString(const BinaryExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "BinaryExpression"; + int total = 3; + int emitted = 0; + appendGroupLeaf( + ss, + prefix, + "Operator", + toString(node->op), + ++emitted == total + ); + appendGroupOne(ss, prefix, "Left", node->left, ++emitted == total); + appendGroupOne(ss, prefix, "Right", node->right, ++emitted == total); + return ss.str(); + } + + std::string toString(const AssignExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "AssignExpression"; + appendGroupOne(ss, prefix, "Left", node->left, false); + appendGroupOne(ss, prefix, "Right", node->right, true); + return ss.str(); + } + + std::string toString(const CompoundAssignExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "CompoundAssignExpression"; + int total = 3; + int emitted = 0; + appendGroupLeaf( + ss, + prefix, + "Operator", + toString(node->op), + ++emitted == total + ); + appendGroupOne(ss, prefix, "Left", node->left, ++emitted == total); + appendGroupOne(ss, prefix, "Right", node->right, ++emitted == total); + return ss.str(); + } + + std::string toString(const FunctionCallExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "FunctionCallExpression"; + int total = 1; + if (! node->arguments.empty()) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Callee", node->callee, ++emitted == total); + if (! node->arguments.empty()) { + appendGroupVec( + ss, + prefix, + "Arguments", + node->arguments, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const SliceAccessExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "SliceAccessExpression"; + appendGroupOne(ss, prefix, "Slice", node->slice, false); + appendGroupOne(ss, prefix, "Index", node->index, true); + return ss.str(); + } + + std::string toString(const SliceRangeExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "SliceRangeExpression"; + int total = 1; + if (node->start) { + ++total; + } + if (node->end) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Slice", node->slice, ++emitted == total); + if (node->start) { + appendGroupOne(ss, prefix, "Start", *node->start, ++emitted == total); + } + if (node->end) { + appendGroupOne(ss, prefix, "End", *node->end, ++emitted == total); + } + return ss.str(); + } + + std::string toString(const MemberAccessExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "MemberAccessExpression"; + appendGroupOne(ss, prefix, "Object", node->object, false); + appendGroupLeaf(ss, prefix, "Member", node->memberName, true); + return ss.str(); + } + + std::string toString(const PointerAccessExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "PointerAccessExpression"; + appendGroupOne(ss, prefix, "Object", node->object, false); + appendGroupLeaf(ss, prefix, "Member", node->memberName, true); + return ss.str(); + } + + std::string toString(const ScopeAccessExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "ScopeAccessExpression"; + int total = 2; + if (! node->genericParams.empty()) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Object", node->scope, ++emitted == total); + if (! node->genericParams.empty()) { + appendGroupVec( + ss, + prefix, + "GenericParams", + node->genericParams, + ++emitted == total + ); + } + appendGroupLeaf(ss, prefix, "Member", node->memberName, ++emitted == total); + return ss.str(); + } + + std::string toString(const ReflectionExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "ReflectionExpression"; + int total = 1; + if (node->attribute) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Object", node->object, ++emitted == total); + if (node->attribute) { + appendGroupLeaf( + ss, + prefix, + "Attribute", + *node->attribute, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const SliceCreationExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "SliceCreationExpression"; + appendGroupOne(ss, prefix, "Object", node->object, false); + appendGroupOne(ss, prefix, "Length", node->length, true); + return ss.str(); + } + + std::string toString(const SliceLengthExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "SliceLengthExpression"; + appendGroupOne(ss, prefix, "Object", node->object, true); + return ss.str(); + } + + std::string toString(const SlicePtrExprNode &node, std::string prefix) { + std::stringstream ss; + ss << "SlicePtrExpression"; + appendGroupOne(ss, prefix, "Object", node->object, true); + return ss.str(); + } + + std::string toString(const ExpressionNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const CharLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const NullLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StringLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FloatLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IntegerLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const BooleanLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const StructLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceLtrlNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IdentifierExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const UnaryExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const BinaryExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const AssignExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const CompoundAssignExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const FunctionCallExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceRangeExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const MemberAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const PointerAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ScopeAccessExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceCreationExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SliceLengthExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SlicePtrExprNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ReflectionExprNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const CodeBlockStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "CodeBlock"; + appendGroupVec(ss, prefix, "Statements", node->statements, true); + return ss.str(); + } + + std::string toString(const VariableStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "VariableDeclaration"; + int total = 2; + if (node->type) { + ++total; + } + if (node->initializer) { + ++total; + } + int emitted = 0; + appendGroupLeaf(ss, prefix, "Name", node->name, ++emitted == total); + appendGroupLeaf( + ss, + prefix, + "Mutability", + node->mutability == Mutability::Mutable ? "let" : "def", + ++emitted == total + ); + if (node->type) { + appendGroupOne(ss, prefix, "Type", *node->type, ++emitted == total); + } + if (node->initializer) { + appendGroupOne( + ss, + prefix, + "Initializer", + *node->initializer, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const IfStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "IfStatement"; + int total = 2; // Condition + Body always present + if (node->unwrappedVar) { + ++total; + } + if (node->elseBranch) { + ++total; + } + int emitted = 0; + if (node->unwrappedVar) { + appendGroupLeaf( + ss, + prefix, + "UnwrappedVar", + *node->unwrappedVar, + ++emitted == total + ); + } + appendGroupOne( + ss, + prefix, + "Condition", + node->condition, + ++emitted == total + ); + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + if (node->elseBranch) { + appendGroupOne( + ss, + prefix, + "ElseBranch", + *node->elseBranch, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const ElseStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "ElseStatement"; + int total = 1; + if (node->unwrappedVar) { + ++total; + } + int emitted = 0; + if (node->unwrappedVar) { + appendGroupLeaf( + ss, + prefix, + "UnwrappedVar", + *node->unwrappedVar, + ++emitted == total + ); + } + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + return ss.str(); + } + + std::string toString(const DeferStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "DeferStatement"; + appendGroupOne(ss, prefix, "Body", node->body, true); + return ss.str(); + } + + std::string toString(const ErrDeferStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "ErrDeferStatement"; + appendGroupOne(ss, prefix, "Body", node->body, true); + return ss.str(); + } + + std::string toString(const ReturnStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "ReturnStatement"; + appendGroupOpt(ss, prefix, "Expression", node->value, true); + return ss.str(); + } + + std::string toString(const BreakStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "BreakStatement"; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, true); + } + return ss.str(); + } + + std::string toString(const ContinueStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "ContinueStatement"; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, true); + } + return ss.str(); + } + + std::string toString(const MatchStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "MatchStatement"; + int total = 1; + if (! node->matchCases.empty()) { + ++total; + } + if (node->defaultCase) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Value", node->value, ++emitted == total); + if (! node->matchCases.empty()) { + appendGroupVec(ss, prefix, "Cases", node->matchCases, ++emitted == total); + } + if (node->defaultCase) { + appendGroupOne( + ss, + prefix, + "DefaultCase", + *node->defaultCase, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const SwitchStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "SwitchStatement"; + int total = 1; + if (! node->switchCases.empty()) { + ++total; + } + if (node->defaultCase) { + ++total; + } + int emitted = 0; + appendGroupOne(ss, prefix, "Value", node->value, ++emitted == total); + if (! node->switchCases.empty()) { + appendGroupVec( + ss, + prefix, + "Cases", + node->switchCases, + ++emitted == total + ); + } + if (node->defaultCase) { + appendGroupOne( + ss, + prefix, + "DefaultCase", + *node->defaultCase, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const CForStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "CForStatement"; + int total = 2; // Condition, Body always + if (node->label) { + ++total; + } + if (node->preLoop) { + ++total; + } + if (node->postLoop) { + ++total; + } + int emitted = 0; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, ++emitted == total); + } + if (node->preLoop) { + appendGroupOne(ss, prefix, "PreLoop", *node->preLoop, ++emitted == total); + } + appendGroupOne( + ss, + prefix, + "Condition", + node->condition, + ++emitted == total + ); + if (node->postLoop) { + appendGroupOne( + ss, + prefix, + "PostLoop", + *node->postLoop, + ++emitted == total + ); + } + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + return ss.str(); + } + + std::string toString(const RangeForStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "ForRangeStatement"; + int total = 3; // Variable, Range, Body + if (node->label) { + ++total; + } + int emitted = 0; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, ++emitted == total); + } + std::string varLabel = node->varName; + if (node->varMutability == Mutability::Mutable) { + varLabel += " [mut]"; + } + appendGroupLeaf(ss, prefix, "Variable", varLabel, ++emitted == total); + appendGroupOne(ss, prefix, "Range", node->range, ++emitted == total); + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + return ss.str(); + } + + std::string toString(const WhileStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "WhileStatement"; + int total = 2; // Condition, Body + if (node->label) { + ++total; + } + if (node->unwrappedVar) { + ++total; + } + if (node->elseBranch) { + ++total; + } + int emitted = 0; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, ++emitted == total); + } + if (node->unwrappedVar) { + appendGroupLeaf( + ss, + prefix, + "UnwrappedVar", + *node->unwrappedVar, + ++emitted == total + ); + } + appendGroupOne( + ss, + prefix, + "Condition", + node->condition, + ++emitted == total + ); + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + if (node->elseBranch) { + appendGroupOne( + ss, + prefix, + "ElseBranch", + *node->elseBranch, + ++emitted == total + ); + } + return ss.str(); + } + + std::string toString(const DoWhileStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "DoWhileStatement"; + int total = 2; // Condition, Body + if (node->label) { + ++total; + } + int emitted = 0; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, ++emitted == total); + } + appendGroupOne( + ss, + prefix, + "Condition", + node->condition, + ++emitted == total + ); + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + return ss.str(); + } + + std::string toString(const InfLoopStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "InfLoopStatement"; + int total = 1; + if (node->label) { + ++total; + } + int emitted = 0; + if (node->label) { + appendGroupLeaf(ss, prefix, "Label", *node->label, ++emitted == total); + } + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + return ss.str(); + } + + std::string toString(const ExpressionStmtNode &node, std::string prefix) { + std::stringstream ss; + ss << "ExpressionStatement"; + appendGroupOne(ss, prefix, "Expression", node->expression, true); + return ss.str(); + } + + std::string toString(const MatchCaseNode &node, std::string prefix) { + std::stringstream ss; + ss << "MatchCase"; + int total = 2; + if (node->unwrappedVar) { + ++total; + } + int emitted = 0; + if (node->unwrappedVar) { + appendGroupLeaf( + ss, + prefix, + "UnwrappedVar", + *node->unwrappedVar, + ++emitted == total + ); + } + appendGroupOne(ss, prefix, "Type", node->matchType, ++emitted == total); + appendGroupOne(ss, prefix, "Body", node->body, ++emitted == total); + return ss.str(); + } + + std::string toString(const SwitchCaseNode &node, std::string prefix) { + std::stringstream ss; + ss << "SwitchCase"; + appendGroupOne(ss, prefix, "Matcher", node->matchExpr, false); + appendGroupOne(ss, prefix, "Body", node->body, true); + return ss.str(); + } + + std::string toString(const StatementNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const VariableStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IfStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const DeferStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ErrDeferStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ReturnStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const BreakStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ContinueStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const MatchStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const SwitchStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const CForStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const RangeForStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const WhileStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const DoWhileStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const InfLoopStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ExpressionStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const ElseBranchNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const ElseStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const IfStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const DeferableNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const ExpressionStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const CodeBlockStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(const PreLoopStmtNode &node, std::string padding) { + auto visitor = OverloadSet{ + [padding](const VariableStmtNode &node) -> std::string { + return toString(node, padding); + }, + [padding](const ExpressionStmtNode &node) -> std::string { + return toString(node, padding); + }, + }; + + return std::visit(visitor, node); + } + + std::string toString(UnaryOperator op) { + using enum UnaryOperator; + + switch (op) { + case Not: return "Not (!)"; + case Minus: return "Minus (-)"; + case BitNot: return "BitNot (~)"; + case Ampersand: return "Ampersand (&)"; + case Star: return "Star (*)"; + default: std::unreachable(); break; + } + + std::unreachable(); + } + + std::string toString(BinaryOperator op) { + using enum BinaryOperator; + + switch (op) { + case Equal: return "Equal (==)"; + case NotEqual: return "NotEqual (!=)"; + case GreaterThan: return "GreaterThan (>)"; + case LessThan: return "LessThan (<)"; + case GreaterEqual: return "GreaterEqual (>=)"; + case LessEqual: return "LessEqual (<=)"; + case BitAnd: return "BitAnd (&)"; + case BitXor: return "BitXor (^)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case Adition: return "Adition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + default: std::unreachable(); break; + } + + std::unreachable(); + } + + std::string toString(CompoundAssignOperator op) { + using enum CompoundAssignOperator; + + switch (op) { + case Addition: return "Addition (+)"; + case Substraction: return "Substraction (-)"; + case Multiplication: return "Multiplication (*)"; + case Division: return "Division (/)"; + case Modulo: return "Modulo (%)"; + case BitAnd: return "BitAnd (&)"; + case BitOr: return "BitOr (|)"; + case LeftShift: return "LeftShift (<<)"; + case RightShift: return "RightShift (>>)"; + case BoolAnd: return "BoolAnd (&&)"; + case BoolOr: return "BoolOr (||)"; + default: std::unreachable(); break; + } + + std::unreachable(); + } + +} // namespace arti::lang::ast diff --git a/lib/src/Tokenizer/Token.cpp b/lib/src/Tokenizer/Token.cpp index 957c9c8..9f74897 100644 --- a/lib/src/Tokenizer/Token.cpp +++ b/lib/src/Tokenizer/Token.cpp @@ -7,109 +7,109 @@ namespace arti::lang { std::string toString(const Token &value) { using enum TokenV; - std::string tokenStr = toString(value.value); + std::string tokenStr{ toString(value.value) }; if (value.value == tkEOF) { - return std::format("Token{{ {} }}", tokenStr); + return "EOF"; } - return std::format("Token{{ {}, {} }}", tokenStr, value.strValue); + return std::format("{}", value.strValue); } - std::string toString(const TokenV &value) { + std::string_view toString(const TokenV &value) { using enum TokenV; switch (value) { - case tkEOF: return "tkEOF"; - case tkString: return "tkString"; - case tkDecimal: return "tkDecimal"; - case tkInteger: return "tkInteger"; - case tkCharacter: return "tkCharacter"; - case tkIdentifier: return "tkIdentifier"; - case opDot: return "opDot"; - case opMod: return "opMod"; - case opPlus: return "opPlus"; - case opHyphen: return "opHyphen"; - case opSlash: return "opSlash"; - case opBang: return "opBang"; - case opStar: return "opStar"; - case opColon: return "opColon"; - case opComma: return "opComma"; - case opAssign: return "opAssign"; - case opAccess: return "opAccess"; - case opSemicolon: return "opSemicolon"; - case opCaret: return "opCaret"; - case opTilde: return "opTilde"; - case opEq: return "opEq"; - case opNeq: return "opNeq"; - case opLt: return "opLt"; - case opGt: return "opGt"; - case opLtEq: return "opLtEq"; - case opGtEq: return "opGtEq"; - case opLShift: return "opLShift"; - case opRShift: return "opRShift"; - case opBoolAnd: return "opBoolAnd"; - case opBoolOr: return "opBoolOr"; - case opAnd: return "opAnd"; - case opOr: return "opOr"; - case opLParen: return "opLParen"; - case opRParen: return "opRParen"; - case opLBracket: return "opLBracket"; - case opRBracket: return "opRBracket"; - case opLSquirly: return "opLSquirly"; - case opRSquirly: return "opRSquirly"; - case opArrow: return "opArrow"; - case opPlusAssign: return "opPlusAssign"; - case opHyphenAssign: return "opHyphenAssign"; - case opStarAssign: return "opStarAssign"; - case opSlashAssign: return "opSlashAssign"; - case opModAssign: return "opModAssign"; - case opAndAssign: return "opAndAssign"; - case opOrAssign: return "opOrAssign"; - case opLShiftAssign: return "opLShiftAssign"; - case opRShiftAssign: return "opRShiftAssign"; - case opBoolAndAssign: return "opBoolAndAssign"; - case opBoolOrAssign: return "opBoolORAssign"; - case opMut: return "opMut"; - case opOpt: return "opOpt"; - case opSliceSize: return "opSliceSize"; - case opPtrSlice: return "opPtrSlice"; - case opSlicePtr: return "opSlicePtr"; - case opReflect: return "opReflect"; - case opLabel: return "opLabel"; - case kwUnderscore: return "kwUnderscore"; - case kwOr: return "kwOr"; - case kwNot: return "kwNot"; - case kwAnd: return "kwAnd"; - case kwIf: return "kwIf"; - case kwElse: return "kwElse"; - case kwFn: return "kwFn"; - case kwEnum: return "kwEnum"; - case kwStruct: return "kwStruct"; - case kwDef: return "kwDef"; - case kwLet: return "kwLet"; - case kwFor: return "kwFor"; - case kwLoop: return "kwLoop"; - case kwBreak: return "kwBreak"; - case kwContinue: return "kwContinue"; - case kwDo: return "kwDo"; - case kwWhile: return "kwWhile"; - case kwMatch: return "kwMatch"; - case kwSwitch: return "kwSwitch"; - case kwReturn: return "kwReturn"; - case kwUnreachable: return "kwUnreachable"; - case kwTypename: return "kwTypename"; - case kwDefer: return "kwDefer"; - case kwErrDefer: return "kwErrDefer"; - case kwTrue: return "kwTrue"; - case kwFalse: return "kwFalse"; - case kwNull: return "kwNull"; - case kwThis: return "kwThis"; - case kwImport: return "kwImport"; - case kwExport: return "kwExport"; - case kwModule: return "kwModule"; - case kwUsing: return "kwUsing"; - default: return ""; + case tkEOF: return "EOF"; + case tkString: return "string"; + case tkDecimal: return "number"; + case tkInteger: return "integer"; + case tkCharacter: return "character"; + case tkIdentifier: return "identifier"; + case opDot: return "'.'"; + case opMod: return "'%'"; + case opPlus: return "'+'"; + case opHyphen: return "'-'"; + case opSlash: return "'/'"; + case opBang: return "'!'"; + case opStar: return "'*'"; + case opColon: return "':'"; + case opComma: return "','"; + case opAssign: return "'='"; + case opAccess: return "'::'"; + case opSemicolon: return "';'"; + case opCaret: return "'^'"; + case opTilde: return "'~'"; + case opEq: return "'=='"; + case opNeq: return "'!='"; + case opLt: return "'<'"; + case opGt: return "'>'"; + case opLtEq: return "'<='"; + case opGtEq: return "'>='"; + case opLShift: return "'<<'"; + case opRShift: return "'>>'"; + case opBoolAnd: return "'&&'"; + case opBoolOr: return "'||'"; + case opAnd: return "'&'"; + case opOr: return "'|'"; + case opLParen: return "'('"; + case opRParen: return "')'"; + case opLBracket: return "'['"; + case opRBracket: return "']'"; + case opLSquirly: return "'{'"; + case opRSquirly: return "'}'"; + case opArrow: return "'->'"; + case opPlusAssign: return "'+='"; + case opHyphenAssign: return "'-='"; + case opStarAssign: return "'*='"; + case opSlashAssign: return "'/='"; + case opModAssign: return "'%='"; + case opAndAssign: return "'&='"; + case opOrAssign: return "'|='"; + case opLShiftAssign: return "'<<='"; + case opRShiftAssign: return "'>>='"; + case opBoolAndAssign: return "'&&='"; + case opBoolOrAssign: return "'||='"; + case opMut: return "'$'"; + case opOpt: return "'?'"; + case opSliceSize: return "'.#'"; + case opPtrSlice: return "'.['"; + case opSlicePtr: return "'.*'"; + case opReflect: return "'.@'"; + case opLabel: return "':='"; + case kwUnderscore: return "'underscore'"; + case kwOr: return "'or'"; + case kwNot: return "'not'"; + case kwAnd: return "'and'"; + case kwIf: return "'if'"; + case kwElse: return "'else'"; + case kwFn: return "'fn'"; + case kwEnum: return "'enum'"; + case kwStruct: return "'struct'"; + case kwDef: return "'def'"; + case kwLet: return "'let'"; + case kwFor: return "'for'"; + case kwLoop: return "'loop'"; + case kwBreak: return "'break'"; + case kwContinue: return "'continue'"; + case kwDo: return "'do'"; + case kwWhile: return "'while'"; + case kwMatch: return "'match'"; + case kwSwitch: return "'switch'"; + case kwReturn: return "'return'"; + case kwUnreachable: return "'unreachable'"; + case kwTypename: return "'typename'"; + case kwDefer: return "'defer'"; + case kwErrDefer: return "'errdefer'"; + case kwTrue: return "'true'"; + case kwFalse: return "'false'"; + case kwNull: return "'null'"; + case kwThis: return "'this'"; + case kwImport: return "'import'"; + case kwExport: return "'export'"; + case kwModule: return "'module'"; + case kwUsing: return "'using'"; + default: return ""; } }