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/emitter/ir.tpz

Topaz · 7222 B · Original file

1import stage1_types { Stage1Module, Stage1Operation, LoweringOutput }
2import types { PreviewTypeAtom }
3import std.bytes { Bytes }
5export record Table {
6 ops: Array<Stage1Operation>,
7 index: Map<string, int>,
8 captures: Map<string, Array<Stage1Operation>>,
9 capturedBindings: Map<string, bool>,
10 nominals: Map<string, Array<PreviewTypeAtom>>,
13export function buildTable(output: LoweringOutput) -> Table {
14 let mut index: Map<string, int> = Map.new()
15 let mut captures: Map<string, Array<Stage1Operation>> = Map.new()
16 let mut capturedBindings: Map<string, bool> = Map.new()
17 let mut nominals: Map<string, Array<PreviewTypeAtom>> = Map.new()
18 let mut position = 0
19 for op in output.operations {
20 index.insert(op.id, position)
21 if op.typeDefinition.length > 0 && op.kind != "type-alias" {
22 nominals.insert(op.typeDefinition[0].identity, op.typeDefinition)
23 }
24 if op.kind == "binding/capture" {
25 let mut found = match captures.get(op.parentId) {
26 case Some(list) => list
27 case None => []
28 }
29 found.push(op)
30 captures.insert(op.parentId, found)
31 capturedBindings.insert(op.declarationIdentity, true)
32 }
33 position += 1
34 }
35 Table { ops: output.operations, index: index, captures: captures, capturedBindings: capturedBindings, nominals: nominals }
38// Find the runtime type name in its lexical scope, including selected imports.
39export function typeDeclaration(table: Table, originId: string, name: string) -> Result<Option<Stage1Operation>, string> {
40 let origin = opAt(table, originId)?
41 let mut scope = origin.id
42 loop {
43 for declaration in table.ops {
44 if declaration.module != origin.module || declaration.typeDefinition.length == 0 ||
45 declaration.typeDefinition[0].name != name { continue }
46 let mut parent = declaration.parentId
47 if parent != "" {
48 let wrapper = opAt(table, parent)?
49 if wrapper.kind == "export" { parent = wrapper.parentId }
50 }
51 if parent == scope { return Ok(Some(declaration)) }
52 }
53 for imported in table.ops {
54 if imported.module != origin.module || imported.kind != "import" { continue }
55 let mut parent = imported.parentId
56 if parent != "" {
57 let wrapper = opAt(table, parent)?
58 if wrapper.kind == "export" { parent = wrapper.parentId }
59 }
60 if parent != scope { continue }
61 for row in imported.importBindings {
62 let parts = row.split("|")
63 if parts.length != 5 || parts[0] != "selected-import" || parts[2] != name { continue }
64 for root in table.ops {
65 if root.module == imported.importModule && root.parentId == "" {
66 return typeDeclaration(table, root.id, parts[1])
67 }
68 }
69 }
70 ()
71 }
72 if scope == "" { break }
73 let owner = opAt(table, scope)?
74 scope = owner.parentId
75 }
76 Ok(None)
79export function opAt(table: Table, id: string) -> Result<Stage1Operation, string> {
80 match table.index.get(id) {
81 case Some(position) => Ok(table.ops[position])
82 case None => Err("unsupported: ir missing operation {id}")
83 }
86export function capturesOf(table: Table, op: Stage1Operation) -> Array<Stage1Operation> {
87 match table.captures.get(op.id) {
88 case Some(list) => list
89 case None => []
90 }
93// The structural field of an operand label: the last path segment before `:`.
94export function fieldOf(label: string) -> string {
95 let segments = label.split("/")
96 let last = segments[segments.length - 1]
97 last.split(":")[0]
100// The name carried by a `field-initializer[i]=<name>` label head.
101export function labelName(label: string) -> string {
102 let head = label.split("/")[0]
103 let parts = head.split("=")
104 if parts.length < 2 {
105 ""
106 } else {
107 parts[1]
108 }
111export function children(
112 table: Table,
113 op: Stage1Operation,
114 field: string,
115) -> Result<Array<Stage1Operation>, string> {
116 let mut found: Array<Stage1Operation> = []
117 let mut position = 0
118 for id in op.operands {
119 if fieldOf(op.operandLabels[position]) == field {
120 found.push(opAt(table, id)?)
121 }
122 position += 1
123 }
124 Ok(found)
127export function child(
128 table: Table,
129 op: Stage1Operation,
130 field: string,
131) -> Result<Stage1Operation, string> {
132 let found = children(table, op, field)?
133 if found.length != 1 {
134 return Err(refusal("shape {op.kind} operand {field} x{found.length}", op))
135 }
136 Ok(found[0])
139export function optionalChild(
140 table: Table,
141 op: Stage1Operation,
142 field: string,
143) -> Result<Option<Stage1Operation>, string> {
144 let found = children(table, op, field)?
145 if found.length == 0 {
146 Ok(None)
147 } else if found.length == 1 {
148 Ok(Some(found[0]))
149 } else {
150 Err(refusal("shape {op.kind} operand {field} x{found.length}", op))
151 }
154// Structured refusal: one line naming the first construct outside the
155// emitter's surface and where it occurs. Never a silent fallback.
156export function refusal(what: string, op: Stage1Operation) -> string {
157 "unsupported: {what} at {op.module}:{op.lo}-{op.hi}"
160// B2a carries the runtime FileId explicitly, independently of operation ids.
161export function fileOf(op: Stage1Operation) -> string {
162 "{op.sourceOrdinal}"
165export function spanOf(op: Stage1Operation) -> string {
166 "Span::new(FileId({fileOf(op)}), {op.lo}, {op.hi})"
169export function spanAt(op: Stage1Operation, lo: int, hi: int) -> string {
170 "Span::new(FileId({fileOf(op)}), {lo}, {hi})"
173// The declared name span of a binding identity `source:<n>:<lo>:<hi>`.
174export function declarationSpan(op: Stage1Operation) -> string {
175 let parts = op.declarationIdentity.split(":")
176 if parts.length == 4 {
177 spanAt(op, toInt(parts[2]) ?? op.lo, toInt(parts[3]) ?? op.hi)
178 } else {
179 spanOf(op)
180 }
183export function rustName(name: string) -> string {
184 "_t_{Bytes.encodeUtf8(name).toHex()}"
187// String text in the IR is source text; decode it exactly as the runtime's
188// shared `decode_escapes` does. `None` marks an escape the runtime rejects.
189export function decodeEscapes(raw: string) -> Option<string> {
190 let mut out: Array<string> = []
191 let mut escaped = false
192 for scalar in raw.scalars() {
193 if escaped {
194 escaped = false
195 if scalar == "n" {
196 out.push("\n")
197 } else if scalar == "t" {
198 out.push("\t")
199 } else if scalar == "r" {
200 out.push("\r")
201 } else if scalar == "\\" || scalar == "\"" || scalar == "\{" || scalar == "\}" {
202 out.push(scalar)
203 } else {
204 return None
205 }
206 } else if scalar == "\\" {
207 escaped = true
208 } else {
209 out.push(scalar)
210 }
211 }
212 if escaped {
213 return None
214 }
215 Some(out.join(""))
218export function patternBindings(table: Table, pattern: Stage1Operation) -> Result<Array<Stage1Operation>, string> {
219 let mut found: Array<Stage1Operation> = []
220 if pattern.kind == "pattern/binding" || pattern.kind == "pattern/typed-binding" {
221 if pattern.bindingName != "" { found.push(pattern) }
222 } else if pattern.kind.startsWith("pattern/") {
223 for id in pattern.operands {
224 let sub = opAt(table, id)?
225 for bind in patternBindings(table, sub)? { found.push(bind) }
226 if pattern.kind == "pattern/alternatives" { break }
227 }
228 ()
229 }
230 Ok(found)

SHA256 2fdfbc874be19638ab8314304b2e7e2ed1130a4f18d08dd5dc7c3698c2dc735f