slow.monster

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 facade
3// retains ownership of the public ResolveResult.
5import 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,
20export 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>,
30export record EdgeDraft {
31 from: string,
32 to: string,
33 moduleIndex: int,
34 lo: int,
35 hi: int,
38export record ModuleDraft {
39 result: PreviewModuleResult,
40 source: Bytes,
41 root: SyntaxNode,
42 imports: Array<ImportDraft>,
43 diagnostics: Array<PreviewDiagnostic>,
46export 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,
60export record ExportBinding {
61 bindingIndex: int,
62 namespace: string,
65export 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>,
74export record ModuleTableCatalog {
75 tables: Array<ModuleTable>,
76 indexes: Map<string, int>,
79export record LocalScope {
80 ordinal: int,
81 values: Map<string, Binding>,
82 types: Map<string, Binding>,
85export 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>,
98// Admitted entry, mount, and host-fact view for one resolution pass.
99export record ResolverInputs {
100 root: string,
101 hostFacts: ResolverHostFactCatalog,
102 externModules: Array<string>,
103 generatedStdModules: Array<PreviewGeneratedStdModule>,
104 maxAstNodes: int,
105 maxAstDepth: int,
108export 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>,
117// Loaded module drafts and dependency edges before scope resolution.
118export record DiscoveredGraph {
119 modules: Array<ModuleDraft>,
120 edges: Array<EdgeDraft>,
123export 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>,
131// Internal scope and reference catalogs used to build the public projection.
132export record ResolverOutputs {
133 modules: Array<PreviewModuleResult>,
134 scopes: Array<PreviewResolvedScope>,
135 declarations: Array<PreviewResolvedDeclaration>,
136 references: Array<PreviewResolvedReference>,
137 exports: Array<PreviewResolvedExport>,
140// SPEC v5.2 §17. This phase mirrors Stage 0 init_rule: it scans only
141// each imported item's own immediately evaluated positions. Short-circuit RHS,
142// optional-call arguments, and function, lambda, defer, branch, arm, and loop
143// bodies stay delayed; top-level consts are ready before runtime initialization
144// and the entry module has the separate §4 rule.
145export record InitViolation {
146 name: string,
147 lo: int,
148 hi: int,
151export record ReferenceResolution {
152 referenceNamespace: string,
153 targetModuleIndex: int,
154 targetLo: int,
155 targetHi: int,
156 targetNamespace: string,
157 targetModule: string,
158 targetName: string,
161export record ImportPathDisposition {
162 kind: string,
163 collisionDetail: string,
166export record PreparedImport {
167 path: string,
168 source: string,
169 physicalAlias: string,
170 generatedStd: bool,
172export function text(source: Bytes, node: SyntaxNode) -> string {
173 spanText(source, node.lo, node.hi)
176export 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 None
185export 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 output
195export function attribute(value: SyntaxNode, name: string) -> string {
196 for found in value.attributes {
197 if found.name == name {
198 return found.value
199 }
200 }
201 ""
204export function boolAttribute(value: SyntaxNode, name: string) -> bool {
205 attribute(value, name) == "true"
208export function contains(values: Array<string>, needle: string) -> bool {
209 for value in values {
210 if value == needle {
211 return true
212 }
213 }
214 false
217export function hasParentPathSegment(path: string) -> bool {
218 for segment in path.split("/") {
219 if segment == ".." {
220 return true
221 }
222 }
223 false
226export function isNone<T>(value: Option<T>) -> bool {
227 match value {
228 case Some(_) => false
229 case None => true
230 }
233export function stringJoin(values: Array<string>, separator: string) -> string {
234 let mut output = ""
235 let mut index = 0
236 for value in values {
237 if index > 0 {
238 output = "{output}{separator}"
239 }
240 output = "{output}{value}"
241 index += 1
242 }
243 output
246export function pathJoin(left: string, right: string) -> string {
247 if left == "" {
248 right
249 } else {
250 "{left}/{right}"
251 }
254export 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 = 0
261 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 += 1
275 }
276 ResolverHostFactCatalog {
277 values: facts,
278 readSources: readSources,
279 directoryListings: directoryListings,
280 physicalContainments: physicalContainments,
281 provider: None,
282 errors: [],
283 }
286export 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 None
299 }
300 match index {
301 case Some(value) => Some(facts.values[value])
302 case None => None
303 }
306export function addQuery(
307 output: Array<PreviewHostQuery>,
308 catalog: ResolverHostFactCatalog,
309 kind: string,
310 path: string,
311) -> () {
312 let mut facts = catalog
313 let mut target = output
314 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.length
339 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 }
355function queryRank(kind: string) -> int {
356 match kind {
357 case "read-source" => 0
358 case "list-directory" => 1
359 case "physical-containment" => 2
360 case _ => 3
361 }
364function 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)
373export function sortQueries(values: Array<PreviewHostQuery>) -> Array<PreviewHostQuery> {
374 let mut output: Array<PreviewHostQuery> = []
375 for value in values {
376 let mut inserted = false
377 let mut index = 0
378 while index < output.length {
379 let existing = output[index]
380 if queryBefore(value, existing) {
381 output.insert(index, value)
382 inserted = true
383 break
384 }
385 index += 1
386 }
387 if !inserted {
388 output.push(value)
389 }
390 }
391 output
394export 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 = output
403 target.push(PreviewResolvedDiagnostic {
404 moduleIndex: moduleIndex,
405 code: code,
406 message: message,
407 lo: lo,
408 hi: hi,
409 notes: [],
410 })
413export function appendModuleDiagnostics(
414 output: Array<PreviewResolvedDiagnostic>,
415 moduleIndexValue: int,
416 values: Array<PreviewDiagnostic>,
417) -> () {
418 for value in values {
419 let mut target = output
420 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 ()
432export function spanText(source: Bytes, lo: int, hi: int) -> string {
433 match source.slice(lo, hi).decodeUtf8() {
434 case Ok(found) => found
435 case Err(_) => ""
436 }

SHA256 b134c16634e30e17445f38838d5dceefeab9c08efc40b46e4ba5e5ae9300c9c4