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/checker/syntax_index.tpz
Topaz · 21372 B · Original file
1
// Syntax lookup and module indexes shared by checker phases.2
// It translates resolved preview rows into stable node and source views without3
// making type decisions or emitting diagnostics on its own.5
import types {6
PreviewAstAttribute,7
PreviewCheckDiagnostic,8
PreviewModuleResult,9
PreviewResolvedDeclaration,10
PreviewResolvedReference,11
PreviewResolvedScope,12
PreviewTypedNode,13
ResolveResult,14
SyntaxNode,15
}16
import checker_types { TypeValue }17
import checker.model {18
CheckerState,19
EnumDefinition,20
FunctionBound,21
FunctionDefinition,22
ModuleSyntax,23
NamedNominalTarget,24
NewtypeDefinition,25
ProtocolDefinition,26
ProtocolMethodDefinition,27
RecordDefinition,28
SymbolType,29
TYPE_BOUND_INDEX_STRIDE,30
TypeParameterFact,31
nominal,32
rigid,33
}34
import checker.type_algebra {35
flattenType,36
hasHole,37
}39
export function text(source: Bytes, node: SyntaxNode) -> string {40
match source.slice(node.lo, node.hi).decodeUtf8() {41
case Ok(value) => value42
case Err(_) => ""43
}44
}46
export function child(value: SyntaxNode, field: string) -> Option<SyntaxNode> {47
for found in value.children {48
if found.field == field {49
return Some(found)50
}51
}52
None53
}55
export function children(value: SyntaxNode, field: string) -> Array<SyntaxNode> {56
let mut output: Array<SyntaxNode> = []57
for found in value.children {58
if found.field == field {59
output.push(found)60
}61
}62
output63
}65
export function attribute(value: SyntaxNode, name: string) -> string {66
for found in value.attributes {67
if found.name == name {68
return found.value69
}70
}71
""72
}74
export function boolAttribute(value: SyntaxNode, name: string) -> bool {75
attribute(value, name) == "true"76
}78
export function unwrapExport(value: SyntaxNode) -> SyntaxNode {79
if value.kind == "statement/export" {80
match child(value, "item") {81
case Some(item) => item82
case None => value83
}84
} else {85
value86
}87
}89
export function spanKey(moduleIndex: int, lo: int, hi: int) -> string {90
"{moduleIndex}:{lo}:{hi}"91
}93
function nodeKey(moduleIndex: int, kind: string, lo: int, hi: int) -> string {94
"{moduleIndex}:{kind}:{lo}:{hi}"95
}97
export function typedNodeIndex(98
state: CheckerState,99
moduleIndex: int,100
kind: string,101
lo: int,102
hi: int,103
) -> int {104
match state.nodeIndexes.get(nodeKey(moduleIndex, kind, lo, hi)) {105
case Some(index) => index106
case None => -1107
}108
}110
export function scopeKey(moduleIndex: int, ordinal: int) -> string {111
"{moduleIndex}:{ordinal}"112
}114
export function definitionNameKey(moduleIndex: int, name: string) -> string {115
"{moduleIndex}:{name}"116
}118
export function emptyStringIntMap() -> Map<string, int> {119
let mut value = map { "": 0 }120
value.remove("")121
value122
}124
export function declarationFor(125
state: CheckerState,126
moduleIndex: int,127
lo: int,128
hi: int,129
) -> Option<PreviewResolvedDeclaration> {130
match state.declarationIndexes.get(spanKey(moduleIndex, lo, hi)) {131
case Some(index) => match state.resolved.declarations.get(index) {132
case Some(declaration) => Some(declaration)133
case None => None134
}135
case None => None136
}137
}139
export function referenceFor(140
state: CheckerState,141
moduleIndex: int,142
lo: int,143
hi: int,144
) -> Option<PreviewResolvedReference> {145
match state.referenceIndexes.get(spanKey(moduleIndex, lo, hi)) {146
case Some(index) => match state.resolved.references.get(index) {147
case Some(reference) => Some(reference)148
case None => None149
}150
case None => None151
}152
}154
export function symbolAt(155
state: CheckerState,156
moduleIndex: int,157
lo: int,158
hi: int,159
) -> Option<SymbolType> {160
match state.symbolIndexes.get(spanKey(moduleIndex, lo, hi)) {161
case Some(index) => match state.symbols.get(index) {162
case Some(symbol) => Some(symbol)163
case None => None164
}165
case None => None166
}167
}169
export function symbolTypeAt(170
state: CheckerState,171
moduleIndex: int,172
lo: int,173
hi: int,174
) -> Option<TypeValue> {175
match symbolAt(state, moduleIndex, lo, hi) {176
case Some(symbol) => Some(symbol.typeValue)177
case None => None178
}179
}181
export function moduleIndexByIdentity(182
state: CheckerState,183
identity: string,184
) -> int {185
for module in state.modules {186
if module.identity == identity {187
return module.moduleIndex188
}189
}190
-1191
}193
export function symbolTypeByName(194
state: CheckerState,195
moduleIndex: int,196
name: string,197
namespace: string,198
) -> Option<TypeValue> {199
for declaration in state.resolved.declarations {200
if declaration.moduleIndex == moduleIndex &&201
declaration.name == name &&202
declaration.namespace == namespace {203
match symbolTypeAt(204
state,205
moduleIndex,206
declaration.lo,207
declaration.hi,208
) {209
case Some(found) => return Some(found)210
case None => ()211
}212
}213
}214
None215
}217
export function bindSymbol(218
state: CheckerState,219
moduleIndex: int,220
lo: int,221
hi: int,222
name: string,223
typeValue: TypeValue,224
mutable: bool,225
) -> () {226
let mut target = state.symbols227
let key = spanKey(moduleIndex, lo, hi)228
match state.symbolIndexes.get(key) {229
case Some(index) => match target.get(index) {230
case Some(symbol) => {231
target[index] = SymbolType {232
moduleIndex: symbol.moduleIndex,233
lo: symbol.lo,234
hi: symbol.hi,235
name: symbol.name,236
typeValue: typeValue,237
mutable: mutable,238
}239
}240
case None => ()241
}242
case None => {243
let mut indexes = state.symbolIndexes244
indexes.insert(key, target.length)245
target.push(SymbolType {246
moduleIndex: moduleIndex,247
lo: lo,248
hi: hi,249
name: name,250
typeValue: typeValue,251
mutable: mutable,252
})253
}254
}255
}257
export function resolvedTypeOfReference(258
state: CheckerState,259
moduleIndex: int,260
node: SyntaxNode,261
) -> Option<TypeValue> {262
match referenceFor(state, moduleIndex, node.lo, node.hi) {263
case Some(reference) => resolvedTypeFromFact(state, reference)264
case None => None265
}266
}268
export function resolvedTypeFromFact(269
state: CheckerState,270
reference: PreviewResolvedReference,271
) -> Option<TypeValue> {272
if reference.targetModuleIndex < 0 {273
return None274
}275
match symbolTypeAt(276
state,277
reference.targetModuleIndex,278
reference.targetLo,279
reference.targetHi,280
) {281
case Some(found) => return Some(found)282
case None => ()283
}284
match declarationFor(285
state,286
reference.targetModuleIndex,287
reference.targetLo,288
reference.targetHi,289
) {290
case Some(declaration) => {291
if declaration.targetModule == "" {292
return None293
}294
let targetModule = moduleIndexByIdentity(295
state,296
declaration.targetModule,297
)298
symbolTypeByName(299
state,300
targetModule,301
declaration.targetName,302
reference.targetNamespace,303
)304
}305
case None => None306
}307
}309
export function addNode(310
state: CheckerState,311
moduleIndex: int,312
kind: string,313
node: SyntaxNode,314
typeValue: TypeValue,315
) -> () {316
let mut target = state.nodes317
let key = nodeKey(moduleIndex, kind, node.lo, node.hi)318
match state.nodeIndexes.get(key) {319
case Some(_) => return ()320
case None => ()321
}322
let mut indexes = state.nodeIndexes323
indexes.insert(key, target.length)324
target.push(PreviewTypedNode {325
moduleIndex: moduleIndex,326
nodeKind: kind,327
lo: node.lo,328
hi: node.hi,329
typeValue: flattenType(typeValue),330
ambient: hasHole(typeValue),331
})332
}334
export function replaceNodeType(335
state: CheckerState,336
moduleIndex: int,337
kind: string,338
node: SyntaxNode,339
typeValue: TypeValue,340
) -> () {341
if typeValue.kind == "unknown" {342
return ()343
}344
let mut target = state.nodes345
let key = nodeKey(moduleIndex, kind, node.lo, node.hi)346
match state.nodeIndexes.get(key) {347
case Some(index) => {348
match target.get(index) {349
case Some(_) => {350
target[index] = PreviewTypedNode {351
moduleIndex: moduleIndex,352
nodeKind: kind,353
lo: node.lo,354
hi: node.hi,355
typeValue: flattenType(typeValue),356
ambient: hasHole(typeValue),357
}358
return ()359
}360
case None => ()361
}362
}363
case None => ()364
}365
addNode(state, moduleIndex, kind, node, typeValue)366
}368
export function addDiagnostic(369
state: CheckerState,370
moduleIndex: int,371
code: string,372
message: string,373
primaryMessage: string,374
node: SyntaxNode,375
) -> () {376
let mut target = state.diagnostics377
target.push(PreviewCheckDiagnostic {378
moduleIndex: moduleIndex,379
code: code,380
message: message,381
primaryMessage: primaryMessage,382
lo: node.lo,383
hi: node.hi,384
secondary: [],385
notes: [],386
profileRule: "",387
})388
}390
function syntaxFromFlat(391
module: PreviewModuleResult,392
maxAstNodes: int,393
maxAstDepth: int,394
) -> Result<SyntaxNode, string> {395
if module.ast.length == 0 {396
return Err("checker received a module without an AST")397
}398
if module.ast.length > maxAstNodes {399
return Err("front-end preview AST node count exceeds {maxAstNodes}")400
}401
let mut rows: Array<SyntaxNode> = []402
let mut depths: Array<int> = []403
let mut ordinal = 0404
for flat in module.ast {405
let mut attributes: Array<PreviewAstAttribute> = []406
if flat.attributeNames.length != flat.attributeKinds.length ||407
flat.attributeNames.length != flat.attributeValues.length {408
return Err("checker received malformed AST attributes")409
}410
let mut attributeIndex = 0411
while attributeIndex < flat.attributeNames.length {412
attributes.push(PreviewAstAttribute {413
name: match flat.attributeNames.get(attributeIndex) {414
case Some(value) => value415
case None => ""416
},417
kind: match flat.attributeKinds.get(attributeIndex) {418
case Some(value) => value419
case None => ""420
},421
value: match flat.attributeValues.get(attributeIndex) {422
case Some(value) => value423
case None => ""424
},425
})426
attributeIndex += 1427
}428
let depth = if ordinal == 0 {429
if flat.parent != -1 {430
return Err("checker received an AST root with a parent")431
}432
0433
} else {434
if flat.parent < 0 || flat.parent >= ordinal {435
return Err("checker received a non-preorder AST parent")436
}437
match depths.get(flat.parent) {438
case Some(parentDepth) => parentDepth + 1439
case None => return Err("checker received a missing AST parent")440
}441
}442
if depth > maxAstDepth {443
return Err("front-end preview AST depth exceeds {maxAstDepth}")444
}445
let rebuilt = SyntaxNode {446
kind: flat.kind,447
lo: flat.lo,448
hi: flat.hi,449
field: flat.field,450
index: flat.index,451
attributes: attributes,452
children: [],453
}454
rows.push(rebuilt)455
depths.push(depth)456
if flat.parent >= 0 {457
match rows.get(flat.parent) {458
case Some(parent) => {459
let mut children = parent.children460
children.push(rebuilt)461
}462
case None => return Err("checker received a missing AST parent")463
}464
}465
ordinal += 1466
}467
match rows.get(0) {468
case Some(root) => Ok(root)469
case None => Err("checker received a module without an AST")470
}471
}473
export function moduleSyntax(474
resolved: ResolveResult,475
maxAstNodes: int,476
maxAstDepth: int,477
) -> Result<Array<ModuleSyntax>, string> {478
let mut modules: Array<ModuleSyntax> = []479
let mut index = 0480
for module in resolved.modules {481
let source = Bytes.encodeUtf8(module.source)482
modules.push(ModuleSyntax {483
moduleIndex: index,484
sourceOrdinal: module.sourceOrdinal,485
identity: module.identity,486
source: source,487
root: syntaxFromFlat(module, maxAstNodes, maxAstDepth)?,488
generatedStd: module.generatedStd,489
})490
index += 1491
}492
Ok(modules)493
}495
export function moduleAt(state: CheckerState, moduleIndex: int) -> Option<ModuleSyntax> {496
state.modules.get(moduleIndex)497
}499
export function typeParameterNames(source: Bytes, node: SyntaxNode) -> Array<string> {500
let mut output: Array<string> = []501
for parameter in children(node, "typeParameters") {502
output.push(text(source, parameter))503
}504
output505
}507
export function functionBounds(source: Bytes, node: SyntaxNode) -> Array<FunctionBound> {508
let parameters = typeParameterNames(source, node)509
let mut output: Array<FunctionBound> = []510
for bound in children(node, "typeParameterBounds") {511
let mut parameterIndex = 0512
let mut encoded = bound.index513
while encoded >= TYPE_BOUND_INDEX_STRIDE {514
encoded -= TYPE_BOUND_INDEX_STRIDE515
parameterIndex += 1516
}517
match parameters.get(parameterIndex) {518
case Some(parameterName) => output.push(FunctionBound {519
parameterName: parameterName,520
protocol: text(source, bound),521
})522
case None => ()523
}524
}525
output526
}528
export function registerTypeParameters(529
state: CheckerState,530
moduleIndex: int,531
source: Bytes,532
node: SyntaxNode,533
) -> () {534
let mut facts = state.typeParameters535
let sourceOrdinal = match moduleAt(state, moduleIndex) {536
case Some(found) => found.sourceOrdinal537
case None => moduleIndex538
}539
let owner = "source:{sourceOrdinal}:{node.lo}:{node.hi}"540
for parameter in children(node, "typeParameters") {541
facts.push(TypeParameterFact {542
moduleIndex: moduleIndex,543
owner: owner,544
name: text(source, parameter),545
origin: "source:{sourceOrdinal}:{parameter.lo}:{parameter.hi}",546
})547
}548
()549
}551
export function typeParameterOrigin(552
state: CheckerState,553
moduleIndex: int,554
name: string,555
fallback: string,556
) -> string {557
let mut result = fallback558
for fact in state.typeParameters {559
if fact.moduleIndex == moduleIndex && fact.owner == fallback &&560
fact.name == name {561
result = fact.origin562
}563
}564
result565
}567
export function activeTypeParameter(568
state: CheckerState,569
moduleIndex: int,570
name: string,571
) -> Option<TypeValue> {572
let mut activeIndex = state.activeFunctions.length573
while activeIndex > 0 {574
activeIndex -= 1575
match state.activeFunctions.get(activeIndex) {576
case Some(functionIndex) => match state.functions.get(functionIndex) {577
case Some(definition) => if definition.moduleIndex == moduleIndex {578
let mut parameterIndex = 0579
for parameterName in definition.typeParameters {580
if parameterName == name {581
return match definition.typeParameterOrigins.get(parameterIndex) {582
case Some(origin) => Some(rigid(name, origin))583
case None => None584
}585
}586
parameterIndex += 1587
}588
}589
case None => ()590
}591
case None => ()592
}593
}594
None595
}597
export function containsString(values: Array<string>, needle: string) -> bool {598
for value in values {599
if value == needle {600
return true601
}602
}603
false604
}606
export function recordDefinitionIndex(607
state: CheckerState,608
moduleIndex: int,609
name: string,610
) -> int {611
match state.recordIndexes.get(definitionNameKey(moduleIndex, name)) {612
case Some(index) => index613
case None => -1614
}615
}617
export function enumDefinitionIndex(618
state: CheckerState,619
moduleIndex: int,620
name: string,621
) -> int {622
match state.enumIndexes.get(definitionNameKey(moduleIndex, name)) {623
case Some(index) => index624
case None => -1625
}626
}628
export function newtypeDefinitionIndex(629
state: CheckerState,630
moduleIndex: int,631
name: string,632
) -> int {633
match state.newtypeIndexes.get(definitionNameKey(moduleIndex, name)) {634
case Some(index) => index635
case None => -1636
}637
}639
export function aliasDefinitionIndex(640
state: CheckerState,641
moduleIndex: int,642
name: string,643
) -> int {644
match state.aliasIndexes.get(definitionNameKey(moduleIndex, name)) {645
case Some(index) => index646
case None => -1647
}648
}650
export function namedNominalTarget(651
state: CheckerState,652
moduleIndex: int,653
name: string,654
) -> Option<NamedNominalTarget> {655
let recordIndex = recordDefinitionIndex(state, moduleIndex, name)656
if recordIndex >= 0 {657
return match state.records.get(recordIndex) {658
case Some(definition) => Some(NamedNominalTarget {659
semanticKind: "nominal-record",660
diagnosticKind: "record",661
typeArgumentCount: definition.typeParameters.length,662
})663
case None => None664
}665
}666
let enumIndex = enumDefinitionIndex(state, moduleIndex, name)667
if enumIndex >= 0 {668
return match state.enums.get(enumIndex) {669
case Some(definition) => Some(NamedNominalTarget {670
semanticKind: "enum",671
diagnosticKind: "enum",672
typeArgumentCount: definition.typeParameters.length,673
})674
case None => None675
}676
}677
let newtypeIndex = newtypeDefinitionIndex(state, moduleIndex, name)678
if newtypeIndex >= 0 {679
return match state.newtypes.get(newtypeIndex) {680
case Some(definition) => Some(NamedNominalTarget {681
semanticKind: "newtype",682
diagnosticKind: "newtype",683
typeArgumentCount: definition.typeParameters.length,684
})685
case None => None686
}687
}688
None689
}691
export function protocolDefinitionIndex(692
state: CheckerState,693
moduleIndex: int,694
name: string,695
) -> int {696
match state.protocolIndexes.get(definitionNameKey(moduleIndex, name)) {697
case Some(index) => index698
case None => -1699
}700
}702
export function protocolDefinition(703
state: CheckerState,704
moduleIndex: int,705
name: string,706
) -> Option<ProtocolDefinition> {707
state.protocols.get(protocolDefinitionIndex(state, moduleIndex, name))708
}710
export function protocolMethodDefinition(711
definition: ProtocolDefinition,712
name: string,713
) -> Option<ProtocolMethodDefinition> {714
for method in definition.methods {715
if method.name == name {716
return Some(method)717
}718
}719
None720
}722
export function functionDefinitionForReference(723
state: CheckerState,724
reference: PreviewResolvedReference,725
) -> Option<FunctionDefinition> {726
let key = spanKey(727
reference.targetModuleIndex,728
reference.targetLo,729
reference.targetHi,730
)731
match state.functionIndexes.get(key) {732
case Some(index) => return state.functions.get(index)733
case None => ()734
}735
match state.functionAliases.get(key) {736
case Some(index) => return state.functions.get(index)737
case None => ()738
}739
let declaration = match declarationFor(740
state,741
reference.targetModuleIndex,742
reference.targetLo,743
reference.targetHi,744
) {745
case Some(found) => found746
case None => return None747
}748
if declaration.targetModule == "" {749
return None750
}751
let targetModule = moduleIndexByIdentity(state, declaration.targetModule)752
match state.functionNameIndexes.get(definitionNameKey(753
targetModule,754
declaration.targetName,755
)) {756
case Some(index) => state.functions.get(index)757
case None => None758
}759
}761
export function functionIndexForReference(762
state: CheckerState,763
reference: PreviewResolvedReference,764
) -> int {765
let key = spanKey(766
reference.targetModuleIndex,767
reference.targetLo,768
reference.targetHi,769
)770
match state.functionIndexes.get(key) {771
case Some(index) => return index772
case None => ()773
}774
match state.functionAliases.get(key) {775
case Some(index) => return index776
case None => ()777
}778
match functionDefinitionForReference(state, reference) {779
case Some(definition) => match state.functionNameIndexes.get(780
definitionNameKey(definition.moduleIndex, definition.name),781
) {782
case Some(index) => index783
case None => -1784
}785
case None => -1786
}787
}789
export function referenceTargetsPendingFunction(790
state: CheckerState,791
reference: PreviewResolvedReference,792
) -> bool {793
let index = functionIndexForReference(state, reference)794
if index < 0 {795
return false796
}797
match state.pendingFunctions.get(index) {798
case Some(found) => found799
case None => false800
}801
}803
export function replaceRecordDefinition(804
state: CheckerState,805
index: int,806
value: RecordDefinition,807
) -> () {808
let mut target = state.records809
target[index] = value810
()811
}813
export function replaceEnumDefinition(814
state: CheckerState,815
index: int,816
value: EnumDefinition,817
) -> () {818
let mut target = state.enums819
target[index] = value820
()821
}823
export function replaceNewtypeDefinition(824
state: CheckerState,825
index: int,826
value: NewtypeDefinition,827
) -> () {828
let mut target = state.newtypes829
target[index] = value830
()831
}833
export function scopeFor(834
state: CheckerState,835
moduleIndex: int,836
ordinal: int,837
) -> Option<PreviewResolvedScope> {838
match state.scopeIndexes.get(scopeKey(moduleIndex, ordinal)) {839
case Some(index) => match state.resolved.scopes.get(index) {840
case Some(scope) => Some(scope)841
case None => None842
}843
case None => None844
}845
}847
export function scopeIsWithin(848
state: CheckerState,849
moduleIndex: int,850
candidateOrdinal: int,851
ancestorOrdinal: int,852
) -> bool {853
let mut current = candidateOrdinal854
while current >= 0 {855
if current == ancestorOrdinal {856
return true857
}858
match scopeFor(state, moduleIndex, current) {859
case Some(scope) => {860
current = scope.parentOrdinal861
}862
case None => return false863
}864
}865
false866
}868
export function moduleIdentityAt(state: CheckerState, moduleIndex: int) -> string {869
match moduleAt(state, moduleIndex) {870
case Some(module) => module.identity871
case None => "<unknown>"872
}873
}SHA256 b05fd16ec17a8edfd75a1355e42e31e5cf41ec136a0f626977ab4397949de2c1