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
1
import stage1_types { Stage1Module, Stage1Operation, LoweringOutput }2
import types { PreviewTypeAtom }3
import std.bytes { Bytes }5
export 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>>,11
}13
export 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 = 019
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) => list27
case None => []28
}29
found.push(op)30
captures.insert(op.parentId, found)31
capturedBindings.insert(op.declarationIdentity, true)32
}33
position += 134
}35
Table { ops: output.operations, index: index, captures: captures, capturedBindings: capturedBindings, nominals: nominals }36
}38
// Find the runtime type name in its lexical scope, including selected imports.39
export function typeDeclaration(table: Table, originId: string, name: string) -> Result<Option<Stage1Operation>, string> {40
let origin = opAt(table, originId)?41
let mut scope = origin.id42
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.parentId47
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.parentId56
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.parentId75
}76
Ok(None)77
}79
export 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
}84
}86
export function capturesOf(table: Table, op: Stage1Operation) -> Array<Stage1Operation> {87
match table.captures.get(op.id) {88
case Some(list) => list89
case None => []90
}91
}93
// The structural field of an operand label: the last path segment before `:`.94
export function fieldOf(label: string) -> string {95
let segments = label.split("/")96
let last = segments[segments.length - 1]97
last.split(":")[0]98
}100
// The name carried by a `field-initializer[i]=<name>` label head.101
export 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
}109
}111
export 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 = 0118
for id in op.operands {119
if fieldOf(op.operandLabels[position]) == field {120
found.push(opAt(table, id)?)121
}122
position += 1123
}124
Ok(found)125
}127
export 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])137
}139
export 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
}152
}154
// Structured refusal: one line naming the first construct outside the155
// emitter's surface and where it occurs. Never a silent fallback.156
export function refusal(what: string, op: Stage1Operation) -> string {157
"unsupported: {what} at {op.module}:{op.lo}-{op.hi}"158
}160
// B2a carries the runtime FileId explicitly, independently of operation ids.161
export function fileOf(op: Stage1Operation) -> string {162
"{op.sourceOrdinal}"163
}165
export function spanOf(op: Stage1Operation) -> string {166
"Span::new(FileId({fileOf(op)}), {op.lo}, {op.hi})"167
}169
export function spanAt(op: Stage1Operation, lo: int, hi: int) -> string {170
"Span::new(FileId({fileOf(op)}), {lo}, {hi})"171
}173
// The declared name span of a binding identity `source:<n>:<lo>:<hi>`.174
export 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
}181
}183
export function rustName(name: string) -> string {184
"_t_{Bytes.encodeUtf8(name).toHex()}"185
}187
// String text in the IR is source text; decode it exactly as the runtime's188
// shared `decode_escapes` does. `None` marks an escape the runtime rejects.189
export function decodeEscapes(raw: string) -> Option<string> {190
let mut out: Array<string> = []191
let mut escaped = false192
for scalar in raw.scalars() {193
if escaped {194
escaped = false195
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 None205
}206
} else if scalar == "\\" {207
escaped = true208
} else {209
out.push(scalar)210
}211
}212
if escaped {213
return None214
}215
Some(out.join(""))216
}218
export 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)231
}SHA256 2fdfbc874be19638ab8314304b2e7e2ed1130a4f18d08dd5dc7c3698c2dc735f