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/resolver/model_facts.tpz
Topaz · 10789 B · Original file
1
// Data contracts for resolver drafts, tables, host facts, and outputs.2
// The records keep graph discovery and scope walking acyclic while the facade3
// retains ownership of the public ResolveResult.5
import types {6
PreviewDiagnostic,7
PreviewGeneratedStdModule,8
PreviewHostFact,9
PreviewHostQuery,10
PreviewImportEdge,11
PreviewModuleResult,12
PreviewResolvedDeclaration,13
PreviewResolvedDiagnostic,14
PreviewResolvedExport,15
PreviewResolvedReference,16
PreviewResolvedScope,17
SyntaxNode,18
}20
export record ImportDraft {21
target: string,22
segments: Array<string>,23
pathLo: int,24
pathHi: int,25
selectedNames: Array<string>,26
selectedLos: Array<int>,27
selectedHis: Array<int>,28
}30
export record EdgeDraft {31
from: string,32
to: string,33
moduleIndex: int,34
lo: int,35
hi: int,36
}38
export record ModuleDraft {39
result: PreviewModuleResult,40
source: Bytes,41
root: SyntaxNode,42
imports: Array<ImportDraft>,43
diagnostics: Array<PreviewDiagnostic>,44
}46
export record Binding {47
moduleIndex: int,48
scopeOrdinal: int,49
name: string,50
namespace: string,51
declarationKind: string,52
lo: int,53
hi: int,54
exported: bool,55
targetModule: string,56
targetName: string,57
mutable: bool,58
}60
export record ExportBinding {61
bindingIndex: int,62
namespace: string,63
}65
export record ModuleTable {66
moduleIndex: int,67
identity: string,68
bindings: Array<Binding>,69
bindingIndexes: Map<string, int>,70
exportedBindings: Map<string, ExportBinding>,71
exportedNames: Array<string>,72
}74
export record ModuleTableCatalog {75
tables: Array<ModuleTable>,76
indexes: Map<string, int>,77
}79
export record LocalScope {80
ordinal: int,81
values: Map<string, Binding>,82
types: Map<string, Binding>,83
}85
export record ResolverWalkContext {86
moduleIndex: int,87
source: Bytes,88
table: ModuleTable,89
tables: ModuleTableCatalog,90
nextScope: Array<int>,91
scopeOutput: Array<PreviewResolvedScope>,92
declarationOutput: Array<PreviewResolvedDeclaration>,93
referenceOutput: Array<PreviewResolvedReference>,94
diagnostics: Array<PreviewResolvedDiagnostic>,95
recordDefaultStack: Array<bool>,96
}98
// Admitted entry, mount, and host-fact view for one resolution pass.99
export record ResolverInputs {100
root: string,101
hostFacts: ResolverHostFactCatalog,102
externModules: Array<string>,103
generatedStdModules: Array<PreviewGeneratedStdModule>,104
maxAstNodes: int,105
maxAstDepth: int,106
}108
export record ResolverHostFactCatalog {109
values: Array<PreviewHostFact>,110
readSources: Map<string, int>,111
directoryListings: Map<string, int>,112
physicalContainments: Map<string, int>,113
provider: Option<(PreviewHostQuery) -> Result<PreviewHostFact, string>>,114
errors: Array<string>,115
}117
// Loaded module drafts and dependency edges before scope resolution.118
export record DiscoveredGraph {119
modules: Array<ModuleDraft>,120
edges: Array<EdgeDraft>,121
}123
export record ModuleDependencyCatalog {124
identities: Array<string>,125
moduleIndexes: Map<string, int>,126
dependencies: Map<string, Set<string>>,127
dependents: Map<string, Set<string>>,128
publicEdges: Array<PreviewImportEdge>,129
}131
// Internal scope and reference catalogs used to build the public projection.132
export record ResolverOutputs {133
modules: Array<PreviewModuleResult>,134
scopes: Array<PreviewResolvedScope>,135
declarations: Array<PreviewResolvedDeclaration>,136
references: Array<PreviewResolvedReference>,137
exports: Array<PreviewResolvedExport>,138
}140
// SPEC v5.2 §17. This phase mirrors Stage 0 init_rule: it scans only141
// each imported item's own immediately evaluated positions. Short-circuit RHS,142
// optional-call arguments, and function, lambda, defer, branch, arm, and loop143
// bodies stay delayed; top-level consts are ready before runtime initialization144
// and the entry module has the separate §4 rule.145
export record InitViolation {146
name: string,147
lo: int,148
hi: int,149
}151
export record ReferenceResolution {152
referenceNamespace: string,153
targetModuleIndex: int,154
targetLo: int,155
targetHi: int,156
targetNamespace: string,157
targetModule: string,158
targetName: string,159
}161
export record ImportPathDisposition {162
kind: string,163
collisionDetail: string,164
}166
export record PreparedImport {167
path: string,168
source: string,169
physicalAlias: string,170
generatedStd: bool,171
}172
export function text(source: Bytes, node: SyntaxNode) -> string {173
spanText(source, node.lo, node.hi)174
}176
export function child(value: SyntaxNode, field: string) -> Option<SyntaxNode> {177
for found in value.children {178
if found.field == field {179
return Some(found)180
}181
}182
None183
}185
export function children(value: SyntaxNode, field: string) -> Array<SyntaxNode> {186
let mut output: Array<SyntaxNode> = []187
for found in value.children {188
if found.field == field {189
output.push(found)190
}191
}192
output193
}195
export function attribute(value: SyntaxNode, name: string) -> string {196
for found in value.attributes {197
if found.name == name {198
return found.value199
}200
}201
""202
}204
export function boolAttribute(value: SyntaxNode, name: string) -> bool {205
attribute(value, name) == "true"206
}208
export function contains(values: Array<string>, needle: string) -> bool {209
for value in values {210
if value == needle {211
return true212
}213
}214
false215
}217
export function hasParentPathSegment(path: string) -> bool {218
for segment in path.split("/") {219
if segment == ".." {220
return true221
}222
}223
false224
}226
export function isNone<T>(value: Option<T>) -> bool {227
match value {228
case Some(_) => false229
case None => true230
}231
}233
export function stringJoin(values: Array<string>, separator: string) -> string {234
let mut output = ""235
let mut index = 0236
for value in values {237
if index > 0 {238
output = "{output}{separator}"239
}240
output = "{output}{value}"241
index += 1242
}243
output244
}246
export function pathJoin(left: string, right: string) -> string {247
if left == "" {248
right249
} else {250
"{left}/{right}"251
}252
}254
export function resolverHostFacts(255
facts: Array<PreviewHostFact>,256
) -> ResolverHostFactCatalog {257
let mut readSources: Map<string, int> = Map.new()258
let mut directoryListings: Map<string, int> = Map.new()259
let mut physicalContainments: Map<string, int> = Map.new()260
let mut index = 0261
for fact in facts {262
if fact.mountId == "root" {263
if fact.kind == "read-source" &&264
!readSources.containsKey(fact.logicalPath) {265
readSources.insert(fact.logicalPath, index)266
} else if fact.kind == "list-directory" &&267
!directoryListings.containsKey(fact.logicalPath) {268
directoryListings.insert(fact.logicalPath, index)269
} else if fact.kind == "physical-containment" &&270
!physicalContainments.containsKey(fact.logicalPath) {271
physicalContainments.insert(fact.logicalPath, index)272
}273
}274
index += 1275
}276
ResolverHostFactCatalog {277
values: facts,278
readSources: readSources,279
directoryListings: directoryListings,280
physicalContainments: physicalContainments,281
provider: None,282
errors: [],283
}284
}286
export function sourceFact(287
facts: ResolverHostFactCatalog,288
kind: string,289
path: string,290
) -> Option<PreviewHostFact> {291
let index = if kind == "read-source" {292
facts.readSources.get(path)293
} else if kind == "list-directory" {294
facts.directoryListings.get(path)295
} else if kind == "physical-containment" {296
facts.physicalContainments.get(path)297
} else {298
None299
}300
match index {301
case Some(value) => Some(facts.values[value])302
case None => None303
}304
}306
export function addQuery(307
output: Array<PreviewHostQuery>,308
catalog: ResolverHostFactCatalog,309
kind: string,310
path: string,311
) -> () {312
let mut facts = catalog313
let mut target = output314
match sourceFact(facts, kind, path) {315
case Some(_) => return ()316
case None => ()317
}318
for query in output {319
if query.kind == kind && query.mountId == "root" && query.logicalPath == path {320
return ()321
}322
}323
let query = PreviewHostQuery {324
kind: kind,325
mountId: "root",326
logicalPath: path,327
}328
match facts.provider {329
case Some(provide) => {330
if facts.errors.length > 0 { return () }331
match provide(query) {332
case Ok(fact) => {333
if fact.kind != query.kind || fact.mountId != query.mountId ||334
fact.logicalPath != query.logicalPath {335
facts.errors.push("host fact does not answer the requested query")336
return ()337
}338
let index = facts.values.length339
facts.values.push(fact)340
if kind == "read-source" {341
facts.readSources.insert(path, index)342
} else if kind == "list-directory" {343
facts.directoryListings.insert(path, index)344
} else {345
facts.physicalContainments.insert(path, index)346
}347
}348
case Err(error) => facts.errors.push(error)349
}350
}351
case None => target.push(query)352
}353
}355
function queryRank(kind: string) -> int {356
match kind {357
case "read-source" => 0358
case "list-directory" => 1359
case "physical-containment" => 2360
case _ => 3361
}362
}364
function queryBefore(left: PreviewHostQuery, right: PreviewHostQuery) -> bool {365
let leftRank = queryRank(left.kind)366
let rightRank = queryRank(right.kind)367
leftRank < rightRank ||368
(leftRank == rightRank && left.mountId < right.mountId) ||369
(leftRank == rightRank && left.mountId == right.mountId &&370
left.logicalPath < right.logicalPath)371
}373
export function sortQueries(values: Array<PreviewHostQuery>) -> Array<PreviewHostQuery> {374
let mut output: Array<PreviewHostQuery> = []375
for value in values {376
let mut inserted = false377
let mut index = 0378
while index < output.length {379
let existing = output[index]380
if queryBefore(value, existing) {381
output.insert(index, value)382
inserted = true383
break384
}385
index += 1386
}387
if !inserted {388
output.push(value)389
}390
}391
output392
}394
export function diagnostic(395
output: Array<PreviewResolvedDiagnostic>,396
moduleIndex: int,397
code: string,398
message: string,399
lo: int,400
hi: int,401
) -> () {402
let mut target = output403
target.push(PreviewResolvedDiagnostic {404
moduleIndex: moduleIndex,405
code: code,406
message: message,407
lo: lo,408
hi: hi,409
notes: [],410
})411
}413
export function appendModuleDiagnostics(414
output: Array<PreviewResolvedDiagnostic>,415
moduleIndexValue: int,416
values: Array<PreviewDiagnostic>,417
) -> () {418
for value in values {419
let mut target = output420
target.push(PreviewResolvedDiagnostic {421
moduleIndex: moduleIndexValue,422
code: value.code,423
message: value.message,424
lo: value.lo,425
hi: value.hi,426
notes: value.notes,427
})428
}429
()430
}432
export function spanText(source: Bytes, lo: int, hi: int) -> string {433
match source.slice(lo, hi).decodeUtf8() {434
case Ok(found) => found435
case Err(_) => ""436
}437
}SHA256 b134c16634e30e17445f38838d5dceefeab9c08efc40b46e4ba5e5ae9300c9c4