// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers // SPDX-FileCopyrightText: 2026 The Project Pterodactyl Developers // // SPDX-License-Identifier: MPL-2.0 public struct SyntaxTreeBuilder { private enum Event: Equatable { case open case close(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata?) case advance(token: Token, metadata: TokenMetadata?) } private var events: [Event] = [] public init() {} public struct Checkpoint { fileprivate let position: Int } public var checkpoint: Checkpoint { Checkpoint(position: events.count) } public mutating func openAtCheckpoint(_ checkpoint: Checkpoint) { events.insert(.open, at: checkpoint.position) } public mutating func open() { events.append(.open) } public mutating func close(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata? = nil) { events.append(.close(kind: kind, metadata: metadata)) } public mutating func advance(token: Token, metadata: TokenMetadata?) { events.append(.advance(token: token, metadata: metadata)) } public var tree: SyntaxTree { var stack: [SyntaxTree.MutableTree] = [] for event in events { switch event { case .open: stack.append(SyntaxTree.MutableTree(kind: .error, metadata: nil, children: [])) case .close(let kind, let metadata): guard var node = stack.popLast() else { fatalError("More close events than open events") } node.kind = kind node.metadata = metadata guard let parentIndex = stack.indices.last else { return node.tree } stack[parentIndex].children.append(.tree(node.tree)) case .advance(let token, let metadata): guard let parentIndex = stack.indices.last else { fatalError("Attempted to insert token at root of parse tree") } stack[parentIndex] .children .append(.token(token, metadata: metadata)) } } if !stack.isEmpty { fatalError("More open events than close events") } return SyntaxTree(kind: .error, children: []) } }