slow.monster

STUDIO HAZE · topaz

Topaz source

Open by house policy. Topaz is our own language.

Topaz 5.20 · 56 files

compiler/crates/topaz_self_frontend/topaz/parser/core.tpz

Topaz · 11077 B · Original file

1// Parser cursor, diagnostics, node construction, and recovery primitives.
2// Grammar leaves share this state and these token helpers; no grammar family is
3// selected or dispatched from the core itself.
5import types {
6 PreviewAstAttribute,
7 PreviewDiagnostic,
8 PreviewToken,
9 SyntaxNode,
12export record ParserRequest {
13 kind: string,
14 precedence: int,
15 field: string,
16 index: int,
19// Position, synthetic-token, and diagnostic state shared across recursive grammar services.
20export record Parser {
21 source: Bytes,
22 tokens: Array<PreviewToken>,
23 position: Array<int>,
24 lastHi: Array<int>,
25 pendingGt: Array<int>,
26 nakedLambda: Array<bool>,
27 recordUpdate: Array<bool>,
28 diagnostics: Array<PreviewDiagnostic>,
31export type ParserService = (Parser, ParserRequest) -> Result<SyntaxNode, string>
33export function invokeParseBlock(
34 service: ParserService,
35 parser: Parser,
36 field: string,
37 index: int,
38) -> Result<SyntaxNode, string> {
39 service(parser, ParserRequest {
40 kind: "parse-block",
41 precedence: 0,
42 field: field,
43 index: index,
44 })
47export function invokeParseExpressionBp(
48 service: ParserService,
49 parser: Parser,
50 precedence: int,
51 field: string,
52 index: int,
53) -> Result<SyntaxNode, string> {
54 service(parser, ParserRequest {
55 kind: "parse-expression-bp",
56 precedence: precedence,
57 field: field,
58 index: index,
59 })
62export function boxValue(values: Array<int>) -> int {
63 values[0]
66export function setBox(values: Array<int>, value: int) -> () {
67 let mut target = values
68 target[0] = value
69 ()
72export function lambdaAllowed(parser: Parser) -> bool {
73 parser.nakedLambda[0]
76export function setLambdaAllowed(parser: Parser, value: bool) -> () {
77 let mut target = parser.nakedLambda
78 target[0] = value
79 ()
82export function recordUpdateAllowed(parser: Parser) -> bool {
83 parser.recordUpdate[0]
86export function setRecordUpdateAllowed(parser: Parser, value: bool) -> () {
87 let mut target = parser.recordUpdate
88 target[0] = value
89 ()
92export function layoutTokenAt(parser: Parser, position: int) -> PreviewToken {
93 match parser.tokens.get(position) {
94 case Some(value) => value
95 case None => parser.tokens[parser.tokens.length - 1]
96 }
99export function tokenAt(parser: Parser, offset: int) -> PreviewToken {
100 if parser.pendingGt.length > 0 {
101 if offset == 0 {
102 let hi = boxValue(parser.pendingGt)
103 return PreviewToken {
104 kind: "gt",
105 lo: hi - 1,
106 hi: hi,
107 stream: "layout",
108 synthetic: true,
109 }
110 }
111 let position = boxValue(parser.position) + offset - 1
112 return layoutTokenAt(parser, position)
113 }
114 let position = boxValue(parser.position) + offset
115 layoutTokenAt(parser, position)
118export function current(parser: Parser) -> PreviewToken {
119 tokenAt(parser, 0)
122export function at(parser: Parser, kind: string) -> bool {
123 current(parser).kind == kind
126export function bump(parser: Parser) -> PreviewToken {
127 let value = current(parser)
128 if parser.pendingGt.length > 0 {
129 let mut pending = parser.pendingGt
130 pending.pop()
131 setBox(parser.lastHi, value.hi)
132 return value
133 }
134 if value.kind != "eof" {
135 setBox(parser.position, boxValue(parser.position) + 1)
136 }
137 setBox(parser.lastHi, value.hi)
138 value
141export function eat(parser: Parser, kind: string) -> bool {
142 if at(parser, kind) {
143 bump(parser)
144 true
145 } else {
146 false
147 }
150export function text(parser: Parser, lo: int, hi: int) -> string {
151 match parser.source.slice(lo, hi).decodeUtf8() {
152 case Ok(value) => value
153 case Err(_) => ""
154 }
157export function diagnostic(code: string, message: string, lo: int, hi: int) -> PreviewDiagnostic {
158 PreviewDiagnostic { code: code, message: message, lo: lo, hi: hi, notes: [] }
161export function addDiagnostic(
162 parser: Parser,
163 code: string,
164 message: string,
165 lo: int,
166 hi: int,
167) -> () {
168 let mut diagnostics = parser.diagnostics
169 diagnostics.push(diagnostic(code, message, lo, hi))
170 ()
173export function rejectReservedBinding(parser: Parser, name: PreviewToken) -> () {
174 if text(parser, name.lo, name.hi) == "None" {
175 addDiagnostic(
176 parser,
177 "TPZ2012",
178 "`None` is the Option constructor (§22.1) and cannot be a binding name",
179 name.lo,
180 name.hi,
181 )
182 }
183 ()
186export function fail(parser: Parser, message: string) -> Result<int, string> {
187 let token = current(parser)
188 let mut diagnostics = parser.diagnostics
189 diagnostics.push(diagnostic("TPZ2001", message, token.lo, token.hi))
190 Err(message)
193export function expect(
194 parser: Parser,
195 kind: string,
196 description: string,
197) -> Result<PreviewToken, string> {
198 if at(parser, kind) {
199 return Ok(bump(parser))
200 }
201 fail(parser, "expected {description}")?
202 Err("expected {description}")
205export function expectLeftBrace(parser: Parser) -> Result<PreviewToken, string> {
206 expect(parser, "left-brace", "`\{`")
209export function expectRightBrace(parser: Parser) -> Result<PreviewToken, string> {
210 expect(parser, "right-brace", "`\}`")
213export function identifier(parser: Parser, description: string) -> Result<PreviewToken, string> {
214 if at(parser, "identifier") {
215 return Ok(bump(parser))
216 }
217 fail(parser, "expected {description}")?
218 Err("expected {description}")
221export function isFieldNameKind(kind: string) -> bool {
222 kind == "identifier" || kind.startsWith("keyword/")
225export function fieldName(parser: Parser, description: string) -> Result<PreviewToken, string> {
226 if isFieldNameKind(current(parser).kind) {
227 return Ok(bump(parser))
228 }
229 fail(parser, "expected {description}")?
230 Err("expected {description}")
233export function matchingParenFollowedBy(parser: Parser, expected: string) -> bool {
234 let mut depth = 0
235 let mut offset = 0
236 while true {
237 let kind = tokenAt(parser, offset).kind
238 if kind == "eof" {
239 return false
240 }
241 if kind == "left-paren" || kind == "left-bracket" || kind == "left-brace" {
242 depth += 1
243 } else if kind == "right-paren" ||
244 kind == "right-bracket" ||
245 kind == "right-brace" {
246 depth -= 1
247 if depth == 0 {
248 return tokenAt(parser, offset + 1).kind == expected
249 }
250 }
251 offset += 1
252 }
253 false
256export function stringAttribute(name: string, value: string) -> PreviewAstAttribute {
257 PreviewAstAttribute { name: name, kind: "string", value: value }
260export function boolAttribute(name: string, value: bool) -> PreviewAstAttribute {
261 PreviewAstAttribute {
262 name: name,
263 kind: "bool",
264 value: if value { "true" } else { "false" },
265 }
268export function node(
269 kind: string,
270 lo: int,
271 hi: int,
272 field: string,
273 index: int,
274 attributes: Array<PreviewAstAttribute>,
275 children: Array<SyntaxNode>,
276) -> SyntaxNode {
277 SyntaxNode {
278 kind: kind,
279 lo: lo,
280 hi: hi,
281 field: field,
282 index: index,
283 attributes: attributes,
284 children: children,
285 }
288export function attach(value: SyntaxNode, field: string, index: int) -> SyntaxNode {
289 node(
290 value.kind,
291 value.lo,
292 value.hi,
293 field,
294 index,
295 value.attributes,
296 value.children,
297 )
300export function identNode(value: PreviewToken, field: string, index: int) -> SyntaxNode {
301 node("identifier", value.lo, value.hi, field, index, [], [])
304function parseNestedExpression(
305 service: ParserService,
306 parser: Parser,
307 field: string,
308 index: int,
309) -> Result<SyntaxNode, string> {
310 let previousLambda = lambdaAllowed(parser)
311 let previousRecordUpdate = recordUpdateAllowed(parser)
312 setLambdaAllowed(parser, true)
313 setRecordUpdateAllowed(parser, true)
314 let result = invokeParseExpressionBp(service, parser, 1, field, index)
315 setRecordUpdateAllowed(parser, previousRecordUpdate)
316 setLambdaAllowed(parser, previousLambda)
317 result
320export function parseString(
321 service: ParserService,
322 parser: Parser,
323 field: string,
324 index: int,
325) -> Result<SyntaxNode, string> {
326 let start = bump(parser)
327 let tagged = start.kind.contains("tagged=true")
328 let multiline = start.kind.contains("multiline=true")
329 let delimiterLength = if multiline { 3 } else { 1 }
330 let mut children: Array<SyntaxNode> = []
331 if tagged {
332 let tagHi = start.hi - delimiterLength
333 let tag = text(parser, start.lo, tagHi)
334 if tag != "p" && tag != "r" && tag != "sh" && tag != "sql" {
335 let mut diagnostics = parser.diagnostics
336 diagnostics.push(diagnostic(
337 "TPZ2002",
338 "unknown template tag `{tag}`; the registry is `p`, `r`, `sh`, `sql`",
339 start.lo,
340 tagHi,
341 ))
342 }
343 children.push(node("template-tag", start.lo, tagHi, "tag", 0, [], []))
344 }
345 let mut partIndex = 0
346 while !at(parser, "string-end") && !at(parser, "eof") {
347 if at(parser, "string-text") {
348 let part = bump(parser)
349 children.push(node(
350 "string-part/text",
351 part.lo,
352 part.hi,
353 "parts",
354 partIndex,
355 [],
356 [],
357 ))
358 partIndex += 1
359 continue
360 }
361 if eat(parser, "interpolation-start") {
362 if at(parser, "interpolation-end") {
363 addDiagnostic(
364 parser,
365 "TPZ2001",
366 "expected an expression",
367 current(parser).lo,
368 current(parser).hi,
369 )
370 bump(parser)
371 } else {
372 let expression = parseNestedExpression(service, parser, "value", 0)?
373 expect(parser, "interpolation-end", "an interpolation close")?
374 children.push(node(
375 "string-part/interpolation",
376 expression.lo,
377 expression.hi,
378 "parts",
379 partIndex,
380 [],
381 [expression],
382 ))
383 partIndex += 1
384 }
385 continue
386 }
387 fail(parser, "expected string content")?
388 }
389 let close = expect(parser, "string-end", "a string close")?
390 Ok(node(
391 if tagged { "expression/template" } else { "expression/string" },
392 start.lo,
393 close.hi,
394 field,
395 index,
396 [],
397 children,
398 ))
401export function skipSeparators(parser: Parser) -> () {
402 while eat(parser, "separator") {}
403 ()
406export function synchronize(parser: Parser) -> () {
407 let mut depth = 0
408 while !at(parser, "eof") {
409 let kind = current(parser).kind
410 if depth == 0 && (kind == "separator" || kind == "right-brace") {
411 break
412 }
413 if kind == "left-paren" || kind == "left-bracket" ||
414 kind == "left-brace" || kind == "interpolation-start" ||
415 kind.startsWith("string-start/") {
416 depth += 1
417 } else if kind == "right-paren" || kind == "right-bracket" ||
418 kind == "right-brace" || kind == "interpolation-end" ||
419 kind == "string-end" {
420 if depth == 0 {
421 break
422 }
423 depth -= 1
424 }
425 bump(parser)
426 }
427 eat(parser, "separator")
428 ()
431export function itemBoundary(parser: Parser) -> () {
432 if at(parser, "separator") {
433 skipSeparators(parser)
434 } else if !at(parser, "right-brace") && !at(parser, "eof") {
435 addDiagnostic(
436 parser,
437 "TPZ2001",
438 "expected a statement separator",
439 current(parser).lo,
440 current(parser).hi,
441 )
442 synchronize(parser)
443 }
444 ()

SHA256 e79446776597eacf0ef0bfd68c36199a0fe32e23f409017a79ef35e6a852e477