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/types_patterns.tpz

Topaz · 15885 B · Original file

1// Type-expression and binding-pattern grammar for the self-host parser.
2// It shares cursor and node helpers with parser.core and returns syntax trees to
3// declaration and expression leaves without owning parse-result flattening.
5import types { PreviewToken, SyntaxNode }
6import parser.core {
7 Parser,
8 ParserService,
9 addDiagnostic,
10 at,
11 attach,
12 boolAttribute,
13 boxValue,
14 bump,
15 current,
16 diagnostic,
17 eat,
18 expect,
19 expectLeftBrace,
20 expectRightBrace,
21 fail,
22 fieldName,
23 identNode,
24 identifier,
25 invokeParseExpressionBp,
26 matchingParenFollowedBy,
27 node,
28 parseString,
29 rejectReservedBinding,
30 setBox,
31 text,
32 tokenAt,
35export function bindingPattern(value: PreviewToken, field: string, index: int) -> SyntaxNode {
36 node(
37 "pattern/binding",
38 value.lo,
39 value.hi,
40 field,
41 index,
42 [],
43 [identNode(value, "name", 0)],
44 )
47export function namedType(value: PreviewToken, field: string, index: int) -> SyntaxNode {
48 node(
49 "type/named",
50 value.lo,
51 value.hi,
52 field,
53 index,
54 [],
55 [identNode(value, "name", 0)],
56 )
59export function expectCloseAngle(parser: Parser) -> Result<int, string> {
60 if at(parser, "gt") {
61 return Ok(bump(parser).hi)
62 }
63 if at(parser, "gt-gt") {
64 let token = bump(parser)
65 let mut pending = parser.pendingGt
66 pending.push(token.hi)
67 setBox(parser.lastHi, token.lo + 1)
68 return Ok(token.lo + 1)
69 }
70 fail(parser, "expected `>`")?
71 Err("expected `>`")
74export function parseTypeArguments(
75 service: ParserService,
76 parser: Parser,
77) -> Result<Array<SyntaxNode>, string> {
78 expect(parser, "lt", "`<`")?
79 let mut arguments: Array<SyntaxNode> = [
80 parseType(service, parser, "arguments", 0)?,
81 ]
82 let mut index = 1
83 while eat(parser, "comma") {
84 arguments.push(parseType(
85 service,
87 parser,
88 "arguments",
89 index,
90 )?)
91 index += 1
92 }
93 expectCloseAngle(parser)?
94 Ok(arguments)
97function parseNamedType(
98 service: ParserService,
99 parser: Parser,
100 field: string,
101 index: int,
102) -> Result<SyntaxNode, string> {
103 let namespace = bump(parser)
104 if eat(parser, "dot") {
105 let name = identifier(parser, "an exported type name")?
106 let mut children: Array<SyntaxNode> = [
107 identNode(namespace, "namespace", 0),
108 identNode(name, "name", 0),
109 ]
110 if at(parser, "lt") {
111 for argument in parseTypeArguments(service, parser)? {
112 children.push(argument)
113 }
114 }
115 return Ok(node(
116 "type/qualified",
117 namespace.lo,
118 boxValue(parser.lastHi),
119 field,
120 index,
121 [],
122 children,
123 ))
124 }
125 let mut children: Array<SyntaxNode> = [identNode(namespace, "name", 0)]
126 if at(parser, "lt") {
127 for argument in parseTypeArguments(service, parser)? {
128 children.push(argument)
129 }
130 }
131 Ok(node(
132 "type/named",
133 namespace.lo,
134 boxValue(parser.lastHi),
135 field,
136 index,
137 [],
138 children,
139 ))
142function parseRecordType(
143 service: ParserService,
144 parser: Parser,
145 field: string,
146 index: int,
147) -> Result<SyntaxNode, string> {
148 let open = bump(parser)
149 let mut fields: Array<SyntaxNode> = []
150 let mut fieldIndex = 0
151 while !at(parser, "right-brace") && !at(parser, "eof") {
152 let name = fieldName(parser, "a field name")?
153 expect(parser, "colon", "`:`")?
154 let typeNode = parseType(service, parser, "type", 0)?
155 fields.push(node(
156 "record-type-field",
157 name.lo,
158 typeNode.hi,
159 "fields",
160 fieldIndex,
161 [],
162 [identNode(name, "name", 0), typeNode],
163 ))
164 fieldIndex += 1
165 if !eat(parser, "comma") {
166 break
167 }
168 }
169 let close = expectRightBrace(parser)?
170 if fields.length == 0 {
171 addDiagnostic(
172 parser,
173 "TPZ2001",
174 "a record type requires at least one field",
175 open.lo,
176 close.hi,
177 )
178 }
179 Ok(node("type/record", open.lo, close.hi, field, index, [], fields))
182function parseFunctionType(
183 service: ParserService,
184 parser: Parser,
185 open: PreviewToken,
186 field: string,
187 index: int,
188) -> Result<SyntaxNode, string> {
189 let mut parameters: Array<SyntaxNode> = []
190 let mut parameterIndex = 0
191 while !at(parser, "right-paren") && !at(parser, "eof") {
192 let variadic = eat(parser, "ellipsis")
193 let typeNode = parseType(service, parser, "type", 0)?
194 parameters.push(node(
195 "function-type-parameter",
196 typeNode.lo,
197 typeNode.hi,
198 "parameters",
199 parameterIndex,
200 [boolAttribute("variadic", variadic)],
201 [typeNode],
202 ))
203 parameterIndex += 1
204 if !eat(parser, "comma") {
205 break
206 }
207 }
208 expect(parser, "right-paren", "`)`")?
209 expect(parser, "thin-arrow", "`->`")?
210 let returnType = parseType(service, parser, "returnType", 0)?
211 let hi = returnType.hi
212 parameters.push(returnType)
213 Ok(node("type/function", open.lo, hi, field, index, [], parameters))
216function parseParenthesizedType(
217 service: ParserService,
218 parser: Parser,
219 field: string,
220 index: int,
221) -> Result<SyntaxNode, string> {
222 let functionType = matchingParenFollowedBy(parser, "thin-arrow")
223 let open = bump(parser)
224 if functionType {
225 return parseFunctionType(service, parser, open, field, index)
226 }
227 if eat(parser, "right-paren") {
228 return Ok(node(
229 "type/unit",
230 open.lo,
231 boxValue(parser.lastHi),
232 field,
233 index,
234 [],
235 [],
236 ))
237 }
238 let inner = parseType(service, parser, field, index)?
239 let close = expect(parser, "right-paren", "`)`")?
240 Ok(node(
241 inner.kind,
242 open.lo,
243 close.hi,
244 field,
245 index,
246 inner.attributes,
247 inner.children,
248 ))
251function parsePrimaryType(
252 service: ParserService,
253 parser: Parser,
254 field: string,
255 index: int,
256) -> Result<SyntaxNode, string> {
257 let start = current(parser)
258 if start.kind == "identifier" {
259 return parseNamedType(service, parser, field, index)
260 }
261 if start.kind == "left-brace" {
262 return parseRecordType(service, parser, field, index)
263 }
264 if start.kind == "left-paren" {
265 return parseParenthesizedType(service, parser, field, index)
266 }
267 if start.kind == "integer" || start.kind == "float" ||
268 start.kind == "keyword/true" || start.kind == "keyword/false" ||
269 start.kind == "keyword/null" {
270 bump(parser)
271 return Ok(node("type/literal", start.lo, start.hi, field, index, [], []))
272 }
273 if start.kind.startsWith("string-start/") {
274 let literal = parseString(service, parser, field, index)?
275 for part in literal.children {
276 if part.kind == "string-part/interpolation" {
277 addDiagnostic(
278 parser,
279 "TPZ2001",
280 "a literal type cannot contain interpolation",
281 literal.lo,
282 literal.hi,
283 )
284 break
285 }
286 }
287 return Ok(node("type/literal", literal.lo, literal.hi, field, index, [], []))
288 }
289 fail(parser, "expected a type")?
290 Err("expected a type")
293export function parseType(
294 service: ParserService,
295 parser: Parser,
296 field: string,
297 index: int,
298) -> Result<SyntaxNode, string> {
299 let first = parsePrimaryType(service, parser, field, index)?
300 if !at(parser, "pipe") {
301 return Ok(first)
302 }
303 let mut members: Array<SyntaxNode> = [attach(first, "members", 0)]
304 let mut memberIndex = 1
305 while eat(parser, "pipe") {
306 members.push(parsePrimaryType(
307 service,
309 parser,
310 "members",
311 memberIndex,
312 )?)
313 memberIndex += 1
314 }
315 Ok(node("type/union", first.lo, boxValue(parser.lastHi), field, index, [], members))
318function isLiteralExpressionKind(kind: string) -> bool {
319 kind == "expression/integer" ||
320 kind == "expression/float" ||
321 kind == "expression/string" ||
322 kind == "expression/boolean" ||
323 kind == "expression/null" ||
324 kind == "expression/unit"
327function parseRecordPatternFields(
328 service: ParserService,
329 parser: Parser,
330) -> Result<Array<SyntaxNode>, string> {
331 let open = expectLeftBrace(parser)?
332 let mut fields: Array<SyntaxNode> = []
333 let mut fieldIndex = 0
334 while !at(parser, "right-brace") && !at(parser, "eof") {
335 let lo = current(parser).lo
336 let name = if current(parser).kind.startsWith("keyword/") &&
337 tokenAt(parser, 1).kind == "colon" {
338 bump(parser)
339 } else {
340 identifier(parser, "a field name")?
341 }
342 let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]
343 if eat(parser, "colon") {
344 children.push(parsePattern(service, parser, "pattern", 0)?)
345 } else {
346 rejectReservedBinding(parser, name)
347 }
348 fields.push(node(
349 "record-pattern-field",
350 lo,
351 boxValue(parser.lastHi),
352 "fields",
353 fieldIndex,
354 [],
355 children,
356 ))
357 fieldIndex += 1
358 if !eat(parser, "comma") {
359 break
360 }
361 }
362 let close = expectRightBrace(parser)?
363 if fields.length == 0 {
364 let mut diagnostics = parser.diagnostics
365 diagnostics.push(diagnostic(
366 "TPZ2001",
367 "a record pattern requires at least one field",
368 open.lo,
369 close.hi,
370 ))
371 }
372 Ok(fields)
375function parseListPattern(
376 service: ParserService,
377 parser: Parser,
378 field: string,
379 index: int,
380) -> Result<SyntaxNode, string> {
381 let open = bump(parser)
382 let mut elements: Array<SyntaxNode> = []
383 let mut elementIndex = 0
384 while !at(parser, "right-bracket") && !at(parser, "eof") {
385 if eat(parser, "dot-dot") {
386 let markerLo = boxValue(parser.lastHi) - 2
387 let mut children: Array<SyntaxNode> = []
388 if !at(parser, "comma") && !at(parser, "right-bracket") {
389 children.push(parsePattern(service, parser, "binding", 0)?)
390 }
391 let restLo = match children.get(0) {
392 case Some(binding) => binding.lo
393 case None => markerLo
394 }
395 let restHi = boxValue(parser.lastHi)
396 elements.push(node(
397 "list-pattern/rest",
398 restLo,
399 restHi,
400 "elements",
401 elementIndex,
402 [],
403 children,
404 ))
405 } else {
406 elements.push(parsePattern(
407 service,
409 parser,
410 "elements",
411 elementIndex,
412 )?)
413 }
414 elementIndex += 1
415 if !eat(parser, "comma") {
416 break
417 }
418 }
419 let close = expect(parser, "right-bracket", "`]`")?
420 let mut normalized: Array<SyntaxNode> = []
421 for element in elements {
422 if element.kind == "list-pattern/rest" && element.children.length == 0 {
423 normalized.push(node(
424 element.kind,
425 open.lo,
426 close.hi,
427 element.field,
428 element.index,
429 element.attributes,
430 element.children,
431 ))
432 } else {
433 normalized.push(element)
434 }
435 }
436 Ok(node("pattern/list", open.lo, close.hi, field, index, [], normalized))
439function parseConstructorPattern(
440 service: ParserService,
441 parser: Parser,
442 field: string,
443 index: int,
444) -> Result<SyntaxNode, string> {
445 let name = bump(parser)
446 bump(parser)
447 let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]
448 let mut argumentIndex = 0
449 while !at(parser, "right-paren") && !at(parser, "eof") {
450 children.push(parsePattern(
451 service,
453 parser,
454 "arguments",
455 argumentIndex,
456 )?)
457 argumentIndex += 1
458 if !eat(parser, "comma") {
459 break
460 }
461 }
462 let close = expect(parser, "right-paren", "`)`")?
463 Ok(node(
464 "pattern/constructor",
465 name.lo,
466 close.hi,
467 field,
468 index,
469 [],
470 children,
471 ))
474function parseNominalRecordPattern(
475 service: ParserService,
476 parser: Parser,
477 field: string,
478 index: int,
479) -> Result<SyntaxNode, string> {
480 let name = bump(parser)
481 let fields = parseRecordPatternFields(service, parser)?
482 let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]
483 for found in fields {
484 children.push(found)
485 }
486 Ok(node(
487 "pattern/nominal-record",
488 name.lo,
489 boxValue(parser.lastHi),
490 field,
491 index,
492 [],
493 children,
494 ))
497function parseTypedPattern(
498 service: ParserService,
499 parser: Parser,
500 field: string,
501 index: int,
502) -> Result<SyntaxNode, string> {
503 let name = bump(parser)
504 rejectReservedBinding(parser, name)
505 bump(parser)
506 let typeNode = parseType(service, parser, "type", 0)?
507 Ok(node(
508 "pattern/typed",
509 name.lo,
510 typeNode.hi,
511 field,
512 index,
513 [],
514 [identNode(name, "name", 0), typeNode],
515 ))
518function identifierStartsBindingPattern(parser: Parser) -> bool {
519 let next = tokenAt(parser, 1).kind
520 next != "dot-dot" && next != "dot-dot-lt" && next != "plus" &&
521 next != "minus" && next != "star" && next != "slash" &&
522 next != "percent" && next != "star-star"
525function parseIdentifierPattern(
526 service: ParserService,
527 parser: Parser,
528 field: string,
529 index: int,
530) -> Result<Option<SyntaxNode>, string> {
531 let next = tokenAt(parser, 1).kind
532 if next == "left-paren" {
533 return Ok(Some(parseConstructorPattern(
534 service,
536 parser,
537 field,
538 index,
539 )?))
540 }
541 if next == "left-brace" {
542 return Ok(Some(parseNominalRecordPattern(
543 service,
545 parser,
546 field,
547 index,
548 )?))
549 }
550 if next == "colon" {
551 return Ok(Some(parseTypedPattern(
552 service,
554 parser,
555 field,
556 index,
557 )?))
558 }
559 if !identifierStartsBindingPattern(parser) {
560 return Ok(None)
561 }
562 let name = bump(parser)
563 if text(parser, name.lo, name.hi) == "None" {
564 return Ok(Some(node(
565 "pattern/constructor",
566 name.lo,
567 name.hi,
568 field,
569 index,
570 [],
571 [identNode(name, "name", 0)],
572 )))
573 }
574 Ok(Some(bindingPattern(name, field, index)))
577function parseExpressionPattern(
578 service: ParserService,
579 parser: Parser,
580 field: string,
581 index: int,
582) -> Result<SyntaxNode, string> {
583 let first = invokeParseExpressionBp(service, parser, 8, "value", 0)?
584 if at(parser, "dot-dot") || at(parser, "dot-dot-lt") {
585 let inclusive = at(parser, "dot-dot")
586 bump(parser)
587 let high = invokeParseExpressionBp(service, parser, 8, "high", 0)?
588 return Ok(node(
589 "pattern/range",
590 first.lo,
591 high.hi,
592 field,
593 index,
594 [boolAttribute("inclusive", inclusive)],
595 [attach(first, "low", 0), high],
596 ))
597 }
598 if isLiteralExpressionKind(first.kind) {
599 return Ok(node(
600 "pattern/literal",
601 first.lo,
602 first.hi,
603 field,
604 index,
605 [],
606 [attach(first, "value", 0)],
607 ))
608 }
609 fail(parser, "expected a pattern")?
610 Err("expected a pattern")
613function parsePrimaryPattern(
614 service: ParserService,
615 parser: Parser,
616 field: string,
617 index: int,
618) -> Result<SyntaxNode, string> {
619 let start = current(parser)
620 if start.kind == "underscore" {
621 let value = bump(parser)
622 return Ok(node("pattern/wildcard", value.lo, value.hi, field, index, [], []))
623 }
624 if start.kind == "left-bracket" {
625 return parseListPattern(service, parser, field, index)
626 }
627 if start.kind == "left-brace" {
628 let fields = parseRecordPatternFields(service, parser)?
629 return Ok(node(
630 "pattern/record",
631 start.lo,
632 boxValue(parser.lastHi),
633 field,
634 index,
635 [],
636 fields,
637 ))
638 }
639 if start.kind == "identifier" {
640 match parseIdentifierPattern(service, parser, field, index)? {
641 case Some(pattern) => return Ok(pattern)
642 case None => ()
643 }
644 }
645 parseExpressionPattern(service, parser, field, index)
648export function parsePattern(
649 service: ParserService,
650 parser: Parser,
651 field: string,
652 index: int,
653) -> Result<SyntaxNode, string> {
654 let first = parsePrimaryPattern(service, parser, field, index)?
655 if !at(parser, "pipe") {
656 return Ok(first)
657 }
658 let mut alternatives: Array<SyntaxNode> = [attach(first, "alternatives", 0)]
659 let mut alternativeIndex = 1
660 while eat(parser, "pipe") {
661 alternatives.push(parsePrimaryPattern(
662 service,
664 parser,
665 "alternatives",
666 alternativeIndex,
667 )?)
668 alternativeIndex += 1
669 }
670 Ok(node(
671 "pattern/or",
672 first.lo,
673 boxValue(parser.lastHi),
674 field,
675 index,
676 [],
677 alternatives,
678 ))

SHA256 72d33df10e53158f4f6c14e821bf540ba0edba97330e61f4045756376fc438a6