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/modules_tables.tpz

Topaz · 12470 B · Original file

1// Import drafts and module binding and export tables.
2// Graph discovery provides parsed roots; scope walking consumes the canonical
3// table catalog produced here.
5import types {
6 PreviewResolvedDiagnostic,
7 PreviewResolvedExport,
8 SyntaxNode,
9}
10import resolver.model_facts {
11 Binding,
12 ExportBinding,
13 ImportDraft,
14 ModuleDraft,
15 ModuleTable,
16 ModuleTableCatalog,
17 boolAttribute,
18 child,
19 children,
20 contains,
21 diagnostic,
22 isNone,
23 stringJoin,
24 text,
27export function importDrafts(root: SyntaxNode, source: Bytes) -> Array<ImportDraft> {
28 let mut output: Array<ImportDraft> = []
29 for statement in root.children {
30 if statement.kind != "statement/import" {
31 continue
32 }
33 let itemOption = child(statement, "import")
34 if isNone(itemOption) {
35 continue
36 }
37 let item = match itemOption {
38 case Some(value) => value
39 case None => statement
40 }
41 let pathOption = child(item, "path")
42 if isNone(pathOption) {
43 continue
44 }
45 let path = match pathOption {
46 case Some(value) => value
47 case None => item
48 }
49 let mut segments: Array<string> = []
50 for segment in children(path, "segments") {
51 segments.push(text(source, segment))
52 }
53 let mut selectedNames: Array<string> = []
54 let mut selectedLos: Array<int> = []
55 let mut selectedHis: Array<int> = []
56 for spec in children(item, "specs") {
57 let nameNode = child(spec, "name")
58 let name = match nameNode {
59 case Some(value) => text(source, value)
60 case None => ""
61 }
62 selectedNames.push(name)
63 selectedLos.push(match nameNode {
64 case Some(value) => value.lo
65 case None => spec.lo
66 })
67 selectedHis.push(match nameNode {
68 case Some(value) => value.hi
69 case None => spec.hi
70 })
71 }
72 output.push(ImportDraft {
73 target: stringJoin(segments, "."),
74 segments: segments,
75 pathLo: path.lo,
76 pathHi: path.hi,
77 selectedNames: selectedNames,
78 selectedLos: selectedLos,
79 selectedHis: selectedHis,
80 })
81 }
82 output
85export function collectPatternBindings(
86 pattern: SyntaxNode,
87 source: Bytes,
88 output: Array<SyntaxNode>,
89) -> () {
90 let mut target = output
91 if pattern.kind == "pattern/binding" || pattern.kind == "pattern/typed" {
92 match child(pattern, "name") {
93 case Some(name) => target.push(name)
94 case None => ()
95 }
96 return ()
97 }
98 if pattern.kind == "record-pattern-field" {
99 match child(pattern, "pattern") {
100 case Some(value) => collectPatternBindings(value, source, output)
101 case None => match child(pattern, "name") {
102 case Some(name) => target.push(name)
103 case None => ()
104 }
105 }
106 return ()
107 }
108 let mut alternatives: Array<string> = []
109 for found in pattern.children {
110 if pattern.kind == "pattern/or" && found.field == "alternatives" {
111 let mut local: Array<SyntaxNode> = []
112 collectPatternBindings(found, source, local)
113 let mut seenHere: Array<string> = []
114 for binding in local {
115 let name = text(source, binding)
116 let repeatedHere = contains(seenHere, name)
117 let firstAlternative = !contains(alternatives, name)
118 if repeatedHere || firstAlternative {
119 target.push(binding)
120 }
121 if !repeatedHere {
122 seenHere.push(name)
123 }
124 if firstAlternative {
125 alternatives.push(name)
126 }
127 }
128 } else if found.kind.startsWith("pattern/") ||
129 found.kind == "record-pattern-field" ||
130 found.kind == "list-pattern/rest" {
131 collectPatternBindings(found, source, output)
132 }
133 ()
134 }
135 ()
138export function unwrapExport(statement: SyntaxNode) -> SyntaxNode {
139 if statement.kind == "statement/export" {
140 return match child(statement, "item") {
141 case Some(value) => value
142 case None => statement
143 }
144 }
145 statement
148function importBindings(
149 moduleIndexValue: int,
150 item: SyntaxNode,
151 source: Bytes,
152) -> Array<Binding> {
153 let mut output: Array<Binding> = []
154 let importItem = match child(item, "import") {
155 case Some(value) => value
156 case None => return output
157 }
158 let path = match child(importItem, "path") {
159 case Some(value) => value
160 case None => return output
161 }
162 let mut segments: Array<string> = []
163 for segment in children(path, "segments") {
164 segments.push(text(source, segment))
165 }
166 let target = stringJoin(segments, ".")
167 let specs = children(importItem, "specs")
168 if specs.length == 0 {
169 let nameNode = match child(importItem, "alias") {
170 case Some(value) => value
171 case None => match path.children.get(path.children.length - 1) {
172 case Some(value) => value
173 case None => return output
174 }
175 }
176 output.push(Binding {
177 moduleIndex: moduleIndexValue,
178 scopeOrdinal: 0,
179 name: text(source, nameNode),
180 namespace: "module",
181 declarationKind: "namespace-import",
182 lo: nameNode.lo,
183 hi: nameNode.hi,
184 exported: false,
185 targetModule: target,
186 targetName: "",
187 mutable: false,
188 })
189 return output
190 }
191 for spec in specs {
192 let sourceName = match child(spec, "name") {
193 case Some(value) => text(source, value)
194 case None => ""
195 }
196 let bound = match child(spec, "alias") {
197 case Some(value) => Some(value)
198 case None => child(spec, "name")
199 }
200 match bound {
201 case Some(name) => output.push(Binding {
202 moduleIndex: moduleIndexValue,
203 scopeOrdinal: 0,
204 name: text(source, name),
205 namespace: "value",
206 declarationKind: "selected-import",
207 lo: name.lo,
208 hi: name.hi,
209 exported: false,
210 targetModule: target,
211 targetName: sourceName,
212 mutable: false,
213 })
214 case None => ()
215 }
216 }
217 output
220function namedStatementBinding(
221 moduleIndexValue: int,
222 item: SyntaxNode,
223 source: Bytes,
224 exported: bool,
225) -> Option<Binding> {
226 let kind = match item.kind {
227 case "statement/function" => "function"
228 case "statement/type-alias" => "type-alias"
229 case "statement/enum" | "statement/record" | "statement/newtype" =>
230 "nominal-type"
231 case "statement/const" => "const"
232 case _ => return None
233 }
234 let holder = if item.kind == "statement/function" {
235 match child(item, "declaration") {
236 case Some(value) => value
237 case None => item
238 }
239 } else {
240 item
241 }
242 match child(holder, "name") {
243 case Some(name) => Some(Binding {
244 moduleIndex: moduleIndexValue,
245 scopeOrdinal: 0,
246 name: text(source, name),
247 namespace: if kind == "type-alias" || kind == "nominal-type" {
248 "type"
249 } else {
250 "value"
251 },
252 declarationKind: kind,
253 lo: name.lo,
254 hi: name.hi,
255 exported: exported,
256 targetModule: "",
257 targetName: "",
258 mutable: false,
259 })
260 case None => None
261 }
264function letBindings(
265 moduleIndexValue: int,
266 item: SyntaxNode,
267 source: Bytes,
268 exported: bool,
269) -> Array<Binding> {
270 let mut output: Array<Binding> = []
271 let pattern = match child(item, "pattern") {
272 case Some(value) => value
273 case None => return output
274 }
275 let mut names: Array<SyntaxNode> = []
276 collectPatternBindings(pattern, source, names)
277 let mutable = boolAttribute(item, "mutable")
278 for name in names {
279 output.push(Binding {
280 moduleIndex: moduleIndexValue,
281 scopeOrdinal: 0,
282 name: text(source, name),
283 namespace: "value",
284 declarationKind: "let",
285 lo: name.lo,
286 hi: name.hi,
287 exported: exported && !mutable,
288 targetModule: "",
289 targetName: "",
290 mutable: mutable,
291 })
292 }
293 output
296export function bindingForStatement(
297 moduleIndexValue: int,
298 statement: SyntaxNode,
299 source: Bytes,
300 exported: bool,
301) -> Array<Binding> {
302 let item = unwrapExport(statement)
303 if item.kind == "statement/import" {
304 return importBindings(moduleIndexValue, item, source)
305 }
306 match namedStatementBinding(moduleIndexValue, item, source, exported) {
307 case Some(binding) => return [binding]
308 case None => ()
309 }
310 if item.kind == "statement/let" {
311 return letBindings(moduleIndexValue, item, source, exported)
312 }
313 []
316function buildTable(
317 moduleIndexValue: int,
318 module: ModuleDraft,
319 diagnostics: Array<PreviewResolvedDiagnostic>,
320) -> ModuleTable {
321 let source = module.source
322 let mut bindings: Array<Binding> = []
323 let mut bindingIndexes: Map<string, int> = Map.new()
324 let mut exportedBindings: Map<string, ExportBinding> = Map.new()
325 for statement in module.root.children {
326 let exported = statement.kind == "statement/export"
327 for binding in bindingForStatement(moduleIndexValue, statement, source, exported) {
328 let existingIndex = bindingIndexes.get(binding.name)
329 if exported && binding.declarationKind == "let" && binding.mutable {
330 diagnostic(
331 diagnostics,
332 moduleIndexValue,
333 "TPZ3011",
334 "`export let mut` is a static error: exported bindings are immutable views",
335 binding.lo,
336 binding.hi,
337 )
338 }
339 if binding.exported {
340 let exportIndex = match existingIndex {
341 case Some(index) => index
342 case None => bindings.length
343 }
344 exportedBindings.insert(binding.name, ExportBinding {
345 bindingIndex: exportIndex,
346 namespace: binding.namespace,
347 })
348 }
349 match existingIndex {
350 case Some(index) => {
351 let firstLo = bindings[index].lo
352 diagnostic(
353 diagnostics,
354 moduleIndexValue,
355 "TPZ3008",
356 "`{binding.name}` is already bound at this module's top level (first binding at byte {firstLo})",
357 binding.lo,
358 binding.hi,
359 )
360 }
361 case None => {
362 let bindingIndexValue = bindings.length
363 bindingIndexes.insert(binding.name, bindingIndexValue)
364 bindings.push(binding)
365 }
366 }
367 }
368 }
369 let mut exportedNames: Array<string> = []
370 for binding in bindings {
371 if !isNone(exportedBindings.get(binding.name)) {
372 exportedNames.push(binding.name)
373 }
374 }
375 exportedNames.sort()
376 ModuleTable {
377 moduleIndex: moduleIndexValue,
378 identity: module.result.identity,
379 bindings: bindings,
380 bindingIndexes: bindingIndexes,
381 exportedBindings: exportedBindings,
382 exportedNames: exportedNames,
383 }
386export function catalogTable(
387 catalog: ModuleTableCatalog,
388 identity: string,
389) -> Option<ModuleTable> {
390 match catalog.indexes.get(identity) {
391 case Some(index) => Some(catalog.tables[index])
392 case None => None
393 }
396export function tableBinding(table: ModuleTable, name: string) -> Option<Binding> {
397 match table.bindingIndexes.get(name) {
398 case Some(index) => Some(table.bindings[index])
399 case None => None
400 }
403export function exportedBinding(table: ModuleTable, name: string) -> Option<ExportBinding> {
404 table.exportedBindings.get(name)
407export function exportedNames(
408 table: ModuleTable,
409 namespace: string,
410) -> Array<string> {
411 if namespace == "" {
412 return table.exportedNames
413 }
414 let mut candidates: Array<string> = []
415 for name in table.exportedNames {
416 let namespaceValue = match exportedBinding(table, name) {
417 case Some(binding) => binding.namespace
418 case None => ""
419 }
420 if (namespace == "type" && namespaceValue == "type") ||
421 (namespace == "value" && namespaceValue != "type") {
422 candidates.push(name)
423 }
424 }
425 candidates
428export function effectiveNamespace(binding: Binding, tables: ModuleTableCatalog) -> string {
429 if binding.declarationKind != "selected-import" {
430 return binding.namespace
431 }
432 let targetTable = match catalogTable(tables, binding.targetModule) {
433 case Some(found) => found
434 case None => return binding.namespace
435 }
436 match exportedBinding(targetTable, binding.targetName) {
437 case Some(found) => found.namespace
438 case None => binding.namespace
439 }
442export function buildModuleTables(
443 modules: Array<ModuleDraft>,
444 diagnostics: Array<PreviewResolvedDiagnostic>,
445) -> ModuleTableCatalog {
446 let mut tables: Array<ModuleTable> = []
447 let mut indexes: Map<string, int> = Map.new()
448 let mut index = 0
449 for module in modules {
450 indexes.insert(module.result.identity, index)
451 tables.push(buildTable(index, module, diagnostics))
452 index += 1
453 }
454 ModuleTableCatalog { tables: tables, indexes: indexes }

SHA256 14157005ccd44d84bb0a23c21225e5b6b3368c7b723cec195dbd2e9336f3787d