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/declarations_modules.tpz
Topaz · 36793 B · Original file
1
// Declaration, statement, block, import, export, and program grammar.2
// Expression and type-pattern parsers are called through their forward service3
// records; the public parse result is assembled by parser.tpz.5
import types { 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
eat,17
expect,18
expectLeftBrace,19
expectRightBrace,20
fail,21
identNode,22
identifier,23
itemBoundary,24
lambdaAllowed,25
node,26
recordUpdateAllowed,27
rejectReservedBinding,28
setLambdaAllowed,29
setRecordUpdateAllowed,30
skipSeparators,31
stringAttribute,32
synchronize,33
text,34
tokenAt,35
}36
import parser.types_patterns {37
bindingPattern,38
expectCloseAngle,39
namedType,40
parsePattern,41
parseType,42
}43
import parser.expressions {44
blockExpression,45
labelNode,46
parseExpression,47
parseExpressionBeforeBlock,48
}50
// The high component is the type-parameter index and the low component is its bound ordinal.51
const TYPE_BOUND_INDEX_STRIDE = 429496729653
function rejectedStatement(message: string) -> Result<SyntaxNode, string> {54
Err(message)55
}57
function skipBalancedBraces(parser: Parser) -> () {58
if !at(parser, "left-brace") {59
return ()60
}61
let mut depth = 062
while !at(parser, "eof") {63
let kind = current(parser).kind64
if kind == "left-brace" {65
depth += 166
} else if kind == "right-brace" {67
depth -= 168
}69
bump(parser)70
if depth == 0 {71
break72
}73
}74
()75
}77
function parseTypeParameters(78
parser: Parser,79
field: string,80
) -> Result<Array<SyntaxNode>, string> {81
let mut parameters: Array<SyntaxNode> = []82
if !eat(parser, "lt") {83
return Ok(parameters)84
}85
let mut index = 086
while true {87
let name = identifier(parser, "a type parameter")?88
parameters.push(identNode(name, field, index))89
index += 190
if !eat(parser, "comma") {91
break92
}93
}94
expectCloseAngle(parser)?95
Ok(parameters)96
}98
function parseFunctionTypeParameters(parser: Parser) -> Result<Array<SyntaxNode>, string> {99
let mut parameters: Array<SyntaxNode> = []100
let mut bounds: Array<SyntaxNode> = []101
if !eat(parser, "lt") {102
return Ok(parameters)103
}104
let mut parameterIndex = 0105
while true {106
let name = identifier(parser, "a type parameter")?107
parameters.push(identNode(name, "typeParameters", parameterIndex))108
if eat(parser, "colon") {109
let mut boundIndex = 0110
while true {111
let bound = identifier(parser, "a protocol bound")?112
bounds.push(identNode(113
bound,114
"typeParameterBounds",115
parameterIndex * TYPE_BOUND_INDEX_STRIDE + boundIndex,116
))117
boundIndex += 1118
if !eat(parser, "plus") {119
break120
}121
}122
}123
parameterIndex += 1124
if !eat(parser, "comma") {125
break126
}127
}128
expectCloseAngle(parser)?129
for bound in bounds {130
parameters.push(bound)131
}132
Ok(parameters)133
}135
function parseFunction(service: ParserService,136
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {137
let keyword = expect(parser, "keyword/function", "`function`")?138
let name = identifier(parser, "a function name")?139
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]140
for parameter in parseFunctionTypeParameters(parser)? {141
children.push(parameter)142
}143
expect(parser, "left-paren", "`(`")?144
let mut parameterIndex = 0145
while !at(parser, "right-paren") && !at(parser, "eof") {146
let parameterLo = current(parser).lo147
let variadic = eat(parser, "ellipsis")148
let parameterName = identifier(parser, "a parameter name")?149
rejectReservedBinding(parser, parameterName)150
let parameterType = if parameterIndex == 0 && !variadic &&151
text(parser, parameterName.lo, parameterName.hi) == "self" &&152
!at(parser, "colon") {153
namedType(parameterName, "type", 0)154
} else {155
expect(parser, "colon", "`:`")?156
parseType(service, parser, "type", 0)?157
}158
let mut parameterChildren: Array<SyntaxNode> = [159
identNode(parameterName, "name", 0),160
parameterType,161
]162
if eat(parser, "eq") {163
parameterChildren.push(parseExpression(service, parser, "default", 0)?)164
}165
children.push(node(166
"parameter",167
parameterLo,168
boxValue(parser.lastHi),169
"parameters",170
parameterIndex,171
[boolAttribute("variadic", variadic)],172
parameterChildren,173
))174
parameterIndex += 1175
if !eat(parser, "comma") {176
break177
}178
}179
expect(parser, "right-paren", "`)`")?180
if eat(parser, "thin-arrow") {181
children.push(parseType(service, parser, "returnType", 0)?)182
}183
let body = parseBlock(service, parser, "body", 0)?184
let hi = body.hi185
children.push(body)186
let declaration = node(187
"function-declaration",188
name.lo,189
hi,190
"declaration",191
0,192
[],193
children,194
)195
Ok(node(196
"statement/function",197
keyword.lo,198
hi,199
field,200
index,201
[],202
[declaration],203
))204
}206
function parseTypeAlias(service: ParserService,207
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {208
let keyword = expect(parser, "keyword/type", "`type`")?209
let name = identifier(parser, "a type name")?210
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]211
for parameter in parseTypeParameters(parser, "typeParameters")? {212
children.push(parameter)213
}214
expect(parser, "eq", "`=`")?215
let typeNode = parseType(service, parser, "type", 0)?216
let hi = typeNode.hi217
children.push(typeNode)218
Ok(node(219
"statement/type-alias",220
keyword.lo,221
hi,222
field,223
index,224
[],225
children,226
))227
}229
function parseDerives(parser: Parser) -> Result<Array<SyntaxNode>, string> {230
let mut derives: Array<SyntaxNode> = []231
if !at(parser, "identifier") || text(parser, current(parser).lo, current(parser).hi) != "derives" {232
return Ok(derives)233
}234
bump(parser)235
let mut index = 0236
while true {237
derives.push(identNode(identifier(parser, "a protocol name to derive")?, "derives", index))238
index += 1239
if !eat(parser, "comma") {240
break241
}242
}243
Ok(derives)244
}246
function contextualHeadAt(parser: Parser, offset: int, word: string) -> bool {247
let head = tokenAt(parser, offset)248
head.kind == "identifier" && text(parser, head.lo, head.hi) == word &&249
tokenAt(parser, offset + 1).kind == "identifier"250
}252
function contextualHead(parser: Parser, word: string) -> bool {253
contextualHeadAt(parser, 0, word)254
}256
function contextualHeadWithFollowAt(257
parser: Parser,258
offset: int,259
word: string,260
firstFollow: string,261
secondFollow: string,262
) -> bool {263
contextualHeadAt(parser, offset, word) &&264
(tokenAt(parser, offset + 2).kind == firstFollow ||265
tokenAt(parser, offset + 2).kind == secondFollow)266
}268
function contextualHeadWithFollow(269
parser: Parser,270
word: string,271
firstFollow: string,272
secondFollow: string,273
) -> bool {274
contextualHeadWithFollowAt(parser, 0, word, firstFollow, secondFollow)275
}277
function derivableDeclarationHeadAt(parser: Parser, offset: int, word: string) -> bool {278
if !contextualHeadAt(parser, offset, word) {279
return false280
}281
let follow = tokenAt(parser, offset + 2)282
follow.kind == "left-brace" || follow.kind == "lt" ||283
(follow.kind == "identifier" && text(parser, follow.lo, follow.hi) == "derives")284
}286
function derivableDeclarationHead(parser: Parser, word: string) -> bool {287
derivableDeclarationHeadAt(parser, 0, word)288
}290
function parseEnum(service: ParserService,291
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {292
let head = identifier(parser, "`enum`")?293
let name = identifier(parser, "an enum name")?294
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]295
for parameter in parseTypeParameters(parser, "typeParameters")? {296
children.push(parameter)297
}298
let derives = parseDerives(parser)?299
expectLeftBrace(parser)?300
let mut variantIndex = 0301
while !at(parser, "right-brace") && !at(parser, "eof") {302
let variant = identifier(parser, "a variant name")?303
let mut variantChildren: Array<SyntaxNode> = [identNode(variant, "name", 0)]304
if eat(parser, "left-paren") {305
let mut payloadIndex = 0306
while true {307
variantChildren.push(parseType(308
service,310
parser,311
"payload",312
payloadIndex,313
)?)314
payloadIndex += 1315
if !eat(parser, "comma") {316
break317
}318
}319
expect(parser, "right-paren", "`)`")?320
}321
children.push(node(322
"enum-variant",323
variant.lo,324
variant.hi,325
"variants",326
variantIndex,327
[],328
variantChildren,329
))330
variantIndex += 1331
if !eat(parser, "comma") {332
break333
}334
}335
let close = expectRightBrace(parser)?336
for derived in derives {337
children.push(derived)338
}339
Ok(node(340
"statement/enum",341
head.lo,342
close.hi,343
field,344
index,345
[],346
children,347
))348
}350
function parseRecord(service: ParserService,351
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {352
let head = identifier(parser, "`record`")?353
let name = identifier(parser, "a record name")?354
let recordName = text(parser, name.lo, name.hi)355
if recordName == "map" || recordName == "set" {356
addDiagnostic(357
parser,358
"TPZ2012",359
"`{recordName}` is reserved for the `{recordName} \{ … \}` collection literal (§6) and cannot be a record name",360
name.lo,361
name.hi,362
)363
}364
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]365
for parameter in parseTypeParameters(parser, "typeParameters")? {366
children.push(parameter)367
}368
let derives = parseDerives(parser)?369
expectLeftBrace(parser)?370
let mut fieldIndex = 0371
while !at(parser, "right-brace") && !at(parser, "eof") {372
let fieldLo = current(parser).lo373
let fieldNameToken = identifier(parser, "a field name")?374
expect(parser, "colon", "`:`")?375
let typeNode = parseType(service, parser, "type", 0)?376
let mut fieldChildren: Array<SyntaxNode> = [377
identNode(fieldNameToken, "name", 0),378
typeNode,379
]380
if eat(parser, "eq") {381
fieldChildren.push(parseExpression(service, parser, "default", 0)?)382
}383
children.push(node(384
"record-field-declaration",385
fieldLo,386
boxValue(parser.lastHi),387
"fields",388
fieldIndex,389
[],390
fieldChildren,391
))392
fieldIndex += 1393
if !eat(parser, "comma") {394
break395
}396
}397
let close = expectRightBrace(parser)?398
for derived in derives {399
children.push(derived)400
}401
Ok(node(402
"statement/record",403
head.lo,404
close.hi,405
field,406
index,407
[],408
children,409
))410
}412
function parseNewtype(service: ParserService,413
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {414
let head = identifier(parser, "`newtype`")?415
let name = identifier(parser, "a newtype name")?416
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]417
for parameter in parseTypeParameters(parser, "typeParameters")? {418
children.push(parameter)419
}420
expect(parser, "eq", "`=`")?421
let base = parseType(service, parser, "base", 0)?422
let hi = base.hi423
children.push(base)424
Ok(node(425
"statement/newtype",426
head.lo,427
hi,428
field,429
index,430
[],431
children,432
))433
}435
function parseImpl(service: ParserService,436
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {437
let head = identifier(parser, "`impl`")?438
let name = identifier(parser, "a type name")?439
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]440
if eat(parser, "lt") {441
children.push(identNode(identifier(parser, "a conforming type name")?, "target", 0))442
expectCloseAngle(parser)?443
}444
expectLeftBrace(parser)?445
skipSeparators(parser)446
let mut methodIndex = 0447
while !at(parser, "right-brace") && !at(parser, "eof") {448
let methodLo = current(parser).lo449
let exported = at(parser, "identifier") &&450
text(parser, current(parser).lo, current(parser).hi) == "export"451
if exported {452
bump(parser)453
}454
if !at(parser, "keyword/function") {455
fail(parser, "an impl body contains only `function` methods")?456
}457
let statement = parseFunction(service, parser, "unused", 0)?458
let declaration = match statement.children.get(0) {459
case Some(value) => attach(value, "declaration", 0)460
case None => return Err("function declaration projection is missing")461
}462
children.push(node(463
"impl-method",464
methodLo,465
statement.hi,466
"methods",467
methodIndex,468
[boolAttribute("exported", exported)],469
[declaration],470
))471
methodIndex += 1472
skipSeparators(parser)473
}474
let close = expectRightBrace(parser)?475
Ok(node(476
"statement/impl",477
head.lo,478
close.hi,479
field,480
index,481
[],482
children,483
))484
}486
function parseProtocolMethod(service: ParserService,487
parser: Parser, index: int) -> Result<SyntaxNode, string> {488
expect(parser, "keyword/function", "`function`")?489
let name = identifier(parser, "a function name")?490
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]491
for parameter in parseFunctionTypeParameters(parser)? {492
children.push(parameter)493
}494
expect(parser, "left-paren", "`(`")?495
let mut parameterIndex = 0496
while !at(parser, "right-paren") && !at(parser, "eof") {497
let parameterLo = current(parser).lo498
let variadic = eat(parser, "ellipsis")499
let parameterName = identifier(parser, "a parameter name")?500
rejectReservedBinding(parser, parameterName)501
expect(parser, "colon", "`:`")?502
let parameterType = parseType(service, parser, "type", 0)?503
let mut parameterChildren: Array<SyntaxNode> = [504
identNode(parameterName, "name", 0),505
parameterType,506
]507
if eat(parser, "eq") {508
parameterChildren.push(parseExpression(service, parser, "default", 0)?)509
}510
children.push(node(511
"parameter",512
parameterLo,513
boxValue(parser.lastHi),514
"parameters",515
parameterIndex,516
[boolAttribute("variadic", variadic)],517
parameterChildren,518
))519
parameterIndex += 1520
if !eat(parser, "comma") {521
break522
}523
}524
expect(parser, "right-paren", "`)`")?525
if eat(parser, "thin-arrow") {526
children.push(parseType(service, parser, "returnType", 0)?)527
}528
let bodyToken = current(parser)529
children.push(node(530
"block",531
bodyToken.lo,532
bodyToken.hi,533
"body",534
0,535
[],536
[],537
))538
Ok(node(539
"function-declaration",540
name.lo,541
bodyToken.hi,542
"methods",543
index,544
[],545
children,546
))547
}549
function parseProtocol(service: ParserService,550
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {551
let head = identifier(parser, "`protocol`")?552
let name = identifier(parser, "a protocol name")?553
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]554
for parameter in parseTypeParameters(parser, "typeParameters")? {555
children.push(parameter)556
}557
expectLeftBrace(parser)?558
skipSeparators(parser)559
let mut methodIndex = 0560
while !at(parser, "right-brace") && !at(parser, "eof") {561
if !at(parser, "keyword/function") {562
fail(parser, "a protocol body contains only `function` signatures")?563
}564
children.push(parseProtocolMethod(service, parser, methodIndex)?)565
methodIndex += 1566
skipSeparators(parser)567
}568
let close = expectRightBrace(parser)?569
Ok(node(570
"statement/protocol",571
head.lo,572
close.hi,573
field,574
index,575
[],576
children,577
))578
}580
function parseUsing(service: ParserService,581
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {582
let head = identifier(parser, "`using`")?583
let name = identifier(parser, "a resource binding name")?584
rejectReservedBinding(parser, name)585
expect(parser, "eq", "`=`")?586
let value = parseExpressionBeforeBlock(service, parser, "value", 0)?587
let body = parseBlock(service, parser, "body", 0)?588
Ok(node(589
"statement/using",590
head.lo,591
body.hi,592
field,593
index,594
[],595
[identNode(name, "name", 0), value, body],596
))597
}599
function parseLet(service: ParserService,600
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {601
let keyword = expect(parser, "keyword/let", "`let`")?602
let mutable = eat(parser, "keyword/mut")603
let pattern = if mutable {604
let name = identifier(parser, "a binding name")?605
rejectReservedBinding(parser, name)606
bindingPattern(name, "pattern", 0)607
} else {608
parsePattern(service, parser, "pattern", 0)?609
}610
let mut children: Array<SyntaxNode> = [pattern]611
if eat(parser, "colon") {612
children.push(parseType(service, parser, "type", 0)?)613
}614
if !mutable && at(parser, "keyword/mut") {615
fail(parser, "`mut` comes right after `let` — write `let mut <name> = …`")?616
}617
expect(parser, "eq", "`=`")?618
children.push(parseExpression(service, parser, "value", 0)?)619
Ok(node(620
"statement/let",621
keyword.lo,622
boxValue(parser.lastHi),623
field,624
index,625
[boolAttribute("mutable", mutable)],626
children,627
))628
}630
function parseConst(service: ParserService,631
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {632
let keyword = expect(parser, "keyword/const", "`const`")?633
let name = identifier(parser, "a constant name")?634
rejectReservedBinding(parser, name)635
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]636
if eat(parser, "colon") {637
children.push(parseType(service, parser, "type", 0)?)638
}639
expect(parser, "eq", "`=`")?640
children.push(parseExpression(service, parser, "value", 0)?)641
Ok(node(642
"statement/const",643
keyword.lo,644
boxValue(parser.lastHi),645
field,646
index,647
[],648
children,649
))650
}652
function parseReturnStatement(653
service: ParserService,654
parser: Parser,655
field: string,656
index: int,657
) -> Result<SyntaxNode, string> {658
let keyword = bump(parser)659
let mut children: Array<SyntaxNode> = []660
if !at(parser, "separator") && !at(parser, "right-brace") &&661
!at(parser, "eof") {662
children.push(parseExpression(service, parser, "value", 0)?)663
}664
Ok(node(665
"statement/return",666
keyword.lo,667
boxValue(parser.lastHi),668
field,669
index,670
[],671
children,672
))673
}675
function parseLoopExitStatement(676
service: ParserService,677
parser: Parser,678
field: string,679
index: int,680
) -> Result<SyntaxNode, string> {681
let kind = current(parser).kind682
let keyword = bump(parser)683
let mut children: Array<SyntaxNode> = []684
if at(parser, "label") {685
children.push(labelNode(bump(parser), "label", 0))686
}687
if kind == "keyword/break" && !at(parser, "separator") &&688
!at(parser, "right-brace") && !at(parser, "eof") {689
children.push(parseExpression(service, parser, "value", 0)?)690
}691
Ok(node(692
if kind == "keyword/break" {693
"statement/break"694
} else {695
"statement/continue"696
},697
keyword.lo,698
boxValue(parser.lastHi),699
field,700
index,701
[],702
children,703
))704
}706
function whileLetNode(707
keyword: PreviewToken,708
pattern: SyntaxNode,709
scrutinee: SyntaxNode,710
sourceBody: SyntaxNode,711
field: string,712
index: int,713
) -> SyntaxNode {714
let hi = sourceBody.hi715
let lo = keyword.lo716
let bodyExpression = blockExpression(sourceBody, "body", 0)717
let firstCase = node(718
"match-case",719
lo,720
hi,721
"cases",722
0,723
[],724
[pattern, bodyExpression],725
)726
let wildcard = node("pattern/wildcard", lo, hi, "pattern", 0, [], [])727
let syntheticBreak = node(728
"statement/break",729
lo,730
hi,731
"statements",732
0,733
[],734
[],735
)736
let syntheticBlock = node(737
"block",738
lo,739
hi,740
"block",741
0,742
[],743
[syntheticBreak],744
)745
let breakExpression = node(746
"expression/block",747
lo,748
hi,749
"body",750
0,751
[],752
[syntheticBlock],753
)754
let secondCase = node(755
"match-case",756
lo,757
hi,758
"cases",759
1,760
[],761
[wildcard, breakExpression],762
)763
let matchExpression = node(764
"expression/match",765
lo,766
hi,767
"tail",768
0,769
[],770
[scrutinee, firstCase, secondCase],771
)772
let body = node("block", lo, hi, "body", 0, [], [matchExpression])773
let condition = node(774
"expression/boolean",775
lo,776
hi,777
"condition",778
0,779
[boolAttribute("value", true)],780
[],781
)782
node("statement/while", lo, hi, field, index, [], [condition, body])783
}785
function parseWhileStatement(786
service: ParserService,787
parser: Parser,788
field: string,789
index: int,790
) -> Result<SyntaxNode, string> {791
let keyword = bump(parser)792
if eat(parser, "keyword/let") {793
let pattern = parsePattern(service, parser, "pattern", 0)?794
expect(parser, "eq", "`=`")?795
let scrutinee = parseExpressionBeforeBlock(service, parser, "scrutinee", 0)?796
let body = parseBlock(service, parser, "block", 0)?797
return Ok(whileLetNode(keyword, pattern, scrutinee, body, field, index))798
}799
let condition = parseExpressionBeforeBlock(service, parser, "condition", 0)?800
let body = parseBlock(service, parser, "body", 0)?801
Ok(node(802
"statement/while",803
keyword.lo,804
body.hi,805
field,806
index,807
[],808
[condition, body],809
))810
}812
function parseDeferStatement(813
service: ParserService,814
parser: Parser,815
field: string,816
index: int,817
) -> Result<SyntaxNode, string> {818
let keyword = bump(parser)819
let value = parseExpression(service, parser, "value", 0)?820
if value.kind != "expression/block" && value.kind != "expression/call" {821
addDiagnostic(822
parser,823
"TPZ2004",824
"`defer` takes a block or a call",825
value.lo,826
value.hi,827
)828
}829
Ok(node(830
"statement/defer",831
keyword.lo,832
value.hi,833
field,834
index,835
[],836
[value],837
))838
}840
function assignmentOperator(kind: string) -> string {841
match kind {842
case "eq" => "assign"843
case "plus-eq" => "add"844
case "minus-eq" => "subtract"845
case "star-eq" => "multiply"846
case "slash-eq" => "divide"847
case "percent-eq" => "remainder"848
case _ => "coalesce"849
}850
}852
function parseExpressionStatement(853
service: ParserService,854
parser: Parser,855
field: string,856
index: int,857
) -> Result<SyntaxNode, string> {858
let expression = parseExpression(service, parser, "value", 0)?859
let assignmentKind = current(parser).kind860
if assignmentKind != "eq" && assignmentKind != "plus-eq" &&861
assignmentKind != "minus-eq" && assignmentKind != "star-eq" &&862
assignmentKind != "slash-eq" && assignmentKind != "percent-eq" &&863
assignmentKind != "question-question-eq" {864
return Ok(node(865
"statement/expression",866
expression.lo,867
expression.hi,868
field,869
index,870
[],871
[expression],872
))873
}874
if expression.kind != "expression/identifier" &&875
expression.kind != "expression/member" &&876
expression.kind != "expression/index" {877
addDiagnostic(878
parser,879
"TPZ2003",880
"assignment target must be an identifier, member access, or index access",881
expression.lo,882
expression.hi,883
)884
}885
bump(parser)886
let value = parseExpression(service, parser, "value", 0)?887
Ok(node(888
"statement/assign",889
expression.lo,890
value.hi,891
field,892
index,893
[stringAttribute("operator", assignmentOperator(assignmentKind))],894
[attach(expression, "target", 0), value],895
))896
}898
function parseStatement(service: ParserService,899
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {900
let kind = current(parser).kind901
if kind == "keyword/function" { return parseFunction(service, parser, field, index) }902
if kind == "keyword/let" { return parseLet(service, parser, field, index) }903
if kind == "keyword/mut" {904
fail(parser, "`mut` comes right after `let` — write `let mut <name> = …`")?905
}906
if kind == "keyword/type" { return parseTypeAlias(service, parser, field, index) }907
if derivableDeclarationHead(parser, "enum") { return parseEnum(service, parser, field, index) }908
if derivableDeclarationHead(parser, "record") { return parseRecord(service, parser, field, index) }909
if contextualHeadWithFollow(parser, "newtype", "eq", "lt") {910
return parseNewtype(service, parser, field, index)911
}912
if contextualHeadWithFollow(parser, "impl", "left-brace", "lt") {913
return parseImpl(service, parser, field, index)914
}915
if contextualHeadWithFollow(parser, "protocol", "left-brace", "lt") {916
return parseProtocol(service, parser, field, index)917
}918
if contextualHead(parser, "using") && tokenAt(parser, 2).kind == "eq" {919
return parseUsing(service, parser, field, index)920
}921
if kind == "keyword/const" { return parseConst(service, parser, field, index) }922
if kind == "keyword/return" {923
return parseReturnStatement(service, parser, field, index)924
}925
if kind == "keyword/break" || kind == "keyword/continue" {926
return parseLoopExitStatement(service, parser, field, index)927
}928
if kind == "keyword/while" {929
return parseWhileStatement(service, parser, field, index)930
}931
if kind == "keyword/defer" {932
return parseDeferStatement(service, parser, field, index)933
}934
parseExpressionStatement(service, parser, field, index)935
}937
function parseBlockBody(938
service: ParserService,939
parser: Parser,940
open: PreviewToken,941
field: string,942
index: int,943
) -> Result<SyntaxNode, string> {944
let mut statements: Array<SyntaxNode> = []945
let mut tail: Option<SyntaxNode> = None946
let mut statementIndex = 0947
skipSeparators(parser)948
while !at(parser, "right-brace") && !at(parser, "eof") {949
let before = boxValue(parser.position)950
match parseStatement(service, parser, "statements", statementIndex) {951
case Ok(statement) => {952
if at(parser, "right-brace") && statement.kind == "statement/expression" {953
tail = statement.children.get(0)954
break955
}956
statements.push(statement)957
statementIndex += 1958
itemBoundary(parser)959
}960
case Err(_) => synchronize(parser)961
}962
if boxValue(parser.position) == before &&963
!at(parser, "right-brace") && !at(parser, "eof") {964
addDiagnostic(965
parser,966
"TPZ2001",967
"expected a statement",968
current(parser).lo,969
current(parser).hi,970
)971
bump(parser)972
}973
skipSeparators(parser)974
}975
let close = expectRightBrace(parser)?976
let mut children = statements977
match tail {978
case Some(value) => children.push(attach(value, "tail", 0))979
case None => ()980
}981
Ok(node("block", open.lo, close.hi, field, index, [], children))982
}984
export function parseBlock(985
service: ParserService,986
parser: Parser,987
field: string,988
index: int,989
) -> Result<SyntaxNode, string> {990
let open = expectLeftBrace(parser)?991
let previousLambda = lambdaAllowed(parser)992
let previousRecordUpdate = recordUpdateAllowed(parser)993
setLambdaAllowed(parser, true)994
setRecordUpdateAllowed(parser, true)995
let result = parseBlockBody(service, parser, open, field, index)996
setLambdaAllowed(parser, previousLambda)997
setRecordUpdateAllowed(parser, previousRecordUpdate)998
result999
}1001
function isContextualWord(parser: Parser, word: string) -> bool {1002
at(parser, "identifier") && text(parser, current(parser).lo, current(parser).hi) == word1003
}1005
record ParsedImportSpec {1006
node: SyntaxNode,1007
sourceName: string,1008
sourceToken: PreviewToken,1009
boundName: string,1010
boundToken: PreviewToken,1011
}1013
function parseModulePath(parser: Parser) -> Result<SyntaxNode, string> {1014
let first = identifier(parser, "a module path segment")?1015
let mut pathChildren: Array<SyntaxNode> = [identNode(first, "segments", 0)]1016
let mut pathIndex = 11017
let mut pathHi = first.hi1018
while eat(parser, "dot") {1019
let segment = identifier(parser, "a module path segment")?1020
pathChildren.push(identNode(segment, "segments", pathIndex))1021
pathIndex += 11022
pathHi = segment.hi1023
}1024
Ok(node(1025
"module-path",1026
first.lo,1027
pathHi,1028
"path",1029
0,1030
[],1031
pathChildren,1032
))1033
}1035
function parseImportSpec(1036
parser: Parser,1037
index: int,1038
) -> Result<ParsedImportSpec, string> {1039
if current(parser).kind.startsWith("keyword/") {1040
let token = current(parser)1041
addDiagnostic(1042
parser,1043
"TPZ2011",1044
"an import name is an identifier; keywords cannot be selected",1045
token.lo,1046
token.hi,1047
)1048
return Err("an import name is an identifier")1049
}1050
let name = identifier(parser, "an import name")?1051
let mut children: Array<SyntaxNode> = [identNode(name, "name", 0)]1052
let mut hi = name.hi1053
let sourceName = text(parser, name.lo, name.hi)1054
let mut boundName = sourceName1055
let mut boundToken = name1056
if isContextualWord(parser, "as") {1057
bump(parser)1058
let alias = identifier(parser, "an import alias")?1059
hi = alias.hi1060
boundName = text(parser, alias.lo, alias.hi)1061
boundToken = alias1062
children.push(identNode(alias, "alias", 0))1063
}1064
Ok(ParsedImportSpec {1065
node: node("import-spec", name.lo, hi, "specs", index, [], children),1066
sourceName: sourceName,1067
sourceToken: name,1068
boundName: boundName,1069
boundToken: boundToken,1070
})1071
}1073
function diagnoseDuplicateImportSpecs(1074
parser: Parser,1075
specs: Array<ParsedImportSpec>,1076
) -> () {1077
// Diagnose each later offending spec once in source order, with1078
// source-name precedence when both duplicate axes apply.1079
let mut sourceNames: Set<string> = set {}1080
let mut boundNames: Set<string> = set {}1081
for spec in specs {1082
let sourceDuplicate = sourceNames.contains(spec.sourceName)1083
let boundDuplicate = boundNames.contains(spec.boundName)1084
sourceNames.add(spec.sourceName)1085
boundNames.add(spec.boundName)1086
if sourceDuplicate {1087
addDiagnostic(1088
parser,1089
"TPZ2011",1090
"this exported name is already selected in this list",1091
spec.sourceToken.lo,1092
spec.sourceToken.hi,1093
)1094
} else if boundDuplicate {1095
addDiagnostic(1096
parser,1097
"TPZ2011",1098
"this local name is already bound by this list",1099
spec.boundToken.lo,1100
spec.boundToken.hi,1101
)1102
}1103
}1104
()1105
}1107
function parseImportSelection(parser: Parser) -> Result<Array<SyntaxNode>, string> {1108
let open = expectLeftBrace(parser)?1109
let mut parsedSpecs: Array<ParsedImportSpec> = []1110
while !at(parser, "right-brace") && !at(parser, "eof") {1111
parsedSpecs.push(parseImportSpec(parser, parsedSpecs.length)?)1112
if !eat(parser, "comma") {1113
break1114
}1115
}1116
let close = expectRightBrace(parser)?1117
if parsedSpecs.length == 0 {1118
addDiagnostic(1119
parser,1120
"TPZ2011",1121
"a selection list selects at least one exported name",1122
open.lo,1123
close.hi,1124
)1125
}1126
diagnoseDuplicateImportSpecs(parser, parsedSpecs)1127
let mut specs: Array<SyntaxNode> = []1128
for spec in parsedSpecs {1129
specs.push(spec.node)1130
}1131
Ok(specs)1132
}1134
function parseImportAlias(parser: Parser) -> Result<SyntaxNode, string> {1135
bump(parser)1136
let alias = identNode(identifier(parser, "an import alias")?, "alias", 0)1137
if at(parser, "left-brace") {1138
let brace = current(parser)1139
addDiagnostic(1140
parser,1141
"TPZ2009",1142
"a namespace alias and a selection list do not compose (`import m as ns \{ x \}` is invalid syntax)",1143
brace.lo,1144
brace.hi,1145
)1146
skipBalancedBraces(parser)1147
}1148
Ok(alias)1149
}1151
function parseImport(parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {1152
let head = identifier(parser, "`import`")?1153
let path = parseModulePath(parser)?1154
let mut importChildren: Array<SyntaxNode> = [path]1155
if at(parser, "left-brace") {1156
for spec in parseImportSelection(parser)? {1157
importChildren.push(spec)1158
}1159
} else if isContextualWord(parser, "as") {1160
importChildren.push(parseImportAlias(parser)?)1161
}1162
let hi = boxValue(parser.lastHi)1163
let importItem = node(1164
"import-item",1165
head.lo,1166
hi,1167
"import",1168
0,1169
[],1170
importChildren,1171
)1172
Ok(node(1173
"statement/import",1174
head.lo,1175
hi,1176
field,1177
index,1178
[],1179
[importItem],1180
))1181
}1183
function parseImportAtProgram(1184
parser: Parser,1185
field: string,1186
index: int,1187
prologueDone: bool,1188
) -> Result<SyntaxNode, string> {1189
let statement = parseImport(parser, field, index)?1190
if prologueDone {1191
addDiagnostic(1192
parser,1193
"TPZ2010",1194
"imports form a prologue: every `import` precedes all other top-level items",1195
statement.lo,1196
statement.hi,1197
)1198
}1199
Ok(statement)1200
}1202
function parseExport(service: ParserService,1203
parser: Parser, field: string, index: int) -> Result<SyntaxNode, string> {1204
let head = identifier(parser, "`export`")?1205
let inner = parseStatement(service, parser, "item", 0)?1206
if inner.kind == "statement/let" {1207
match inner.children.get(0) {1208
case Some(pattern) => {1209
if pattern.kind != "pattern/binding" && pattern.kind != "pattern/typed" {1210
addDiagnostic(1211
parser,1212
"TPZ2007",1213
"an exported `let` binds exactly one identifier; destructure privately, then export the identifier",1214
pattern.lo,1215
pattern.hi,1216
)1217
}1218
}1219
case None => ()1220
}1221
}1222
Ok(node(1223
"statement/export",1224
head.lo,1225
inner.hi,1226
field,1227
index,1228
[],1229
[inner],1230
))1231
}1233
function exportDeclarationHead(parser: Parser) -> bool {1234
if !isContextualWord(parser, "export") {1235
return false1236
}1237
let follow = tokenAt(parser, 1)1238
if follow.kind == "keyword/function" ||1239
follow.kind == "keyword/type" ||1240
follow.kind == "keyword/let" ||1241
follow.kind == "keyword/const" {1242
return true1243
}1244
if follow.kind != "identifier" {1245
return false1246
}1247
let word = text(parser, follow.lo, follow.hi)1248
if word == "enum" || word == "record" {1249
return derivableDeclarationHeadAt(parser, 1, word)1250
}1251
word == "newtype" && contextualHeadWithFollowAt(parser, 1, word, "eq", "lt")1252
}1254
function rejectStringModulePath(parser: Parser) -> Result<SyntaxNode, string> {1255
let token = current(parser)1256
addDiagnostic(1257
parser,1258
"TPZ2008",1259
"string and template module paths are reserved and unused; module paths are dotted identifiers",1260
token.lo,1261
token.hi,1262
)1263
rejectedStatement("reserved module path")1264
}1266
function rejectUseItem(parser: Parser) -> Result<SyntaxNode, string> {1267
let token = current(parser)1268
addDiagnostic(1269
parser,1270
"TPZ2008",1271
"`use` is reserved and unused; did you mean `import`?",1272
token.lo,1273
token.hi,1274
)1275
rejectedStatement("reserved use item")1276
}1278
function rejectExportList(parser: Parser) -> Result<SyntaxNode, string> {1279
let token = current(parser)1280
addDiagnostic(1281
parser,1282
"TPZ2009",1283
"export lists are invalid syntax; put `export` on each public declaration",1284
token.lo,1285
token.hi,1286
)1287
bump(parser)1288
skipBalancedBraces(parser)1289
rejectedStatement("rejected export list")1290
}1292
function rejectReexport(parser: Parser) -> Result<SyntaxNode, string> {1293
let token = current(parser)1294
addDiagnostic(1295
parser,1296
"TPZ2009",1297
"re-export syntax does not exist (`export import ...` is rejected)",1298
token.lo,1299
token.hi,1300
)1301
rejectedStatement("re-export is rejected")1302
}1304
function parseProgramItem(1305
service: ParserService,1306
parser: Parser,1307
index: int,1308
prologueDone: bool,1309
) -> Result<SyntaxNode, string> {1310
if isContextualWord(parser, "import") && tokenAt(parser, 1).kind == "identifier" {1311
return parseImportAtProgram(parser, "items", index, prologueDone)1312
}1313
if isContextualWord(parser, "import") &&1314
tokenAt(parser, 1).kind.startsWith("string-start/") {1315
return rejectStringModulePath(parser)1316
}1317
if isContextualWord(parser, "use") && tokenAt(parser, 1).kind == "identifier" {1318
return rejectUseItem(parser)1319
}1320
if isContextualWord(parser, "export") && tokenAt(parser, 1).kind == "left-brace" {1321
return rejectExportList(parser)1322
}1323
if isContextualWord(parser, "export") &&1324
tokenAt(parser, 1).kind == "identifier" &&1325
text(parser, tokenAt(parser, 1).lo, tokenAt(parser, 1).hi) == "import" {1326
return rejectReexport(parser)1327
}1328
if exportDeclarationHead(parser) {1329
parseExport(service, parser, "items", index)1330
} else {1331
parseStatement(service, parser, "items", index)1332
}1333
}1335
export function parseProgram(service: ParserService,1336
parser: Parser, sourceLength: int) -> SyntaxNode {1337
let mut items: Array<SyntaxNode> = []1338
let mut index = 01339
let mut prologueDone = false1340
skipSeparators(parser)1341
while !at(parser, "eof") {1342
let before = boxValue(parser.position)1343
match parseProgramItem(service, parser, index, prologueDone) {1344
case Ok(statement) => {1345
if statement.kind != "statement/import" {1346
prologueDone = true1347
}1348
items.push(statement)1349
index += 11350
itemBoundary(parser)1351
}1352
case Err(_) => synchronize(parser)1353
}1354
if boxValue(parser.position) == before && !at(parser, "eof") {1355
addDiagnostic(1356
parser,1357
"TPZ2001",1358
"expected a statement",1359
current(parser).lo,1360
current(parser).hi,1361
)1362
bump(parser)1363
}1364
skipSeparators(parser)1365
}1366
node("program", 0, sourceLength, "root", 0, [], items)1367
}SHA256 4368280c4461c3d81a2d0b734372dd054ce75ef243ba87b61a9c8200d4246887