// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers // SPDX-FileCopyrightText: 2026 The Project Pterodactyl Developers // // SPDX-License-Identifier: MPL-2.0 import OrderedCollections extension Parser { struct DelimiterInfo { let left: TokenKind let sep: TokenKind let right: TokenKind static var block: DelimiterInfo { DelimiterInfo(left: .blockBegin, sep: .blockSep, right: .blockEnd) } static var curly: DelimiterInfo { DelimiterInfo(left: .punctuation(.lbrace), sep: .punctuation(.semicolon), right: .punctuation(.rbrace)) } } mutating func open(_ delim: DelimiterInfo) -> Bool { guard isAt(kind: delim.left) else { return false } expect(kind: delim.left) _ = eatSeparators(delim) return true } /// A liberal parser that allows unlimited dead delimiters separating only trivia. mutating func eatSeparators(_ delim: DelimiterInfo) -> Bool { eatTrivia() var status = false while eat(kind: delim.sep) { status = true eatTrivia() } return status } mutating func close(_ delim: DelimiterInfo) { eatTrivia() expect(kind: delim.right) } mutating func declLhs() { var handle = structure(kind: .declLhs) defer { handle.close() } problemPattern() } mutating func problemPattern() { var appHandle = structure(kind: .spine) defer { appHandle.close() } do { var handle = structure(kind: .identifier) defer { handle.close() } expect(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .function)) } eatTrivia() var isNonempty = false while trySpineFrame() { eatTrivia() isNonempty = true } if !isNonempty { appHandle.cancel() } } struct Scanner { let tokens: [Token] var position: Int var currentToken: Token { tokens[position] } mutating func eat(kindSatisfying predicate: (TokenKind) -> Bool) -> Bool { guard position < tokens.count, predicate(currentToken.kind) else { return false } position += 1 return true } mutating func eat(kind: TokenKind) -> Bool { eat(kindSatisfying: { $0 == kind }) } mutating func eatTrivia() { while position < tokens.count, currentToken.kind.isTrivia { position += 1 } } } var isAtTypedBinder: Bool { var scanner = Scanner(tokens: tokens, position: self.position) guard scanner.eat(kind: .punctuation(.lparen)) else { return false } scanner.eatTrivia() guard scanner.eat(kind: .identifier) else { return false } scanner.eatTrivia() guard scanner.eat(kind: .punctuation(.colon)) else { return false } return true } mutating func typedBinder() { var handle = structure(kind: .typedBinder) defer { handle.close() } expect(kind: .punctuation(.lparen)) eatTrivia() expect(kind: .identifier) eatTrivia() expect(kind: .punctuation(.colon)) eatTrivia() expression() eatTrivia() expect(kind: .punctuation(.rparen)) } mutating func tryDepFunType() -> Bool { guard isAtTypedBinder else { return false } var handle = structure(kind: .depFunType) defer { handle.close() } while isAtTypedBinder { typedBinder() eatTrivia() } expect(kind: .punctuation(.rightArrow), metadata: TokenMetadata(semanticTokenType: .keyword)) eatTrivia() expression() return true } mutating func tryRecordItem() -> Bool { guard isAt(kind: .identifier) else { return false } var handle = structure() defer { handle.close() } expect(kind: .identifier) eatTrivia() let choices: OrderedDictionary = [ .punctuation(.colon): (TokenMetadata(semanticTokenType: .keyword), .spec), .punctuation(.doubleRightArrow): (TokenMetadata(semanticTokenType: .keyword), .impl) ] if let kind = expect(choices: choices) { handle.kind = .recordItem(kind: kind) eatTrivia() expression() } else { handle.cancel() } return true } mutating func tryRecord() -> Bool { var handle = structure(kind: .record) defer { handle.close() } let delim = DelimiterInfo.curly guard open(delim) else { handle.cancel() return false } defer { close(delim) } while true { _ = tryRecordItem() guard eatSeparators(delim) else { break } } return true } mutating func tryReflExpression() -> Bool { guard isAt(kind: .keyword(.refl)) else { return false } var handle = structure(kind: .refl) defer { handle.close() } expect(kind: .keyword(.refl), metadata: TokenMetadata(semanticTokenType: .keyword)) return true } mutating func universeCodesExpression() -> Bool { let kind = TokenKind.keyword(.type) guard isAt(kind: kind) else { return false } var handle = structure(kind: .universeCodes) defer { handle.close() } expect(kind: kind, metadata: TokenMetadata(semanticTokenType: .type)) // TODO: is this the right semantic token type? return true } mutating func holeExpression() -> Bool { let kind = TokenKind.punctuation(.questionMark) guard isAt(kind: kind) else { return false } var handle = structure(kind: .hole) defer { handle.close() } expect(kind: kind, metadata: TokenMetadata(semanticTokenType: .comment, semanticTokenModifiers: [.abstract])) return true } mutating func identifierExpression() -> Bool { guard isAt(kind: .identifier) else { return false } var handle = structure(kind: .identifier) defer { handle.close() } expect(kind: .identifier) return true } mutating func parenthesisedExpression() -> Bool { guard isAt(kind: .punctuation(.lparen)) else { return false } expect(kind: .punctuation(.lparen)) eatTrivia() expression() eatTrivia() expect(kind: .punctuation(.rparen)) return true } mutating func tryAppFrame() -> Bool { var handle = structure(kind: .appFrame) defer { handle.close() } guard tryAtomicExpression() else { handle.cancel() return false } return true } mutating func tryProjFrame() -> Bool { guard isAt(kind: .punctuation(.dot)) else { return false } var handle = structure(kind: .projFrame) defer { handle.close() } expect(kind: .punctuation(.dot)) expect(kind: .identifier) return true } mutating func trySpineFrame() -> Bool { tryAppFrame() || tryProjFrame() } mutating func trySpineOrIdExpression() -> Bool { var handle = structure() defer { handle.close() } guard tryAtomicExpression() else { handle.cancel() return false } eatTrivia() if isAt(kind: .punctuation(.equal)) { handle.kind = .idType expect(kind: .punctuation(.equal), metadata: TokenMetadata(semanticTokenType: .operator)) eatTrivia() atomicExpression() return true } else { handle.kind = .spine var isNonempty = false while trySpineFrame() { eatTrivia() isNonempty = true } if !isNonempty { handle.cancel() } return true } } mutating func tryAtomicExpression() -> Bool { return holeExpression() || universeCodesExpression() || identifierExpression() || parenthesisedExpression() || tryRecord() || tryReflExpression() } mutating func atomicExpression() { guard tryAtomicExpression() else { return advance(error: "Expected atomic expression") } } mutating func tryExpression() -> Bool { if tryDepFunType() { return true } var handle = structure() defer { handle.close() } guard trySpineOrIdExpression() else { handle.cancel() return false } var scanner = Scanner(tokens: tokens, position: position) scanner.eatTrivia() if scanner.eat(kind: .punctuation(.rightArrow)) { handle.kind = .funType eatTrivia() expect(kind: .punctuation(.rightArrow), metadata: TokenMetadata(semanticTokenType: .keyword)) eatTrivia() expression() } else { handle.cancel() } return true } mutating func expression() { guard tryExpression() else { return advance(error: "Expected expression") } } mutating func declRhs(declKind: SyntaxTreeKind) { var handle = structure(kind: .declRhs) defer { handle.close() } switch declKind { case .claimDecl, .defineDecl: _ = tryExpression() case .refineDecl: _ = eat(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .keyword)) // TODO default: return } } mutating func tryLeafDecl() -> Bool { guard isAt(kind: .identifier) else { return false } var handle = structure() defer { handle.close() } declLhs() eatTrivia() switch currentToken.kind { case .punctuation(.doubleLeftArrow): advance(metadata: TokenMetadata(semanticTokenType: .keyword)) handle.kind = .refineDecl case .punctuation(.doubleRightArrow): advance(metadata: TokenMetadata(semanticTokenType: .keyword)) handle.kind = .defineDecl case .punctuation(.colon): advance(metadata: TokenMetadata(semanticTokenType: .keyword)) handle.kind = .claimDecl default: handle.kind = .probeDecl } eatTrivia() declRhs(declKind: handle.kind) return true } mutating func tryDecl() -> Bool { return tryTheory() || tryLeafDecl() } mutating func keyword(_ keyword: Keyword) { expect(kind: .keyword(keyword), metadata: TokenMetadata(semanticTokenType: .keyword)) } mutating func theoryName() { var handle = structure(kind: .theoryName) defer { handle.close() } expect(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .interface)) } mutating func theoryBlock() { var handle = recoveryStack.push([.blockSep, .blockEnd]) defer { handle.close() } let delim = DelimiterInfo.block guard open(delim) else { return } defer { close(delim) } while true { _ = tryDecl() guard eatSeparators(delim) else { break } } } mutating func tryTheory() -> Bool { guard isAt(kind: .keyword(.theory)) else { return false } var handle = structure(kind: .theory, metadata: SyntaxTreeMetadata(delimitedFoldingRangeKind: .region)) defer { handle.close() } eatTrivia() keyword(.theory) eatTrivia() theoryName() eatTrivia() keyword(.where) eatTrivia() theoryBlock() return true } mutating func importName() { var handle = structure(kind: .importName) defer { handle.close() } expect(kind: .identifier) } mutating func importDecl() -> Bool { guard isAt(kind: .keyword(.import)) else { return false } var handle = structure(kind: .import) defer { handle.close() } keyword(.import) eatTrivia() importName() return true } mutating func tryDocumentItem() -> Bool { if importDecl() { return true } return tryDecl() } mutating func documentBlock() { var handle = recoveryStack.push([.blockSep, .blockEnd]) defer { handle.close() } let delim = DelimiterInfo.block guard open(delim) else { return } defer { close(delim) } while true { _ = tryDocumentItem() guard eatSeparators(delim) else { break } } } public mutating func document() { var handle = structure(kind: .document) defer { handle.close() } eatTrivia() documentBlock() eatTrivia() expect(kind: .eof) } }