// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers // // SPDX-License-Identifier: MPL-2.0 import Foundation /// This is a “green tree” in the sense of Roslyn. public struct SyntaxTree: Codable, Sendable { public let kind: SyntaxTreeKind public let metadata: SyntaxTreeMetadata? public let children: [Child] public let utf16Length: Int public enum Child: Codable, Sendable { case token(Token, metadata: TokenMetadata?) case tree(SyntaxTree) } public init(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata? = nil, children: [SyntaxTree.Child]) { self.kind = kind self.metadata = metadata self.children = children self.utf16Length = children.reduce(0) { length, child in length + child.utf16Length } } } extension SyntaxTree { /// A mutable version of ``SyntaxTree`` that does not keep track of textual length, for use when constructing trees. public struct MutableTree { public var kind: SyntaxTreeKind public var metadata: SyntaxTreeMetadata? public var children: [Child] var tree: SyntaxTree { SyntaxTree(kind: kind, metadata: metadata, children: children) } } } extension SyntaxTree { public var text: String { children.map(\.text).joined() } } extension SyntaxTree.Child { public var text: String { switch self { case let .token(tok, _): tok.text case let .tree(tree): tree.text } } public var tree: SyntaxTree? { switch self { case let .tree(tree): tree default: nil } } var token: (Token, TokenMetadata?)? { switch self { case let .token(token, metadata): (token, metadata) default: nil } } var utf16Length: Int { switch self { case let .token(token, _): token.utf16Length case let .tree(tree): tree.utf16Length } } var children: [Self] { switch self { case .token: [] case let .tree(tree): tree.children } } } extension SyntaxTree: CustomStringConvertible { public var description: String { prettyPrint() } /// Pretty-print the tree with indentation. func prettyPrint(indent: String = "") -> String { var result = "\(indent)\(kind)" for child in children { switch child { case .token(let token, _): result += "\n\(indent) \(token.kind): \(token.text.replacingOccurrences(of: "\n", with: "\\n"))" case .tree(let subtree): result += "\n" + subtree.prettyPrint(indent: indent + " ") } } return result } }