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.tpz
Topaz · 8384 B · Original file
1
import emitter.module { emitProgram }2
import stage1_types {3
LoweringOutput,4
STAGE1_IR_SCHEMA,5
Stage1Module,6
Stage1Operation,7
Stage1Provenance,8
Stage1Response,9
decodeStage1Response,10
}12
record EmitPayload derives JSON {13
schema: string,14
irSchema: string,15
runtimeTemplate: string,16
sourceSetId: string,17
loweredModules: Array<Stage1Module>,18
loweredOperations: Array<Stage1Operation>,19
}21
const COMPILER_RUNTIME_REGISTRY_SCHEMA =22
"topaz.compiler.fixed-point-runtime-templates/v1"23
const COMPILER_RUNTIME_TEMPLATE = "compiler-ir-table/v2"24
const COMPILER_PAYLOAD_SCHEMA =25
"topaz.compiler.fixed-point-ir-payload/v1"26
// Closed folding bounds operands and traversal so preview generation stays finite and portable.27
const CLOSED_INT_OPERAND_LIMIT = 100000000028
const CLOSED_INT_PRODUCT_OPERAND_LIMIT = 100000029
const CLOSED_INT_DEPTH_LIMIT = 25630
const ENTRY_PREVIEW_OPERATION_LIMIT = 409632
function rustRaw(value: string) -> string {33
let mut hashes = ""34
while value.contains("\"{hashes}") {35
hashes = "{hashes}#"36
}37
"r{hashes}\"{value}\"{hashes}"38
}40
function rustStringConstant(name: string, value: string) -> string {41
"pub const {name}: &str = {rustRaw(value)};\n"42
}44
record EmitterOperationCatalog {45
values: Array<Stage1Operation>,46
positions: Map<string, int>,47
runtimeLeaves: Array<string>,48
}50
function emitterOperationCatalog(51
operations: Array<Stage1Operation>,52
) -> EmitterOperationCatalog {53
let mut positions: Map<string, int> = Map.new()54
let mut leaves: Set<string> = set {}55
let mut index = 056
for operation in operations {57
if !positions.containsKey(operation.id) {58
positions.insert(operation.id, index)59
}60
if operation.runtimeLeaf != "" {61
leaves.add(operation.runtimeLeaf)62
}63
index += 164
}65
let mut orderedLeaves = leaves.toArray()66
orderedLeaves.sort()67
EmitterOperationCatalog {68
values: operations,69
positions: positions,70
runtimeLeaves: orderedLeaves,71
}72
}74
function operation(75
context: EmitterOperationCatalog,76
wanted: string,77
) -> Option<Stage1Operation> {78
match context.positions.get(wanted) {79
case Some(index) => Some(context.values[index])80
case None => None81
}82
}84
function evaluateOperand(85
context: EmitterOperationCatalog,86
found: Stage1Operation,87
index: int,88
depth: int,89
) -> Option<int> {90
match found.operands.get(index) {91
case Some(id) => evaluateClosedInt(context, id, depth + 1)92
case None => None93
}94
}96
function evaluateUnaryClosedInt(97
context: EmitterOperationCatalog,98
found: Stage1Operation,99
depth: int,100
) -> Option<int> {101
if found.operands.length != 1 {102
return None103
}104
match evaluateOperand(context, found, 0, depth) {105
case Some(number) => if found.detail == "neg" &&106
number >= -CLOSED_INT_OPERAND_LIMIT && number <= CLOSED_INT_OPERAND_LIMIT {107
Some(-number)108
} else if found.detail == "pos" {109
Some(number)110
} else {111
None112
}113
case None => None114
}115
}117
function evaluateBinaryClosedInt(118
context: EmitterOperationCatalog,119
found: Stage1Operation,120
depth: int,121
) -> Option<int> {122
if found.operands.length != 2 {123
return None124
}125
match [126
evaluateOperand(context, found, 0, depth),127
evaluateOperand(context, found, 1, depth),128
] {129
case [Some(lhs), Some(rhs)] => match found.detail {130
case "add" => if lhs >= -CLOSED_INT_OPERAND_LIMIT && lhs <= CLOSED_INT_OPERAND_LIMIT &&131
rhs >= -CLOSED_INT_OPERAND_LIMIT && rhs <= CLOSED_INT_OPERAND_LIMIT {132
Some(lhs + rhs)133
} else {134
None135
}136
case "sub" => if lhs >= -CLOSED_INT_OPERAND_LIMIT && lhs <= CLOSED_INT_OPERAND_LIMIT &&137
rhs >= -CLOSED_INT_OPERAND_LIMIT && rhs <= CLOSED_INT_OPERAND_LIMIT {138
Some(lhs - rhs)139
} else {140
None141
}142
case "mul" => if lhs >= -CLOSED_INT_PRODUCT_OPERAND_LIMIT &&143
lhs <= CLOSED_INT_PRODUCT_OPERAND_LIMIT &&144
rhs >= -CLOSED_INT_PRODUCT_OPERAND_LIMIT &&145
rhs <= CLOSED_INT_PRODUCT_OPERAND_LIMIT {146
Some(lhs * rhs)147
} else {148
None149
}150
case _ => None151
}152
case _ => None153
}154
}156
function evaluateSequenceClosedInt(157
context: EmitterOperationCatalog,158
found: Stage1Operation,159
depth: int,160
) -> Option<int> {161
let mut result: Option<int> = None162
for id in found.operands {163
match evaluateClosedInt(context, id, depth + 1) {164
case Some(value) => { result = Some(value) }165
case None => ()166
}167
}168
result169
}171
function evaluateClosedInt(172
context: EmitterOperationCatalog,173
wanted: string,174
depth: int,175
) -> Option<int> {176
if depth > CLOSED_INT_DEPTH_LIMIT {177
return None178
}179
match operation(context, wanted) {180
case None => None181
case Some(found) => {182
if found.kind == "expression/integer" {183
return toInt(found.detail)184
}185
if found.kind == "expression/unary" {186
return evaluateUnaryClosedInt(context, found, depth)187
}188
if found.kind == "expression/binary" {189
return evaluateBinaryClosedInt(context, found, depth)190
}191
if found.kind == "let" ||192
found.kind == "constant" ||193
found.kind == "expression/parenthesized" ||194
found.kind == "expression/block" ||195
found.kind == "module" {196
return evaluateSequenceClosedInt(context, found, depth)197
}198
None199
}200
}201
}203
function entryPreview(204
modules: Array<Stage1Module>,205
catalog: EmitterOperationCatalog,206
) -> Option<int> {207
if catalog.values.length > ENTRY_PREVIEW_OPERATION_LIMIT {208
return None209
}210
for module in modules {211
if module.entry {212
for id in module.operationIds {213
match operation(catalog, id) {214
case Some(found) => if found.kind == "module" {215
return evaluateClosedInt(catalog, id, 0)216
}217
case None => ()218
}219
}220
}221
}222
None223
}225
function renderRuntimeLeaves(leaves: Array<string>) -> string {226
let mut rendered: Array<string> = []227
for leaf in leaves {228
rendered.push("{rustRaw(leaf)},")229
}230
"&[{rendered.join("")}]"231
}233
function render(response: Stage1Response) -> Result<string, string> {234
let ir = JSON.stringify(EmitPayload {235
schema: COMPILER_PAYLOAD_SCHEMA,236
irSchema: STAGE1_IR_SCHEMA,237
runtimeTemplate: COMPILER_RUNTIME_TEMPLATE,238
sourceSetId: response.provenance.sourceSetId,239
loweredModules: response.loweredModules,240
loweredOperations: response.loweredOperations,241
})?242
let catalog = emitterOperationCatalog(response.loweredOperations)243
let preview = match entryPreview(response.loweredModules, catalog) {244
case Some(value) => "Some({value}i64)"245
case None => "None"246
}247
let leaves = renderRuntimeLeaves(catalog.runtimeLeaves)248
Ok(249
"// @generated by the Topaz compiler kernel; do not edit.\n" +250
rustStringConstant(251
"TOPAZ_COMPILER_RUNTIME_REGISTRY_SCHEMA",252
COMPILER_RUNTIME_REGISTRY_SCHEMA,253
) +254
rustStringConstant(255
"TOPAZ_COMPILER_RUNTIME_TEMPLATE",256
COMPILER_RUNTIME_TEMPLATE,257
) +258
rustStringConstant("TOPAZ_COMPILER_IR_SCHEMA", STAGE1_IR_SCHEMA) +259
rustStringConstant(260
"TOPAZ_COMPILER_IR_PAYLOAD_SCHEMA",261
COMPILER_PAYLOAD_SCHEMA,262
) +263
rustStringConstant(264
"TOPAZ_COMPILER_SOURCE_SET",265
response.provenance.sourceSetId,266
) +267
rustStringConstant("TOPAZ_COMPILER_IR_JSON", ir) +268
"pub const TOPAZ_COMPILER_RUNTIME_LEAVES: &[&str] = {leaves};\n" +269
"pub fn compiler_preview_i64() -> Option<i64> \{ {preview} \}\n",270
)271
}273
export function emitStep(274
loweredBytes: Bytes,275
maxGeneratedRustBytes: int,276
) -> Result<Bytes, string> {277
let lowered = decodeStage1Response(loweredBytes.decodeUtf8()?)?278
if lowered.status != "completed" {279
return Ok(loweredBytes)280
}281
if lowered.generatedRust != "" {282
return Err("Stage 1 emitter received pre-existing generated Rust")283
}284
let generated = render(lowered)?285
if generated.byteLength() > maxGeneratedRustBytes {286
return Err(287
"Stage 1 generated Rust exceeds {maxGeneratedRustBytes} bytes",288
)289
}290
let response = Stage1Response {291
...lowered,292
generatedRust: generated,293
}294
Ok(Bytes.encodeUtf8(JSON.stringify(response)?))295
}297
// Real Rust source has a separate value boundary from emitStep's table carrier.298
export function emitRust(output: LoweringOutput) -> Result<string, string> {299
if output.unsupported.length > 0 {300
return Err(output.unsupported.join("\n"))301
}302
emitProgram(output)303
}SHA256 505e5fa4fd43cb5b5cb97f57753a9d5c1c93a7e821c59a014cc39bbc12631365