// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers // // SPDX-License-Identifier: MPL-2.0 public struct Parser { enum Event: Equatable { case open(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata?) case close case advance(metadata: TokenMetadata?) } public struct MarkOpened { internal let index: Int } let source: String let tokens: [Token] public init(source: String, tokens: [Token]) { self.source = source self.tokens = tokens } public private(set) var diagnostics: [Diagnostic] = [] private var fuel: Int = 0 private var position: Int = 0 private var events: [Event] = [] private var absoluteUtf16Offset: Int = 0 public var absoluteRangeAtCursor: Range { return absoluteUtf16Offset.. { return absoluteUtf16Offset.. Bool { currentToken.kind == kind } public func isAt(kindSatisfying predicate: (TokenKind) -> Bool) -> Bool { return predicate(currentToken.kind) } 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 mutating func advance(metadata: TokenMetadata?) { precondition(!isEndOfFile) events.append(.advance(metadata: metadata)) absoluteUtf16Offset += currentToken.utf16Length position += 1 fuel = 256 } public mutating func advance(error: String?, metadata: TokenMetadata? = nil) { let mark = open() if let error { let diagnostic = Diagnostic( message: error, absoluteRange: absoluteRangeOfCurrentToken ) diagnostics.append(diagnostic) } advance(metadata: metadata) close(mark: mark, kind: .error, metadata: nil) } public mutating func lookahead(_ k: Int) -> TokenKind? { precondition(fuel > 0, "Parser is stuck!") fuel -= 1 let index = position + k guard tokens.indices.contains(index) else { return nil } return tokens[index].kind } public mutating func eat(kindSatisfying predicate: (TokenKind) -> Bool, metadata: TokenMetadata?) -> Bool { guard !isEndOfFile && isAt(kindSatisfying: predicate) else { return false } advance(metadata: metadata) return true } public mutating func eat(kind: TokenKind, metadata: TokenMetadata?) -> Bool { eat(kindSatisfying: { $0 == kind }, metadata: metadata) } public mutating func expect(kind: TokenKind, metadata: TokenMetadata?, error: String? = nil) { if eat(kind: kind, metadata: metadata) { return } let diagnostic = Diagnostic( message: error ?? "Expected \(kind) but got \(currentToken.kind): `\(currentToken.text)`", absoluteRange: absoluteRangeAtCursor ) diagnostics.append(diagnostic) } public var tree: SyntaxTree { var events = events var stack: [SyntaxTree.Builder] = [] var cursor: Int = 0 precondition(events.popLast() == .close) for event in events { switch event { case .open(let kind, let metadata): stack.append(SyntaxTree.Builder(kind: kind, metadata: metadata, children: [])) case .close: let tree = stack.popLast()! stack.modifyLast { last in last.children.append(.tree(tree.tree)) } case .advance(let metadata): let token = tokens[cursor] cursor += 1 stack.modifyLast { last in last.children.append(.token(token, metadata: metadata)) } } } assert(stack.count == 1) return stack.popLast()!.tree } mutating func eatTrivium() -> Bool { switch currentToken.kind { case .whitespace: advance(metadata: nil) return true case .blockComment(let terminated): let metadata = TokenMetadata( semanticTokenType: .comment, delimitedFoldingRangeKind: .comment ) if terminated { advance(metadata: metadata) } else { advance(error: "Block comment was not terminated") } return true case .lineComment: advance(metadata: TokenMetadata(semanticTokenType: .comment)) return true default: return false } } mutating func eatTrivia() { while !isEndOfFile && eatTrivium() {} } } extension Array { fileprivate mutating func modifyLast(_ modifier: (inout Element) -> Void) { if var last = popLast() { modifier(&last) append(last) } } }