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

Topaz · 43315 B · Original file

1// Atom, postfix, precedence, and control-expression grammar.
2// Type and pattern parsing enter through parser services, while node allocation
3// and recovery remain in parser.core.
5import types { PreviewAstAttribute, PreviewDiagnostic, 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 isFieldNameKind,
26 itemBoundary,
27 invokeParseBlock,
28 lambdaAllowed,
29 layoutTokenAt,
30 matchingParenFollowedBy,
31 node,
32 parseString,
33 recordUpdateAllowed,
34 rejectReservedBinding,
35 setLambdaAllowed,
36 setRecordUpdateAllowed,
37 skipSeparators,
38 stringAttribute,
39 synchronize,
40 text,
41 tokenAt,
43import parser.types_patterns { parsePattern, parseType, parseTypeArguments }
45function diagnosticWithNotes(
46 code: string,
47 message: string,
48 lo: int,
49 hi: int,
50 notes: Array<string>,
51) -> PreviewDiagnostic {
52 PreviewDiagnostic { code: code, message: message, lo: lo, hi: hi, notes: notes }
55function nullAttribute(name: string) -> PreviewAstAttribute {
56 PreviewAstAttribute { name: name, kind: "null", value: "" }
59function binaryPrecedence(kind: string) -> int {
60 match kind {
61 case "pipe-gt" => 1
62 case "gt-gt" => 2
63 case "question-question" => 3
64 case "or-or" => 4
65 case "and-and" => 5
66 case "eq-eq" | "not-eq" => 6
67 case "lt" | "le" | "gt" | "ge" | "keyword/in" => 6
68 case "dot-dot" | "dot-dot-lt" => 7
69 case "plus" | "minus" => 8
70 case "star" | "slash" | "percent" => 9
71 case _ => 0
72 }
75function binaryName(kind: string) -> string {
76 match kind {
77 case "plus" => "add"
78 case "minus" => "subtract"
79 case "star" => "multiply"
80 case "slash" => "divide"
81 case "percent" => "remainder"
82 case "lt" => "less-than"
83 case "le" => "less-or-equal"
84 case "gt" => "greater-than"
85 case "ge" => "greater-or-equal"
86 case "eq-eq" => "equal"
87 case "not-eq" => "not-equal"
88 case "keyword/in" => "in"
89 case "and-and" => "and"
90 case "or-or" => "or"
91 case "question-question" => "coalesce"
92 case _ => ""
93 }
96function parseCallArguments(
97 service: ParserService,
98 parser: Parser,
99) -> Result<Array<SyntaxNode>, string> {
100 expect(parser, "left-paren", "`(`")?
101 let mut arguments: Array<SyntaxNode> = []
102 let mut index = 0
103 while !at(parser, "right-paren") && !at(parser, "eof") {
104 if eat(parser, "ellipsis") {
105 let value = parseNestedExpression(service, parser, "value", 0)?
106 arguments.push(node(
107 "call-argument/spread",
108 value.lo,
109 value.hi,
110 "arguments",
111 index,
112 [],
113 [value],
114 ))
115 } else if at(parser, "identifier") && tokenAt(parser, 1).kind == "colon" {
116 let name = bump(parser)
117 bump(parser)
118 let value = parseNestedExpression(service, parser, "value", 0)?
119 arguments.push(node(
120 "call-argument/named",
121 name.lo,
122 value.hi,
123 "arguments",
124 index,
125 [],
126 [identNode(name, "name", 0), value],
127 ))
128 } else {
129 arguments.push(parseNestedExpression(service, parser, "arguments", index)?)
130 }
131 index += 1
132 if !eat(parser, "comma") {
133 break
134 }
135 }
136 expect(parser, "right-paren", "`)`")?
137 Ok(arguments)
140function looksLikeCallTypeArguments(parser: Parser) -> bool {
141 if !at(parser, "lt") {
142 return false
143 }
144 let mut angle = 0
145 let mut paren = 0
146 let mut brace = 0
147 let mut offset = 0
148 let mut sawTypeToken = false
149 while true {
150 let token = tokenAt(parser, offset)
151 if token.kind == "lt" {
152 angle += 1
153 } else if token.kind == "gt" || token.kind == "gt-gt" {
154 if paren != 0 || brace != 0 {
155 return false
156 }
157 if token.kind == "gt-gt" {
158 angle -= 2
159 } else {
160 angle -= 1
161 }
162 if angle <= 0 {
163 let next = tokenAt(parser, offset + 1)
164 return angle == 0 && sawTypeToken &&
165 next.kind == "left-paren" && token.hi == next.lo
166 }
167 sawTypeToken = true
168 } else if token.kind == "left-paren" {
169 paren += 1
170 sawTypeToken = true
171 } else if token.kind == "right-paren" {
172 paren -= 1
173 if paren < 0 {
174 return false
175 }
176 } else if token.kind == "left-brace" {
177 brace += 1
178 sawTypeToken = true
179 } else if token.kind == "right-brace" {
180 brace -= 1
181 if brace < 0 {
182 return false
183 }
184 } else if token.kind == "identifier" || token.kind == "dot" ||
185 token.kind == "thin-arrow" || token.kind == "ellipsis" ||
186 token.kind == "pipe" || token.kind == "colon" ||
187 token.kind == "integer" || token.kind == "float" ||
188 token.kind == "keyword/true" || token.kind == "keyword/false" ||
189 token.kind == "keyword/null" || token.kind == "string-end" ||
190 token.kind == "string-text" || token.kind.startsWith("string-start/") {
191 sawTypeToken = true
192 } else if token.kind != "comma" {
193 return false
194 }
195 offset += 1
196 }
197 false
200function parseLambdaParameters(
201 service: ParserService,
202 parser: Parser,
203) -> Result<Array<SyntaxNode>, string> {
204 let mut parameters: Array<SyntaxNode> = []
205 let mut index = 0
206 while !at(parser, "right-paren") && !at(parser, "eof") {
207 let lo = current(parser).lo
208 let name = identifier(parser, "a parameter name")?
209 rejectReservedBinding(parser, name)
210 let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]
211 if eat(parser, "colon") {
212 children.push(parseType(service, parser, "type", 0)?)
213 }
214 parameters.push(node(
215 "lambda-parameter",
216 lo,
217 boxValue(parser.lastHi),
218 "parameters",
219 index,
220 [],
221 children,
222 ))
223 index += 1
224 if !eat(parser, "comma") {
225 break
226 }
227 }
228 Ok(parameters)
231function parseSingleLambda(
232 service: ParserService,
233 parser: Parser,
234 field: string,
235 index: int,
236) -> Result<SyntaxNode, string> {
237 let name = identifier(parser, "a parameter name")?
238 rejectReservedBinding(parser, name)
239 expect(parser, "fat-arrow", "`=>`")?
240 let body = parseExpression(service, parser, "body", 0)?
241 Ok(node(
242 "expression/lambda",
243 name.lo,
244 body.hi,
245 field,
246 index,
247 [],
248 [
249 node(
250 "lambda-parameter",
251 name.lo,
252 name.hi,
253 "parameters",
254 0,
255 [],
256 [identNode(name, "name", 0)],
257 ),
258 body,
259 ],
260 ))
263function parseParenLambda(
264 service: ParserService,
265 parser: Parser,
266 field: string,
267 index: int,
268) -> Result<SyntaxNode, string> {
269 let open = expect(parser, "left-paren", "`(`")?
270 let mut children = parseLambdaParameters(service, parser)?
271 expect(parser, "right-paren", "`)`")?
272 expect(parser, "fat-arrow", "`=>`")?
273 let body = parseExpression(service, parser, "body", 0)?
274 let hi = body.hi
275 children.push(body)
276 Ok(node("expression/lambda", open.lo, hi, field, index, [], children))
279function parseArray(
280 service: ParserService,
281 parser: Parser,
282 field: string,
283 index: int,
284) -> Result<SyntaxNode, string> {
285 let open = expect(parser, "left-bracket", "`[`")?
286 let mut elements: Array<SyntaxNode> = []
287 let mut elementIndex = 0
288 while !at(parser, "right-bracket") && !at(parser, "eof") {
289 if eat(parser, "ellipsis") {
290 let value = parseNestedExpression(service, parser, "value", 0)?
291 elements.push(node(
292 "array-element/spread",
293 value.lo,
294 value.hi,
295 "elements",
296 elementIndex,
297 [],
298 [value],
299 ))
300 } else {
301 elements.push(parseNestedExpression(service, parser, "elements", elementIndex)?)
302 }
303 elementIndex += 1
304 if !eat(parser, "comma") {
305 break
306 }
307 }
308 let close = expect(parser, "right-bracket", "`]`")?
309 Ok(node(
310 "expression/array",
311 open.lo,
312 close.hi,
313 field,
314 index,
315 [],
316 elements,
317 ))
320function parseFieldInitializers(
321 service: ParserService,
322 parser: Parser,
323 rejectRecordSpread: bool,
324) -> Result<Array<SyntaxNode>, string> {
325 let mut fields: Array<SyntaxNode> = []
326 let mut fieldIndex = 0
327 while !at(parser, "right-brace") && !at(parser, "eof") {
328 if rejectRecordSpread && at(parser, "ellipsis") {
329 let spread = current(parser)
330 addDiagnostic(
331 parser,
332 "TPZ2001",
333 "a record spread `...` must be the FIRST element and appear at most once",
334 spread.lo,
335 spread.hi,
336 )
337 recoverToMatchingRightBrace(parser)
338 break
339 }
340 let name = fieldName(parser, "a field name")?
341 expect(parser, "colon", "`:`")?
342 let value = parseNestedExpression(service, parser, "value", 0)?
343 fields.push(node(
344 "field-initializer",
345 name.lo,
346 value.hi,
347 "fields",
348 fieldIndex,
349 [],
350 [identNode(name, "name", 0), value],
351 ))
352 fieldIndex += 1
353 if !eat(parser, "comma") {
354 break
355 }
356 }
357 Ok(fields)
360function parseRecordLiteral(
361 service: ParserService,
362 parser: Parser,
363 field: string,
364 index: int,
365) -> Result<SyntaxNode, string> {
366 let open = expectLeftBrace(parser)?
367 let children = parseFieldInitializers(service, parser, false)?
368 let close = expectRightBrace(parser)?
369 Ok(node(
370 "expression/record-literal",
371 open.lo,
372 close.hi,
373 field,
374 index,
375 [],
376 children,
377 ))
380function parseRecordUpdate(
381 service: ParserService,
382 parser: Parser,
383 base: SyntaxNode,
384 field: string,
385 index: int,
386) -> Result<SyntaxNode, string> {
387 expectLeftBrace(parser)?
388 let mut children: Array<SyntaxNode> = [attach(base, "base", 0)]
389 if eat(parser, "ellipsis") {
390 children.push(parseNestedExpression(service, parser, "spread", 0)?)
391 if !at(parser, "right-brace") && !eat(parser, "comma") {
392 let token = current(parser)
393 addDiagnostic(parser, "TPZ2001", "expected `,` or `\}`", token.lo, token.hi)
394 recoverToMatchingRightBrace(parser)
395 }
396 }
397 for found in parseFieldInitializers(service, parser, true)? {
398 children.push(found)
399 }
400 let close = expectRightBrace(parser)?
401 Ok(node(
402 "expression/record-update",
403 base.lo,
404 close.hi,
405 field,
406 index,
407 [],
408 children,
409 ))
412function parseComprehensionClauses(
413 service: ParserService,
414 parser: Parser,
415) -> Result<Array<SyntaxNode>, string> {
416 let mut clauses: Array<SyntaxNode> = []
417 let mut clauseIndex = 0
418 while true {
419 if eat(parser, "keyword/for") {
420 let pattern = parsePattern(service, parser, "pattern", 0)?
421 expect(parser, "keyword/in", "`in`")?
422 let iterator = parseExpressionWithoutNakedLambda(service, parser, "iterator", 0)?
423 clauses.push(node(
424 "comprehension-clause/for",
425 pattern.lo,
426 iterator.hi,
427 "clauses",
428 clauseIndex,
429 [],
430 [pattern, iterator],
431 ))
432 clauseIndex += 1
433 continue
434 }
435 if eat(parser, "keyword/if") {
436 let condition = parseExpressionWithoutNakedLambda(service, parser, "condition", 0)?
437 clauses.push(node(
438 "comprehension-clause/if",
439 condition.lo,
440 condition.hi,
441 "clauses",
442 clauseIndex,
443 [],
444 [condition],
445 ))
446 clauseIndex += 1
447 continue
448 }
449 break
450 }
451 expect(parser, "fat-arrow", "`=>` to introduce the comprehension body")?
452 Ok(clauses)
455function parseArrayComprehension(
456 service: ParserService,
457 parser: Parser,
458 field: string,
459 index: int,
460) -> Result<SyntaxNode, string> {
461 let open = expect(parser, "left-bracket", "`[`")?
462 let mut children = parseComprehensionClauses(service, parser)?
463 children.push(parseNestedExpression(service, parser, "body", 0)?)
464 let close = expect(parser, "right-bracket", "`]`")?
465 Ok(node(
466 "expression/comprehension",
467 open.lo,
468 close.hi,
469 field,
470 index,
471 [stringAttribute("collection", "array")],
472 children,
473 ))
476function parseSetComprehension(
477 service: ParserService,
478 parser: Parser,
479 field: string,
480 index: int,
481) -> Result<SyntaxNode, string> {
482 let head = identifier(parser, "`set`")?
483 expectLeftBrace(parser)?
484 let mut children = parseComprehensionClauses(service, parser)?
485 children.push(parseNestedExpression(service, parser, "body", 0)?)
486 let close = expectRightBrace(parser)?
487 Ok(node(
488 "expression/comprehension",
489 head.lo,
490 close.hi,
491 field,
492 index,
493 [stringAttribute("collection", "set")],
494 children,
495 ))
498function parseMapComprehension(
499 service: ParserService,
500 parser: Parser,
501 field: string,
502 index: int,
503) -> Result<SyntaxNode, string> {
504 let head = identifier(parser, "`map`")?
505 expectLeftBrace(parser)?
506 let mut children = parseComprehensionClauses(service, parser)?
507 children.push(parseNestedExpression(service, parser, "bodyKey", 0)?)
508 expect(parser, "colon", "`:` (a map comprehension body is `key: value`)")?
509 children.push(parseNestedExpression(service, parser, "bodyValue", 0)?)
510 let close = expectRightBrace(parser)?
511 Ok(node(
512 "expression/comprehension",
513 head.lo,
514 close.hi,
515 field,
516 index,
517 [stringAttribute("collection", "map")],
518 children,
519 ))
522function parseSet(
523 service: ParserService,
524 parser: Parser,
525 field: string,
526 index: int,
527) -> Result<SyntaxNode, string> {
528 let head = identifier(parser, "`set`")?
529 expectLeftBrace(parser)?
530 let mut elements: Array<SyntaxNode> = []
531 let mut elementIndex = 0
532 while !at(parser, "right-brace") && !at(parser, "eof") {
533 elements.push(parseNestedExpression(service, parser, "elements", elementIndex)?)
534 elementIndex += 1
535 if !eat(parser, "comma") {
536 break
537 }
538 }
539 let close = expectRightBrace(parser)?
540 Ok(node("expression/set", head.lo, close.hi, field, index, [], elements))
543function parseMap(
544 service: ParserService,
545 parser: Parser,
546 field: string,
547 index: int,
548) -> Result<SyntaxNode, string> {
549 let head = identifier(parser, "`map`")?
550 expectLeftBrace(parser)?
551 let mut entries: Array<SyntaxNode> = []
552 let mut entryIndex = 0
553 while !at(parser, "right-brace") && !at(parser, "eof") {
554 let key = parseNestedExpression(service, parser, "key", 0)?
555 expect(parser, "colon", "`:`")?
556 let value = parseNestedExpression(service, parser, "value", 0)?
557 entries.push(node(
558 "map-entry",
559 key.lo,
560 value.hi,
561 "entries",
562 entryIndex,
563 [],
564 [key, value],
565 ))
566 entryIndex += 1
567 if !eat(parser, "comma") {
568 break
569 }
570 }
571 let close = expectRightBrace(parser)?
572 Ok(node("expression/map", head.lo, close.hi, field, index, [], entries))
575export function blockExpression(block: SyntaxNode, field: string, index: int) -> SyntaxNode {
576 node(
577 "expression/block",
578 block.lo,
579 block.hi,
580 field,
581 index,
582 [],
583 [attach(block, "block", 0)],
584 )
587function parseIfLet(
588 service: ParserService,
589 parser: Parser,
590 keyword: PreviewToken,
591 field: string,
592 index: int,
593) -> Result<SyntaxNode, string> {
594 expect(parser, "keyword/let", "`let`")?
595 let pattern = parsePattern(service, parser, "pattern", 0)?
596 expect(parser, "eq", "`=`")?
597 let scrutinee = parseExpressionBeforeBlock(service, parser, "scrutinee", 0)?
598 let thenBlock = invokeParseBlock(service, parser, "block", 0)?
599 let thenExpression = blockExpression(thenBlock, "body", 0)
600 let mut elseExpression = node(
601 "expression/unit",
602 thenBlock.hi,
603 thenBlock.hi,
604 "body",
605 0,
606 [],
607 [],
608 )
609 if eat(parser, "keyword/else") {
610 elseExpression = if at(parser, "keyword/if") {
611 parseIf(service, parser, "body", 0)?
612 } else {
613 let block = invokeParseBlock(service, parser, "block", 0)?
614 blockExpression(block, "body", 0)
615 }
616 }
617 let hi = elseExpression.hi
618 let wholeLo = keyword.lo
619 let firstCase = node(
620 "match-case",
621 wholeLo,
622 hi,
623 "cases",
624 0,
625 [],
626 [pattern, thenExpression],
627 )
628 let wildcard = node("pattern/wildcard", wholeLo, hi, "pattern", 0, [], [])
629 let secondCase = node(
630 "match-case",
631 wholeLo,
632 hi,
633 "cases",
634 1,
635 [],
636 [wildcard, elseExpression],
637 )
638 Ok(node(
639 "expression/match",
640 wholeLo,
641 hi,
642 field,
643 index,
644 [],
645 [scrutinee, firstCase, secondCase],
646 ))
649function parseIf(
650 service: ParserService,
651 parser: Parser,
652 field: string,
653 index: int,
654) -> Result<SyntaxNode, string> {
655 let keyword = expect(parser, "keyword/if", "`if`")?
656 if at(parser, "keyword/let") {
657 return parseIfLet(service, parser, keyword, field, index)
658 }
659 let condition = parseExpressionBeforeBlock(service, parser, "condition", 0)?
660 let thenBlock = invokeParseBlock(service, parser, "then", 0)?
661 let mut children: Array<SyntaxNode> = [condition, thenBlock]
662 let mut hi = thenBlock.hi
663 if eat(parser, "keyword/else") {
664 let elseValue = if at(parser, "keyword/if") {
665 parseIf(service, parser, "else", 0)?
666 } else {
667 let block = invokeParseBlock(service, parser, "block", 0)?
668 blockExpression(block, "else", 0)
669 }
670 hi = elseValue.hi
671 children.push(elseValue)
672 }
673 Ok(node("expression/if", keyword.lo, hi, field, index, [], children))
676function parseFor(
677 service: ParserService,
678 parser: Parser,
679 field: string,
680 index: int,
681) -> Result<SyntaxNode, string> {
682 let keyword = expect(parser, "keyword/for", "`for`")?
683 let pattern = parsePattern(service, parser, "pattern", 0)?
684 expect(parser, "keyword/in", "`in`")?
685 let iterator = parseExpressionBeforeBlock(service, parser, "iterator", 0)?
686 let body = invokeParseBlock(service, parser, "body", 0)?
687 Ok(node(
688 "expression/for",
689 keyword.lo,
690 body.hi,
691 field,
692 index,
693 [],
694 [pattern, iterator, body],
695 ))
698export function labelNode(value: PreviewToken, field: string, index: int) -> SyntaxNode {
699 node("identifier", value.lo + 1, value.hi, field, index, [], [])
702function parseLoop(
703 service: ParserService,
704 parser: Parser,
705 field: string,
706 index: int,
707) -> Result<SyntaxNode, string> {
708 let head = identifier(parser, "`loop`")?
709 let mut children: Array<SyntaxNode> = []
710 if at(parser, "label") {
711 children.push(labelNode(bump(parser), "label", 0))
712 }
713 let body = invokeParseBlock(service, parser, "body", 0)?
714 let hi = body.hi
715 children.push(body)
716 Ok(node("expression/loop", head.lo, hi, field, index, [], children))
719function parseConcurrentTimeout(parser: Parser) -> Result<Option<SyntaxNode>, string> {
720 if !at(parser, "left-paren") {
721 return Ok(None)
722 }
723 bump(parser)
724 let key = identifier(parser, "`timeout`")?
725 if text(parser, key.lo, key.hi) != "timeout" {
726 addDiagnostic(
727 parser,
728 "TPZ2005",
729 "`concurrent` takes only a `timeout:` argument",
730 key.lo,
731 key.hi,
732 )
733 }
734 if !eat(parser, "colon") {
735 let token = current(parser)
736 addDiagnostic(parser, "TPZ2001", "expected `:` after `timeout`", token.lo, token.hi)
737 }
738 let duration = current(parser)
739 let mut durationHi = duration.hi
740 let mut unit = "s"
741 let mut valueAttribute = nullAttribute("valueDecimal")
742 if duration.kind.startsWith("duration/") {
743 bump(parser)
744 unit = duration.kind.replace("duration/", "")
745 let spelling = text(parser, duration.lo, duration.hi)
746 valueAttribute = stringAttribute(
747 "valueDecimal",
748 spelling.replace(unit, "").replace("_", ""),
749 )
750 } else {
751 addDiagnostic(
752 parser,
753 "TPZ2001",
754 "expected a duration literal, e.g. `3s`",
755 duration.lo,
756 duration.hi,
757 )
758 recoverToMatchingRightParen(parser, false)
759 let recoveredHi = boxValue(parser.lastHi)
760 if recoveredHi > durationHi {
761 durationHi = recoveredHi
762 }
763 }
764 let value = node(
765 "expression/duration",
766 duration.lo,
767 durationHi,
768 "timeout",
769 0,
770 [
771 stringAttribute("unit", unit),
772 valueAttribute,
773 ],
774 [],
775 )
776 expect(parser, "right-paren", "`)`")?
777 Ok(Some(value))
780function parseConcurrentArm(
781 service: ParserService,
782 parser: Parser,
783 index: int,
784) -> Result<SyntaxNode, string> {
785 let name = identifier(parser, "an arm name")?
786 expect(parser, "colon", "`:`")?
787 let value = parseNestedExpression(service, parser, "value", 0)?
788 Ok(node(
789 "concurrent-arm",
790 name.lo,
791 value.hi,
792 "arms",
793 index,
794 [],
795 [identNode(name, "name", 0), value],
796 ))
799function parseConcurrentArms(
800 service: ParserService,
801 parser: Parser,
802 keyword: PreviewToken,
803) -> Result<Array<SyntaxNode>, string> {
804 expectLeftBrace(parser)?
805 let mut arms: Array<SyntaxNode> = []
806 skipSeparators(parser)
807 while !at(parser, "right-brace") && !at(parser, "eof") {
808 let before = boxValue(parser.position)
809 match parseConcurrentArm(service, parser, arms.length) {
810 case Ok(arm) => {
811 arms.push(arm)
812 itemBoundary(parser)
813 }
814 case Err(_) => synchronize(parser)
815 }
816 if boxValue(parser.position) == before &&
817 !at(parser, "right-brace") && !at(parser, "eof") {
818 addDiagnostic(
819 parser,
820 "TPZ2001",
821 "expected a concurrent arm",
822 current(parser).lo,
823 current(parser).hi,
824 )
825 bump(parser)
826 }
827 skipSeparators(parser)
828 }
829 expectRightBrace(parser)?
830 if arms.length == 0 {
831 addDiagnostic(
832 parser,
833 "TPZ2001",
834 "`concurrent` requires at least one arm",
835 keyword.lo,
836 boxValue(parser.lastHi),
837 )
838 }
839 Ok(arms)
842function parseConcurrentElse(
843 service: ParserService,
844 parser: Parser,
845 keyword: PreviewToken,
846 hasTimeout: bool,
847) -> Result<Option<SyntaxNode>, string> {
848 let elseToken = current(parser)
849 if eat(parser, "keyword/else") {
850 if !hasTimeout {
851 addDiagnostic(
852 parser,
853 "TPZ2005",
854 "`else` requires the `concurrent(timeout: ...)` form",
855 elseToken.lo,
856 elseToken.hi,
857 )
858 }
859 return Ok(Some(invokeParseBlock(service, parser, "else", 0)?))
860 }
861 if hasTimeout {
862 addDiagnostic(
863 parser,
864 "TPZ2005",
865 "the `concurrent(timeout: ...)` form requires an `else` block",
866 keyword.lo,
867 boxValue(parser.lastHi),
868 )
869 }
870 Ok(None)
873function parseConcurrent(
874 service: ParserService,
875 parser: Parser,
876 field: string,
877 index: int,
878) -> Result<SyntaxNode, string> {
879 let keyword = expect(parser, "keyword/concurrent", "`concurrent`")?
880 let mut children: Array<SyntaxNode> = []
881 let timeout = parseConcurrentTimeout(parser)?
882 let mut hasTimeout = false
883 match timeout {
884 case Some(value) => {
885 hasTimeout = true
886 children.push(value)
887 }
888 case None => ()
889 }
890 for arm in parseConcurrentArms(service, parser, keyword)? {
891 children.push(arm)
892 }
893 match parseConcurrentElse(service, parser, keyword, hasTimeout)? {
894 case Some(value) => children.push(value)
895 case None => ()
896 }
897 Ok(node(
898 "expression/concurrent",
899 keyword.lo,
900 boxValue(parser.lastHi),
901 field,
902 index,
903 [],
904 children,
905 ))
908function parseMatchCase(
909 service: ParserService,
910 parser: Parser,
911 index: int,
912) -> Result<SyntaxNode, string> {
913 let keyword = expect(parser, "keyword/case", "`case`")?
914 let pattern = parsePattern(service, parser, "pattern", 0)?
915 let mut children: Array<SyntaxNode> = [pattern]
916 if eat(parser, "keyword/if") {
917 children.push(parseExpressionWithoutNakedLambda(service, parser, "guard", 0)?)
918 }
919 expect(parser, "fat-arrow", "`=>`")?
920 let hi = if at(parser, "keyword/return") {
921 let returnToken = bump(parser)
922 let mut returnChildren: Array<SyntaxNode> = []
923 if !at(parser, "separator") && !at(parser, "right-brace") && !at(parser, "eof") {
924 returnChildren.push(parseNestedExpression(service, parser, "value", 0)?)
925 }
926 let returnHi = boxValue(parser.lastHi)
927 children.push(node(
928 "match-case-return",
929 returnToken.lo,
930 returnHi,
931 "body",
932 0,
933 [],
934 returnChildren,
935 ))
936 returnHi
937 } else {
938 let body = parseNestedExpression(service, parser, "body", 0)?
939 let bodyHi = body.hi
940 children.push(body)
941 bodyHi
942 }
943 Ok(node("match-case", keyword.lo, hi, "cases", index, [], children))
946function parseMatch(
947 service: ParserService,
948 parser: Parser,
949 field: string,
950 index: int,
951) -> Result<SyntaxNode, string> {
952 let keyword = expect(parser, "keyword/match", "`match`")?
953 let scrutinee = parseExpressionBeforeBlock(service, parser, "scrutinee", 0)?
954 let open = expectLeftBrace(parser)?
955 let mut children: Array<SyntaxNode> = [scrutinee]
956 let mut caseIndex = 0
957 skipSeparators(parser)
958 while !at(parser, "right-brace") && !at(parser, "eof") {
959 let before = boxValue(parser.position)
960 match parseMatchCase(service, parser, caseIndex) {
961 case Ok(found) => {
962 children.push(found)
963 caseIndex += 1
964 itemBoundary(parser)
965 }
966 case Err(_) => synchronize(parser)
967 }
968 if boxValue(parser.position) == before &&
969 !at(parser, "right-brace") && !at(parser, "eof") {
970 addDiagnostic(
971 parser,
972 "TPZ2001",
973 "expected `case`",
974 current(parser).lo,
975 current(parser).hi,
976 )
977 bump(parser)
978 }
979 skipSeparators(parser)
980 }
981 let close = expectRightBrace(parser)?
982 if caseIndex == 0 {
983 addDiagnostic(
984 parser,
985 "TPZ2001",
986 "`match` requires at least one `case`",
987 open.lo,
988 close.hi,
989 )
990 }
991 Ok(node(
992 "expression/match",
993 keyword.lo,
994 close.hi,
995 field,
996 index,
997 [],
998 children,
999 ))

SHA256 0c5f605a942e3efabafdaffad87d979c4d7590cc05399450eeeba1418a508946