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 to3
// declaration and expression leaves without owning parse-result flattening.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
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,33
}35
export 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
)45
}47
export 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
)57
}59
export 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.pendingGt66
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 `>`")72
}74
export 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 = 183
while eat(parser, "comma") {84
arguments.push(parseType(85
service,87
parser,88
"arguments",89
index,90
)?)91
index += 192
}93
expectCloseAngle(parser)?94
Ok(arguments)95
}97
function 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
))140
}142
function 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 = 0151
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 += 1165
if !eat(parser, "comma") {166
break167
}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))180
}182
function 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 = 0191
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 += 1204
if !eat(parser, "comma") {205
break206
}207
}208
expect(parser, "right-paren", "`)`")?209
expect(parser, "thin-arrow", "`->`")?210
let returnType = parseType(service, parser, "returnType", 0)?211
let hi = returnType.hi212
parameters.push(returnType)213
Ok(node("type/function", open.lo, hi, field, index, [], parameters))214
}216
function 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
))249
}251
function 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
break285
}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")291
}293
export 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 = 1305
while eat(parser, "pipe") {306
members.push(parsePrimaryType(307
service,309
parser,310
"members",311
memberIndex,312
)?)313
memberIndex += 1314
}315
Ok(node("type/union", first.lo, boxValue(parser.lastHi), field, index, [], members))316
}318
function 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"325
}327
function 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 = 0334
while !at(parser, "right-brace") && !at(parser, "eof") {335
let lo = current(parser).lo336
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 += 1358
if !eat(parser, "comma") {359
break360
}361
}362
let close = expectRightBrace(parser)?363
if fields.length == 0 {364
let mut diagnostics = parser.diagnostics365
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)373
}375
function 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 = 0384
while !at(parser, "right-bracket") && !at(parser, "eof") {385
if eat(parser, "dot-dot") {386
let markerLo = boxValue(parser.lastHi) - 2387
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.lo393
case None => markerLo394
}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 += 1415
if !eat(parser, "comma") {416
break417
}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))437
}439
function 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 = 0449
while !at(parser, "right-paren") && !at(parser, "eof") {450
children.push(parsePattern(451
service,453
parser,454
"arguments",455
argumentIndex,456
)?)457
argumentIndex += 1458
if !eat(parser, "comma") {459
break460
}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
))472
}474
function 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
))495
}497
function 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
))516
}518
function identifierStartsBindingPattern(parser: Parser) -> bool {519
let next = tokenAt(parser, 1).kind520
next != "dot-dot" && next != "dot-dot-lt" && next != "plus" &&521
next != "minus" && next != "star" && next != "slash" &&522
next != "percent" && next != "star-star"523
}525
function parseIdentifierPattern(526
service: ParserService,527
parser: Parser,528
field: string,529
index: int,530
) -> Result<Option<SyntaxNode>, string> {531
let next = tokenAt(parser, 1).kind532
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)))575
}577
function 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")611
}613
function 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)646
}648
export 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 = 1660
while eat(parser, "pipe") {661
alternatives.push(parsePrimaryPattern(662
service,664
parser,665
"alternatives",666
alternativeIndex,667
)?)668
alternativeIndex += 1669
}670
Ok(node(671
"pattern/or",672
first.lo,673
boxValue(parser.lastHi),674
field,675
index,676
[],677
alternatives,678
))679
}SHA256 72d33df10e53158f4f6c14e821bf540ba0edba97330e61f4045756376fc438a6