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 allocation3
// and recovery remain in parser.core.5
import types { PreviewAstAttribute, PreviewDiagnostic, PreviewToken, SyntaxNode }6
import 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,42
}43
import parser.types_patterns { parsePattern, parseType, parseTypeArguments }45
function 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 }53
}55
function nullAttribute(name: string) -> PreviewAstAttribute {56
PreviewAstAttribute { name: name, kind: "null", value: "" }57
}59
function binaryPrecedence(kind: string) -> int {60
match kind {61
case "pipe-gt" => 162
case "gt-gt" => 263
case "question-question" => 364
case "or-or" => 465
case "and-and" => 566
case "eq-eq" | "not-eq" => 667
case "lt" | "le" | "gt" | "ge" | "keyword/in" => 668
case "dot-dot" | "dot-dot-lt" => 769
case "plus" | "minus" => 870
case "star" | "slash" | "percent" => 971
case _ => 072
}73
}75
function 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
}94
}96
function 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 = 0103
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 += 1132
if !eat(parser, "comma") {133
break134
}135
}136
expect(parser, "right-paren", "`)`")?137
Ok(arguments)138
}140
function looksLikeCallTypeArguments(parser: Parser) -> bool {141
if !at(parser, "lt") {142
return false143
}144
let mut angle = 0145
let mut paren = 0146
let mut brace = 0147
let mut offset = 0148
let mut sawTypeToken = false149
while true {150
let token = tokenAt(parser, offset)151
if token.kind == "lt" {152
angle += 1153
} else if token.kind == "gt" || token.kind == "gt-gt" {154
if paren != 0 || brace != 0 {155
return false156
}157
if token.kind == "gt-gt" {158
angle -= 2159
} else {160
angle -= 1161
}162
if angle <= 0 {163
let next = tokenAt(parser, offset + 1)164
return angle == 0 && sawTypeToken &&165
next.kind == "left-paren" && token.hi == next.lo166
}167
sawTypeToken = true168
} else if token.kind == "left-paren" {169
paren += 1170
sawTypeToken = true171
} else if token.kind == "right-paren" {172
paren -= 1173
if paren < 0 {174
return false175
}176
} else if token.kind == "left-brace" {177
brace += 1178
sawTypeToken = true179
} else if token.kind == "right-brace" {180
brace -= 1181
if brace < 0 {182
return false183
}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 = true192
} else if token.kind != "comma" {193
return false194
}195
offset += 1196
}197
false198
}200
function parseLambdaParameters(201
service: ParserService,202
parser: Parser,203
) -> Result<Array<SyntaxNode>, string> {204
let mut parameters: Array<SyntaxNode> = []205
let mut index = 0206
while !at(parser, "right-paren") && !at(parser, "eof") {207
let lo = current(parser).lo208
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 += 1224
if !eat(parser, "comma") {225
break226
}227
}228
Ok(parameters)229
}231
function 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
))261
}263
function 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.hi275
children.push(body)276
Ok(node("expression/lambda", open.lo, hi, field, index, [], children))277
}279
function 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 = 0288
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 += 1304
if !eat(parser, "comma") {305
break306
}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
))318
}320
function 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 = 0327
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
break339
}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 += 1353
if !eat(parser, "comma") {354
break355
}356
}357
Ok(fields)358
}360
function 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
))378
}380
function 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
))410
}412
function parseComprehensionClauses(413
service: ParserService,414
parser: Parser,415
) -> Result<Array<SyntaxNode>, string> {416
let mut clauses: Array<SyntaxNode> = []417
let mut clauseIndex = 0418
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 += 1433
continue434
}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 += 1447
continue448
}449
break450
}451
expect(parser, "fat-arrow", "`=>` to introduce the comprehension body")?452
Ok(clauses)453
}455
function 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
))474
}476
function 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
))496
}498
function 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
))520
}522
function 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 = 0532
while !at(parser, "right-brace") && !at(parser, "eof") {533
elements.push(parseNestedExpression(service, parser, "elements", elementIndex)?)534
elementIndex += 1535
if !eat(parser, "comma") {536
break537
}538
}539
let close = expectRightBrace(parser)?540
Ok(node("expression/set", head.lo, close.hi, field, index, [], elements))541
}543
function 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 = 0553
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 += 1567
if !eat(parser, "comma") {568
break569
}570
}571
let close = expectRightBrace(parser)?572
Ok(node("expression/map", head.lo, close.hi, field, index, [], entries))573
}575
export 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
)585
}587
function 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.hi618
let wholeLo = keyword.lo619
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
))647
}649
function 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.hi663
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.hi671
children.push(elseValue)672
}673
Ok(node("expression/if", keyword.lo, hi, field, index, [], children))674
}676
function 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
))696
}698
export function labelNode(value: PreviewToken, field: string, index: int) -> SyntaxNode {699
node("identifier", value.lo + 1, value.hi, field, index, [], [])700
}702
function 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.hi715
children.push(body)716
Ok(node("expression/loop", head.lo, hi, field, index, [], children))717
}719
function 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.hi740
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 = recoveredHi762
}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))778
}780
function 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
))797
}799
function 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)840
}842
function 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)871
}873
function 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 = false883
match timeout {884
case Some(value) => {885
hasTimeout = true886
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
))906
}908
function 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
returnHi937
} else {938
let body = parseNestedExpression(service, parser, "body", 0)?939
let bodyHi = body.hi940
children.push(body)941
bodyHi942
}943
Ok(node("match-case", keyword.lo, hi, "cases", index, [], children))944
}946
function 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 = 0957
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 += 1964
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
))1000
}SHA256 0c5f605a942e3efabafdaffad87d979c4d7590cc05399450eeeba1418a508946