1// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers 2// 3// SPDX-License-Identifier: MPL-2.0 4 5import Foundation 6 7public struct SyntaxTree: Codable, Sendable { 8 public let kind: SyntaxTreeKind 9 public let metadata: SyntaxTreeMetadata? 10 public let children: [Child] 11 public let utf16Length: Int 12 13 public enum Child: Codable, Sendable { 14 case token(Token, metadata: TokenMetadata?) 15 case tree(SyntaxTree) 16 } 17 18 public init(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata? = nil, children: [SyntaxTree.Child]) { 19 self.kind = kind 20 self.metadata = metadata 21 self.children = children 22 self.utf16Length = children.reduce(0) { length, child in 23 length + child.utf16Length 24 } 25 } 26} 27 28extension SyntaxTree { 29 /// A mutable version of ``SyntaxTree`` that does not keep track of textual length, for use when constructing trees. 30 public struct Builder { 31 public var kind: SyntaxTreeKind 32 public var metadata: SyntaxTreeMetadata? 33 public var children: [Child] 34 35 var tree: SyntaxTree { 36 SyntaxTree(kind: kind, metadata: metadata, children: children) 37 } 38 } 39} 40 41extension SyntaxTree { 42 public var text: String { 43 children.map(\.text).joined() 44 } 45} 46 47extension SyntaxTree.Child { 48 public var text: String { 49 switch self { 50 case let .token(tok, _): tok.text 51 case let .tree(tree): tree.text 52 } 53 } 54 55 public var tree: SyntaxTree? { 56 switch self { 57 case let .tree(tree): tree 58 default: nil 59 } 60 } 61 62 var token: (Token, TokenMetadata?)? { 63 switch self { 64 case let .token(token, metadata): (token, metadata) 65 default: nil 66 } 67 } 68 69 var utf16Length: Int { 70 switch self { 71 case let .token(token, _): token.utf16Length 72 case let .tree(tree): tree.utf16Length 73 } 74 } 75 76 var children: [Self] { 77 switch self { 78 case .token: [] 79 case let .tree(tree): tree.children 80 } 81 } 82} 83 84extension SyntaxTree: CustomStringConvertible { 85 public var description: String { 86 prettyPrint() 87 } 88 89 /// Pretty-print the tree with indentation. 90 func prettyPrint(indent: String = "") -> String { 91 var result = "\(indent)\(kind)" 92 for child in children { 93 switch child { 94 case .token(let token, _): 95 result += "\n\(indent) \(token.kind): \(token.text.replacingOccurrences(of: "\n", with: "\\n"))" 96 case .tree(let subtree): 97 result += "\n" + subtree.prettyPrint(indent: indent + " ") 98 } 99 } 100 return result 101 } 102}