this repo has no description
www.jonmsterling.com/01HC/
1// SPDX-FileCopyrightText: 2025 The Project Pterodactyl Developers
2//
3// SPDX-License-Identifier: MPL-2.0
4
5public struct Parser {
6 enum Event: Equatable {
7 case open(kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata?)
8 case close
9 case advance(metadata: TokenMetadata?)
10 }
11
12 public struct MarkOpened {
13 internal let index: Int
14 }
15
16 let source: String
17 let tokens: [Token]
18
19 public init(source: String, tokens: [Token]) {
20 self.source = source
21 self.tokens = tokens
22 }
23
24 public private(set) var diagnostics: [Diagnostic] = []
25
26 private var fuel: Int = 0
27 private var position: Int = 0
28 private var events: [Event] = []
29 private var absoluteUtf16Offset: Int = 0
30 public var absoluteRangeAtCursor: Range<Int> {
31 return absoluteUtf16Offset..<absoluteUtf16Offset
32 }
33
34 public var absoluteRangeOfCurrentToken: Range<Int> {
35 return absoluteUtf16Offset..<absoluteUtf16Offset + currentToken.utf16Length
36 }
37
38 public var isEndOfFile: Bool {
39 position == tokens.count
40 }
41
42 public var currentToken: Token {
43 if tokens.indices.contains(position) {
44 return tokens[position]
45 } else {
46 return Token(
47 kind: .eof,
48 text: "",
49 )
50 }
51 }
52
53 public func isAt(kind: TokenKind) -> Bool {
54 currentToken.kind == kind
55 }
56
57 public func isAt(kindSatisfying predicate: (TokenKind) -> Bool) -> Bool {
58 return predicate(currentToken.kind)
59 }
60
61
62 public mutating func open() -> MarkOpened {
63 let mark = MarkOpened(index: events.count)
64 events.append(.open(kind: .error, metadata: nil))
65 return mark
66 }
67
68 public mutating func close(mark: MarkOpened, kind: SyntaxTreeKind, metadata: SyntaxTreeMetadata?) {
69 events[mark.index] = .open(kind: kind, metadata: metadata)
70 events.append(.close)
71 }
72
73 public mutating func advance(metadata: TokenMetadata?) {
74 precondition(!isEndOfFile)
75 events.append(.advance(metadata: metadata))
76 absoluteUtf16Offset += currentToken.utf16Length
77 position += 1
78 fuel = 256
79 }
80
81 public mutating func advance(error: String?, metadata: TokenMetadata? = nil) {
82 let mark = open()
83 if let error {
84 let diagnostic = Diagnostic(
85 message: error,
86 absoluteRange: absoluteRangeOfCurrentToken
87 )
88
89 diagnostics.append(diagnostic)
90 }
91 advance(metadata: metadata)
92 close(mark: mark, kind: .error, metadata: nil)
93 }
94
95 public mutating func lookahead(_ k: Int) -> TokenKind? {
96 precondition(fuel > 0, "Parser is stuck!")
97 fuel -= 1
98 let index = position + k
99 guard tokens.indices.contains(index) else { return nil }
100 return tokens[index].kind
101 }
102
103 public mutating func eat(kindSatisfying predicate: (TokenKind) -> Bool, metadata: TokenMetadata?) -> Bool {
104 guard !isEndOfFile && isAt(kindSatisfying: predicate) else { return false }
105 advance(metadata: metadata)
106 return true
107 }
108
109 public mutating func eat(kind: TokenKind, metadata: TokenMetadata?) -> Bool {
110 eat(kindSatisfying: { $0 == kind }, metadata: metadata)
111 }
112
113 public mutating func expect(kind: TokenKind, metadata: TokenMetadata?, error: String? = nil) {
114 if eat(kind: kind, metadata: metadata) { return }
115 let diagnostic = Diagnostic(
116 message: error ?? "Expected \(kind) but got \(currentToken.kind): `\(currentToken.text)`",
117 absoluteRange: absoluteRangeAtCursor
118 )
119 diagnostics.append(diagnostic)
120 }
121
122 public var tree: SyntaxTree {
123 var events = events
124 var stack: [SyntaxTree.Builder] = []
125 var cursor: Int = 0
126
127 precondition(events.popLast() == .close)
128
129 for event in events {
130 switch event {
131 case .open(let kind, let metadata):
132 stack.append(SyntaxTree.Builder(kind: kind, metadata: metadata, children: []))
133 case .close:
134 let tree = stack.popLast()!
135 stack.modifyLast { last in
136 last.children.append(.tree(tree.tree))
137 }
138 case .advance(let metadata):
139 let token = tokens[cursor]
140 cursor += 1
141 stack.modifyLast { last in
142 last.children.append(.token(token, metadata: metadata))
143 }
144 }
145 }
146
147 assert(stack.count == 1)
148 return stack.popLast()!.tree
149 }
150
151 mutating func eatTrivium() -> Bool {
152 switch currentToken.kind {
153 case .whitespace:
154 advance(metadata: nil)
155 return true
156 case .blockComment(let terminated):
157 let metadata = TokenMetadata(
158 semanticTokenType: .comment,
159 delimitedFoldingRangeKind: .comment
160 )
161 if terminated {
162 advance(metadata: metadata)
163 } else {
164 advance(error: "Block comment was not terminated")
165 }
166 return true
167 case .lineComment:
168 advance(metadata: TokenMetadata(semanticTokenType: .comment))
169 return true
170 default:
171 return false
172 }
173 }
174
175 mutating func eatTrivia() {
176 while !isEndOfFile && eatTrivium() {}
177 }
178
179}
180
181extension Array {
182 fileprivate mutating func modifyLast(_ modifier: (inout Element) -> Void) {
183 if var last = popLast() {
184 modifier(&last)
185 append(last)
186 }
187 }
188}