// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers // // SPDX-License-Identifier: MPL-2.0 import Foundation public struct SyntaxTreeBuilder { private enum Event: Equatable { case open(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata?) case close case advance(token: Token, metadata: TokenMetadata?) } public struct MarkOpened { internal let index: Int } private var events: [Event] = [] public mutating func advance(token: Token, metadata: TokenMetadata?) { events.append(.advance(token: token, metadata: metadata)) } public mutating func open() -> MarkOpened { let mark = MarkOpened(index: events.count) events.append(.open(kind: .error, metadata: nil)) return mark } public mutating func close(mark: MarkOpened, kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata?) { events[mark.index] = .open(kind: kind, metadata: metadata) events.append(.close) } public var tree: SyntaxTree { var events = events var stack: [SyntaxTree.MutableTree] = [] precondition(events.popLast() == .close) for event in events { switch event { case .open(let kind, let metadata): stack.append(SyntaxTree.MutableTree(kind: kind, metadata: metadata, children: [])) case .close: let tree = stack.popLast()! stack.modifyLast { last in last.children.append(.tree(tree.tree)) } case .advance(let token, let metadata): stack.modifyLast { last in last.children.append(.token(token, metadata: metadata)) } } } assert(stack.count == 1) return stack.popLast()!.tree } } extension Array { fileprivate mutating func modifyLast(_ modifier: (inout Element) -> Void) { if var last = popLast() { modifier(&last) append(last) } } }