this repo has no description www.jonmsterling.com/01HC/
dependent-types proof-assistant swift
at main 649 lines 19 kB view raw
1// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers 2// SPDX-FileCopyrightText: 2026 The Project Pterodactyl Developers 3// 4// SPDX-License-Identifier: MPL-2.0 5 6import OrderedCollections 7import PterodactylFeedback 8 9enum PrecedenceGroup { 10 case equality 11 case arrow 12 13 enum Associativity { 14 case right 15 case none 16 } 17 18 var associativity: Associativity { 19 switch self { 20 case .equality: return .none 21 case .arrow: return .right 22 } 23 } 24 25 enum Relation { 26 case tighter 27 case looser 28 case same 29 case unrelated 30 } 31 32 private var ordinal: Int { 33 switch self { 34 case .arrow: return 0 35 case .equality: return 1 36 } 37 } 38 39 func relation(to other: PrecedenceGroup) -> Relation { 40 if self == other { return .same } 41 return ordinal > other.ordinal ? .tighter : .looser 42 } 43 44 func canBind(above lowerBound: PrecedenceGroup?) -> Bool { 45 guard let lowerBound else { return true } 46 switch relation(to: lowerBound) { 47 case .tighter: return true 48 case .looser: return false 49 case .same: return lowerBound.associativity == .right 50 case .unrelated: return true 51 } 52 } 53} 54 55 56struct Scanner { 57 let tokens: [Token] 58 var position: Int 59 60 var currentToken: Token? { 61 if tokens.indices.contains(position) { 62 tokens[position] 63 } else { 64 nil 65 } 66 } 67 68 mutating func eat(kindSatisfying predicate: (TokenKind) -> Bool) -> Bool { 69 guard let currentToken, predicate(currentToken.kind) else { return false } 70 position += 1 71 return true 72 } 73 74 mutating func eat(kind: TokenKind) -> Bool { 75 eat(kindSatisfying: { $0 == kind }) 76 } 77 78 mutating func eatTrivia() { 79 while let currentToken, currentToken.kind.isTrivia { position += 1 } 80 } 81 82 public func isAt(kind: TokenKind) -> Bool { 83 currentToken?.kind == kind 84 } 85 86 public func isAt(kindSatisfying predicate: (TokenKind) -> Bool) -> Bool { 87 guard let currentToken else { return false } 88 return predicate(currentToken.kind) 89 } 90} 91 92 93extension Parser { 94 var scanner: Scanner { 95 Scanner(tokens: tokens, position: position) 96 } 97} 98 99 100struct DelimiterInfo { 101 let left: TokenKind 102 let sep: TokenKind 103 let right: TokenKind 104 105 static var block: DelimiterInfo { 106 DelimiterInfo(left: .blockBegin, sep: .blockSep, right: .blockEnd) 107 } 108 109 static var curly: DelimiterInfo { 110 DelimiterInfo(left: .punctuation(.lbrace), sep: .punctuation(.semicolon), right: .punctuation(.rbrace)) 111 } 112 113 var leftProduction: Production { 114 Parser.token(kind: left) 115 .then { state in 116 _ = separatorProduction.tryParse(&state) 117 } 118 } 119 120 var separatorProduction: Production { 121 Parser.token(kind: sep).precededByTrivia.many1 122 } 123 124 var rightProduction: Production { 125 Parser.token(kind: right).precededByTrivia 126 } 127} 128 129extension Parser { 130 mutating func keyword(_ keyword: Keyword) { 131 expect(kind: .keyword(keyword), metadata: TokenMetadata(semanticTokenType: .keyword)) 132 } 133} 134 135struct Production { 136 var name: String 137 var _precondition: (_ state: Scanner) -> Bool 138 var _commit: (_ state: inout Parser) -> Void 139 140 init(name: String, precondition: @escaping (_: Scanner) -> Bool, commit: @escaping (_: inout Parser) -> Void) { 141 self.name = name 142 self._precondition = precondition 143 self._commit = commit 144 } 145 146 init( 147 name: String, 148 kind: SyntaxTreeKind, 149 metadata: SyntaxTreeMetadata? = nil, 150 precondition: @escaping (_: Scanner) -> Bool, 151 commit: @escaping (_: inout Parser) -> Void 152 ) { 153 self.init(name: name) { state in 154 precondition(state) 155 } commit: { state in 156 state.open() 157 defer { state.close(kind: kind, metadata: metadata) } 158 commit(&state) 159 } 160 } 161 162 func then(_ body: @escaping (_ state: inout Parser) -> Void) -> Production { 163 Production(name: "\(name).then") { scanner in 164 self._precondition(scanner) 165 } commit: { state in 166 self._commit(&state) 167 body(&state) 168 } 169 } 170 171 func precondition(_ state: Scanner) -> Bool { 172 _precondition(state) 173 } 174 175 func commit(_ state: inout Parser) { 176 let position = state.position 177 _commit(&state) 178 assert(state.position > position, "Parser `\(name)` did not consume any tokens.") 179 } 180 181 func tryParse(_ state: inout Parser, error: String? = nil) -> Bool { 182 guard precondition(state.scanner) else { 183 if let error { 184 state.diagnostics.append(Diagnostic(message: error, severity: .error, utf16Range: state.absoluteRangeOfCurrentToken)) 185 } 186 return false 187 } 188 commit(&state) 189 return true 190 } 191 192 func parse(_ state: inout Parser, error: String) -> Bool { 193 if !tryParse(&state) { 194 state.recover(until: state.recoveryStack.current, expecting: [], message: error) 195 return false 196 } 197 198 return true 199 } 200 201 var precededByTrivia: Production { 202 Production(name: self.name + ".precededByTrivia") { scanner in 203 var scanner = scanner 204 scanner.eatTrivia() 205 return precondition(scanner) 206 } commit: { state in 207 state.eatTrivia() 208 _commit(&state) 209 } 210 } 211 212 func wrap(name: String, kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata? = nil) -> Production { 213 Production(name: name, kind: kind, metadata: metadata, precondition: _precondition, commit: _commit) 214 } 215 216 static func cascade(_ productions: [Production]) -> Production { 217 Production(name: "cascade[_\(productions.map(\.name))]") { scanner in 218 productions.contains { $0.precondition(scanner) } 219 } commit: { state in 220 for production in productions { 221 if production.tryParse(&state) { 222 return 223 } 224 } 225 } 226 } 227 228 func delimitedList(_ delim: DelimiterInfo) -> Production { 229 delim.leftProduction.then { state in 230 state.recoveryStack.push([.blockSep, .blockEnd]) 231 defer { state.recoveryStack.pop() } 232 defer { _ = delim.rightProduction.parse(&state, error: "Missing closing delimiter: \(delim.right)") } 233 while true { 234 _ = precededByTrivia.tryParse(&state) 235 guard delim.separatorProduction.tryParse(&state) else { break } 236 } 237 } 238 } 239 240 var many1: Production { 241 return then { state in 242 while precededByTrivia.tryParse(&state) {} 243 } 244 } 245} 246 247extension Parser { 248 static func token(kind: TokenKind, metadata: TokenMetadata? = nil) -> Production { 249 Production(name: "token[\(kind)]") { scanner in 250 scanner.isAt(kind: kind) 251 } commit: { state in 252 state.expect(kind: kind, metadata: metadata) 253 } 254 } 255 256 private mutating func typedBinder() { 257 recoveryStack.push([.punctuation(.lparen), .punctuation(.colon), .punctuation(.rparen)]) 258 defer { recoveryStack.pop() } 259 260 expect(kind: .punctuation(.lparen)) 261 let gotIdentifier = Self.token(kind: .identifier).precededByTrivia.tryParse(&self, error: "Binder: expected identifier here") 262 let gotColon = Self.token(kind: .punctuation(.colon)).precededByTrivia.tryParse(&self, error: gotIdentifier ? "Binder: expected type annotation here" : nil) 263 let gotTypeExpr = gotColon ? Self.expression.precededByTrivia.parse(&self, error: "Binder: expected type expression here") : false 264 _ = Self.token(kind: .punctuation(.rparen)).precededByTrivia.tryParse(&self, error: gotTypeExpr ? "Binder: expected closing parenthesis" : nil) 265 } 266 267 static var funTypeBinder: Production { 268 Production(name: "funTypeBinder", kind: .typedBinder) { scanner in 269 var scanner = scanner 270 guard scanner.eat(kind: .punctuation(.lparen)) else { return false } 271 scanner.eatTrivia() 272 guard scanner.eat(kind: .identifier) else { return false } 273 scanner.eatTrivia() 274 guard scanner.eat(kind: .punctuation(.colon)) else { return false } 275 return true 276 } commit: { 277 $0.typedBinder() 278 } 279 } 280 281 static var programParam: Production { 282 Production(name: "programParam", kind: .typedBinder) { 283 $0.isAt(kind: .punctuation(.lparen)) 284 } commit: { 285 $0.typedBinder() 286 } 287 } 288 289 static var holeExpression: Production { 290 token( 291 kind: .punctuation(.questionMark), 292 metadata: TokenMetadata(semanticTokenType: .comment, semanticTokenModifiers: [.abstract]) 293 ) 294 .wrap(name: "holeExpression", kind: .hole) 295 } 296 297 static var universeCodesExpression: Production { 298 token(kind: .keyword(.type), metadata: TokenMetadata(semanticTokenType: .type)) 299 .wrap(name: "universeCodesExpression", kind: .universeCodes) 300 } 301 302 static var parenthesisedExpression: Production { 303 token(kind: .punctuation(.lparen)) 304 .then { state in 305 _ = expression.precededByTrivia.tryParse(&state, error: "Expected expression") 306 _ = token(kind: .punctuation(.rparen)).precededByTrivia.parse(&state, error: "Expected right parenthesis") 307 } 308 } 309 310 static var reflExpression: Production { 311 token(kind: .keyword(.refl), metadata: TokenMetadata(semanticTokenType: .keyword)) 312 .wrap(name: "reflExpression", kind: .refl) 313 } 314 315 static var identifierExpression: Production { 316 token(kind: .identifier) 317 .wrap(name: "identifierExpression", kind: .identifier) 318 } 319 320 static var atomicExpression: Production { 321 .cascade([ 322 recordType, 323 holeExpression, 324 universeCodesExpression, 325 identifierExpression, 326 parenthesisedExpression, 327 recordExpression, 328 reflExpression 329 ]) 330 } 331 332 static var depFunType: Production { 333 let typedBinder = Self.funTypeBinder 334 return typedBinder.then { state in 335 while typedBinder.precededByTrivia.tryParse(&state) {} 336 337 let gotArrow = 338 token(kind: .punctuation(.rightArrow), metadata: TokenMetadata(semanticTokenType: .keyword)) 339 .precededByTrivia 340 .tryParse(&state, error: "Expected function arrow `->`") 341 342 if gotArrow { 343 _ = expression.precededByTrivia.parse(&state, error: "Expected type expression to the right of function arrow") 344 } 345 346 } 347 .wrap(name: "depFunType", kind: .depFunType) 348 } 349 350 static var recordType: Production { 351 token(kind: .keyword(.record), metadata: TokenMetadata(semanticTokenType: .keyword)).then { state in 352 _ = recordItemSpec.delimitedList(.curly).precededByTrivia.parse(&state, error: "Expected record type specification") 353 } 354 .wrap(name: "recordType", kind: .recordType) 355 } 356 357 private static func afterIdentifier(_ kind: TokenKind) -> (Scanner) -> Bool { 358 return { scanner in 359 var scanner = scanner 360 guard scanner.eat(kind: .identifier) else { return false } 361 scanner.eatTrivia() 362 return scanner.eat(kind: kind) 363 } 364 } 365 366 static var recordItemSpec: Production { 367 Production( 368 name: "recordItemSpec", 369 kind: .recordItem(kind: .spec), 370 precondition: afterIdentifier(.punctuation(.colon)) 371 ) { state in 372 state.expect(kind: .identifier) 373 _ = token(kind: .punctuation(.colon), metadata: TokenMetadata(semanticTokenType: .keyword)) 374 .precededByTrivia 375 .parse(&state, error: "Expected `:`") 376 _ = expression.precededByTrivia.parse(&state, error: "Expected expression") 377 } 378 } 379 380 static var namedRecordItemImpl: Production { 381 Production( 382 name: "recordItemImpl", 383 kind: .recordItem(kind: .impl), 384 precondition: afterIdentifier(.punctuation(.doubleRightArrow)) 385 ) { state in 386 state.expect(kind: .identifier) 387 _ = token(kind: .punctuation(.doubleRightArrow), metadata: TokenMetadata(semanticTokenType: .keyword)) 388 .precededByTrivia 389 .parse(&state, error: "Expected `=>`") 390 _ = expression.precededByTrivia.parse(&state, error: "Expected expression") 391 } 392 } 393 394 static var unnamedRecordItemImpl: Production { 395 expression.wrap(name: "unnamedRecordItemImpl", kind: .recordItem(kind: .impl)) 396 } 397 398 static var recordItemImpl: Production { 399 .cascade([namedRecordItemImpl, unnamedRecordItemImpl]) 400 } 401 402 static var recordExpression: Production { 403 recordItemImpl.delimitedList(.curly).wrap(name: "recordExpression", kind: .record) 404 } 405 406 static var appFrame: Production { 407 atomicExpression.wrap(name: "appFrame", kind: .appFrame) 408 } 409 410 static var dotFrame: Production { 411 token(kind: .punctuation(.dot)) 412 .then { $0.expect(kind: .identifier) } 413 .wrap(name: "dotFrame", kind: .projFrame) 414 } 415 416 static var spineFrame: Production { 417 .cascade([ 418 appFrame, 419 dotFrame 420 ]) 421 } 422 423 private static var operatorTable: [(TokenKind, SyntaxTreeKind, PrecedenceGroup)] { 424 [ 425 (.punctuation(.equal), .idType, .equality), 426 (.punctuation(.rightArrow), .funType, .arrow) 427 ] 428 } 429 430 private static func peekOperator(_ scanner: Scanner) -> (TokenKind, SyntaxTreeKind, PrecedenceGroup)? { 431 var scanner = scanner 432 scanner.eatTrivia() 433 for (tokenKind, treeKind, group) in operatorTable { 434 if scanner.isAt(kind: tokenKind) { 435 return (tokenKind, treeKind, group) 436 } 437 } 438 return nil 439 } 440 441 static func spineExpression() -> Production { 442 Production(name: "spineExpression") { 443 atomicExpression.precondition($0) 444 } commit: { state in 445 let checkPoint = state.checkpoint 446 _ = atomicExpression.parse(&state, error: "Expected expression") 447 448 if spineFrame.precededByTrivia.precondition(state.scanner) { 449 state.openAtCheckpoint(checkPoint) 450 while spineFrame.precededByTrivia.tryParse(&state) {} 451 state.close(kind: .spine) 452 } 453 } 454 } 455 456 static func operatorExpression(lowerBound: PrecedenceGroup?) -> Production { 457 Production(name: "operatorExpression") { 458 spineExpression().precondition($0) 459 } commit: { state in 460 var checkPoint = state.checkpoint 461 _ = spineExpression().parse(&state, error: "Expected expression") 462 463 while let (tokenKind, treeKind, group) = peekOperator(state.scanner), group.canBind(above: lowerBound) { 464 state.openAtCheckpoint(checkPoint) 465 state.eatTrivia() 466 state.expect(kind: tokenKind, metadata: TokenMetadata(semanticTokenType: .operator)) 467 _ = Self.operatorExpression(lowerBound: group) 468 .precededByTrivia 469 .parse(&state, error: "Expected argument on right hand side of `\(tokenKind)` operator") 470 state.close(kind: treeKind) 471 checkPoint = state.checkpoint 472 } 473 } 474 } 475 476 static var operatorExpression: Production { 477 operatorExpression(lowerBound: nil) 478 } 479 480 static var expression: Production { 481 .cascade([depFunType, lambdaExpression, operatorExpression]) 482 } 483 484 static var lambdaExpression: Production { 485 token(kind: .punctuation(.backslash)).then { state in 486 _ = token(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .variable)).wrap(name: "untypedBinder", kind: .untypedBinder).precededByTrivia.many1.tryParse(&state) 487 _ = token(kind: .punctuation(.doubleRightArrow)).precededByTrivia.parse(&state, error: "Expected `=>`") 488 _ = expression.precededByTrivia.parse(&state, error: "Expected expression") 489 } 490 .wrap(name: "lambdaExpression", kind: .lambda) 491 } 492 493 static var decl: Production { 494 .cascade([theory, programDecl]) 495 } 496 497 static var theory: Production { 498 token(kind: .keyword(.theory), metadata: TokenMetadata(semanticTokenType: .keyword)) 499 .then { state in 500 _ = theoryName.precededByTrivia.parse(&state, error: "Expected theory name") 501 let gotWhere = 502 token(kind: .keyword(.where), metadata: TokenMetadata(semanticTokenType: .keyword)) 503 .precededByTrivia 504 .parse(&state, error: "Expected `where` keyword") 505 if gotWhere { 506 _ = decl.delimitedList(.block).parse(&state, error: "Expected theory block") 507 } 508 } 509 .wrap(name: "theory", kind: .theory, metadata: SyntaxTreeMetadata(delimitedFoldingRangeKind: .region)) 510 } 511 512 static var theoryName: Production { 513 token(kind: .identifier, metadata: TokenMetadata(semanticTokenType: .interface)) 514 .wrap(name: "theoryName", kind: .theoryName) 515 } 516 517 static var programName: Production { 518 token(kind: .identifier) 519 .wrap(name: "programName", kind: .programName) 520 } 521 522 static var programClauseLhs: Production { 523 expression.wrap(name: "programClauseLhs", kind: .programClauseLhs) 524 } 525 526 static var programReturn: Production { 527 token(kind: .punctuation(.doubleRightArrow), metadata: TokenMetadata(semanticTokenType: .operator)) 528 .then { state in 529 _ = expression.precededByTrivia.tryParse(&state, error: "Expected right-hand side to return expression") 530 } 531 .wrap(name: "programReturn", kind: .programReturn) 532 } 533 534 static var programGadgetSplit: Production { 535 token(kind: .keyword(.split)) 536 .wrap(name: "programGadgetSplit", kind: .splitGadget) 537 } 538 539 static var programGadgetIntro: Production { 540 token(kind: .keyword(.intro)) 541 .wrap(name: "programGadgetIntro", kind: .introGadget) 542 } 543 544 static var programGadget: Production { 545 .cascade([programGadgetSplit, programGadgetIntro]) 546 } 547 548 static var programBy: Production { 549 token(kind: .punctuation(.doubleLeftArrow), metadata: TokenMetadata(semanticTokenType: .operator)) 550 .then { state in 551 _ = programGadget.precededByTrivia.tryParse(&state, error: "Expected `intro` or `split` gadget after `<=`.") 552 } 553 .wrap(name: "programBy", kind: .programBy) 554 } 555 556 static var programCommand: Production { 557 .cascade([programReturn, programBy]) 558 } 559 560 static var programType: Production { 561 token(kind: .punctuation(.colon)) 562 .then { state in 563 _ = expression.precededByTrivia.parse(&state, error: "Expected a type expression here") 564 } 565 .wrap(name: "programType", kind: .programType) 566 } 567 568 static var programMatchClause: Production { 569 programClauseLhs 570 .then { state in 571 _ = 572 programCommand 573 .precededByTrivia 574 .wrap(name: "programMatchClauseRhs", kind: .programClauseRhs) 575 .tryParse(&state, error: "Expected right-hand side to program match clause") 576 } 577 .wrap(name: "programMatchClauseRhs", kind: .programMatchClause) 578 } 579 580 static var programDeclWhereBody: Production { 581 token(kind: .keyword(.where), metadata: TokenMetadata(semanticTokenType: .keyword)) 582 .then { state in 583 _ = 584 Production.cascade([programCommand, programMatchClause]) 585 .delimitedList(.block) 586 .precededByTrivia 587 .tryParse(&state) 588 } 589 } 590 591 static var programDeclBody: Production { 592 .cascade([programReturn, programDeclWhereBody]) 593 .wrap(name: "programDeclBody", kind: .programBody) 594 } 595 596 static var programDecl: Production { 597 programName.then { state in 598 let gotParams = 599 programParam 600 .many1 601 .wrap(name: "programParams", kind: .programParams) 602 .precededByTrivia 603 .tryParse(&state) 604 605 let gotType = programType.precededByTrivia.parse( 606 &state, 607 error: gotParams ? "Program declaration: expected type annotation here" : "Program declaration: expected parameters or type annotation here" 608 ) 609 610 _ = programDeclBody.precededByTrivia.tryParse(&state, error: gotType ? "Expected `=>` or `where` clause" : nil) 611 } 612 .wrap(name: "programDecl", kind: .programDecl, metadata: SyntaxTreeMetadata(delimitedFoldingRangeKind: .region)) 613 } 614 615 static var importName: Production { 616 token(kind: .identifier) 617 .wrap(name: "importName", kind: .importName) 618 } 619 620 static var importDecl: Production { 621 token(kind: .keyword(.import), metadata: TokenMetadata(semanticTokenType: .keyword)) 622 .then { state in 623 _ = importName.precededByTrivia.parse(&state, error: "Expected import name") // TODO 624 } 625 .wrap(name: "importDecl", kind: .import) 626 } 627 628 static var documentItem: Production { 629 .cascade([importDecl, decl]) 630 } 631 632 static var document: Production { 633 let documentBlockAndEof = 634 documentItem.delimitedList(.block).then { state in 635 _ = token(kind: .eof).precededByTrivia.parse(&state, error: "Expected EOF") 636 } 637 638 return 639 .cascade([token(kind: .eof), documentBlockAndEof]) 640 .precededByTrivia 641 .wrap(name: "document", kind: .document) 642 } 643} 644 645extension Parser { 646 public mutating func document() { 647 _ = Self.document.parse(&self, error: "Invalid start of document") 648 } 649}