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/core.tpz
Topaz · 11077 B · Original file
1
// Parser cursor, diagnostics, node construction, and recovery primitives.2
// Grammar leaves share this state and these token helpers; no grammar family is3
// selected or dispatched from the core itself.5
import types {6
PreviewAstAttribute,7
PreviewDiagnostic,8
PreviewToken,9
SyntaxNode,10
}12
export record ParserRequest {13
kind: string,14
precedence: int,15
field: string,16
index: int,17
}19
// Position, synthetic-token, and diagnostic state shared across recursive grammar services.20
export record Parser {21
source: Bytes,22
tokens: Array<PreviewToken>,23
position: Array<int>,24
lastHi: Array<int>,25
pendingGt: Array<int>,26
nakedLambda: Array<bool>,27
recordUpdate: Array<bool>,28
diagnostics: Array<PreviewDiagnostic>,29
}31
export type ParserService = (Parser, ParserRequest) -> Result<SyntaxNode, string>33
export function invokeParseBlock(34
service: ParserService,35
parser: Parser,36
field: string,37
index: int,38
) -> Result<SyntaxNode, string> {39
service(parser, ParserRequest {40
kind: "parse-block",41
precedence: 0,42
field: field,43
index: index,44
})45
}47
export function invokeParseExpressionBp(48
service: ParserService,49
parser: Parser,50
precedence: int,51
field: string,52
index: int,53
) -> Result<SyntaxNode, string> {54
service(parser, ParserRequest {55
kind: "parse-expression-bp",56
precedence: precedence,57
field: field,58
index: index,59
})60
}62
export function boxValue(values: Array<int>) -> int {63
values[0]64
}66
export function setBox(values: Array<int>, value: int) -> () {67
let mut target = values68
target[0] = value69
()70
}72
export function lambdaAllowed(parser: Parser) -> bool {73
parser.nakedLambda[0]74
}76
export function setLambdaAllowed(parser: Parser, value: bool) -> () {77
let mut target = parser.nakedLambda78
target[0] = value79
()80
}82
export function recordUpdateAllowed(parser: Parser) -> bool {83
parser.recordUpdate[0]84
}86
export function setRecordUpdateAllowed(parser: Parser, value: bool) -> () {87
let mut target = parser.recordUpdate88
target[0] = value89
()90
}92
export function layoutTokenAt(parser: Parser, position: int) -> PreviewToken {93
match parser.tokens.get(position) {94
case Some(value) => value95
case None => parser.tokens[parser.tokens.length - 1]96
}97
}99
export function tokenAt(parser: Parser, offset: int) -> PreviewToken {100
if parser.pendingGt.length > 0 {101
if offset == 0 {102
let hi = boxValue(parser.pendingGt)103
return PreviewToken {104
kind: "gt",105
lo: hi - 1,106
hi: hi,107
stream: "layout",108
synthetic: true,109
}110
}111
let position = boxValue(parser.position) + offset - 1112
return layoutTokenAt(parser, position)113
}114
let position = boxValue(parser.position) + offset115
layoutTokenAt(parser, position)116
}118
export function current(parser: Parser) -> PreviewToken {119
tokenAt(parser, 0)120
}122
export function at(parser: Parser, kind: string) -> bool {123
current(parser).kind == kind124
}126
export function bump(parser: Parser) -> PreviewToken {127
let value = current(parser)128
if parser.pendingGt.length > 0 {129
let mut pending = parser.pendingGt130
pending.pop()131
setBox(parser.lastHi, value.hi)132
return value133
}134
if value.kind != "eof" {135
setBox(parser.position, boxValue(parser.position) + 1)136
}137
setBox(parser.lastHi, value.hi)138
value139
}141
export function eat(parser: Parser, kind: string) -> bool {142
if at(parser, kind) {143
bump(parser)144
true145
} else {146
false147
}148
}150
export function text(parser: Parser, lo: int, hi: int) -> string {151
match parser.source.slice(lo, hi).decodeUtf8() {152
case Ok(value) => value153
case Err(_) => ""154
}155
}157
export function diagnostic(code: string, message: string, lo: int, hi: int) -> PreviewDiagnostic {158
PreviewDiagnostic { code: code, message: message, lo: lo, hi: hi, notes: [] }159
}161
export function addDiagnostic(162
parser: Parser,163
code: string,164
message: string,165
lo: int,166
hi: int,167
) -> () {168
let mut diagnostics = parser.diagnostics169
diagnostics.push(diagnostic(code, message, lo, hi))170
()171
}173
export function rejectReservedBinding(parser: Parser, name: PreviewToken) -> () {174
if text(parser, name.lo, name.hi) == "None" {175
addDiagnostic(176
parser,177
"TPZ2012",178
"`None` is the Option constructor (§22.1) and cannot be a binding name",179
name.lo,180
name.hi,181
)182
}183
()184
}186
export function fail(parser: Parser, message: string) -> Result<int, string> {187
let token = current(parser)188
let mut diagnostics = parser.diagnostics189
diagnostics.push(diagnostic("TPZ2001", message, token.lo, token.hi))190
Err(message)191
}193
export function expect(194
parser: Parser,195
kind: string,196
description: string,197
) -> Result<PreviewToken, string> {198
if at(parser, kind) {199
return Ok(bump(parser))200
}201
fail(parser, "expected {description}")?202
Err("expected {description}")203
}205
export function expectLeftBrace(parser: Parser) -> Result<PreviewToken, string> {206
expect(parser, "left-brace", "`\{`")207
}209
export function expectRightBrace(parser: Parser) -> Result<PreviewToken, string> {210
expect(parser, "right-brace", "`\}`")211
}213
export function identifier(parser: Parser, description: string) -> Result<PreviewToken, string> {214
if at(parser, "identifier") {215
return Ok(bump(parser))216
}217
fail(parser, "expected {description}")?218
Err("expected {description}")219
}221
export function isFieldNameKind(kind: string) -> bool {222
kind == "identifier" || kind.startsWith("keyword/")223
}225
export function fieldName(parser: Parser, description: string) -> Result<PreviewToken, string> {226
if isFieldNameKind(current(parser).kind) {227
return Ok(bump(parser))228
}229
fail(parser, "expected {description}")?230
Err("expected {description}")231
}233
export function matchingParenFollowedBy(parser: Parser, expected: string) -> bool {234
let mut depth = 0235
let mut offset = 0236
while true {237
let kind = tokenAt(parser, offset).kind238
if kind == "eof" {239
return false240
}241
if kind == "left-paren" || kind == "left-bracket" || kind == "left-brace" {242
depth += 1243
} else if kind == "right-paren" ||244
kind == "right-bracket" ||245
kind == "right-brace" {246
depth -= 1247
if depth == 0 {248
return tokenAt(parser, offset + 1).kind == expected249
}250
}251
offset += 1252
}253
false254
}256
export function stringAttribute(name: string, value: string) -> PreviewAstAttribute {257
PreviewAstAttribute { name: name, kind: "string", value: value }258
}260
export function boolAttribute(name: string, value: bool) -> PreviewAstAttribute {261
PreviewAstAttribute {262
name: name,263
kind: "bool",264
value: if value { "true" } else { "false" },265
}266
}268
export function node(269
kind: string,270
lo: int,271
hi: int,272
field: string,273
index: int,274
attributes: Array<PreviewAstAttribute>,275
children: Array<SyntaxNode>,276
) -> SyntaxNode {277
SyntaxNode {278
kind: kind,279
lo: lo,280
hi: hi,281
field: field,282
index: index,283
attributes: attributes,284
children: children,285
}286
}288
export function attach(value: SyntaxNode, field: string, index: int) -> SyntaxNode {289
node(290
value.kind,291
value.lo,292
value.hi,293
field,294
index,295
value.attributes,296
value.children,297
)298
}300
export function identNode(value: PreviewToken, field: string, index: int) -> SyntaxNode {301
node("identifier", value.lo, value.hi, field, index, [], [])302
}304
function parseNestedExpression(305
service: ParserService,306
parser: Parser,307
field: string,308
index: int,309
) -> Result<SyntaxNode, string> {310
let previousLambda = lambdaAllowed(parser)311
let previousRecordUpdate = recordUpdateAllowed(parser)312
setLambdaAllowed(parser, true)313
setRecordUpdateAllowed(parser, true)314
let result = invokeParseExpressionBp(service, parser, 1, field, index)315
setRecordUpdateAllowed(parser, previousRecordUpdate)316
setLambdaAllowed(parser, previousLambda)317
result318
}320
export function parseString(321
service: ParserService,322
parser: Parser,323
field: string,324
index: int,325
) -> Result<SyntaxNode, string> {326
let start = bump(parser)327
let tagged = start.kind.contains("tagged=true")328
let multiline = start.kind.contains("multiline=true")329
let delimiterLength = if multiline { 3 } else { 1 }330
let mut children: Array<SyntaxNode> = []331
if tagged {332
let tagHi = start.hi - delimiterLength333
let tag = text(parser, start.lo, tagHi)334
if tag != "p" && tag != "r" && tag != "sh" && tag != "sql" {335
let mut diagnostics = parser.diagnostics336
diagnostics.push(diagnostic(337
"TPZ2002",338
"unknown template tag `{tag}`; the registry is `p`, `r`, `sh`, `sql`",339
start.lo,340
tagHi,341
))342
}343
children.push(node("template-tag", start.lo, tagHi, "tag", 0, [], []))344
}345
let mut partIndex = 0346
while !at(parser, "string-end") && !at(parser, "eof") {347
if at(parser, "string-text") {348
let part = bump(parser)349
children.push(node(350
"string-part/text",351
part.lo,352
part.hi,353
"parts",354
partIndex,355
[],356
[],357
))358
partIndex += 1359
continue360
}361
if eat(parser, "interpolation-start") {362
if at(parser, "interpolation-end") {363
addDiagnostic(364
parser,365
"TPZ2001",366
"expected an expression",367
current(parser).lo,368
current(parser).hi,369
)370
bump(parser)371
} else {372
let expression = parseNestedExpression(service, parser, "value", 0)?373
expect(parser, "interpolation-end", "an interpolation close")?374
children.push(node(375
"string-part/interpolation",376
expression.lo,377
expression.hi,378
"parts",379
partIndex,380
[],381
[expression],382
))383
partIndex += 1384
}385
continue386
}387
fail(parser, "expected string content")?388
}389
let close = expect(parser, "string-end", "a string close")?390
Ok(node(391
if tagged { "expression/template" } else { "expression/string" },392
start.lo,393
close.hi,394
field,395
index,396
[],397
children,398
))399
}401
export function skipSeparators(parser: Parser) -> () {402
while eat(parser, "separator") {}403
()404
}406
export function synchronize(parser: Parser) -> () {407
let mut depth = 0408
while !at(parser, "eof") {409
let kind = current(parser).kind410
if depth == 0 && (kind == "separator" || kind == "right-brace") {411
break412
}413
if kind == "left-paren" || kind == "left-bracket" ||414
kind == "left-brace" || kind == "interpolation-start" ||415
kind.startsWith("string-start/") {416
depth += 1417
} else if kind == "right-paren" || kind == "right-bracket" ||418
kind == "right-brace" || kind == "interpolation-end" ||419
kind == "string-end" {420
if depth == 0 {421
break422
}423
depth -= 1424
}425
bump(parser)426
}427
eat(parser, "separator")428
()429
}431
export function itemBoundary(parser: Parser) -> () {432
if at(parser, "separator") {433
skipSeparators(parser)434
} else if !at(parser, "right-brace") && !at(parser, "eof") {435
addDiagnostic(436
parser,437
"TPZ2001",438
"expected a statement separator",439
current(parser).lo,440
current(parser).hi,441
)442
synchronize(parser)443
}444
()445
}SHA256 e79446776597eacf0ef0bfd68c36199a0fe32e23f409017a79ef35e6a852e477