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

Topaz · 23552 B · Original file

1import emitter.rust_text { rustStr }
2import emitter.emit {
3 Bind,
4 FnInfo,
5 ModInfo,
6 Ctx,
7 Code,
8 pushBind,
9 restoreScope,
10 newCell,
11 modBind,
12 paramsOf,
13 argumentGuard,
14 defaultValues,
15 restArgument,
16 emitExpr,
17 emitStatement,
18 emitImplementation,
19 into,
20 has,
22import emitter.exports { projectExports, hasExplicitMain }
23import emitter.ir { Table, buildTable, patternBindings, opAt, child, children, refusal, spanOf, declarationSpan, rustName }
24import emitter.repr { reprOf, slotRepr, rustType, isCopyRepr, boxed, bare }
25import stage1_types { Stage1Module, Stage1Operation, LoweringOutput }
27// The emitted-crate contract every Topaz program crate exposes (the same
28// items the reference crate's `emitted.rs` defines). This emitter names
29// nominal values by their defining-module identity in every language mode, so
30// the runtime context projects host-returned std.lispex values to the same
31// identities (the Rust emitter selects this context from 5.20 on).
32const CONTRACT = """
33pub fn run_with_host(host: Rc<dyn Host>) -> RunOutcome \{
34 run_with_host_and_input(host, Vec::new(), String::new())
35\}
37pub fn run_with_host_and_input(host: Rc<dyn Host>, args: Vec<String>, stdin: String) -> RunOutcome \{
38 let cx = RtCx::new_module_stable(host);
39 let __topaz_args = Value::array(args.into_iter().map(Value::str).collect());
40 let __topaz_stdin = Value::str(stdin);
41 match block_on(entry(cx, __topaz_args, __topaz_stdin)) \{
42 Ok(value) => RunOutcome::Completed(value),
43 Err(error) => RunOutcome::Faulted(error),
44 \}
45\}
47pub fn call_export_with_host(host: Rc<dyn Host>, name: &str, args: Vec<Value>) -> RunOutcome \{
48 call_export_with_host_and_input(host, name, args, Vec::new(), String::new())
49\}
51pub fn call_export_with_host_until(
52 host: Rc<dyn Host>,
53 name: &str,
54 args: Vec<Value>,
55 deadline: std::time::Instant,
56) -> Result<RunOutcome, DeadlineExceeded> \{
57 let outcome = block_on_until(deadline, call_export_with_host_future(host, name, args))?;
58 Ok(match outcome \{
59 Ok(value) => RunOutcome::Completed(value),
60 Err(error) => RunOutcome::Faulted(error),
61 \})
62\}
64pub fn call_export_with_host_future(
65 host: Rc<dyn Host>,
66 name: &str,
67 args: Vec<Value>,
68) -> CallFuture \{
69 let cx = RtCx::new_module_stable(host);
70 let __span = Span::new(FileId(0), 0, 0);
71 let name = name.to_string();
72 Box::pin(async move \{
73 __topaz_call_export(cx, &name, args, __span).await
74 \})
75\}
77pub fn call_export_with_host_and_input(
78 host: Rc<dyn Host>,
79 name: &str,
80 args: Vec<Value>,
81 _program_args: Vec<String>,
82 _stdin: String,
83) -> RunOutcome \{
84 let cx = RtCx::new_module_stable(host);
85 let __span = Span::new(FileId(0), 0, 0);
86 match block_on(__topaz_call_export(cx, name, args, __span)) \{
87 Ok(value) => RunOutcome::Completed(value),
88 Err(error) => RunOutcome::Faulted(error),
89 \}
90\}
92pub fn call_export_json_with_host(host: Rc<dyn Host>, name: &str, args_json: &str) -> String \{
93 call_export_json_with_host_and_input(host, name, args_json, Vec::new(), String::new())
94\}
96pub fn call_export_json_with_host_and_input(
97 host: Rc<dyn Host>,
98 name: &str,
99 args_json: &str,
100 program_args: Vec<String>,
101 stdin: String,
102) -> String \{
103 let args = match canonical_abi_decode_args(args_json) \{
104 Ok(args) => args,
105 Err(error) => return canonical_abi_error(&error),
106 \};
107 match call_export_with_host_and_input(host, name, args, program_args, stdin) \{
108 RunOutcome::Completed(value) => canonical_abi_completed(&value),
109 RunOutcome::Faulted(error) => canonical_abi_faulted(&error),
110 \}
111\}
113async fn __topaz_exports(cx: RtCx) -> Result<Value, RtError> \{
114 let (_, __exports) = __topaz_initialize(cx).await?;
115 Ok(__exports)
116\}
118async fn __topaz_call_export(cx: RtCx, name: &str, args: Vec<Value>, span: Span) -> Result<Value, RtError> \{
119 let __exports = __topaz_exports(cx.clone()).await?;
120 let __callee = member_value_required(&__exports, name, span)?;
121 call_value(__callee, args, cx, span).await
122\}
123"""
125const CELL_HELPER = """
126fn __cell_get<T: Copy>(cell: &std::cell::Cell<Option<T>>, name: &str, span: Span) -> Result<T, RtError> \{
127 cell.get().ok_or_else(|| fault(codes::GUARD_UNBOUND, format!("`\{name\}` is not bound"), span))
128\}
129"""
131const CONST_HELPER = """
132fn __cg<T>(result: Result<T, RtError>, span: Span) -> Result<T, RtError> \{
133 result.map_err(|error| \{
134 if error.is_fault() \{
135 fault(codes::GUARD_TYPE, format!("constant expression error: \{\}", error.message), span)
136 \} else \{
137 error
138 \}
139 \})
140\}
141"""
143record Item {
144 decl: Stage1Operation,
145 exported: bool,
148function itemsOf(table: Table, root: Stage1Operation) -> Result<Array<Item>, string> {
149 let mut items: Array<Item> = []
150 for id in root.operands {
151 let item = opAt(table, id)?
152 if item.kind == "export" {
153 items.push(Item { decl: child(table, item, "item")?, exported: true })
154 } else {
155 items.push(Item { decl: item, exported: false })
156 }
157 }
158 Ok(items)
161record ImportRow {
162 kind: string,
163 source: string,
164 bound: string,
167function importRows(op: Stage1Operation) -> Result<Array<ImportRow>, string> {
168 let mut rows: Array<ImportRow> = []
169 for row in op.importBindings {
170 let parts = row.split("|")
171 if parts.length != 5 {
172 return Err(refusal("import row {row}", op))
173 }
174 rows.push(ImportRow { kind: parts[0], source: parts[1], bound: parts[2] })
175 }
176 Ok(rows)
179function fnKey(ctx: Ctx) -> int {
180 ctx.fns.length
183// Module-level names become cells or function entries before any body is
184// emitted, so a function may name a binding declared later in its module.
185function registerModule(ctx: Ctx, module: Stage1Module, root: Stage1Operation) -> Result<ModInfo, string> {
186 let mut binds: Array<Bind> = []
187 let mut exported: Array<string> = []
188 for item in itemsOf(ctx.table, root)? {
189 let decl = item.decl
190 if decl.kind == "function" {
191 let params = paramsOf(ctx, decl, decl.declaredTypeAtoms)?
192 let ret = slotRepr(decl.declaredTypeAtoms, "result", 0)
193 if ret == "none" {
194 return Err(refusal("function result type", decl))
195 }
196 let number = fnKey(ctx)
197 let info = FnInfo {
198 id: decl.id,
199 name: decl.bindingName,
200 body: "__b{number}",
201 adapter: "__v{number}",
202 cell: newCell(ctx, "value"),
203 paramNames: params.names,
204 paramReprs: params.reprs,
205 paramSpans: params.spans,
206 defaultIds: params.defaultIds,
207 variadic: params.variadic,
208 retRepr: ret,
209 }
210 let mut fns = ctx.fns
211 fns.insert(decl.id, info)
212 binds.push(Bind { name: decl.bindingName, kind: "fn", rust: decl.id, repr: "value", mutable: false })
213 if item.exported {
214 exported.push(decl.bindingName)
215 }
216 } else if decl.kind == "let" {
217 let pattern = child(ctx.table, decl, "pattern")?
218 for binding in patternBindings(ctx.table, pattern)? {
219 let declared = reprOf(binding.semanticTypeAtoms)
220 let repr = if declared == "none" { "value" } else { declared }
221 binds.push(Bind { name: binding.bindingName, kind: "global", rust: newCell(ctx, repr), repr: repr, mutable: binding.bindingMutable })
222 if item.exported { exported.push(binding.bindingName) }
223 }
224 } else if decl.kind == "constant" {
225 let declared = reprOf(decl.declaredTypeAtoms)
226 let repr = if declared != "none" {
227 declared
228 } else {
229 let value = child(ctx.table, decl, "value")?
230 reprOf(value.semanticTypeAtoms)
231 }
232 if repr == "none" {
233 return Err(refusal("binding type {decl.bindingName}", decl))
234 }
235 binds.push(Bind { name: decl.bindingName, kind: "global", rust: newCell(ctx, repr), repr: repr, mutable: false })
236 if item.exported {
237 exported.push(decl.bindingName)
238 }
239 } else if decl.kind == "import" {
240 for row in importRows(decl)? {
241 if row.kind == "namespace-import" {
242 binds.push(Bind { name: row.bound, kind: "namespace", rust: decl.importModule, repr: "value", mutable: false })
243 } else if row.kind == "selected-import" {
244 let target = match ctx.mods.get(decl.importModule) {
245 case Some(found) => found
246 case None => return Err(refusal("import of {decl.importModule}", decl))
247 }
248 match modBind(target, row.source) {
249 case Some(bind) => if bind.kind == "fn" {
250 binds.push(Bind { ...bind, name: row.bound })
251 } else if has(target.exports, row.source) {
252 binds.push(Bind { name: row.bound, kind: "global", rust: newCell(ctx, "value"), repr: "value", mutable: false })
253 }
254 case None => ()
255 }
256 } else {
257 return Err(refusal("import form {row.kind}", decl))
258 }
259 }
260 }
261 }
262 let info = ModInfo { identity: module.identity, cell: newCell(ctx, "value"), binds: binds, exports: exported, generatedStd: module.generatedStd }
263 let mut mods = ctx.mods
264 mods.insert(module.identity, info)
265 Ok(info)
268function enterModule(ctx: Ctx, info: ModInfo) -> () {
269 restoreScope(ctx, 0)
270 for bind in info.binds {
271 pushBind(ctx, bind)
272 }
273 ()
276function findBind(info: ModInfo, name: string, op: Stage1Operation) -> Result<Bind, string> {
277 match modBind(info, name) {
278 case Some(bind) => Ok(bind)
279 case None => Err(refusal("module binding {name}", op))
280 }
283function fnOf(ctx: Ctx, key: string, op: Stage1Operation) -> Result<FnInfo, string> {
284 match ctx.fns.get(key) {
285 case Some(info) => Ok(info)
286 case None => Err(refusal("ir function {key}", op))
287 }
290// The boxed value of a module binding, for export records.
291function exportValue(ctx: Ctx, bind: Bind, op: Stage1Operation) -> Result<string, string> {
292 let name = rustStr(bind.name)
293 if bind.kind == "fn" {
294 let info = fnOf(ctx, bind.rust, op)?
295 Ok("top_cell_value(&g.{info.cell}, {name})?")
296 } else if bind.kind == "global" && isCopyRepr(bind.repr) {
297 Ok(boxed("__cell_get(&g.{bind.rust}, {name}, Span::new(FileId(0), 0, 0))?", bind.repr))
298 } else if bind.kind == "global" {
299 Ok("top_cell_value(&g.{bind.rust}, {name})?")
300 } else {
301 Err(refusal("export of {bind.kind} binding {bind.name}", op))
302 }
305function exportRecord(ctx: Ctx, info: ModInfo, root: Stage1Operation) -> Result<string, string> {
306 let mut fields: Array<string> = []
307 for name in info.exports {
308 let bind = findBind(info, name, root)?
309 fields.push("({rustStr(name)}.to_string(), {exportValue(ctx, bind, root)?})")
310 }
311 Ok("Value::record([{fields.join(", ")}])")
314// The typed body function and the boxed adapter of one module function.
315function emitFunction(ctx: Ctx, info: FnInfo, modInfo: ModInfo) -> Result<(), string> {
316 let decl = opAt(ctx.table, info.id)?
317 enterModule(ctx, modInfo)
318 let fnCtx = Ctx { ...ctx, fnRet: info.retRepr, inConst: false, atModuleTop: false, loops: [], defers: [] }
319 let defaults = defaultValues(ctx, info.defaultIds, info.variadic)?
320 let mut params: Array<string> = []
321 let mut entry: Array<string> = []
322 let mut position = 0
323 for name in info.paramNames {
324 let rust = rustName(name)
325 params.push(", {rust}: {rustType(info.paramReprs[position])}")
326 let repr = if info.paramReprs[position].startsWith("vec:") { "value" } else { info.paramReprs[position] }
327 if repr == "value" && info.paramReprs[position].startsWith("vec:") {
328 entry.push("let {rust} = {boxed(rust, info.paramReprs[position])};")
329 }
330 pushBind(ctx, Bind { name: name, kind: "local", rust: rust, repr: repr, mutable: false })
331 position += 1
332 }
333 let body = emitExpr(fnCtx, child(ctx.table, decl, "body")?)?
334 let result = if body.repr == "never" {
335 body.rs
336 } else {
337 "Ok({bare(into(body, info.retRepr, decl)?)})"
338 }
339 let mut items = ctx.items
340 items.push(
341 "async fn {info.body}(cx: RtCx, g: Rc<__G>{params.join("")}) -> Result<{rustType(info.retRepr)}, RtError> \{\n let _ = &cx;\n let _ = &g;\n {entry.join(" ")}\n {result}\n\}\n",
342 )
343 let mut prologue: Array<string> = []
344 let mut passed: Array<string> = ["cx", "g"]
345 if info.paramNames.length > 0 {
346 prologue.push("let mut __args = args.into_iter();")
347 } else {
348 prologue.push("let _ = args;")
349 }
350 position = 0
351 for name in info.paramNames {
352 let value = if info.paramReprs[position].startsWith("vec:") {
353 restArgument(info.paramReprs[position], info.paramSpans[position])
354 } else { argumentGuard("__args.next().expect(\"arity checked at the call site\")", info.paramReprs[position], info.paramSpans[position]) }
355 prologue.push("let __a{position} = {value};")
356 passed.push("__a{position}")
357 position += 1
358 }
359 let mut quoted = quotedAll(info.paramNames)
360 if info.variadic { let _ = quoted.pop() }
361 let returned = boxed("{info.body}({passed.join(", ")}).await?", info.retRepr)
362 items.push(
363 "fn {info.adapter}(g: &Rc<__G>) -> Value \{\n let __cap_g = g.clone();\n Value::Closure(Rc::new(EmittedClosure \{ call: move |cx: RtCx, args: Vec<Value>| -> CallFuture \{ let g = __cap_g.clone(); Box::pin(async move \{ {prologue.join(" ")} Ok({bare(returned)}) \}) \}, params: &[{quoted.join(", ")}], defaults: {defaults}, variadic: {info.variadic} \}))\n\}\n",
364 )
365 Ok(())
368function quotedAll(names: Array<string>) -> Array<string> {
369 let mut out: Array<string> = []
370 for name in names {
371 out.push(rustStr(name))
372 }
373 out
376function importsOf(items: Array<Item>) -> Array<Item> {
377 let mut out: Array<Item> = []
378 for item in items {
379 if item.decl.kind == "import" {
380 out.push(item)
381 }
382 }
383 out
386record ModuleCode {
387 lines: Array<string>,
388 tail: string,
391// A module's top-level code in execution order: hoisted constants, value
392// imports, then its items (functions bind positionally).
393function emitModuleInit(
394 ctx: Ctx,
395 module: Stage1Module,
396 root: Stage1Operation,
397 info: ModInfo,
398) -> Result<ModuleCode, string> {
399 enterModule(ctx, info)
400 let topCtx = Ctx { ...ctx, fnRet: "", inConst: false, atModuleTop: true }
401 let constCtx = Ctx { ...topCtx, inConst: true }
402 let items = itemsOf(ctx.table, root)?
403 let mut lines: Array<string> = []
404 for item in items {
405 if item.decl.kind == "constant" {
406 lines.push(emitStatement(constCtx, item.decl)?)
407 }
408 }
409 for item in importsOf(items) {
410 let target = match ctx.mods.get(item.decl.importModule) {
411 case Some(found) => found
412 case None => return Err(refusal("import of {item.decl.importModule}", item.decl))
413 }
414 for row in importRows(item.decl)? {
415 if row.kind == "selected-import" {
416 match modBind(info, row.bound) {
417 case Some(bind) => if bind.kind == "global" {
418 let read = "member_value_required(&top_cell_get(&g.{target.cell}, {rustStr(target.identity)}, {spanOf(item.decl)})?, {rustStr(row.source)}, {spanOf(item.decl)})?"
419 lines.push("top_cell_set(&g.{bind.rust}, {read});")
420 }
421 case None => ()
422 }
423 }
424 }
425 }
426 let mut tail = "Value::Unit"
427 for item in items {
428 if item.decl.kind == "implementation" { lines.push(emitImplementation(topCtx, item.decl)?) }
429 }
430 let mut position = 0
431 for item in items {
432 let decl = item.decl
433 let last = position == items.length - 1
434 if decl.kind == "function" {
435 let declared = fnOf(ctx, decl.id, decl)?
436 lines.push("top_cell_set(&g.{declared.cell}, {declared.adapter}(&g));")
437 } else if has(["constant", "import", "type-alias", "record", "enum", "newtype", "protocol", "implementation"], decl.kind) {
438 ()
439 } else if decl.kind == "expression/block" && module.entry && last {
440 let expression = child(ctx.table, decl, "value")?
441 // Module-level for remains a statement at the last item as well.
442 if expression.kind == "expression/for" {
443 lines.push(emitStatement(topCtx, decl)?)
444 } else {
445 let value = emitExpr(topCtx, expression)?
446 tail = bare(into(value, "value", decl)?)
447 }
448 } else {
449 lines.push(emitStatement(topCtx, decl)?)
450 }
451 position += 1
452 }
453 Ok(ModuleCode { lines: lines, tail: tail })
456function chainTo(modules: Array<Stage1Module>, edges: Array<Array<string>>, entry: string, target: string) -> string {
457 let mut queue: Array<string> = [entry]
458 let mut previous: Map<string, string> = Map.new()
459 let mut head = 0
460 while head < queue.length {
461 let current = queue[head]
462 head += 1
463 if current == target {
464 let mut chain: Array<string> = [current]
465 let mut at = current
466 while previous.containsKey(at) {
467 at = previous.get(at) ?? ""
468 chain.push(at)
469 }
470 chain.reverse()
471 return "import chain: " + chain.join(" -> ")
472 }
473 for edge in edges {
474 if edge[0] == current && !previous.containsKey(edge[1]) && edge[1] != entry {
475 previous.insert(edge[1], current)
476 queue.push(edge[1])
477 }
478 }
479 }
480 "imported from `{entry}`"
483function sortedModules(output: LoweringOutput) -> Array<Stage1Module> {
484 let mut out: Array<Stage1Module> = []
485 let mut ordinal = 0
486 while out.length < output.modules.length {
487 for module in output.modules {
488 if module.initializationOrdinal == ordinal {
489 out.push(module)
490 }
491 }
492 ordinal += 1
493 if ordinal > output.modules.length + 1 {
494 return out
495 }
496 }
497 out
500export function emitProgram(output: LoweringOutput) -> Result<string, string> {
501 let table = buildTable(output)
502 let ctx = Ctx {
503 table: table,
504 scope: [],
505 fns: Map.new(),
506 mods: Map.new(),
507 cellDecls: [],
508 items: [],
509 fnRet: "",
510 inConst: false,
511 atModuleTop: false,
512 loops: [],
513 defers: [],
514 }
515 let modules = sortedModules(output)
516 if modules.length != output.modules.length {
517 return Err("unsupported: module initialization order")
518 }
519 let mut roots: Array<Stage1Operation> = []
520 let mut infos: Array<ModInfo> = []
521 let mut entryIndex = -1
522 let mut position = 0
523 for module in modules {
524 if module.extern {
525 return Err("unsupported: extern module {module.identity}")
526 }
527 if module.operationIds.length == 0 {
528 return Err("unsupported: empty module {module.identity}")
529 }
530 let root = opAt(table, module.operationIds[0])?
531 roots.push(root)
532 infos.push(registerModule(ctx, module, root)?)
533 if module.entry {
534 entryIndex = position
535 }
536 position += 1
537 }
538 if entryIndex < 0 {
539 return Err("unsupported: no entry module")
540 }
541 let entryModule = modules[entryIndex]
542 let entryRoot = roots[entryIndex]
543 let entryInfo = infos[entryIndex]
545 // Import edges in module source order, for the initialization-fault chain.
546 let mut edges: Array<Array<string>> = []
547 let mut ordinal = 0
548 while ordinal < modules.length {
549 let mut index = 0
550 for module in modules {
551 if module.sourceOrdinal == ordinal {
552 for id in roots[index].operands {
553 let item = opAt(table, id)?
554 if item.kind == "import" {
555 edges.push([module.identity, item.importModule])
556 }
557 }
558 }
559 index += 1
560 }
561 ordinal += 1
562 }
564 position = 0
565 for module in modules {
566 for bind in infos[position].binds {
567 if bind.kind == "fn" {
568 let declaration = opAt(table, bind.rust)?
569 if declaration.module != module.identity {
570 continue
571 }
572 emitFunction(ctx, fnOf(ctx, bind.rust, roots[position])?, infos[position])?
573 }
574 }
575 position += 1
576 }
578 let mut body: Array<string> = []
579 position = 0
580 for module in modules {
581 let code = emitModuleInit(ctx, module, roots[position], infos[position])?
582 if module.entry {
583 for line in code.lines {
584 body.push(" {line}")
585 }
586 body.push(" let __topaz_init_value = {code.tail};")
587 } else {
588 let record = exportRecord(ctx, infos[position], roots[position])?
589 let suffix = "(during initialization of module `{module.identity}`; {chainTo(modules, edges, entryModule.identity, module.identity)})"
590 body.push(" match (async \{")
591 for line in code.lines {
592 body.push(" {line}")
593 }
594 body.push(" top_cell_set(&g.{infos[position].cell}, {record});")
595 body.push(" Ok::<(), RtError>(())")
596 body.push(" \}).await \{ Ok(()) => \{\}, Err(__e) => return Err(RtError \{ code: __e.code, message: format!(\"\{\} \{\}\", __e.message, {rustStr(suffix)}), span: __e.span \}), \}")
597 }
598 position += 1
599 }
601 let facts = projectExports(table, entryRoot)?
602 let explicitMain = hasExplicitMain(facts)
603 let mut entryValue = "__topaz_init_value"
604 let mut mainSpan = "Span::new(FileId(0), 0, 0)"
605 if explicitMain {
606 let bind = findBind(entryInfo, "main", entryRoot)?
607 let info = fnOf(ctx, bind.rust, entryRoot)?
608 mainSpan = declarationSpan(opAt(table, info.id)?)
609 entryValue = "top_cell_get(&g.{info.cell}, \"main\", {mainSpan})?"
610 }
611 let record = exportRecord(ctx, entryInfo, entryRoot)?
612 let exportNames = quotedAll(entryInfo.exports).join(", ")
614 let mut out: Array<string> = []
615 out.push("// @generated by the Topaz self emitter; do not edit.\n")
616 out.push("#![allow(non_snake_case)]\n")
617 out.push("use std::rc::Rc;\nuse topaz_rt::*;\n\n")
618 out.push("pub const TOPAZ_EXPLICIT_MAIN: bool = {explicitMain};\n\n")
619 out.push("pub fn topaz_export_names() -> &'static [&'static str] \{\n &[{exportNames}]\n\}\n")
620 out.push(CONTRACT)
621 let bodyText = body.join("\n")
622 let itemText = ctx.items.join("\n")
623 let mut fields: Array<string> = []
624 let mut inits: Array<string> = []
625 if bodyText.contains("g.methods") || itemText.contains("g.methods") {
626 fields.push(" methods: std::cell::RefCell<std::collections::HashMap<(String, String), Value>>,\n")
627 inits.push("methods: Default::default()")
628 }
629 if bodyText.contains("g.protocols") || itemText.contains("g.protocols") {
630 fields.push(" protocols: std::cell::RefCell<std::collections::HashMap<(String, String, String, String), Value>>,\n")
631 inits.push("protocols: Default::default()")
632 }
633 for decl in ctx.cellDecls {
634 let parts = decl.split("|")
635 fields.push(" {parts[0]},\n")
636 inits.push("{parts[0].split(":")[0]}: {parts[1]}")
637 }
638 out.push("\nstruct __G \{\n{fields.join("")}\}\n\n")
639 if bodyText.contains("__cell_get(") || itemText.contains("__cell_get(") || record.contains("__cell_get(") {
640 out.push(CELL_HELPER)
641 }
642 if bodyText.contains("__cg(") || itemText.contains("__cg(") {
643 out.push(CONST_HELPER)
644 }
645 out.push("\n")
646 out.push(itemText)
647 if explicitMain {
648 out.push("\nasync fn entry(cx: RtCx, __topaz_args: Value, __topaz_stdin: Value) -> Result<Value, RtError> \{\n let (__topaz_entry_value, _) = __topaz_initialize(cx.clone()).await?;\n call_value(__topaz_entry_value, vec![__topaz_args, __topaz_stdin], cx, {mainSpan}).await\n\}\n")
649 } else {
650 out.push("\nasync fn entry(cx: RtCx, __topaz_args: Value, __topaz_stdin: Value) -> Result<Value, RtError> \{\n let _ = (&__topaz_args, &__topaz_stdin);\n let (__topaz_entry_value, _) = __topaz_initialize(cx).await?;\n Ok(__topaz_entry_value)\n\}\n")
651 }
652 out.push("\nasync fn __topaz_initialize(cx: RtCx) -> Result<(Value, Value), RtError> \{\n let _ = &cx;\n")
653 out.push(" let g = Rc::new(__G \{ {inits.join(", ")} \});\n let _ = &g;\n")
654 out.push(bodyText)
655 out.push("\n let __topaz_entry_value = {entryValue};\n Ok((__topaz_entry_value, {record}))\n\}\n")
656 Ok(out.join(""))

SHA256 82146b611354801641b6d867c4de6f961f0771efdd5ab47152a8fc38bc1414f2