1use std::collections::{HashMap, HashSet};
65use std::fmt::Write;
66
67use crate::analysis::registry::{FieldDef, FieldType, TypeDef, TypeRegistry, VariantDef};
68use crate::analysis::policy::{PolicyRegistry, PredicateDef, CapabilityDef, PolicyCondition};
69use crate::ast::logic::{LogicExpr, NumberKind, Term};
70use crate::ast::stmt::{BinaryOpKind, Expr, Literal, ReadSource, Stmt, TypeExpr};
71use crate::formatter::RustFormatter;
72use crate::intern::{Interner, Symbol};
73use crate::registry::SymbolRegistry;
74
75pub struct RefinementContext<'a> {
107 scopes: Vec<HashMap<Symbol, (Symbol, &'a LogicExpr<'a>)>>,
109
110 variable_types: HashMap<Symbol, String>,
114
115 boxed_binding_scopes: Vec<HashSet<Symbol>>,
118
119 string_vars: HashSet<Symbol>,
122}
123
124impl<'a> RefinementContext<'a> {
125 pub fn new() -> Self {
126 Self {
127 scopes: vec![HashMap::new()],
128 variable_types: HashMap::new(),
129 boxed_binding_scopes: vec![HashSet::new()],
130 string_vars: HashSet::new(),
131 }
132 }
133
134 fn push_scope(&mut self) {
135 self.scopes.push(HashMap::new());
136 self.boxed_binding_scopes.push(HashSet::new());
137 }
138
139 fn pop_scope(&mut self) {
140 self.scopes.pop();
141 self.boxed_binding_scopes.pop();
142 }
143
144 fn register_boxed_binding(&mut self, var: Symbol) {
147 if let Some(scope) = self.boxed_binding_scopes.last_mut() {
148 scope.insert(var);
149 }
150 }
151
152 fn is_boxed_binding(&self, var: Symbol) -> bool {
154 for scope in self.boxed_binding_scopes.iter().rev() {
155 if scope.contains(&var) {
156 return true;
157 }
158 }
159 false
160 }
161
162 fn register_string_var(&mut self, var: Symbol) {
165 self.string_vars.insert(var);
166 }
167
168 fn is_string_var(&self, var: Symbol) -> bool {
170 self.string_vars.contains(&var)
171 }
172
173 fn get_string_vars(&self) -> &HashSet<Symbol> {
175 &self.string_vars
176 }
177
178 fn register(&mut self, var: Symbol, bound_var: Symbol, predicate: &'a LogicExpr<'a>) {
179 if let Some(scope) = self.scopes.last_mut() {
180 scope.insert(var, (bound_var, predicate));
181 }
182 }
183
184 fn get_constraint(&self, var: Symbol) -> Option<(Symbol, &'a LogicExpr<'a>)> {
185 for scope in self.scopes.iter().rev() {
186 if let Some(entry) = scope.get(&var) {
187 return Some(*entry);
188 }
189 }
190 None
191 }
192
193 fn register_variable_type(&mut self, var: Symbol, type_name: String) {
195 self.variable_types.insert(var, type_name);
196 }
197
198 fn get_variable_types(&self) -> &HashMap<Symbol, String> {
200 &self.variable_types
201 }
202
203 fn find_variable_by_type(&self, type_name: &str, interner: &Interner) -> Option<String> {
205 let type_lower = type_name.to_lowercase();
206 for (var_sym, var_type) in &self.variable_types {
207 if var_type.to_lowercase() == type_lower {
208 return Some(interner.resolve(*var_sym).to_string());
209 }
210 }
211 None
212 }
213}
214
215fn emit_refinement_check(
217 var_name: &str,
218 bound_var: Symbol,
219 predicate: &LogicExpr,
220 interner: &Interner,
221 indent_str: &str,
222 output: &mut String,
223) {
224 let assertion = codegen_assertion(predicate, interner);
225 let bound = interner.resolve(bound_var);
226 let check = if bound == var_name {
227 assertion
228 } else {
229 replace_word(&assertion, bound, var_name)
230 };
231 writeln!(output, "{}debug_assert!({});", indent_str, check).unwrap();
232}
233
234fn replace_word(text: &str, from: &str, to: &str) -> String {
236 let mut result = String::with_capacity(text.len());
237 let mut word = String::new();
238 for c in text.chars() {
239 if c.is_alphanumeric() || c == '_' {
240 word.push(c);
241 } else {
242 if !word.is_empty() {
243 result.push_str(if word == from { to } else { &word });
244 word.clear();
245 }
246 result.push(c);
247 }
248 }
249 if !word.is_empty() {
250 result.push_str(if word == from { to } else { &word });
251 }
252 result
253}
254
255#[derive(Debug, Default)]
276pub struct VariableCapabilities {
277 mounted: bool,
279 synced: bool,
281 mount_path: Option<String>,
283 sync_topic: Option<String>,
285}
286
287pub fn empty_var_caps() -> HashMap<Symbol, VariableCapabilities> {
289 HashMap::new()
290}
291
292fn analyze_variable_capabilities<'a>(
295 stmts: &[Stmt<'a>],
296 interner: &Interner,
297) -> HashMap<Symbol, VariableCapabilities> {
298 let mut caps: HashMap<Symbol, VariableCapabilities> = HashMap::new();
299 let empty_synced = HashSet::new();
300
301 for stmt in stmts {
302 match stmt {
303 Stmt::Mount { var, path } => {
304 let entry = caps.entry(*var).or_default();
305 entry.mounted = true;
306 entry.mount_path = Some(codegen_expr(path, interner, &empty_synced));
307 }
308 Stmt::Sync { var, topic } => {
309 let entry = caps.entry(*var).or_default();
310 entry.synced = true;
311 entry.sync_topic = Some(codegen_expr(topic, interner, &empty_synced));
312 }
313 Stmt::If { then_block, else_block, .. } => {
315 let nested = analyze_variable_capabilities(then_block, interner);
316 for (var, cap) in nested {
317 let entry = caps.entry(var).or_default();
318 if cap.mounted { entry.mounted = true; entry.mount_path = cap.mount_path; }
319 if cap.synced { entry.synced = true; entry.sync_topic = cap.sync_topic; }
320 }
321 if let Some(else_b) = else_block {
322 let nested = analyze_variable_capabilities(else_b, interner);
323 for (var, cap) in nested {
324 let entry = caps.entry(var).or_default();
325 if cap.mounted { entry.mounted = true; entry.mount_path = cap.mount_path; }
326 if cap.synced { entry.synced = true; entry.sync_topic = cap.sync_topic; }
327 }
328 }
329 }
330 Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
331 let nested = analyze_variable_capabilities(body, interner);
332 for (var, cap) in nested {
333 let entry = caps.entry(var).or_default();
334 if cap.mounted { entry.mounted = true; entry.mount_path = cap.mount_path; }
335 if cap.synced { entry.synced = true; entry.sync_topic = cap.sync_topic; }
336 }
337 }
338 _ => {}
339 }
340 }
341
342 caps
343}
344
345fn has_wasm_exports(stmts: &[Stmt], interner: &Interner) -> bool {
348 stmts.iter().any(|stmt| {
349 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
350 interner.resolve(*target).eq_ignore_ascii_case("wasm")
351 } else {
352 false
353 }
354 })
355}
356
357fn has_c_exports(stmts: &[Stmt], interner: &Interner) -> bool {
360 stmts.iter().any(|stmt| {
361 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
362 match export_target {
363 None => true,
364 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
365 }
366 } else {
367 false
368 }
369 })
370}
371
372fn has_c_exports_with_text(stmts: &[Stmt], interner: &Interner) -> bool {
375 stmts.iter().any(|stmt| {
376 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
377 let is_c = match export_target {
378 None => true,
379 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
380 };
381 if !is_c { return false; }
382 let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
383 let has_text_return = return_type.as_ref().map_or(false, |ty| is_text_type(ty, interner));
384 has_text_param || has_text_return
385 } else {
386 false
387 }
388 })
389}
390
391#[derive(Debug, Clone, Copy, PartialEq)]
400pub enum CAbiClass {
401 ValueType,
403 ReferenceType,
405}
406
407fn classify_type_for_c_abi(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> CAbiClass {
412 match ty {
413 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
414 let name = interner.resolve(*sym);
415 match name {
416 "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
417 | "Byte" | "Char" | "Unit" => CAbiClass::ValueType,
418 "Text" | "String" => CAbiClass::ValueType,
419 _ => {
420 if let Some(type_def) = registry.get(*sym) {
422 match type_def {
423 TypeDef::Struct { fields, .. } => {
424 let all_value = fields.iter().all(|f| {
426 is_value_type_field(&f.ty, interner)
427 });
428 if all_value && fields.len() <= 4 {
429 CAbiClass::ValueType
430 } else {
431 CAbiClass::ReferenceType
432 }
433 }
434 TypeDef::Enum { .. } => CAbiClass::ReferenceType,
435 TypeDef::Primitive => CAbiClass::ValueType,
436 TypeDef::Generic { .. } => CAbiClass::ReferenceType,
437 TypeDef::Alias { .. } => CAbiClass::ValueType,
438 }
439 } else {
440 CAbiClass::ValueType }
442 }
443 }
444 }
445 TypeExpr::Refinement { base, .. } => classify_type_for_c_abi(base, interner, registry),
446 TypeExpr::Generic { base, .. } => {
447 let base_name = interner.resolve(*base);
448 match base_name {
449 "Option" | "Maybe" => {
450 CAbiClass::ReferenceType
454 }
455 "Result" | "Seq" | "List" | "Vec" | "Map" | "HashMap"
456 | "Set" | "HashSet" => CAbiClass::ReferenceType,
457 _ => CAbiClass::ReferenceType,
458 }
459 }
460 TypeExpr::Function { .. } => CAbiClass::ReferenceType,
461 TypeExpr::Persistent { .. } => CAbiClass::ReferenceType,
462 }
463}
464
465fn is_value_type_field(ft: &FieldType, interner: &Interner) -> bool {
467 match ft {
468 FieldType::Primitive(sym) | FieldType::Named(sym) => {
469 let name = interner.resolve(*sym);
470 matches!(name, "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
471 | "Byte" | "Char" | "Unit")
472 }
474 FieldType::Generic { .. } => false, FieldType::TypeParam(_) => false,
476 }
477}
478
479fn is_rust_keyword(name: &str) -> bool {
481 matches!(name,
482 "as" | "async" | "await" | "break" | "const" | "continue" | "crate" |
483 "dyn" | "else" | "enum" | "extern" | "false" | "fn" | "for" | "if" |
484 "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut" |
485 "pub" | "ref" | "return" | "self" | "Self" | "static" | "struct" |
486 "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" |
487 "while" | "abstract" | "become" | "box" | "do" | "final" | "macro" |
488 "override" | "priv" | "try" | "typeof" | "unsized" | "virtual" | "yield"
489 )
490}
491
492fn escape_rust_ident(name: &str) -> String {
495 if is_rust_keyword(name) {
496 format!("r#{}", name)
497 } else {
498 name.to_string()
499 }
500}
501
502fn mangle_type_for_c(ty: &TypeExpr, interner: &Interner) -> String {
505 match ty {
506 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
507 let name = interner.resolve(*sym);
508 match name {
509 "Int" => "i64".to_string(),
510 "Nat" => "u64".to_string(),
511 "Real" | "Float" => "f64".to_string(),
512 "Bool" | "Boolean" => "bool".to_string(),
513 "Byte" => "u8".to_string(),
514 "Char" => "char".to_string(),
515 "Text" | "String" => "string".to_string(),
516 other => other.to_lowercase(),
517 }
518 }
519 TypeExpr::Refinement { base, .. } => mangle_type_for_c(base, interner),
520 TypeExpr::Generic { base, params } => {
521 let base_name = interner.resolve(*base);
522 let param_strs: Vec<String> = params.iter()
523 .map(|p| mangle_type_for_c(p, interner))
524 .collect();
525 match base_name {
526 "Seq" | "List" | "Vec" => format!("seq_{}", param_strs.join("_")),
527 "Map" | "HashMap" => format!("map_{}", param_strs.join("_")),
528 "Set" | "HashSet" => format!("set_{}", param_strs.join("_")),
529 "Option" | "Maybe" => format!("option_{}", param_strs.join("_")),
530 "Result" => format!("result_{}", param_strs.join("_")),
531 other => format!("{}_{}", other.to_lowercase(), param_strs.join("_")),
532 }
533 }
534 TypeExpr::Function { .. } => "fn".to_string(),
535 TypeExpr::Persistent { inner } => mangle_type_for_c(inner, interner),
536 }
537}
538
539fn codegen_logos_runtime_preamble() -> String {
547 let mut out = String::new();
548
549 writeln!(out, "// ═══ LogicAffeine Universal ABI Runtime ═══\n").unwrap();
550
551 writeln!(out, "#[repr(C)]").unwrap();
553 writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq)]").unwrap();
554 writeln!(out, "pub enum LogosStatus {{").unwrap();
555 writeln!(out, " Ok = 0,").unwrap();
556 writeln!(out, " Error = 1,").unwrap();
557 writeln!(out, " RefinementViolation = 2,").unwrap();
558 writeln!(out, " NullPointer = 3,").unwrap();
559 writeln!(out, " OutOfBounds = 4,").unwrap();
560 writeln!(out, " DeserializationFailed = 5,").unwrap();
561 writeln!(out, " InvalidHandle = 6,").unwrap();
562 writeln!(out, " ContainsNullByte = 7,").unwrap();
563 writeln!(out, " ThreadPanic = 8,").unwrap();
564 writeln!(out, " MemoryExhausted = 9,").unwrap();
565 writeln!(out, "}}\n").unwrap();
566
567 writeln!(out, "pub type LogosHandle = *mut std::ffi::c_void;\n").unwrap();
569
570 writeln!(out, "fn logos_error_store() -> &'static std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>> {{").unwrap();
572 writeln!(out, " use std::sync::OnceLock;").unwrap();
573 writeln!(out, " static STORE: OnceLock<std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>>> = OnceLock::new();").unwrap();
574 writeln!(out, " STORE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))").unwrap();
575 writeln!(out, "}}\n").unwrap();
576
577 writeln!(out, "fn logos_set_last_error(msg: String) {{").unwrap();
579 writeln!(out, " let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
580 writeln!(out, " store.insert(std::thread::current().id(), msg);").unwrap();
581 writeln!(out, "}}\n").unwrap();
582
583 writeln!(out, "thread_local! {{").unwrap();
587 writeln!(out, " static LOGOS_ERROR_CACHE: std::cell::RefCell<Option<std::ffi::CString>> = std::cell::RefCell::new(None);").unwrap();
588 writeln!(out, "}}\n").unwrap();
589
590 writeln!(out, "#[no_mangle]").unwrap();
591 writeln!(out, "pub extern \"C\" fn logos_last_error() -> *const std::os::raw::c_char {{").unwrap();
592 writeln!(out, " let msg = logos_error_store().lock().unwrap_or_else(|e| e.into_inner())").unwrap();
593 writeln!(out, " .get(&std::thread::current().id()).cloned();").unwrap();
594 writeln!(out, " match msg {{").unwrap();
595 writeln!(out, " Some(s) => match std::ffi::CString::new(s) {{").unwrap();
596 writeln!(out, " Ok(cstr) => {{").unwrap();
597 writeln!(out, " let ptr = cstr.as_ptr();").unwrap();
598 writeln!(out, " LOGOS_ERROR_CACHE.with(|cache| {{ cache.borrow_mut().replace(cstr); }});").unwrap();
599 writeln!(out, " LOGOS_ERROR_CACHE.with(|cache| {{").unwrap();
600 writeln!(out, " cache.borrow().as_ref().map_or(std::ptr::null(), |c| c.as_ptr())").unwrap();
601 writeln!(out, " }})").unwrap();
602 writeln!(out, " }}").unwrap();
603 writeln!(out, " Err(_) => std::ptr::null(),").unwrap();
604 writeln!(out, " }}").unwrap();
605 writeln!(out, " None => std::ptr::null(),").unwrap();
606 writeln!(out, " }}").unwrap();
607 writeln!(out, "}}\n").unwrap();
608
609 writeln!(out, "#[no_mangle]").unwrap();
611 writeln!(out, "pub extern \"C\" fn logos_get_last_error() -> *const std::os::raw::c_char {{").unwrap();
612 writeln!(out, " logos_last_error()").unwrap();
613 writeln!(out, "}}\n").unwrap();
614
615 writeln!(out, "#[no_mangle]").unwrap();
617 writeln!(out, "pub extern \"C\" fn logos_clear_error() {{").unwrap();
618 writeln!(out, " let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
619 writeln!(out, " store.remove(&std::thread::current().id());").unwrap();
620 writeln!(out, "}}\n").unwrap();
621
622 writeln!(out, "#[no_mangle]").unwrap();
624 writeln!(out, "pub extern \"C\" fn logos_free_string(ptr: *mut std::os::raw::c_char) {{").unwrap();
625 writeln!(out, " if !ptr.is_null() {{").unwrap();
626 writeln!(out, " unsafe {{ drop(std::ffi::CString::from_raw(ptr)); }}").unwrap();
627 writeln!(out, " }}").unwrap();
628 writeln!(out, "}}\n").unwrap();
629
630 writeln!(out, "pub const LOGOS_ABI_VERSION: u32 = 1;\n").unwrap();
632
633 writeln!(out, "#[no_mangle]").unwrap();
634 writeln!(out, "pub extern \"C\" fn logos_version() -> *const std::os::raw::c_char {{").unwrap();
635 writeln!(out, " concat!(env!(\"CARGO_PKG_VERSION\"), \"\\0\").as_ptr() as *const std::os::raw::c_char").unwrap();
636 writeln!(out, "}}\n").unwrap();
637
638 writeln!(out, "#[no_mangle]").unwrap();
639 writeln!(out, "pub extern \"C\" fn logos_abi_version() -> u32 {{").unwrap();
640 writeln!(out, " LOGOS_ABI_VERSION").unwrap();
641 writeln!(out, "}}\n").unwrap();
642
643 writeln!(out, "struct HandleEntry {{").unwrap();
645 writeln!(out, " data: usize,").unwrap();
646 writeln!(out, " generation: u64,").unwrap();
647 writeln!(out, "}}\n").unwrap();
648
649 writeln!(out, "struct HandleRegistry {{").unwrap();
650 writeln!(out, " entries: std::collections::HashMap<u64, HandleEntry>,").unwrap();
651 writeln!(out, " counter: u64,").unwrap();
652 writeln!(out, "}}\n").unwrap();
653
654 writeln!(out, "impl HandleRegistry {{").unwrap();
655 writeln!(out, " fn new() -> Self {{").unwrap();
656 writeln!(out, " HandleRegistry {{ entries: std::collections::HashMap::new(), counter: 0 }}").unwrap();
657 writeln!(out, " }}").unwrap();
658 writeln!(out, " fn register(&mut self, ptr: usize) -> (u64, u64) {{").unwrap();
659 writeln!(out, " self.counter += 1;").unwrap();
660 writeln!(out, " let id = self.counter;").unwrap();
661 writeln!(out, " let generation = id;").unwrap();
662 writeln!(out, " self.entries.insert(id, HandleEntry {{ data: ptr, generation }});").unwrap();
663 writeln!(out, " (id, generation)").unwrap();
664 writeln!(out, " }}").unwrap();
665 writeln!(out, " fn validate_handle(&self, id: u64, generation: u64) -> bool {{").unwrap();
666 writeln!(out, " self.entries.get(&id).map_or(false, |e| e.generation == generation)").unwrap();
667 writeln!(out, " }}").unwrap();
668 writeln!(out, " fn deref(&self, id: u64) -> Option<usize> {{").unwrap();
669 writeln!(out, " self.entries.get(&id).map(|e| e.data)").unwrap();
670 writeln!(out, " }}").unwrap();
671 writeln!(out, " fn free(&mut self, id: u64) -> Result<usize, ()> {{").unwrap();
672 writeln!(out, " if let Some(entry) = self.entries.remove(&id) {{ Ok(entry.data) }} else {{ Err(()) }}").unwrap();
673 writeln!(out, " }}").unwrap();
674 writeln!(out, "}}\n").unwrap();
675
676 writeln!(out, "fn logos_handle_registry() -> &'static std::sync::Mutex<HandleRegistry> {{").unwrap();
677 writeln!(out, " use std::sync::OnceLock;").unwrap();
678 writeln!(out, " static REGISTRY: OnceLock<std::sync::Mutex<HandleRegistry>> = OnceLock::new();").unwrap();
679 writeln!(out, " REGISTRY.get_or_init(|| std::sync::Mutex::new(HandleRegistry::new()))").unwrap();
680 writeln!(out, "}}\n").unwrap();
681
682 out
683}
684
685fn emit_catch_unwind_open(out: &mut String) {
687 writeln!(out, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
688}
689
690fn emit_catch_unwind_close(out: &mut String, default_expr: &str) {
693 writeln!(out, " }})) {{").unwrap();
694 writeln!(out, " Ok(__v) => __v,").unwrap();
695 writeln!(out, " Err(__panic) => {{").unwrap();
696 writeln!(out, " let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
697 writeln!(out, " logos_set_last_error(__msg);").unwrap();
698 writeln!(out, " {}", default_expr).unwrap();
699 writeln!(out, " }}").unwrap();
700 writeln!(out, " }}").unwrap();
701}
702
703fn emit_null_handle_check(out: &mut String, default_expr: &str) {
705 writeln!(out, " if handle.is_null() {{ logos_set_last_error(\"NullPointer: handle is null\".to_string()); return {}; }}", default_expr).unwrap();
706}
707
708fn emit_null_out_check(out: &mut String, default_expr: &str) {
710 writeln!(out, " if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return {}; }}", default_expr).unwrap();
711}
712
713fn emit_registry_deref(out: &mut String, default_expr: &str) {
715 writeln!(out, " let __id = handle as u64;").unwrap();
716 writeln!(out, " let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
717 writeln!(out, " let __ptr = match __reg.deref(__id) {{").unwrap();
718 writeln!(out, " Some(p) => p,").unwrap();
719 writeln!(out, " None => {{ logos_set_last_error(\"InvalidHandle: handle not found in registry\".to_string()); return {}; }}", default_expr).unwrap();
720 writeln!(out, " }};").unwrap();
721 writeln!(out, " drop(__reg);").unwrap();
722}
723
724fn emit_registry_create(out: &mut String, alloc_expr: &str, _rust_type: &str) {
727 writeln!(out, " let __data = {};", alloc_expr).unwrap();
728 writeln!(out, " let __ptr = Box::into_raw(Box::new(__data)) as usize;").unwrap();
729 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
730 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
731 writeln!(out, " __id as LogosHandle").unwrap();
732}
733
734fn emit_registry_free(out: &mut String, rust_type: &str) {
737 writeln!(out, " if handle.is_null() {{ return; }}").unwrap();
738 writeln!(out, " let __id = handle as u64;").unwrap();
739 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
740 writeln!(out, " match __reg.free(__id) {{").unwrap();
741 writeln!(out, " Ok(__ptr) => {{ unsafe {{ drop(Box::from_raw(__ptr as *mut {})); }} }}", rust_type).unwrap();
742 writeln!(out, " Err(()) => {{ logos_set_last_error(\"InvalidHandle: handle already freed or not found\".to_string()); }}").unwrap();
743 writeln!(out, " }}").unwrap();
744}
745
746fn codegen_c_accessors(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
749 let mut out = String::new();
750 let mangled = mangle_type_for_c(ty, interner);
751
752 match ty {
753 TypeExpr::Generic { base, params } => {
754 let base_name = interner.resolve(*base);
755 match base_name {
756 "Seq" | "List" | "Vec" if !params.is_empty() => {
757 let inner_rust_type = codegen_type_expr(¶ms[0], interner);
758 let _inner_mangled = mangle_type_for_c(¶ms[0], interner);
759 let is_inner_text = is_text_type(¶ms[0], interner);
760 let vec_type = format!("Vec<{}>", inner_rust_type);
761
762 writeln!(out, "#[no_mangle]").unwrap();
764 writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
765 emit_catch_unwind_open(&mut out);
766 emit_null_handle_check(&mut out, "0");
767 emit_registry_deref(&mut out, "0");
768 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
769 writeln!(out, " seq.len()").unwrap();
770 emit_catch_unwind_close(&mut out, "0");
771 writeln!(out, "}}\n").unwrap();
772
773 if is_inner_text {
775 writeln!(out, "#[no_mangle]").unwrap();
776 writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize) -> *mut std::os::raw::c_char {{", mangled).unwrap();
777 emit_catch_unwind_open(&mut out);
778 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
779 emit_registry_deref(&mut out, "std::ptr::null_mut()");
780 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
781 writeln!(out, " if index >= seq.len() {{").unwrap();
782 writeln!(out, " logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
783 writeln!(out, " return std::ptr::null_mut();").unwrap();
784 writeln!(out, " }}").unwrap();
785 writeln!(out, " match std::ffi::CString::new(seq[index].clone()) {{").unwrap();
786 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
787 writeln!(out, " Err(_) => {{ logos_set_last_error(\"String contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
788 writeln!(out, " }}").unwrap();
789 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
790 writeln!(out, "}}\n").unwrap();
791 } else {
792 writeln!(out, "#[no_mangle]").unwrap();
793 writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
794 emit_catch_unwind_open(&mut out);
795 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
796 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
797 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
798 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
799 writeln!(out, " if index >= seq.len() {{").unwrap();
800 writeln!(out, " logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
801 writeln!(out, " return LogosStatus::OutOfBounds;").unwrap();
802 writeln!(out, " }}").unwrap();
803 writeln!(out, " unsafe {{ *out = seq[index].clone(); }}").unwrap();
804 writeln!(out, " LogosStatus::Ok").unwrap();
805 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
806 writeln!(out, "}}\n").unwrap();
807 }
808
809 writeln!(out, "#[no_mangle]").unwrap();
811 writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
812 emit_catch_unwind_open(&mut out);
813 emit_registry_create(&mut out, &format!("Vec::<{}>::new()", inner_rust_type), &vec_type);
814 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
815 writeln!(out, "}}\n").unwrap();
816
817 if is_inner_text {
819 writeln!(out, "#[no_mangle]").unwrap();
820 writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
821 emit_catch_unwind_open(&mut out);
822 emit_null_handle_check(&mut out, "()");
823 emit_registry_deref(&mut out, "()");
824 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
825 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
826 writeln!(out, " seq.push(val_str);").unwrap();
827 emit_catch_unwind_close(&mut out, "()");
828 writeln!(out, "}}\n").unwrap();
829 } else {
830 writeln!(out, "#[no_mangle]").unwrap();
831 writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
832 emit_catch_unwind_open(&mut out);
833 emit_null_handle_check(&mut out, "()");
834 emit_registry_deref(&mut out, "()");
835 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
836 writeln!(out, " seq.push(value);").unwrap();
837 emit_catch_unwind_close(&mut out, "()");
838 writeln!(out, "}}\n").unwrap();
839 }
840
841 if is_inner_text {
843 writeln!(out, "#[no_mangle]").unwrap();
844 writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
845 emit_catch_unwind_open(&mut out);
846 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
847 emit_registry_deref(&mut out, "std::ptr::null_mut()");
848 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
849 writeln!(out, " match seq.pop() {{").unwrap();
850 writeln!(out, " Some(val) => match std::ffi::CString::new(val) {{").unwrap();
851 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
852 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
853 writeln!(out, " }},").unwrap();
854 writeln!(out, " None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); std::ptr::null_mut() }}").unwrap();
855 writeln!(out, " }}").unwrap();
856 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
857 writeln!(out, "}}\n").unwrap();
858 } else {
859 writeln!(out, "#[no_mangle]").unwrap();
860 writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
861 emit_catch_unwind_open(&mut out);
862 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
863 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
864 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
865 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
866 writeln!(out, " match seq.pop() {{").unwrap();
867 writeln!(out, " Some(val) => {{ unsafe {{ *out = val; }} LogosStatus::Ok }}").unwrap();
868 writeln!(out, " None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); LogosStatus::Error }}").unwrap();
869 writeln!(out, " }}").unwrap();
870 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
871 writeln!(out, "}}\n").unwrap();
872 }
873
874 writeln!(out, "#[no_mangle]").unwrap();
876 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
877 emit_catch_unwind_open(&mut out);
878 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
879 emit_registry_deref(&mut out, "std::ptr::null_mut()");
880 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
881 writeln!(out, " match serde_json::to_string(seq) {{").unwrap();
882 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
883 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
884 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
885 writeln!(out, " }},").unwrap();
886 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
887 writeln!(out, " }}").unwrap();
888 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
889 writeln!(out, "}}\n").unwrap();
890
891 writeln!(out, "#[no_mangle]").unwrap();
893 writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
894 emit_catch_unwind_open(&mut out);
895 writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
896 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
897 writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
898 writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", vec_type).unwrap();
899 writeln!(out, " Ok(val) => {{").unwrap();
900 writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
901 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
902 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
903 writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
904 writeln!(out, " LogosStatus::Ok").unwrap();
905 writeln!(out, " }}").unwrap();
906 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
907 writeln!(out, " }}").unwrap();
908 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
909 writeln!(out, "}}\n").unwrap();
910
911 writeln!(out, "#[no_mangle]").unwrap();
913 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
914 emit_catch_unwind_open(&mut out);
915 emit_registry_free(&mut out, &vec_type);
916 emit_catch_unwind_close(&mut out, "()");
917 writeln!(out, "}}\n").unwrap();
918 }
919
920 "Map" | "HashMap" if params.len() >= 2 => {
921 let key_rust = codegen_type_expr(¶ms[0], interner);
922 let val_rust = codegen_type_expr(¶ms[1], interner);
923 let is_key_text = is_text_type(¶ms[0], interner);
924 let is_val_text = is_text_type(¶ms[1], interner);
925 let map_type = format!("std::collections::HashMap<{}, {}>", key_rust, val_rust);
926
927 writeln!(out, "#[no_mangle]").unwrap();
929 writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
930 emit_catch_unwind_open(&mut out);
931 emit_null_handle_check(&mut out, "0");
932 emit_registry_deref(&mut out, "0");
933 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
934 writeln!(out, " map.len()").unwrap();
935 emit_catch_unwind_close(&mut out, "0");
936 writeln!(out, "}}\n").unwrap();
937
938 if is_key_text {
940 if is_val_text {
941 writeln!(out, "#[no_mangle]").unwrap();
942 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: *const std::os::raw::c_char) -> *mut std::os::raw::c_char {{", mangled).unwrap();
943 emit_catch_unwind_open(&mut out);
944 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
945 emit_registry_deref(&mut out, "std::ptr::null_mut()");
946 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
947 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
948 writeln!(out, " match map.get(&key_str) {{").unwrap();
949 writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
950 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
951 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
952 writeln!(out, " }},").unwrap();
953 writeln!(out, " None => std::ptr::null_mut(),").unwrap();
954 writeln!(out, " }}").unwrap();
955 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
956 writeln!(out, "}}\n").unwrap();
957 } else {
958 writeln!(out, "#[no_mangle]").unwrap();
959 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: *const std::os::raw::c_char, out: *mut {}) -> LogosStatus {{", mangled, val_rust).unwrap();
960 emit_catch_unwind_open(&mut out);
961 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
962 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
963 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
964 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
965 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
966 writeln!(out, " match map.get(&key_str) {{").unwrap();
967 writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
968 writeln!(out, " None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key_str)); LogosStatus::Error }}").unwrap();
969 writeln!(out, " }}").unwrap();
970 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
971 writeln!(out, "}}\n").unwrap();
972 }
973 } else {
974 if is_val_text {
975 writeln!(out, "#[no_mangle]").unwrap();
976 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}) -> *mut std::os::raw::c_char {{", mangled, key_rust).unwrap();
977 emit_catch_unwind_open(&mut out);
978 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
979 emit_registry_deref(&mut out, "std::ptr::null_mut()");
980 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
981 writeln!(out, " match map.get(&key) {{").unwrap();
982 writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
983 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
984 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
985 writeln!(out, " }},").unwrap();
986 writeln!(out, " None => std::ptr::null_mut(),").unwrap();
987 writeln!(out, " }}").unwrap();
988 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
989 writeln!(out, "}}\n").unwrap();
990 } else {
991 writeln!(out, "#[no_mangle]").unwrap();
992 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}, out: *mut {}) -> LogosStatus {{", mangled, key_rust, val_rust).unwrap();
993 emit_catch_unwind_open(&mut out);
994 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
995 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
996 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
997 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
998 writeln!(out, " match map.get(&key) {{").unwrap();
999 writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
1000 writeln!(out, " None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key)); LogosStatus::Error }}").unwrap();
1001 writeln!(out, " }}").unwrap();
1002 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1003 writeln!(out, "}}\n").unwrap();
1004 }
1005 }
1006
1007 writeln!(out, "#[no_mangle]").unwrap();
1009 writeln!(out, "pub extern \"C\" fn logos_{}_keys(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
1010 emit_catch_unwind_open(&mut out);
1011 emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
1012 emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
1013 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
1014 writeln!(out, " let keys: Vec<{}> = map.keys().cloned().collect();", key_rust).unwrap();
1015 writeln!(out, " let __kptr = Box::into_raw(Box::new(keys)) as usize;").unwrap();
1016 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1017 writeln!(out, " let (__id, _) = __reg.register(__kptr);").unwrap();
1018 writeln!(out, " __id as LogosHandle").unwrap();
1019 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1020 writeln!(out, "}}\n").unwrap();
1021
1022 writeln!(out, "#[no_mangle]").unwrap();
1024 writeln!(out, "pub extern \"C\" fn logos_{}_values(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
1025 emit_catch_unwind_open(&mut out);
1026 emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
1027 emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
1028 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
1029 writeln!(out, " let values: Vec<{}> = map.values().cloned().collect();", val_rust).unwrap();
1030 writeln!(out, " let __vptr = Box::into_raw(Box::new(values)) as usize;").unwrap();
1031 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1032 writeln!(out, " let (__id, _) = __reg.register(__vptr);").unwrap();
1033 writeln!(out, " __id as LogosHandle").unwrap();
1034 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1035 writeln!(out, "}}\n").unwrap();
1036
1037 writeln!(out, "#[no_mangle]").unwrap();
1039 writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
1040 emit_catch_unwind_open(&mut out);
1041 emit_registry_create(&mut out, &format!("std::collections::HashMap::<{}, {}>::new()", key_rust, val_rust), &map_type);
1042 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1043 writeln!(out, "}}\n").unwrap();
1044
1045 if is_key_text {
1047 let val_param = if is_val_text {
1048 "value: *const std::os::raw::c_char".to_string()
1049 } else {
1050 format!("value: {}", val_rust)
1051 };
1052 writeln!(out, "#[no_mangle]").unwrap();
1053 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: *const std::os::raw::c_char, {}) {{", mangled, val_param).unwrap();
1054 emit_catch_unwind_open(&mut out);
1055 emit_null_handle_check(&mut out, "()");
1056 emit_registry_deref(&mut out, "()");
1057 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1058 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
1059 if is_val_text {
1060 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1061 writeln!(out, " map.insert(key_str, val_str);").unwrap();
1062 } else {
1063 writeln!(out, " map.insert(key_str, value);").unwrap();
1064 }
1065 emit_catch_unwind_close(&mut out, "()");
1066 writeln!(out, "}}\n").unwrap();
1067 } else {
1068 let val_param = if is_val_text {
1069 "value: *const std::os::raw::c_char".to_string()
1070 } else {
1071 format!("value: {}", val_rust)
1072 };
1073 writeln!(out, "#[no_mangle]").unwrap();
1074 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: {}, {}) {{", mangled, key_rust, val_param).unwrap();
1075 emit_catch_unwind_open(&mut out);
1076 emit_null_handle_check(&mut out, "()");
1077 emit_registry_deref(&mut out, "()");
1078 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1079 if is_val_text {
1080 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1081 writeln!(out, " map.insert(key, val_str);").unwrap();
1082 } else {
1083 writeln!(out, " map.insert(key, value);").unwrap();
1084 }
1085 emit_catch_unwind_close(&mut out, "()");
1086 writeln!(out, "}}\n").unwrap();
1087 }
1088
1089 if is_key_text {
1091 writeln!(out, "#[no_mangle]").unwrap();
1092 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
1093 emit_catch_unwind_open(&mut out);
1094 emit_null_handle_check(&mut out, "false");
1095 emit_registry_deref(&mut out, "false");
1096 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1097 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
1098 writeln!(out, " map.remove(&key_str).is_some()").unwrap();
1099 emit_catch_unwind_close(&mut out, "false");
1100 writeln!(out, "}}\n").unwrap();
1101 } else {
1102 writeln!(out, "#[no_mangle]").unwrap();
1103 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: {}) -> bool {{", mangled, key_rust).unwrap();
1104 emit_catch_unwind_open(&mut out);
1105 emit_null_handle_check(&mut out, "false");
1106 emit_registry_deref(&mut out, "false");
1107 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1108 writeln!(out, " map.remove(&key).is_some()").unwrap();
1109 emit_catch_unwind_close(&mut out, "false");
1110 writeln!(out, "}}\n").unwrap();
1111 }
1112
1113 writeln!(out, "#[no_mangle]").unwrap();
1115 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
1116 emit_catch_unwind_open(&mut out);
1117 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1118 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1119 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
1120 writeln!(out, " match serde_json::to_string(map) {{").unwrap();
1121 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1122 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1123 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1124 writeln!(out, " }},").unwrap();
1125 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1126 writeln!(out, " }}").unwrap();
1127 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1128 writeln!(out, "}}\n").unwrap();
1129
1130 writeln!(out, "#[no_mangle]").unwrap();
1132 writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
1133 emit_catch_unwind_open(&mut out);
1134 writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
1135 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1136 writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
1137 writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", map_type).unwrap();
1138 writeln!(out, " Ok(val) => {{").unwrap();
1139 writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
1140 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1141 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1142 writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
1143 writeln!(out, " LogosStatus::Ok").unwrap();
1144 writeln!(out, " }}").unwrap();
1145 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
1146 writeln!(out, " }}").unwrap();
1147 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1148 writeln!(out, "}}\n").unwrap();
1149
1150 writeln!(out, "#[no_mangle]").unwrap();
1152 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1153 emit_catch_unwind_open(&mut out);
1154 emit_registry_free(&mut out, &map_type);
1155 emit_catch_unwind_close(&mut out, "()");
1156 writeln!(out, "}}\n").unwrap();
1157 }
1158
1159 "Set" | "HashSet" if !params.is_empty() => {
1160 let inner_rust_type = codegen_type_expr(¶ms[0], interner);
1161 let is_inner_text = is_text_type(¶ms[0], interner);
1162 let set_type = format!("std::collections::HashSet<{}>", inner_rust_type);
1163
1164 writeln!(out, "#[no_mangle]").unwrap();
1166 writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
1167 emit_catch_unwind_open(&mut out);
1168 emit_null_handle_check(&mut out, "0");
1169 emit_registry_deref(&mut out, "0");
1170 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1171 writeln!(out, " set.len()").unwrap();
1172 emit_catch_unwind_close(&mut out, "0");
1173 writeln!(out, "}}\n").unwrap();
1174
1175 if is_inner_text {
1177 writeln!(out, "#[no_mangle]").unwrap();
1178 writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
1179 emit_catch_unwind_open(&mut out);
1180 emit_null_handle_check(&mut out, "false");
1181 emit_registry_deref(&mut out, "false");
1182 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1183 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1184 writeln!(out, " set.contains(&val_str)").unwrap();
1185 emit_catch_unwind_close(&mut out, "false");
1186 writeln!(out, "}}\n").unwrap();
1187 } else {
1188 writeln!(out, "#[no_mangle]").unwrap();
1189 writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
1190 emit_catch_unwind_open(&mut out);
1191 emit_null_handle_check(&mut out, "false");
1192 emit_registry_deref(&mut out, "false");
1193 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1194 writeln!(out, " set.contains(&value)").unwrap();
1195 emit_catch_unwind_close(&mut out, "false");
1196 writeln!(out, "}}\n").unwrap();
1197 }
1198
1199 writeln!(out, "#[no_mangle]").unwrap();
1201 writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
1202 emit_catch_unwind_open(&mut out);
1203 emit_registry_create(&mut out, &format!("std::collections::HashSet::<{}>::new()", inner_rust_type), &set_type);
1204 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1205 writeln!(out, "}}\n").unwrap();
1206
1207 if is_inner_text {
1209 writeln!(out, "#[no_mangle]").unwrap();
1210 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
1211 emit_catch_unwind_open(&mut out);
1212 emit_null_handle_check(&mut out, "()");
1213 emit_registry_deref(&mut out, "()");
1214 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1215 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1216 writeln!(out, " set.insert(val_str);").unwrap();
1217 emit_catch_unwind_close(&mut out, "()");
1218 writeln!(out, "}}\n").unwrap();
1219 } else {
1220 writeln!(out, "#[no_mangle]").unwrap();
1221 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
1222 emit_catch_unwind_open(&mut out);
1223 emit_null_handle_check(&mut out, "()");
1224 emit_registry_deref(&mut out, "()");
1225 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1226 writeln!(out, " set.insert(value);").unwrap();
1227 emit_catch_unwind_close(&mut out, "()");
1228 writeln!(out, "}}\n").unwrap();
1229 }
1230
1231 if is_inner_text {
1233 writeln!(out, "#[no_mangle]").unwrap();
1234 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
1235 emit_catch_unwind_open(&mut out);
1236 emit_null_handle_check(&mut out, "false");
1237 emit_registry_deref(&mut out, "false");
1238 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1239 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1240 writeln!(out, " set.remove(&val_str)").unwrap();
1241 emit_catch_unwind_close(&mut out, "false");
1242 writeln!(out, "}}\n").unwrap();
1243 } else {
1244 writeln!(out, "#[no_mangle]").unwrap();
1245 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
1246 emit_catch_unwind_open(&mut out);
1247 emit_null_handle_check(&mut out, "false");
1248 emit_registry_deref(&mut out, "false");
1249 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1250 writeln!(out, " set.remove(&value)").unwrap();
1251 emit_catch_unwind_close(&mut out, "false");
1252 writeln!(out, "}}\n").unwrap();
1253 }
1254
1255 writeln!(out, "#[no_mangle]").unwrap();
1257 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
1258 emit_catch_unwind_open(&mut out);
1259 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1260 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1261 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1262 writeln!(out, " match serde_json::to_string(set) {{").unwrap();
1263 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1264 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1265 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1266 writeln!(out, " }},").unwrap();
1267 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1268 writeln!(out, " }}").unwrap();
1269 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1270 writeln!(out, "}}\n").unwrap();
1271
1272 writeln!(out, "#[no_mangle]").unwrap();
1274 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1275 emit_catch_unwind_open(&mut out);
1276 emit_registry_free(&mut out, &set_type);
1277 emit_catch_unwind_close(&mut out, "()");
1278 writeln!(out, "}}\n").unwrap();
1279 }
1280
1281 "Option" | "Maybe" if !params.is_empty() => {
1282 let inner_rust_type = codegen_type_expr(¶ms[0], interner);
1283 let is_inner_text = is_text_type(¶ms[0], interner);
1284 let opt_type = format!("Option<{}>", inner_rust_type);
1285
1286 writeln!(out, "#[no_mangle]").unwrap();
1288 writeln!(out, "pub extern \"C\" fn logos_{}_is_some(handle: LogosHandle) -> bool {{", mangled).unwrap();
1289 emit_catch_unwind_open(&mut out);
1290 emit_null_handle_check(&mut out, "false");
1291 emit_registry_deref(&mut out, "false");
1292 writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
1293 writeln!(out, " opt.is_some()").unwrap();
1294 emit_catch_unwind_close(&mut out, "false");
1295 writeln!(out, "}}\n").unwrap();
1296
1297 if is_inner_text {
1299 writeln!(out, "#[no_mangle]").unwrap();
1300 writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
1301 emit_catch_unwind_open(&mut out);
1302 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1303 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1304 writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
1305 writeln!(out, " match opt {{").unwrap();
1306 writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
1307 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1308 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1309 writeln!(out, " }},").unwrap();
1310 writeln!(out, " None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); std::ptr::null_mut() }}").unwrap();
1311 writeln!(out, " }}").unwrap();
1312 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1313 writeln!(out, "}}\n").unwrap();
1314 } else {
1315 writeln!(out, "#[no_mangle]").unwrap();
1316 writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
1317 emit_catch_unwind_open(&mut out);
1318 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
1319 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1320 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
1321 writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
1322 writeln!(out, " match opt {{").unwrap();
1323 writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
1324 writeln!(out, " None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); LogosStatus::Error }}").unwrap();
1325 writeln!(out, " }}").unwrap();
1326 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1327 writeln!(out, "}}\n").unwrap();
1328 }
1329
1330 if is_inner_text {
1332 writeln!(out, "#[no_mangle]").unwrap();
1333 writeln!(out, "pub extern \"C\" fn logos_{}_some(value: *const std::os::raw::c_char) -> LogosHandle {{", mangled).unwrap();
1334 emit_catch_unwind_open(&mut out);
1335 writeln!(out, " if value.is_null() {{ logos_set_last_error(\"NullPointer: value is null\".to_string()); return std::ptr::null_mut() as LogosHandle; }}").unwrap();
1336 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1337 writeln!(out, " let opt: {} = Some(val_str);", opt_type).unwrap();
1338 writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1339 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1340 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1341 writeln!(out, " __id as LogosHandle").unwrap();
1342 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1343 writeln!(out, "}}\n").unwrap();
1344 } else {
1345 writeln!(out, "#[no_mangle]").unwrap();
1346 writeln!(out, "pub extern \"C\" fn logos_{}_some(value: {}) -> LogosHandle {{", mangled, inner_rust_type).unwrap();
1347 emit_catch_unwind_open(&mut out);
1348 writeln!(out, " let opt: {} = Some(value);", opt_type).unwrap();
1349 writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1350 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1351 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1352 writeln!(out, " __id as LogosHandle").unwrap();
1353 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1354 writeln!(out, "}}\n").unwrap();
1355 }
1356
1357 writeln!(out, "#[no_mangle]").unwrap();
1359 writeln!(out, "pub extern \"C\" fn logos_{}_none() -> LogosHandle {{", mangled).unwrap();
1360 emit_catch_unwind_open(&mut out);
1361 writeln!(out, " let opt: {} = None;", opt_type).unwrap();
1362 writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1363 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1364 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1365 writeln!(out, " __id as LogosHandle").unwrap();
1366 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1367 writeln!(out, "}}\n").unwrap();
1368
1369 writeln!(out, "#[no_mangle]").unwrap();
1371 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1372 emit_catch_unwind_open(&mut out);
1373 emit_registry_free(&mut out, &opt_type);
1374 emit_catch_unwind_close(&mut out, "()");
1375 writeln!(out, "}}\n").unwrap();
1376 }
1377
1378 _ => {}
1379 }
1380 }
1381 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1382 let type_name = interner.resolve(*sym);
1383 let type_def = registry.get(*sym);
1384
1385 match type_def {
1386 Some(TypeDef::Struct { fields, is_portable, .. }) => {
1387 let mangled_struct = type_name.to_lowercase();
1388 let rust_struct_name = type_name.to_string();
1389
1390 for field in fields {
1391 let field_name = interner.resolve(field.name);
1392 let is_field_text = match &field.ty {
1393 FieldType::Primitive(s) | FieldType::Named(s) => {
1394 let n = interner.resolve(*s);
1395 n == "Text" || n == "String"
1396 }
1397 _ => false,
1398 };
1399
1400 writeln!(out, "#[no_mangle]").unwrap();
1401 if is_field_text {
1402 writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
1403 mangled_struct, field = field_name).unwrap();
1404 emit_catch_unwind_open(&mut out);
1405 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1406 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1407 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1408 writeln!(out, " match std::ffi::CString::new(obj.{}.clone()) {{", field_name).unwrap();
1409 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1410 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1411 writeln!(out, " }}").unwrap();
1412 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1413 writeln!(out, "}}\n").unwrap();
1414 } else {
1415 let (field_rust_type, is_char) = match &field.ty {
1416 FieldType::Primitive(s) | FieldType::Named(s) => {
1417 let n = interner.resolve(*s);
1418 match n {
1419 "Int" => ("i64", false),
1420 "Nat" => ("u64", false),
1421 "Real" | "Float" => ("f64", false),
1422 "Bool" | "Boolean" => ("bool", false),
1423 "Byte" => ("u8", false),
1424 "Char" => ("u32", true),
1425 _ => (n, false),
1426 }
1427 }
1428 _ => ("LogosHandle", false),
1429 };
1430 writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> {} {{",
1431 mangled_struct, field_rust_type, field = field_name).unwrap();
1432 emit_catch_unwind_open(&mut out);
1433 emit_null_handle_check(&mut out, "Default::default()");
1434 emit_registry_deref(&mut out, "Default::default()");
1435 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1436 if is_char {
1437 writeln!(out, " obj.{}.clone() as u32", field_name).unwrap();
1438 } else {
1439 writeln!(out, " obj.{}.clone()", field_name).unwrap();
1440 }
1441 emit_catch_unwind_close(&mut out, "Default::default()");
1442 writeln!(out, "}}\n").unwrap();
1443 }
1444 }
1445
1446 {
1447 writeln!(out, "#[no_mangle]").unwrap();
1449 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled_struct).unwrap();
1450 emit_catch_unwind_open(&mut out);
1451 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1452 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1453 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1454 writeln!(out, " match serde_json::to_string(obj) {{").unwrap();
1455 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1456 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1457 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1458 writeln!(out, " }},").unwrap();
1459 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1460 writeln!(out, " }}").unwrap();
1461 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1462 writeln!(out, "}}\n").unwrap();
1463
1464 writeln!(out, "#[no_mangle]").unwrap();
1466 writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled_struct).unwrap();
1467 emit_catch_unwind_open(&mut out);
1468 writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
1469 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1470 writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
1471 writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", rust_struct_name).unwrap();
1472 writeln!(out, " Ok(val) => {{").unwrap();
1473 writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
1474 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1475 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1476 writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
1477 writeln!(out, " LogosStatus::Ok").unwrap();
1478 writeln!(out, " }}").unwrap();
1479 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
1480 writeln!(out, " }}").unwrap();
1481 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1482 writeln!(out, "}}\n").unwrap();
1483 }
1484
1485 writeln!(out, "#[no_mangle]").unwrap();
1487 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_struct).unwrap();
1488 emit_catch_unwind_open(&mut out);
1489 emit_registry_free(&mut out, &rust_struct_name);
1490 emit_catch_unwind_close(&mut out, "()");
1491 writeln!(out, "}}\n").unwrap();
1492 }
1493 Some(TypeDef::Enum { variants, .. }) => {
1494 let mangled_enum = type_name.to_lowercase();
1495 let rust_enum_name = type_name.to_string();
1496
1497 writeln!(out, "#[no_mangle]").unwrap();
1499 writeln!(out, "pub extern \"C\" fn logos_{}_tag(handle: LogosHandle) -> i32 {{", mangled_enum).unwrap();
1500 emit_catch_unwind_open(&mut out);
1501 emit_null_handle_check(&mut out, "-1");
1502 emit_registry_deref(&mut out, "-1");
1503 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1504 writeln!(out, " match obj {{").unwrap();
1505 for (i, variant) in variants.iter().enumerate() {
1506 let vname = interner.resolve(variant.name);
1507 if variant.fields.is_empty() {
1508 writeln!(out, " {}::{} => {},", rust_enum_name, vname, i).unwrap();
1509 } else {
1510 writeln!(out, " {}::{}{{ .. }} => {},", rust_enum_name, vname, i).unwrap();
1511 }
1512 }
1513 writeln!(out, " }}").unwrap();
1514 emit_catch_unwind_close(&mut out, "-1");
1515 writeln!(out, "}}\n").unwrap();
1516
1517 for variant in variants {
1518 let vname = interner.resolve(variant.name);
1519 let vname_lower = vname.to_lowercase();
1520 for field in &variant.fields {
1521 let fname = interner.resolve(field.name);
1522 let is_field_text = match &field.ty {
1523 FieldType::Primitive(s) | FieldType::Named(s) => {
1524 let n = interner.resolve(*s);
1525 n == "Text" || n == "String"
1526 }
1527 _ => false,
1528 };
1529
1530 writeln!(out, "#[no_mangle]").unwrap();
1531 if is_field_text {
1532 writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
1533 mangled_enum, vname_lower, fname = fname).unwrap();
1534 emit_catch_unwind_open(&mut out);
1535 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1536 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1537 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1538 writeln!(out, " if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
1539 writeln!(out, " match std::ffi::CString::new({fname}.clone()) {{", fname = fname).unwrap();
1540 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1541 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1542 writeln!(out, " }}").unwrap();
1543 writeln!(out, " }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); std::ptr::null_mut() }}", vname).unwrap();
1544 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1545 writeln!(out, "}}\n").unwrap();
1546 } else {
1547 let field_rust_type = match &field.ty {
1548 FieldType::Primitive(s) | FieldType::Named(s) => {
1549 let n = interner.resolve(*s);
1550 match n {
1551 "Int" => "i64",
1552 "Nat" => "u64",
1553 "Real" | "Float" => "f64",
1554 "Bool" | "Boolean" => "bool",
1555 "Byte" => "u8",
1556 "Char" => "u32",
1557 _ => n,
1558 }
1559 }
1560 _ => "LogosHandle",
1561 };
1562 writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> {} {{",
1563 mangled_enum, vname_lower, field_rust_type, fname = fname).unwrap();
1564 emit_catch_unwind_open(&mut out);
1565 emit_null_handle_check(&mut out, "Default::default()");
1566 emit_registry_deref(&mut out, "Default::default()");
1567 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1568 writeln!(out, " if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
1569 writeln!(out, " {fname}.clone()", fname = fname).unwrap();
1570 writeln!(out, " }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); Default::default() }}", vname).unwrap();
1571 emit_catch_unwind_close(&mut out, "Default::default()");
1572 writeln!(out, "}}\n").unwrap();
1573 }
1574 }
1575 }
1576
1577 writeln!(out, "#[no_mangle]").unwrap();
1579 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_enum).unwrap();
1580 emit_catch_unwind_open(&mut out);
1581 emit_registry_free(&mut out, &rust_enum_name);
1582 emit_catch_unwind_close(&mut out, "()");
1583 writeln!(out, "}}\n").unwrap();
1584 }
1585 _ => {}
1586 }
1587 }
1588 _ => {}
1589 }
1590
1591 out
1592}
1593
1594fn collect_c_export_reference_types<'a>(
1597 stmts: &'a [Stmt<'a>],
1598 interner: &Interner,
1599 registry: &TypeRegistry,
1600) -> Vec<&'a TypeExpr<'a>> {
1601 let mut seen = HashSet::new();
1602 let mut types = Vec::new();
1603
1604 for stmt in stmts {
1605 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1606 let is_c = match export_target {
1607 None => true,
1608 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1609 };
1610 if !is_c { continue; }
1611
1612 for (_, ty) in params.iter() {
1613 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
1614 let mangled = mangle_type_for_c(ty, interner);
1615 if seen.insert(mangled) {
1616 types.push(*ty);
1617 }
1618 }
1619 }
1620 if let Some(ty) = return_type {
1621 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
1622 let mangled = mangle_type_for_c(ty, interner);
1623 if seen.insert(mangled) {
1624 types.push(*ty);
1625 }
1626 }
1627 }
1628 }
1629 }
1630
1631 types
1632}
1633
1634fn collect_c_export_value_type_structs(
1637 stmts: &[Stmt],
1638 interner: &Interner,
1639 registry: &TypeRegistry,
1640) -> HashSet<Symbol> {
1641 let mut value_structs = HashSet::new();
1642
1643 for stmt in stmts {
1644 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1645 let is_c = match export_target {
1646 None => true,
1647 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1648 };
1649 if !is_c { continue; }
1650
1651 let all_types: Vec<&TypeExpr> = params.iter()
1652 .map(|(_, ty)| *ty)
1653 .chain(return_type.iter().copied())
1654 .collect();
1655
1656 for ty in all_types {
1657 if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1658 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
1659 if registry.get(*sym).is_some() {
1660 value_structs.insert(*sym);
1661 }
1662 }
1663 }
1664 }
1665 }
1666 }
1667
1668 value_structs
1669}
1670
1671fn collect_c_export_ref_structs(
1674 stmts: &[Stmt],
1675 interner: &Interner,
1676 registry: &TypeRegistry,
1677) -> HashSet<Symbol> {
1678 let mut ref_structs = HashSet::new();
1679 let ref_types = collect_c_export_reference_types(stmts, interner, registry);
1680 for ty in ref_types {
1681 if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1682 if registry.get(*sym).map_or(false, |d| matches!(d, TypeDef::Struct { .. })) {
1683 ref_structs.insert(*sym);
1684 }
1685 }
1686 }
1687 ref_structs
1688}
1689
1690pub fn generate_c_header(
1699 stmts: &[Stmt],
1700 module_name: &str,
1701 interner: &Interner,
1702 registry: &TypeRegistry,
1703) -> String {
1704 let mut out = String::new();
1705 let guard = module_name.to_uppercase().replace('-', "_");
1706
1707 writeln!(out, "// Generated from {}.lg — LogicAffeine Universal ABI", module_name).unwrap();
1708 writeln!(out, "#ifndef {}_H", guard).unwrap();
1709 writeln!(out, "#define {}_H\n", guard).unwrap();
1710 writeln!(out, "#include <stdint.h>").unwrap();
1711 writeln!(out, "#include <stdbool.h>").unwrap();
1712 writeln!(out, "#include <stddef.h>\n").unwrap();
1713
1714 writeln!(out, "#ifdef __cplusplus").unwrap();
1715 writeln!(out, "extern \"C\" {{").unwrap();
1716 writeln!(out, "#endif\n").unwrap();
1717
1718 writeln!(out, "// ═══ Runtime ═══").unwrap();
1720 writeln!(out, "typedef enum {{").unwrap();
1721 writeln!(out, " LOGOS_STATUS_OK = 0,").unwrap();
1722 writeln!(out, " LOGOS_STATUS_ERROR = 1,").unwrap();
1723 writeln!(out, " LOGOS_STATUS_REFINEMENT_VIOLATION = 2,").unwrap();
1724 writeln!(out, " LOGOS_STATUS_NULL_POINTER = 3,").unwrap();
1725 writeln!(out, " LOGOS_STATUS_OUT_OF_BOUNDS = 4,").unwrap();
1726 writeln!(out, " LOGOS_STATUS_DESERIALIZATION_FAILED = 5,").unwrap();
1727 writeln!(out, " LOGOS_STATUS_INVALID_HANDLE = 6,").unwrap();
1728 writeln!(out, " LOGOS_STATUS_CONTAINS_NULL_BYTE = 7,").unwrap();
1729 writeln!(out, " LOGOS_STATUS_THREAD_PANIC = 8,").unwrap();
1730 writeln!(out, " LOGOS_STATUS_MEMORY_EXHAUSTED = 9,").unwrap();
1731 writeln!(out, "}} logos_status_t;\n").unwrap();
1732 writeln!(out, "typedef void* logos_handle_t;\n").unwrap();
1733 writeln!(out, "const char* logos_last_error(void);").unwrap();
1734 writeln!(out, "const char* logos_get_last_error(void);").unwrap();
1735 writeln!(out, "void logos_clear_error(void);").unwrap();
1736 writeln!(out, "void logos_free_string(char* str);\n").unwrap();
1737
1738 writeln!(out, "#define LOGOS_ABI_VERSION 1").unwrap();
1739 writeln!(out, "const char* logos_version(void);").unwrap();
1740 writeln!(out, "uint32_t logos_abi_version(void);\n").unwrap();
1741
1742 let mut emitted_structs = HashSet::new();
1744 for stmt in stmts {
1745 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1746 let is_c = match export_target {
1747 None => true,
1748 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1749 };
1750 if !is_c { continue; }
1751
1752 let all_types: Vec<&TypeExpr> = params.iter()
1754 .map(|(_, ty)| *ty)
1755 .chain(return_type.iter().copied())
1756 .collect();
1757
1758 for ty in all_types {
1759 if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1760 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
1761 if let Some(TypeDef::Struct { fields, .. }) = registry.get(*sym) {
1762 let name = interner.resolve(*sym);
1763 if emitted_structs.insert(name.to_string()) {
1764 writeln!(out, "// ═══ Value Types ═══").unwrap();
1765 writeln!(out, "typedef struct {{").unwrap();
1766 for field in fields {
1767 let c_type = map_field_type_to_c(&field.ty, interner);
1768 writeln!(out, " {} {};", c_type, interner.resolve(field.name)).unwrap();
1769 }
1770 writeln!(out, "}} {};\n", name).unwrap();
1771 }
1772 }
1773 }
1774 }
1775 }
1776 }
1777 }
1778
1779 writeln!(out, "// ═══ Exported Functions ═══").unwrap();
1781 for stmt in stmts {
1782 if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
1783 let is_c = match export_target {
1784 None => true,
1785 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1786 };
1787 if !is_c { continue; }
1788
1789 let func_name = format!("logos_{}", interner.resolve(*name));
1790 let has_ref_return = return_type.map_or(false, |ty| {
1791 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
1792 });
1793 let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
1794 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
1795 let has_refinement_param = params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
1796
1797 let uses_status_code = has_ref_return || has_result_return || has_text_return || has_refinement_param;
1799
1800 let mut c_params = Vec::new();
1802 for (pname, ptype) in params.iter() {
1803 let pn = interner.resolve(*pname);
1804 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
1805 c_params.push(format!("logos_handle_t {}", pn));
1806 } else {
1807 c_params.push(format!("{} {}", map_type_to_c_header(ptype, interner, false), pn));
1808 }
1809 }
1810
1811 if uses_status_code {
1812 if let Some(ret_ty) = return_type {
1814 if is_result_type(ret_ty, interner) {
1815 if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
1816 if !rparams.is_empty() {
1817 let ok_ty = &rparams[0];
1818 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
1819 c_params.push("logos_handle_t* out".to_string());
1820 } else {
1821 c_params.push(format!("{}* out", map_type_to_c_header(ok_ty, interner, false)));
1822 }
1823 }
1824 }
1825 } else if classify_type_for_c_abi(ret_ty, interner, registry) == CAbiClass::ReferenceType {
1826 c_params.push("logos_handle_t* out".to_string());
1827 } else if has_text_return {
1828 c_params.push("char** out".to_string());
1829 }
1830 }
1831 writeln!(out, "logos_status_t {}({});", func_name, c_params.join(", ")).unwrap();
1832 } else {
1833 let ret = return_type
1835 .map(|ty| map_type_to_c_header(ty, interner, true))
1836 .unwrap_or_else(|| "void".to_string());
1837 writeln!(out, "{} {}({});", ret, func_name, c_params.join(", ")).unwrap();
1838 }
1839 }
1840 }
1841 writeln!(out).unwrap();
1842
1843 let ref_types = collect_c_export_reference_types(stmts, interner, registry);
1845 if !ref_types.is_empty() {
1846 for ref_ty in &ref_types {
1847 let mangled = mangle_type_for_c(ref_ty, interner);
1848 writeln!(out, "// ═══ {} Accessors ═══", mangled).unwrap();
1849
1850 match ref_ty {
1851 TypeExpr::Generic { base, params } => {
1852 let base_name = interner.resolve(*base);
1853 match base_name {
1854 "Seq" | "List" | "Vec" if !params.is_empty() => {
1855 let is_inner_text = is_text_type(¶ms[0], interner);
1856 writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1857 if is_inner_text {
1858 writeln!(out, "char* logos_{}_at(logos_handle_t handle, size_t index);", mangled).unwrap();
1859 } else {
1860 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1861 writeln!(out, "logos_status_t logos_{}_at(logos_handle_t handle, size_t index, {}* out);", mangled, inner_c).unwrap();
1862 }
1863 writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1864 if is_inner_text {
1865 writeln!(out, "void logos_{}_push(logos_handle_t handle, const char* value);", mangled).unwrap();
1866 } else {
1867 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1868 writeln!(out, "void logos_{}_push(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1869 }
1870 if is_inner_text {
1871 writeln!(out, "char* logos_{}_pop(logos_handle_t handle);", mangled).unwrap();
1872 } else {
1873 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1874 writeln!(out, "logos_status_t logos_{}_pop(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
1875 }
1876 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1877 writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
1878 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1879 }
1880 "Map" | "HashMap" if params.len() >= 2 => {
1881 let is_key_text = is_text_type(¶ms[0], interner);
1882 let is_val_text = is_text_type(¶ms[1], interner);
1883 writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1884 if is_key_text {
1885 if is_val_text {
1886 writeln!(out, "char* logos_{}_get(logos_handle_t handle, const char* key);", mangled).unwrap();
1887 } else {
1888 let val_c = map_type_to_c_header(¶ms[1], interner, false);
1889 writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, const char* key, {}* out);", mangled, val_c).unwrap();
1890 }
1891 } else {
1892 let key_c = map_type_to_c_header(¶ms[0], interner, false);
1893 if is_val_text {
1894 writeln!(out, "char* logos_{}_get(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
1895 } else {
1896 let val_c = map_type_to_c_header(¶ms[1], interner, false);
1897 writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, {} key, {}* out);", mangled, key_c, val_c).unwrap();
1898 }
1899 }
1900 writeln!(out, "logos_handle_t logos_{}_keys(logos_handle_t handle);", mangled).unwrap();
1901 writeln!(out, "logos_handle_t logos_{}_values(logos_handle_t handle);", mangled).unwrap();
1902 writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1903 if is_key_text {
1904 let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(¶ms[1], interner, false) };
1905 writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* key, {} value);", mangled, val_c).unwrap();
1906 } else {
1907 let key_c = map_type_to_c_header(¶ms[0], interner, false);
1908 let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(¶ms[1], interner, false) };
1909 writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} key, {} value);", mangled, key_c, val_c).unwrap();
1910 }
1911 if is_key_text {
1912 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* key);", mangled).unwrap();
1913 } else {
1914 let key_c = map_type_to_c_header(¶ms[0], interner, false);
1915 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
1916 }
1917 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1918 writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
1919 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1920 }
1921 "Set" | "HashSet" if !params.is_empty() => {
1922 let is_inner_text = is_text_type(¶ms[0], interner);
1923 writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1924 if is_inner_text {
1925 writeln!(out, "bool logos_{}_contains(logos_handle_t handle, const char* value);", mangled).unwrap();
1926 } else {
1927 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1928 writeln!(out, "bool logos_{}_contains(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1929 }
1930 writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1931 if is_inner_text {
1932 writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* value);", mangled).unwrap();
1933 } else {
1934 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1935 writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1936 }
1937 if is_inner_text {
1938 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* value);", mangled).unwrap();
1939 } else {
1940 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1941 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1942 }
1943 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1944 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1945 }
1946 "Option" | "Maybe" if !params.is_empty() => {
1947 let is_inner_text = is_text_type(¶ms[0], interner);
1948 writeln!(out, "bool logos_{}_is_some(logos_handle_t handle);", mangled).unwrap();
1949 if is_inner_text {
1950 writeln!(out, "char* logos_{}_unwrap(logos_handle_t handle);", mangled).unwrap();
1951 } else {
1952 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1953 writeln!(out, "logos_status_t logos_{}_unwrap(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
1954 }
1955 if is_inner_text {
1956 writeln!(out, "logos_handle_t logos_{}_some(const char* value);", mangled).unwrap();
1957 } else {
1958 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1959 writeln!(out, "logos_handle_t logos_{}_some({} value);", mangled, inner_c).unwrap();
1960 }
1961 writeln!(out, "logos_handle_t logos_{}_none(void);", mangled).unwrap();
1962 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1963 }
1964 _ => {}
1965 }
1966 }
1967 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1968 let type_name = interner.resolve(*sym);
1969 match registry.get(*sym) {
1970 Some(TypeDef::Struct { fields, is_portable, .. }) => {
1971 let struct_lower = type_name.to_lowercase();
1972 for field in fields {
1973 let field_name = interner.resolve(field.name);
1974 let is_field_text = match &field.ty {
1975 FieldType::Primitive(s) | FieldType::Named(s) => {
1976 let n = interner.resolve(*s);
1977 n == "Text" || n == "String"
1978 }
1979 _ => false,
1980 };
1981 if is_field_text {
1982 writeln!(out, "char* logos_{}_{}(logos_handle_t handle);", struct_lower, field_name).unwrap();
1983 } else {
1984 let c_type = map_field_type_to_c(&field.ty, interner);
1985 writeln!(out, "{} logos_{}_{}(logos_handle_t handle);", c_type, struct_lower, field_name).unwrap();
1986 }
1987 }
1988 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", struct_lower).unwrap();
1990 writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", struct_lower).unwrap();
1991 writeln!(out, "void logos_{}_free(logos_handle_t handle);", struct_lower).unwrap();
1992 }
1993 Some(TypeDef::Enum { variants, .. }) => {
1994 let enum_lower = type_name.to_lowercase();
1995 writeln!(out, "typedef enum {{").unwrap();
1997 for (i, variant) in variants.iter().enumerate() {
1998 let vname = interner.resolve(variant.name).to_uppercase();
1999 writeln!(out, " LOGOS_{}_{} = {},", type_name.to_uppercase(), vname, i).unwrap();
2000 }
2001 writeln!(out, "}} logos_{}_tag_t;", enum_lower).unwrap();
2002 writeln!(out, "int32_t logos_{}_tag(logos_handle_t handle);", enum_lower).unwrap();
2003 for variant in variants {
2005 let vname = interner.resolve(variant.name);
2006 let vname_lower = vname.to_lowercase();
2007 for field in &variant.fields {
2008 let fname = interner.resolve(field.name);
2009 let is_field_text = match &field.ty {
2010 FieldType::Primitive(s) | FieldType::Named(s) => {
2011 let n = interner.resolve(*s);
2012 n == "Text" || n == "String"
2013 }
2014 _ => false,
2015 };
2016 if is_field_text {
2017 writeln!(out, "char* logos_{}_{}_{fname}(logos_handle_t handle);", enum_lower, vname_lower, fname = fname).unwrap();
2018 } else {
2019 let c_type = map_field_type_to_c(&field.ty, interner);
2020 writeln!(out, "{} logos_{}_{}_{fname}(logos_handle_t handle);", c_type, enum_lower, vname_lower, fname = fname).unwrap();
2021 }
2022 }
2023 }
2024 writeln!(out, "void logos_{}_free(logos_handle_t handle);", enum_lower).unwrap();
2025 }
2026 _ => {}
2027 }
2028 }
2029 _ => {}
2030 }
2031 writeln!(out).unwrap();
2032 }
2033 }
2034
2035 writeln!(out, "#ifdef __cplusplus").unwrap();
2036 writeln!(out, "}}").unwrap();
2037 writeln!(out, "#endif\n").unwrap();
2038 writeln!(out, "#endif // {}_H", guard).unwrap();
2039
2040 out
2041}
2042
2043pub fn generate_python_bindings(
2045 stmts: &[Stmt],
2046 module_name: &str,
2047 interner: &Interner,
2048 registry: &TypeRegistry,
2049) -> String {
2050 let mut out = String::new();
2051
2052 writeln!(out, "\"\"\"Auto-generated Python bindings for {}.\"\"\"", module_name).unwrap();
2053 writeln!(out, "import ctypes").unwrap();
2054 writeln!(out, "from ctypes import c_int64, c_uint64, c_double, c_bool, c_char_p, c_void_p, c_size_t, POINTER").unwrap();
2055 writeln!(out, "import os").unwrap();
2056 writeln!(out, "import sys\n").unwrap();
2057
2058 writeln!(out, "class LogosError(Exception):").unwrap();
2059 writeln!(out, " pass\n").unwrap();
2060
2061 writeln!(out, "class LogosRefinementError(LogosError):").unwrap();
2062 writeln!(out, " pass\n").unwrap();
2063
2064 writeln!(out, "def _lib_ext():").unwrap();
2065 writeln!(out, " if sys.platform == \"darwin\":").unwrap();
2066 writeln!(out, " return \".dylib\"").unwrap();
2067 writeln!(out, " elif sys.platform == \"win32\":").unwrap();
2068 writeln!(out, " return \".dll\"").unwrap();
2069 writeln!(out, " else:").unwrap();
2070 writeln!(out, " return \".so\"\n").unwrap();
2071
2072 let class_name = module_name.chars().next().unwrap_or('M').to_uppercase().to_string()
2073 + &module_name[1..];
2074
2075 writeln!(out, "class {}:", class_name).unwrap();
2076 writeln!(out, " OK = 0").unwrap();
2077 writeln!(out, " ERROR = 1").unwrap();
2078 writeln!(out, " REFINEMENT_VIOLATION = 2").unwrap();
2079 writeln!(out, " NULL_POINTER = 3").unwrap();
2080 writeln!(out, " OUT_OF_BOUNDS = 4\n").unwrap();
2081
2082 writeln!(out, " def __init__(self, path=None):").unwrap();
2083 writeln!(out, " if path is None:").unwrap();
2084 writeln!(out, " path = os.path.join(os.path.dirname(__file__), \"lib{}\" + _lib_ext())", module_name).unwrap();
2085 writeln!(out, " self._lib = ctypes.CDLL(path)").unwrap();
2086 writeln!(out, " self._setup()\n").unwrap();
2087
2088 writeln!(out, " def _check(self, status):").unwrap();
2089 writeln!(out, " if status != self.OK:").unwrap();
2090 writeln!(out, " err = self._lib.logos_get_last_error()").unwrap();
2091 writeln!(out, " msg = err.decode(\"utf-8\") if err else \"Unknown error\"").unwrap();
2092 writeln!(out, " self._lib.logos_clear_error()").unwrap();
2093 writeln!(out, " if status == self.REFINEMENT_VIOLATION:").unwrap();
2094 writeln!(out, " raise LogosRefinementError(msg)").unwrap();
2095 writeln!(out, " raise LogosError(msg)\n").unwrap();
2096
2097 writeln!(out, " def _setup(self):").unwrap();
2099 writeln!(out, " self._lib.logos_get_last_error.restype = c_char_p").unwrap();
2100 writeln!(out, " self._lib.logos_clear_error.restype = None").unwrap();
2101 writeln!(out, " self._lib.logos_free_string.argtypes = [c_char_p]").unwrap();
2102 writeln!(out, " self._lib.logos_free_string.restype = None").unwrap();
2103
2104 for stmt in stmts {
2106 if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
2107 let is_c = match export_target {
2108 None => true,
2109 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2110 };
2111 if !is_c { continue; }
2112
2113 let func_name = format!("logos_{}", interner.resolve(*name));
2114 let mut argtypes = Vec::new();
2115 for (_, ptype) in params.iter() {
2116 argtypes.push(python_ctypes_type(ptype, interner, registry));
2117 }
2118 let restype = return_type
2119 .map(|ty| python_ctypes_type(ty, interner, registry))
2120 .unwrap_or_else(|| "None".to_string());
2121
2122 writeln!(out, " self._lib.{}.argtypes = [{}]", func_name, argtypes.join(", ")).unwrap();
2123 writeln!(out, " self._lib.{}.restype = {}", func_name, restype).unwrap();
2124 }
2125 }
2126 writeln!(out).unwrap();
2127
2128 for stmt in stmts {
2130 if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
2131 let is_c = match export_target {
2132 None => true,
2133 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2134 };
2135 if !is_c { continue; }
2136
2137 let raw_name = interner.resolve(*name);
2138 let c_func_name = format!("logos_{}", raw_name);
2139 let param_names: Vec<String> = params.iter()
2140 .map(|(pname, _)| interner.resolve(*pname).to_string())
2141 .collect();
2142 let type_hints: Vec<String> = params.iter()
2143 .map(|(pname, ptype)| {
2144 format!("{}: {}", interner.resolve(*pname), python_type_hint(ptype, interner))
2145 })
2146 .collect();
2147 let ret_hint = return_type
2148 .map(|ty| format!(" -> {}", python_type_hint(ty, interner)))
2149 .unwrap_or_default();
2150
2151 writeln!(out, " def {}(self, {}){}:", raw_name, type_hints.join(", "), ret_hint).unwrap();
2153 writeln!(out, " return self._lib.{}({})", c_func_name, param_names.join(", ")).unwrap();
2154 writeln!(out).unwrap();
2155 }
2156 }
2157
2158 out
2159}
2160
2161fn python_ctypes_type(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
2162 match classify_type_for_c_abi(ty, interner, registry) {
2163 CAbiClass::ReferenceType => "c_void_p".to_string(),
2164 CAbiClass::ValueType => {
2165 match ty {
2166 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2167 let name = interner.resolve(*sym);
2168 match name {
2169 "Int" => "c_int64".to_string(),
2170 "Nat" => "c_uint64".to_string(),
2171 "Real" | "Float" => "c_double".to_string(),
2172 "Bool" | "Boolean" => "c_bool".to_string(),
2173 "Text" | "String" => "c_char_p".to_string(),
2174 _ => "c_void_p".to_string(),
2175 }
2176 }
2177 _ => "c_void_p".to_string(),
2178 }
2179 }
2180 }
2181}
2182
2183fn python_type_hint(ty: &TypeExpr, interner: &Interner) -> String {
2184 match ty {
2185 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2186 let name = interner.resolve(*sym);
2187 match name {
2188 "Int" | "Nat" => "int".to_string(),
2189 "Real" | "Float" => "float".to_string(),
2190 "Bool" | "Boolean" => "bool".to_string(),
2191 "Text" | "String" => "str".to_string(),
2192 other => other.to_string(),
2193 }
2194 }
2195 _ => "object".to_string(),
2196 }
2197}
2198
2199pub fn generate_typescript_bindings(
2201 stmts: &[Stmt],
2202 module_name: &str,
2203 interner: &Interner,
2204 registry: &TypeRegistry,
2205) -> (String, String) {
2206 let mut dts = String::new();
2207 let mut js = String::new();
2208
2209 writeln!(dts, "// Auto-generated TypeScript definitions for {}", module_name).unwrap();
2211 let mut ffi_entries = Vec::new();
2212
2213 for stmt in stmts {
2214 if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
2215 let is_c = match export_target {
2216 None => true,
2217 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2218 };
2219 if !is_c { continue; }
2220
2221 let raw_name = interner.resolve(*name);
2222 let c_symbol = format!("logos_{}", raw_name);
2223 let ts_params: Vec<String> = params.iter()
2224 .map(|(pname, ptype)| format!("{}: {}", interner.resolve(*pname), typescript_type(ptype, interner)))
2225 .collect();
2226 let ts_ret = return_type
2227 .map(|ty| typescript_type(ty, interner))
2228 .unwrap_or_else(|| "void".to_string());
2229 writeln!(dts, "export declare function {}({}): {};", raw_name, ts_params.join(", "), ts_ret).unwrap();
2230
2231 let ffi_params: Vec<String> = params.iter()
2233 .map(|(_, ptype)| ffi_napi_type(ptype, interner, registry))
2234 .collect();
2235 let ffi_ret = return_type
2236 .map(|ty| ffi_napi_type(ty, interner, registry))
2237 .unwrap_or_else(|| "'void'".to_string());
2238 ffi_entries.push((raw_name.to_string(), c_symbol, ffi_ret, ffi_params));
2239 }
2240 }
2241
2242 writeln!(js, "const koffi = require('koffi');").unwrap();
2244 writeln!(js, "const path = require('path');\n").unwrap();
2245 writeln!(js, "const libPath = path.join(__dirname, 'lib{}');", module_name).unwrap();
2246 writeln!(js, "const lib = koffi.load(libPath);\n").unwrap();
2247
2248 writeln!(js, "const logos_get_last_error = lib.func('const char* logos_get_last_error()');").unwrap();
2250 writeln!(js, "const logos_clear_error = lib.func('void logos_clear_error()');").unwrap();
2251 writeln!(js, "const logos_free_string = lib.func('void logos_free_string(void* ptr)');\n").unwrap();
2252
2253 for (raw_name, c_symbol, ffi_ret, ffi_params) in &ffi_entries {
2255 let koffi_ret = ffi_napi_to_koffi(ffi_ret);
2256 let koffi_params: Vec<String> = ffi_params.iter()
2257 .enumerate()
2258 .map(|(i, p)| format!("{} arg{}", ffi_napi_to_koffi(p), i))
2259 .collect();
2260 writeln!(js, "const _{} = lib.func('{} {}({})');\n", raw_name, koffi_ret, c_symbol, koffi_params.join(", ")).unwrap();
2261 }
2262
2263 writeln!(js, "function checkStatus(status) {{").unwrap();
2264 writeln!(js, " if (status !== 0) {{").unwrap();
2265 writeln!(js, " const err = logos_get_last_error();").unwrap();
2266 writeln!(js, " logos_clear_error();").unwrap();
2267 writeln!(js, " throw new Error(err || 'Unknown LogicAffeine error');").unwrap();
2268 writeln!(js, " }}").unwrap();
2269 writeln!(js, "}}\n").unwrap();
2270
2271 for (raw_name, _, _, _) in &ffi_entries {
2272 let params_from_stmts = stmts.iter().find_map(|s| {
2273 if let Stmt::FunctionDef { name, is_exported: true, params, .. } = s {
2274 if interner.resolve(*name) == raw_name.as_str() {
2275 Some(params)
2276 } else {
2277 None
2278 }
2279 } else {
2280 None
2281 }
2282 });
2283 if let Some(params) = params_from_stmts {
2284 let param_names: Vec<String> = params.iter()
2285 .map(|(pname, _)| interner.resolve(*pname).to_string())
2286 .collect();
2287 writeln!(js, "module.exports.{} = ({}) => _{}({});", raw_name, param_names.join(", "), raw_name, param_names.join(", ")).unwrap();
2288 }
2289 }
2290
2291 (js, dts)
2292}
2293
2294fn typescript_type(ty: &TypeExpr, interner: &Interner) -> String {
2295 match ty {
2296 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2297 let name = interner.resolve(*sym);
2298 match name {
2299 "Int" | "Nat" | "Real" | "Float" | "Byte" => "number".to_string(),
2300 "Bool" | "Boolean" => "boolean".to_string(),
2301 "Text" | "String" | "Char" => "string".to_string(),
2302 "Unit" => "void".to_string(),
2303 other => other.to_string(),
2304 }
2305 }
2306 TypeExpr::Generic { base, params } => {
2307 let base_name = interner.resolve(*base);
2308 match base_name {
2309 "Seq" | "List" | "Vec" if !params.is_empty() => {
2310 format!("{}[]", typescript_type(¶ms[0], interner))
2311 }
2312 "Option" | "Maybe" if !params.is_empty() => {
2313 format!("{} | null", typescript_type(¶ms[0], interner))
2314 }
2315 _ => "any".to_string(),
2316 }
2317 }
2318 _ => "any".to_string(),
2319 }
2320}
2321
2322fn ffi_napi_to_koffi(ffi_type: &str) -> &str {
2324 match ffi_type {
2325 "'int64'" => "int64_t",
2326 "'uint64'" => "uint64_t",
2327 "'double'" => "double",
2328 "'bool'" => "bool",
2329 "'string'" => "const char*",
2330 "'pointer'" => "void*",
2331 "'void'" => "void",
2332 _ => "void*",
2333 }
2334}
2335
2336fn ffi_napi_type(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
2337 match classify_type_for_c_abi(ty, interner, registry) {
2338 CAbiClass::ReferenceType => "'pointer'".to_string(),
2339 CAbiClass::ValueType => {
2340 match ty {
2341 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2342 let name = interner.resolve(*sym);
2343 match name {
2344 "Int" => "'int64'".to_string(),
2345 "Nat" => "'uint64'".to_string(),
2346 "Real" | "Float" => "'double'".to_string(),
2347 "Bool" | "Boolean" => "'bool'".to_string(),
2348 "Text" | "String" => "'string'".to_string(),
2349 _ => "'pointer'".to_string(),
2350 }
2351 }
2352 _ => "'pointer'".to_string(),
2353 }
2354 }
2355 }
2356}
2357
2358fn map_type_to_c_header(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
2360 match ty {
2361 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2362 let name = interner.resolve(*sym);
2363 match name {
2364 "Int" => "int64_t".to_string(),
2365 "Nat" => "uint64_t".to_string(),
2366 "Real" | "Float" => "double".to_string(),
2367 "Bool" | "Boolean" => "bool".to_string(),
2368 "Byte" => "uint8_t".to_string(),
2369 "Char" => "uint32_t".to_string(), "Text" | "String" => {
2371 if is_return { "char*".to_string() } else { "const char*".to_string() }
2372 }
2373 "Unit" => "void".to_string(),
2374 other => other.to_string(), }
2376 }
2377 TypeExpr::Refinement { base, .. } => map_type_to_c_header(base, interner, is_return),
2378 TypeExpr::Generic { .. } => "logos_handle_t".to_string(),
2379 _ => "logos_handle_t".to_string(),
2380 }
2381}
2382
2383fn map_field_type_to_c(ft: &FieldType, interner: &Interner) -> String {
2385 match ft {
2386 FieldType::Primitive(sym) | FieldType::Named(sym) => {
2387 let name = interner.resolve(*sym);
2388 match name {
2389 "Int" => "int64_t".to_string(),
2390 "Nat" => "uint64_t".to_string(),
2391 "Real" | "Float" => "double".to_string(),
2392 "Bool" | "Boolean" => "bool".to_string(),
2393 "Byte" => "uint8_t".to_string(),
2394 "Char" => "uint32_t".to_string(),
2395 "Text" | "String" => "const char*".to_string(),
2396 other => other.to_string(),
2397 }
2398 }
2399 FieldType::Generic { .. } => "logos_handle_t".to_string(),
2400 FieldType::TypeParam(_) => "logos_handle_t".to_string(),
2401 }
2402}
2403
2404fn is_result_type(ty: &TypeExpr, interner: &Interner) -> bool {
2406 if let TypeExpr::Generic { base, .. } = ty {
2407 interner.resolve(*base) == "Result"
2408 } else {
2409 false
2410 }
2411}
2412
2413fn requires_async(stmts: &[Stmt]) -> bool {
2416 stmts.iter().any(|s| requires_async_stmt(s))
2417}
2418
2419fn requires_async_stmt(stmt: &Stmt) -> bool {
2420 match stmt {
2421 Stmt::Concurrent { tasks } => true,
2423 Stmt::Listen { .. } => true,
2425 Stmt::ConnectTo { .. } => true,
2426 Stmt::Sleep { .. } => true,
2427 Stmt::Sync { .. } => true,
2429 Stmt::Mount { .. } => true,
2431 Stmt::ReadFrom { source: ReadSource::File(_), .. } => true,
2433 Stmt::WriteFile { .. } => true,
2434 Stmt::LaunchTask { .. } => true,
2436 Stmt::LaunchTaskWithHandle { .. } => true,
2437 Stmt::SendPipe { .. } => true,
2438 Stmt::ReceivePipe { .. } => true,
2439 Stmt::Select { .. } => true,
2440 Stmt::If { then_block, else_block, .. } => {
2444 then_block.iter().any(|s| requires_async_stmt(s))
2445 || else_block.map_or(false, |b| b.iter().any(|s| requires_async_stmt(s)))
2446 }
2447 Stmt::While { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2448 Stmt::Repeat { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2449 Stmt::Zone { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2450 Stmt::Parallel { tasks } => tasks.iter().any(|s| requires_async_stmt(s)),
2451 Stmt::FunctionDef { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2452 Stmt::Inspect { arms, .. } => {
2454 arms.iter().any(|arm| arm.body.iter().any(|s| requires_async_stmt(s)))
2455 }
2456 _ => false,
2457 }
2458}
2459
2460fn requires_vfs(stmts: &[Stmt]) -> bool {
2463 stmts.iter().any(|s| requires_vfs_stmt(s))
2464}
2465
2466fn requires_vfs_stmt(stmt: &Stmt) -> bool {
2467 match stmt {
2468 Stmt::Mount { .. } => true,
2470 Stmt::ReadFrom { source: ReadSource::File(_), .. } => true,
2472 Stmt::WriteFile { .. } => true,
2473 Stmt::If { then_block, else_block, .. } => {
2475 then_block.iter().any(|s| requires_vfs_stmt(s))
2476 || else_block.map_or(false, |b| b.iter().any(|s| requires_vfs_stmt(s)))
2477 }
2478 Stmt::While { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2479 Stmt::Repeat { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2480 Stmt::Zone { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2481 Stmt::Concurrent { tasks } => tasks.iter().any(|s| requires_vfs_stmt(s)),
2482 Stmt::Parallel { tasks } => tasks.iter().any(|s| requires_vfs_stmt(s)),
2483 Stmt::FunctionDef { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2484 _ => false,
2485 }
2486}
2487
2488fn get_root_identifier_for_mutability(expr: &Expr) -> Option<Symbol> {
2491 match expr {
2492 Expr::Identifier(sym) => Some(*sym),
2493 Expr::FieldAccess { object, .. } => get_root_identifier_for_mutability(object),
2494 _ => None,
2495 }
2496}
2497
2498fn collect_mutable_vars(stmts: &[Stmt]) -> HashSet<Symbol> {
2504 let mut targets = HashSet::new();
2505 for stmt in stmts {
2506 collect_mutable_vars_stmt(stmt, &mut targets);
2507 }
2508 targets
2509}
2510
2511fn collect_mutable_vars_stmt(stmt: &Stmt, targets: &mut HashSet<Symbol>) {
2512 match stmt {
2513 Stmt::Set { target, .. } => {
2514 targets.insert(*target);
2515 }
2516 Stmt::Push { collection, .. } => {
2517 if let Some(sym) = get_root_identifier_for_mutability(collection) {
2519 targets.insert(sym);
2520 }
2521 }
2522 Stmt::Pop { collection, .. } => {
2523 if let Some(sym) = get_root_identifier_for_mutability(collection) {
2525 targets.insert(sym);
2526 }
2527 }
2528 Stmt::Add { collection, .. } => {
2529 if let Some(sym) = get_root_identifier_for_mutability(collection) {
2531 targets.insert(sym);
2532 }
2533 }
2534 Stmt::Remove { collection, .. } => {
2535 if let Some(sym) = get_root_identifier_for_mutability(collection) {
2537 targets.insert(sym);
2538 }
2539 }
2540 Stmt::SetIndex { collection, .. } => {
2541 if let Some(sym) = get_root_identifier_for_mutability(collection) {
2543 targets.insert(sym);
2544 }
2545 }
2546 Stmt::If { then_block, else_block, .. } => {
2547 for s in *then_block {
2548 collect_mutable_vars_stmt(s, targets);
2549 }
2550 if let Some(else_stmts) = else_block {
2551 for s in *else_stmts {
2552 collect_mutable_vars_stmt(s, targets);
2553 }
2554 }
2555 }
2556 Stmt::While { body, .. } => {
2557 for s in *body {
2558 collect_mutable_vars_stmt(s, targets);
2559 }
2560 }
2561 Stmt::Repeat { body, .. } => {
2562 for s in *body {
2563 collect_mutable_vars_stmt(s, targets);
2564 }
2565 }
2566 Stmt::Zone { body, .. } => {
2567 for s in *body {
2568 collect_mutable_vars_stmt(s, targets);
2569 }
2570 }
2571 Stmt::Inspect { arms, .. } => {
2573 for arm in arms.iter() {
2574 for s in arm.body.iter() {
2575 collect_mutable_vars_stmt(s, targets);
2576 }
2577 }
2578 }
2579 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
2581 for s in *tasks {
2582 collect_mutable_vars_stmt(s, targets);
2583 }
2584 }
2585 Stmt::IncreaseCrdt { object, .. } | Stmt::DecreaseCrdt { object, .. } => {
2587 if let Some(sym) = get_root_identifier_for_mutability(object) {
2589 targets.insert(sym);
2590 }
2591 }
2592 Stmt::AppendToSequence { sequence, .. } => {
2593 if let Some(sym) = get_root_identifier_for_mutability(sequence) {
2594 targets.insert(sym);
2595 }
2596 }
2597 Stmt::ResolveConflict { object, .. } => {
2598 if let Some(sym) = get_root_identifier_for_mutability(object) {
2599 targets.insert(sym);
2600 }
2601 }
2602 Stmt::SetField { object, .. } => {
2604 if let Some(sym) = get_root_identifier_for_mutability(object) {
2605 targets.insert(sym);
2606 }
2607 }
2608 _ => {}
2609 }
2610}
2611
2612fn codegen_policy_impls(policies: &PolicyRegistry, interner: &Interner) -> String {
2618 let mut output = String::new();
2619
2620 let mut type_predicates: HashMap<Symbol, Vec<&PredicateDef>> = HashMap::new();
2622 let mut type_capabilities: HashMap<Symbol, Vec<&CapabilityDef>> = HashMap::new();
2623
2624 for (type_sym, predicates) in policies.iter_predicates() {
2625 type_predicates.entry(*type_sym).or_insert_with(Vec::new).extend(predicates.iter());
2626 }
2627
2628 for (type_sym, capabilities) in policies.iter_capabilities() {
2629 type_capabilities.entry(*type_sym).or_insert_with(Vec::new).extend(capabilities.iter());
2630 }
2631
2632 let mut all_types: HashSet<Symbol> = HashSet::new();
2634 all_types.extend(type_predicates.keys().copied());
2635 all_types.extend(type_capabilities.keys().copied());
2636
2637 for type_sym in all_types {
2639 let type_name = interner.resolve(type_sym);
2640
2641 writeln!(output, "impl {} {{", type_name).unwrap();
2642
2643 if let Some(predicates) = type_predicates.get(&type_sym) {
2645 for pred in predicates {
2646 let pred_name = interner.resolve(pred.predicate_name).to_lowercase();
2647 writeln!(output, " pub fn is_{}(&self) -> bool {{", pred_name).unwrap();
2648 let condition_code = codegen_policy_condition(&pred.condition, interner);
2649 writeln!(output, " {}", condition_code).unwrap();
2650 writeln!(output, " }}\n").unwrap();
2651 }
2652 }
2653
2654 if let Some(capabilities) = type_capabilities.get(&type_sym) {
2656 for cap in capabilities {
2657 let action_name = interner.resolve(cap.action).to_lowercase();
2658 let object_type = interner.resolve(cap.object_type);
2659 let object_param = object_type.to_lowercase();
2660
2661 writeln!(output, " pub fn can_{}(&self, {}: &{}) -> bool {{",
2662 action_name, object_param, object_type).unwrap();
2663 let condition_code = codegen_policy_condition(&cap.condition, interner);
2664 writeln!(output, " {}", condition_code).unwrap();
2665 writeln!(output, " }}\n").unwrap();
2666 }
2667 }
2668
2669 writeln!(output, "}}\n").unwrap();
2670 }
2671
2672 output
2673}
2674
2675fn codegen_policy_condition(condition: &PolicyCondition, interner: &Interner) -> String {
2677 match condition {
2678 PolicyCondition::FieldEquals { field, value, is_string_literal } => {
2679 let field_name = interner.resolve(*field);
2680 let value_str = interner.resolve(*value);
2681 if *is_string_literal {
2682 format!("self.{} == \"{}\"", field_name, value_str)
2683 } else {
2684 format!("self.{} == {}", field_name, value_str)
2685 }
2686 }
2687 PolicyCondition::FieldBool { field, value } => {
2688 let field_name = interner.resolve(*field);
2689 format!("self.{} == {}", field_name, value)
2690 }
2691 PolicyCondition::Predicate { subject: _, predicate } => {
2692 let pred_name = interner.resolve(*predicate).to_lowercase();
2693 format!("self.is_{}()", pred_name)
2694 }
2695 PolicyCondition::ObjectFieldEquals { subject: _, object, field } => {
2696 let object_name = interner.resolve(*object).to_lowercase();
2697 let field_name = interner.resolve(*field);
2698 format!("self == &{}.{}", object_name, field_name)
2699 }
2700 PolicyCondition::Or(left, right) => {
2701 let left_code = codegen_policy_condition(left, interner);
2702 let right_code = codegen_policy_condition(right, interner);
2703 format!("{} || {}", left_code, right_code)
2704 }
2705 PolicyCondition::And(left, right) => {
2706 let left_code = codegen_policy_condition(left, interner);
2707 let right_code = codegen_policy_condition(right, interner);
2708 format!("{} && {}", left_code, right_code)
2709 }
2710 }
2711}
2712
2713fn collect_crdt_register_fields(registry: &TypeRegistry, interner: &Interner) -> (HashSet<(String, String)>, HashSet<(String, String)>) {
2718 let mut lww_fields = HashSet::new();
2719 let mut mv_fields = HashSet::new();
2720 for (type_sym, def) in registry.iter_types() {
2721 if let TypeDef::Struct { fields, .. } = def {
2722 let type_name = interner.resolve(*type_sym).to_string();
2723 for field in fields {
2724 if let FieldType::Generic { base, .. } = &field.ty {
2725 let base_name = interner.resolve(*base);
2726 let field_name = interner.resolve(field.name).to_string();
2727 if base_name == "LastWriteWins" {
2728 lww_fields.insert((type_name.clone(), field_name));
2729 } else if base_name == "Divergent" || base_name == "MVRegister" {
2730 mv_fields.insert((type_name.clone(), field_name));
2731 }
2732 }
2733 }
2734 }
2735 }
2736 (lww_fields, mv_fields)
2737}
2738
2739fn collect_boxed_fields(registry: &TypeRegistry, interner: &Interner) -> HashSet<(String, String, String)> {
2742 let mut boxed_fields = HashSet::new();
2743 for (type_sym, def) in registry.iter_types() {
2744 if let TypeDef::Enum { variants, .. } = def {
2745 let enum_name = interner.resolve(*type_sym);
2746 for variant in variants {
2747 let variant_name = interner.resolve(variant.name);
2748 for field in &variant.fields {
2749 if is_recursive_field(&field.ty, enum_name, interner) {
2750 let field_name = interner.resolve(field.name).to_string();
2751 boxed_fields.insert((
2752 enum_name.to_string(),
2753 variant_name.to_string(),
2754 field_name,
2755 ));
2756 }
2757 }
2758 }
2759 }
2760 }
2761 boxed_fields
2762}
2763
2764pub fn collect_async_functions(stmts: &[Stmt]) -> HashSet<Symbol> {
2771 let mut func_bodies: HashMap<Symbol, &[Stmt]> = HashMap::new();
2773 for stmt in stmts {
2774 if let Stmt::FunctionDef { name, body, .. } = stmt {
2775 func_bodies.insert(*name, *body);
2776 }
2777 }
2778
2779 let mut async_fns = HashSet::new();
2781 for stmt in stmts {
2782 if let Stmt::FunctionDef { name, body, .. } = stmt {
2783 if body.iter().any(|s| requires_async_stmt(s)) {
2784 async_fns.insert(*name);
2785 }
2786 }
2787 }
2788
2789 loop {
2791 let mut changed = false;
2792 for (func_name, body) in &func_bodies {
2793 if async_fns.contains(func_name) {
2794 continue; }
2796 if body.iter().any(|s| calls_async_function(s, &async_fns)) {
2798 async_fns.insert(*func_name);
2799 changed = true;
2800 }
2801 }
2802 if !changed {
2803 break;
2804 }
2805 }
2806
2807 async_fns
2808}
2809
2810fn calls_async_function(stmt: &Stmt, async_fns: &HashSet<Symbol>) -> bool {
2812 match stmt {
2813 Stmt::Call { function, args } => {
2814 async_fns.contains(function)
2816 || args.iter().any(|a| calls_async_function_in_expr(a, async_fns))
2817 }
2818 Stmt::If { cond, then_block, else_block } => {
2819 calls_async_function_in_expr(cond, async_fns)
2820 || then_block.iter().any(|s| calls_async_function(s, async_fns))
2821 || else_block.map_or(false, |b| b.iter().any(|s| calls_async_function(s, async_fns)))
2822 }
2823 Stmt::While { cond, body, .. } => {
2824 calls_async_function_in_expr(cond, async_fns)
2825 || body.iter().any(|s| calls_async_function(s, async_fns))
2826 }
2827 Stmt::Repeat { iterable, body, .. } => {
2828 calls_async_function_in_expr(iterable, async_fns)
2829 || body.iter().any(|s| calls_async_function(s, async_fns))
2830 }
2831 Stmt::Zone { body, .. } => {
2832 body.iter().any(|s| calls_async_function(s, async_fns))
2833 }
2834 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
2835 tasks.iter().any(|s| calls_async_function(s, async_fns))
2836 }
2837 Stmt::FunctionDef { body, .. } => {
2838 body.iter().any(|s| calls_async_function(s, async_fns))
2839 }
2840 Stmt::Let { value, .. } => calls_async_function_in_expr(value, async_fns),
2842 Stmt::Set { value, .. } => calls_async_function_in_expr(value, async_fns),
2844 Stmt::Return { value } => {
2846 value.as_ref().map_or(false, |v| calls_async_function_in_expr(v, async_fns))
2847 }
2848 Stmt::RuntimeAssert { condition } => calls_async_function_in_expr(condition, async_fns),
2850 Stmt::Show { object, .. } => calls_async_function_in_expr(object, async_fns),
2852 Stmt::Push { collection, value } => {
2854 calls_async_function_in_expr(collection, async_fns)
2855 || calls_async_function_in_expr(value, async_fns)
2856 }
2857 Stmt::SetIndex { collection, index, value } => {
2859 calls_async_function_in_expr(collection, async_fns)
2860 || calls_async_function_in_expr(index, async_fns)
2861 || calls_async_function_in_expr(value, async_fns)
2862 }
2863 Stmt::SendPipe { value, pipe } | Stmt::TrySendPipe { value, pipe, .. } => {
2865 calls_async_function_in_expr(value, async_fns)
2866 || calls_async_function_in_expr(pipe, async_fns)
2867 }
2868 Stmt::Inspect { target, arms, .. } => {
2870 calls_async_function_in_expr(target, async_fns)
2871 || arms.iter().any(|arm| arm.body.iter().any(|s| calls_async_function(s, async_fns)))
2872 }
2873 _ => false,
2874 }
2875}
2876
2877fn calls_async_function_in_expr(expr: &Expr, async_fns: &HashSet<Symbol>) -> bool {
2879 match expr {
2880 Expr::Call { function, args } => {
2881 async_fns.contains(function)
2882 || args.iter().any(|a| calls_async_function_in_expr(a, async_fns))
2883 }
2884 Expr::BinaryOp { left, right, .. } => {
2885 calls_async_function_in_expr(left, async_fns)
2886 || calls_async_function_in_expr(right, async_fns)
2887 }
2888 Expr::Index { collection, index } => {
2889 calls_async_function_in_expr(collection, async_fns)
2890 || calls_async_function_in_expr(index, async_fns)
2891 }
2892 Expr::FieldAccess { object, .. } => calls_async_function_in_expr(object, async_fns),
2893 Expr::List(items) | Expr::Tuple(items) => {
2894 items.iter().any(|i| calls_async_function_in_expr(i, async_fns))
2895 }
2896 Expr::Closure { body, .. } => {
2897 match body {
2898 crate::ast::stmt::ClosureBody::Expression(expr) => calls_async_function_in_expr(expr, async_fns),
2899 crate::ast::stmt::ClosureBody::Block(_) => false,
2900 }
2901 }
2902 Expr::CallExpr { callee, args } => {
2903 calls_async_function_in_expr(callee, async_fns)
2904 || args.iter().any(|a| calls_async_function_in_expr(a, async_fns))
2905 }
2906 _ => false,
2907 }
2908}
2909
2910fn collect_pure_functions(stmts: &[Stmt]) -> HashSet<Symbol> {
2915 let mut func_bodies: HashMap<Symbol, &[Stmt]> = HashMap::new();
2916 for stmt in stmts {
2917 if let Stmt::FunctionDef { name, body, .. } = stmt {
2918 func_bodies.insert(*name, *body);
2919 }
2920 }
2921
2922 let mut impure_fns = HashSet::new();
2924 for (func_name, body) in &func_bodies {
2925 if body.iter().any(|s| is_directly_impure_stmt(s)) {
2926 impure_fns.insert(*func_name);
2927 }
2928 }
2929
2930 loop {
2932 let mut changed = false;
2933 for (func_name, body) in &func_bodies {
2934 if impure_fns.contains(func_name) {
2935 continue;
2936 }
2937 if body.iter().any(|s| calls_impure_function(s, &impure_fns)) {
2938 impure_fns.insert(*func_name);
2939 changed = true;
2940 }
2941 }
2942 if !changed {
2943 break;
2944 }
2945 }
2946
2947 let mut pure_fns = HashSet::new();
2949 for func_name in func_bodies.keys() {
2950 if !impure_fns.contains(func_name) {
2951 pure_fns.insert(*func_name);
2952 }
2953 }
2954 pure_fns
2955}
2956
2957fn is_directly_impure_stmt(stmt: &Stmt) -> bool {
2958 match stmt {
2959 Stmt::Show { .. }
2960 | Stmt::Give { .. }
2961 | Stmt::WriteFile { .. }
2962 | Stmt::ReadFrom { .. }
2963 | Stmt::Listen { .. }
2964 | Stmt::ConnectTo { .. }
2965 | Stmt::SendMessage { .. }
2966 | Stmt::AwaitMessage { .. }
2967 | Stmt::Sleep { .. }
2968 | Stmt::Sync { .. }
2969 | Stmt::Mount { .. }
2970 | Stmt::MergeCrdt { .. }
2971 | Stmt::IncreaseCrdt { .. }
2972 | Stmt::DecreaseCrdt { .. }
2973 | Stmt::AppendToSequence { .. }
2974 | Stmt::ResolveConflict { .. }
2975 | Stmt::CreatePipe { .. }
2976 | Stmt::SendPipe { .. }
2977 | Stmt::ReceivePipe { .. }
2978 | Stmt::TrySendPipe { .. }
2979 | Stmt::TryReceivePipe { .. }
2980 | Stmt::LaunchTask { .. }
2981 | Stmt::LaunchTaskWithHandle { .. }
2982 | Stmt::StopTask { .. }
2983 | Stmt::Concurrent { .. }
2984 | Stmt::Parallel { .. } => true,
2985 Stmt::If { then_block, else_block, .. } => {
2986 then_block.iter().any(|s| is_directly_impure_stmt(s))
2987 || else_block.map_or(false, |b| b.iter().any(|s| is_directly_impure_stmt(s)))
2988 }
2989 Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
2990 body.iter().any(|s| is_directly_impure_stmt(s))
2991 }
2992 Stmt::Zone { body, .. } => {
2993 body.iter().any(|s| is_directly_impure_stmt(s))
2994 }
2995 Stmt::Inspect { arms, .. } => {
2996 arms.iter().any(|arm| arm.body.iter().any(|s| is_directly_impure_stmt(s)))
2997 }
2998 _ => false,
2999 }
3000}
3001
3002fn calls_impure_function(stmt: &Stmt, impure_fns: &HashSet<Symbol>) -> bool {
3003 match stmt {
3004 Stmt::Call { function, args } => {
3005 impure_fns.contains(function)
3006 || args.iter().any(|a| expr_calls_impure(a, impure_fns))
3007 }
3008 Stmt::Let { value, .. } => expr_calls_impure(value, impure_fns),
3009 Stmt::Set { value, .. } => expr_calls_impure(value, impure_fns),
3010 Stmt::Return { value } => value.as_ref().map_or(false, |v| expr_calls_impure(v, impure_fns)),
3011 Stmt::If { cond, then_block, else_block } => {
3012 expr_calls_impure(cond, impure_fns)
3013 || then_block.iter().any(|s| calls_impure_function(s, impure_fns))
3014 || else_block.map_or(false, |b| b.iter().any(|s| calls_impure_function(s, impure_fns)))
3015 }
3016 Stmt::While { cond, body, .. } => {
3017 expr_calls_impure(cond, impure_fns)
3018 || body.iter().any(|s| calls_impure_function(s, impure_fns))
3019 }
3020 Stmt::Repeat { body, .. } => body.iter().any(|s| calls_impure_function(s, impure_fns)),
3021 Stmt::Zone { body, .. } => body.iter().any(|s| calls_impure_function(s, impure_fns)),
3022 Stmt::Inspect { arms, .. } => {
3023 arms.iter().any(|arm| arm.body.iter().any(|s| calls_impure_function(s, impure_fns)))
3024 }
3025 Stmt::Show { object, .. } => expr_calls_impure(object, impure_fns),
3026 Stmt::Push { value, collection } | Stmt::Add { value, collection } | Stmt::Remove { value, collection } => {
3027 expr_calls_impure(value, impure_fns) || expr_calls_impure(collection, impure_fns)
3028 }
3029 _ => false,
3030 }
3031}
3032
3033fn expr_calls_impure(expr: &Expr, impure_fns: &HashSet<Symbol>) -> bool {
3034 match expr {
3035 Expr::Call { function, args } => {
3036 impure_fns.contains(function)
3037 || args.iter().any(|a| expr_calls_impure(a, impure_fns))
3038 }
3039 Expr::BinaryOp { left, right, .. } => {
3040 expr_calls_impure(left, impure_fns) || expr_calls_impure(right, impure_fns)
3041 }
3042 Expr::Index { collection, index } => {
3043 expr_calls_impure(collection, impure_fns) || expr_calls_impure(index, impure_fns)
3044 }
3045 Expr::FieldAccess { object, .. } => expr_calls_impure(object, impure_fns),
3046 Expr::List(items) | Expr::Tuple(items) => items.iter().any(|i| expr_calls_impure(i, impure_fns)),
3047 Expr::CallExpr { callee, args } => {
3048 expr_calls_impure(callee, impure_fns)
3049 || args.iter().any(|a| expr_calls_impure(a, impure_fns))
3050 }
3051 _ => false,
3052 }
3053}
3054
3055fn count_self_calls(func_name: Symbol, body: &[Stmt]) -> usize {
3060 let mut count = 0;
3061 for stmt in body {
3062 count += count_self_calls_in_stmt(func_name, stmt);
3063 }
3064 count
3065}
3066
3067fn count_self_calls_in_stmt(func_name: Symbol, stmt: &Stmt) -> usize {
3068 match stmt {
3069 Stmt::Return { value: Some(expr) } => count_self_calls_in_expr(func_name, expr),
3070 Stmt::Let { value, .. } => count_self_calls_in_expr(func_name, value),
3071 Stmt::Set { value, .. } => count_self_calls_in_expr(func_name, value),
3072 Stmt::Call { function, args } => {
3073 let mut c = if *function == func_name { 1 } else { 0 };
3074 c += args.iter().map(|a| count_self_calls_in_expr(func_name, a)).sum::<usize>();
3075 c
3076 }
3077 Stmt::If { cond, then_block, else_block } => {
3078 let mut c = count_self_calls_in_expr(func_name, cond);
3079 c += count_self_calls(func_name, then_block);
3080 if let Some(else_stmts) = else_block {
3081 c += count_self_calls(func_name, else_stmts);
3082 }
3083 c
3084 }
3085 Stmt::While { cond, body, .. } => {
3086 count_self_calls_in_expr(func_name, cond) + count_self_calls(func_name, body)
3087 }
3088 Stmt::Repeat { body, .. } => count_self_calls(func_name, body),
3089 Stmt::Show { object, .. } => count_self_calls_in_expr(func_name, object),
3090 _ => 0,
3091 }
3092}
3093
3094fn count_self_calls_in_expr(func_name: Symbol, expr: &Expr) -> usize {
3095 match expr {
3096 Expr::Call { function, args } => {
3097 let mut c = if *function == func_name { 1 } else { 0 };
3098 c += args.iter().map(|a| count_self_calls_in_expr(func_name, a)).sum::<usize>();
3099 c
3100 }
3101 Expr::BinaryOp { left, right, .. } => {
3102 count_self_calls_in_expr(func_name, left) + count_self_calls_in_expr(func_name, right)
3103 }
3104 Expr::Index { collection, index } => {
3105 count_self_calls_in_expr(func_name, collection) + count_self_calls_in_expr(func_name, index)
3106 }
3107 Expr::FieldAccess { object, .. } => count_self_calls_in_expr(func_name, object),
3108 Expr::List(items) | Expr::Tuple(items) => {
3109 items.iter().map(|i| count_self_calls_in_expr(func_name, i)).sum()
3110 }
3111 _ => 0,
3112 }
3113}
3114
3115fn is_hashable_type(ty: &TypeExpr, interner: &Interner) -> bool {
3116 match ty {
3117 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
3118 let name = interner.resolve(*sym);
3119 matches!(name, "Int" | "Nat" | "Bool" | "Char" | "Byte" | "Text"
3120 | "i64" | "u64" | "bool" | "char" | "u8" | "String")
3121 }
3122 TypeExpr::Refinement { base, .. } => is_hashable_type(base, interner),
3123 _ => false,
3124 }
3125}
3126
3127fn is_copy_type_expr(ty: &TypeExpr, interner: &Interner) -> bool {
3128 match ty {
3129 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
3130 let name = interner.resolve(*sym);
3131 matches!(name, "Int" | "Nat" | "Bool" | "Char" | "Byte"
3132 | "i64" | "u64" | "bool" | "char" | "u8")
3133 }
3134 TypeExpr::Refinement { base, .. } => is_copy_type_expr(base, interner),
3135 _ => false,
3136 }
3137}
3138
3139fn should_memoize(
3140 name: Symbol,
3141 body: &[Stmt],
3142 params: &[(Symbol, &TypeExpr)],
3143 return_type: Option<&TypeExpr>,
3144 is_pure: bool,
3145 interner: &Interner,
3146) -> bool {
3147 if !is_pure {
3148 return false;
3149 }
3150 if !body_contains_self_call(name, body) {
3151 return false;
3152 }
3153 if count_self_calls(name, body) < 2 {
3154 return false;
3155 }
3156 if params.is_empty() {
3157 return false;
3158 }
3159 if !params.iter().all(|(_, ty)| is_hashable_type(ty, interner)) {
3160 return false;
3161 }
3162 if return_type.is_none() {
3163 return false;
3164 }
3165 true
3166}
3167
3168fn expr_is_self_call(func_name: Symbol, expr: &Expr) -> bool {
3173 matches!(expr, Expr::Call { function, .. } if *function == func_name)
3174}
3175
3176fn has_tail_call_in_stmt(func_name: Symbol, stmt: &Stmt) -> bool {
3177 match stmt {
3178 Stmt::Return { value: Some(expr) } => {
3179 if expr_is_self_call(func_name, expr) {
3180 return true;
3181 }
3182 if let Expr::Call { function, args } = expr {
3185 if *function == func_name {
3186 return true;
3187 }
3188 let _ = args;
3190 }
3191 false
3192 }
3193 Stmt::If { then_block, else_block, .. } => {
3194 let then_tail = then_block.last()
3195 .map_or(false, |s| has_tail_call_in_stmt(func_name, s));
3196 let else_tail = else_block
3197 .and_then(|block| block.last())
3198 .map_or(false, |s| has_tail_call_in_stmt(func_name, s));
3199 then_tail || else_tail
3200 }
3201 _ => false,
3202 }
3203}
3204
3205fn is_tail_recursive(func_name: Symbol, body: &[Stmt]) -> bool {
3206 body.iter().any(|s| has_tail_call_in_stmt(func_name, s))
3207}
3208
3209fn body_contains_self_call(func_name: Symbol, body: &[Stmt]) -> bool {
3210 body.iter().any(|s| stmt_contains_self_call(func_name, s))
3211}
3212
3213fn stmt_contains_self_call(func_name: Symbol, stmt: &Stmt) -> bool {
3214 match stmt {
3215 Stmt::Return { value: Some(expr) } => expr_contains_self_call(func_name, expr),
3216 Stmt::Return { value: None } => false,
3217 Stmt::Let { value, .. } => expr_contains_self_call(func_name, value),
3218 Stmt::Set { value, .. } => expr_contains_self_call(func_name, value),
3219 Stmt::Call { function, args } => {
3220 *function == func_name || args.iter().any(|a| expr_contains_self_call(func_name, a))
3221 }
3222 Stmt::If { cond, then_block, else_block } => {
3223 expr_contains_self_call(func_name, cond)
3224 || then_block.iter().any(|s| stmt_contains_self_call(func_name, s))
3225 || else_block.map_or(false, |b| b.iter().any(|s| stmt_contains_self_call(func_name, s)))
3226 }
3227 Stmt::While { cond, body, .. } => {
3228 expr_contains_self_call(func_name, cond)
3229 || body.iter().any(|s| stmt_contains_self_call(func_name, s))
3230 }
3231 Stmt::Repeat { body, .. } => {
3232 body.iter().any(|s| stmt_contains_self_call(func_name, s))
3233 }
3234 Stmt::Show { object, .. } => expr_contains_self_call(func_name, object),
3235 _ => false,
3236 }
3237}
3238
3239fn expr_contains_self_call(func_name: Symbol, expr: &Expr) -> bool {
3240 match expr {
3241 Expr::Call { function, args } => {
3242 *function == func_name || args.iter().any(|a| expr_contains_self_call(func_name, a))
3243 }
3244 Expr::BinaryOp { left, right, .. } => {
3245 expr_contains_self_call(func_name, left) || expr_contains_self_call(func_name, right)
3246 }
3247 Expr::Index { collection, index } => {
3248 expr_contains_self_call(func_name, collection) || expr_contains_self_call(func_name, index)
3249 }
3250 Expr::FieldAccess { object, .. } => expr_contains_self_call(func_name, object),
3251 Expr::List(items) | Expr::Tuple(items) => {
3252 items.iter().any(|i| expr_contains_self_call(func_name, i))
3253 }
3254 _ => false,
3255 }
3256}
3257
3258fn should_inline(name: Symbol, body: &[Stmt], is_native: bool, is_exported: bool, is_async: bool) -> bool {
3263 !is_native && !is_exported && !is_async
3264 && body.len() <= 5
3265 && !body_contains_self_call(name, body)
3266}
3267
3268#[derive(Debug)]
3273enum NonRecSide { Left, Right }
3274
3275#[derive(Debug)]
3276struct AccumulatorInfo {
3277 op: BinaryOpKind,
3278 identity: &'static str,
3279 non_recursive_side: NonRecSide,
3280}
3281
3282fn detect_accumulator_pattern(func_name: Symbol, body: &[Stmt]) -> Option<AccumulatorInfo> {
3283 if has_non_return_self_calls(func_name, body) {
3284 return None;
3285 }
3286 let (base_count, recursive_count) = count_recursive_returns(func_name, body);
3287 if recursive_count != 1 {
3288 return None;
3289 }
3290 if base_count == 0 {
3291 return None;
3292 }
3293 find_accumulator_return(func_name, body)
3294}
3295
3296fn count_recursive_returns(func_name: Symbol, body: &[Stmt]) -> (usize, usize) {
3297 let mut base = 0;
3298 let mut recursive = 0;
3299 for stmt in body {
3300 match stmt {
3301 Stmt::Return { value: Some(expr) } => {
3302 if expr_contains_self_call(func_name, expr) {
3303 recursive += 1;
3304 } else {
3305 base += 1;
3306 }
3307 }
3308 Stmt::Return { value: None } => {
3309 base += 1;
3310 }
3311 Stmt::If { then_block, else_block, .. } => {
3312 let (tb, tr) = count_recursive_returns(func_name, then_block);
3313 base += tb;
3314 recursive += tr;
3315 if let Some(else_stmts) = else_block {
3316 let (eb, er) = count_recursive_returns(func_name, else_stmts);
3317 base += eb;
3318 recursive += er;
3319 }
3320 }
3321 _ => {}
3322 }
3323 }
3324 (base, recursive)
3325}
3326
3327fn has_non_return_self_calls(func_name: Symbol, body: &[Stmt]) -> bool {
3328 for stmt in body {
3329 match stmt {
3330 Stmt::Return { .. } => {}
3331 Stmt::If { cond, then_block, else_block } => {
3332 if expr_contains_self_call(func_name, cond) {
3333 return true;
3334 }
3335 if has_non_return_self_calls(func_name, then_block) {
3336 return true;
3337 }
3338 if let Some(else_stmts) = else_block {
3339 if has_non_return_self_calls(func_name, else_stmts) {
3340 return true;
3341 }
3342 }
3343 }
3344 Stmt::Let { value, .. } => {
3345 if expr_contains_self_call(func_name, value) {
3346 return true;
3347 }
3348 }
3349 Stmt::Set { value, .. } => {
3350 if expr_contains_self_call(func_name, value) {
3351 return true;
3352 }
3353 }
3354 Stmt::Show { object, .. } => {
3355 if expr_contains_self_call(func_name, object) {
3356 return true;
3357 }
3358 }
3359 Stmt::While { cond, body, .. } => {
3360 if expr_contains_self_call(func_name, cond) {
3361 return true;
3362 }
3363 if has_non_return_self_calls(func_name, body) {
3364 return true;
3365 }
3366 }
3367 Stmt::Repeat { body, .. } => {
3368 if has_non_return_self_calls(func_name, body) {
3369 return true;
3370 }
3371 }
3372 Stmt::Call { function, args } => {
3373 if *function == func_name || args.iter().any(|a| expr_contains_self_call(func_name, a)) {
3374 return true;
3375 }
3376 }
3377 _ => {}
3378 }
3379 }
3380 false
3381}
3382
3383fn find_accumulator_return(func_name: Symbol, body: &[Stmt]) -> Option<AccumulatorInfo> {
3384 for stmt in body {
3385 match stmt {
3386 Stmt::Return { value: Some(expr) } => {
3387 if let Expr::BinaryOp { op, left, right } = expr {
3388 match op {
3389 BinaryOpKind::Add | BinaryOpKind::Multiply => {
3390 let left_has_call = expr_is_self_call(func_name, left);
3391 let right_has_call = expr_is_self_call(func_name, right);
3392 let left_contains_call = expr_contains_self_call(func_name, left);
3393 let right_contains_call = expr_contains_self_call(func_name, right);
3394 let identity = match op {
3395 BinaryOpKind::Add => "0",
3396 BinaryOpKind::Multiply => "1",
3397 _ => unreachable!(),
3398 };
3399 if left_has_call && !right_contains_call {
3400 return Some(AccumulatorInfo {
3401 op: *op,
3402 identity,
3403 non_recursive_side: NonRecSide::Right,
3404 });
3405 }
3406 if right_has_call && !left_contains_call {
3407 return Some(AccumulatorInfo {
3408 op: *op,
3409 identity,
3410 non_recursive_side: NonRecSide::Left,
3411 });
3412 }
3413 }
3414 _ => {}
3415 }
3416 }
3417 }
3418 Stmt::If { then_block, else_block, .. } => {
3419 if let Some(info) = find_accumulator_return(func_name, then_block) {
3420 return Some(info);
3421 }
3422 if let Some(else_stmts) = else_block {
3423 if let Some(info) = find_accumulator_return(func_name, else_stmts) {
3424 return Some(info);
3425 }
3426 }
3427 }
3428 _ => {}
3429 }
3430 }
3431 None
3432}
3433
3434fn codegen_stmt_acc<'a>(
3439 stmt: &Stmt<'a>,
3440 func_name: Symbol,
3441 param_names: &[Symbol],
3442 acc_info: &AccumulatorInfo,
3443 interner: &Interner,
3444 indent: usize,
3445 mutable_vars: &HashSet<Symbol>,
3446 ctx: &mut RefinementContext<'a>,
3447 lww_fields: &HashSet<(String, String)>,
3448 mv_fields: &HashSet<(String, String)>,
3449 synced_vars: &mut HashSet<Symbol>,
3450 var_caps: &HashMap<Symbol, VariableCapabilities>,
3451 async_functions: &HashSet<Symbol>,
3452 pipe_vars: &HashSet<Symbol>,
3453 boxed_fields: &HashSet<(String, String, String)>,
3454 registry: &TypeRegistry,
3455) -> String {
3456 let indent_str = " ".repeat(indent);
3457 let op_str = match acc_info.op {
3458 BinaryOpKind::Add => "+",
3459 BinaryOpKind::Multiply => "*",
3460 _ => unreachable!(),
3461 };
3462
3463 match stmt {
3464 Stmt::Return { value: Some(expr) } if expr_contains_self_call(func_name, expr) => {
3466 if let Expr::BinaryOp { left, right, .. } = expr {
3467 let (call_expr, non_rec_expr) = match acc_info.non_recursive_side {
3468 NonRecSide::Left => (right, left),
3469 NonRecSide::Right => (left, right),
3470 };
3471 if let Expr::Call { args, .. } = call_expr {
3473 let mut output = String::new();
3474 writeln!(output, "{}{{", indent_str).unwrap();
3475 let non_rec_str = codegen_expr_with_async(non_rec_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
3476 writeln!(output, "{} let __acc_expr = {};", indent_str, non_rec_str).unwrap();
3477 writeln!(output, "{} __acc = __acc {} __acc_expr;", indent_str, op_str).unwrap();
3478 for (i, arg) in args.iter().enumerate() {
3480 let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3481 writeln!(output, "{} let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3482 }
3483 for (i, param_sym) in param_names.iter().enumerate() {
3485 let param_name = interner.resolve(*param_sym);
3486 writeln!(output, "{} {} = __tce_{};", indent_str, param_name, i).unwrap();
3487 }
3488 writeln!(output, "{} continue;", indent_str).unwrap();
3489 writeln!(output, "{}}}", indent_str).unwrap();
3490 return output;
3491 }
3492 }
3493 codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3495 }
3496
3497 Stmt::Return { value: Some(expr) } => {
3499 let val_str = codegen_expr_with_async(expr, interner, synced_vars, async_functions, ctx.get_variable_types());
3500 format!("{}return __acc {} {};\n", indent_str, op_str, val_str)
3501 }
3502
3503 Stmt::Return { value: None } => {
3504 format!("{}return __acc;\n", indent_str)
3505 }
3506
3507 Stmt::If { cond, then_block, else_block } => {
3509 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
3510 let mut output = String::new();
3511 writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
3512 ctx.push_scope();
3513 for s in *then_block {
3514 output.push_str(&codegen_stmt_acc(s, func_name, param_names, acc_info, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3515 }
3516 ctx.pop_scope();
3517 if let Some(else_stmts) = else_block {
3518 writeln!(output, "{}}} else {{", indent_str).unwrap();
3519 ctx.push_scope();
3520 for s in *else_stmts {
3521 output.push_str(&codegen_stmt_acc(s, func_name, param_names, acc_info, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3522 }
3523 ctx.pop_scope();
3524 }
3525 writeln!(output, "{}}}", indent_str).unwrap();
3526 output
3527 }
3528
3529 _ => codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry),
3531 }
3532}
3533
3534fn find_tail_call_targets(func_name: Symbol, body: &[Stmt]) -> HashSet<Symbol> {
3539 let mut targets = HashSet::new();
3540 for stmt in body {
3541 collect_tail_targets(func_name, stmt, &mut targets);
3542 }
3543 targets
3544}
3545
3546fn collect_tail_targets(func_name: Symbol, stmt: &Stmt, targets: &mut HashSet<Symbol>) {
3547 match stmt {
3548 Stmt::Return { value: Some(Expr::Call { function, .. }) } => {
3549 if *function != func_name {
3550 targets.insert(*function);
3551 }
3552 }
3553 Stmt::If { then_block, else_block, .. } => {
3554 if let Some(last) = then_block.last() {
3555 collect_tail_targets(func_name, last, targets);
3556 }
3557 if let Some(else_stmts) = else_block {
3558 if let Some(last) = else_stmts.last() {
3559 collect_tail_targets(func_name, last, targets);
3560 }
3561 }
3562 }
3563 _ => {}
3564 }
3565}
3566
3567fn detect_mutual_tce_pairs<'a>(stmts: &'a [Stmt<'a>], interner: &Interner) -> Vec<(Symbol, Symbol)> {
3568 let mut func_defs: HashMap<Symbol, (&[(Symbol, &TypeExpr)], &[Stmt], Option<&TypeExpr>, bool, bool, bool)> = HashMap::new();
3570 for stmt in stmts {
3571 if let Stmt::FunctionDef { name, params, body, return_type, is_native, is_exported, .. } = stmt {
3572 let is_async_fn = false; func_defs.insert(*name, (params, body, return_type.as_ref().copied(), *is_native, *is_exported, is_async_fn));
3574 }
3575 }
3576
3577 let mut tail_targets: HashMap<Symbol, HashSet<Symbol>> = HashMap::new();
3579 for (name, (_, body, _, _, _, _)) in &func_defs {
3580 tail_targets.insert(*name, find_tail_call_targets(*name, body));
3581 }
3582
3583 let mut pairs = Vec::new();
3585 let mut used = HashSet::new();
3586 let names: Vec<Symbol> = func_defs.keys().copied().collect();
3587
3588 for i in 0..names.len() {
3589 for j in (i + 1)..names.len() {
3590 let a = names[i];
3591 let b = names[j];
3592 if used.contains(&a) || used.contains(&b) {
3593 continue;
3594 }
3595
3596 let a_targets = tail_targets.get(&a).cloned().unwrap_or_default();
3597 let b_targets = tail_targets.get(&b).cloned().unwrap_or_default();
3598
3599 if !a_targets.contains(&b) || !b_targets.contains(&a) {
3601 continue;
3602 }
3603
3604 let (a_params, _, a_ret, a_native, a_exported, _) = func_defs[&a];
3605 let (b_params, _, b_ret, b_native, b_exported, _) = func_defs[&b];
3606
3607 if a_native || b_native || a_exported || b_exported {
3609 continue;
3610 }
3611
3612 if a_params.len() != b_params.len() {
3614 continue;
3615 }
3616
3617 let same_params = a_params.iter().zip(b_params.iter()).all(|((_, t1), (_, t2))| {
3619 codegen_type_expr(t1, interner) == codegen_type_expr(t2, interner)
3620 });
3621 if !same_params {
3622 continue;
3623 }
3624
3625 let a_ret_str = a_ret.map(|t| codegen_type_expr(t, interner));
3627 let b_ret_str = b_ret.map(|t| codegen_type_expr(t, interner));
3628 if a_ret_str != b_ret_str {
3629 continue;
3630 }
3631
3632 pairs.push((a, b));
3635 used.insert(a);
3636 used.insert(b);
3637 }
3638 }
3639
3640 pairs
3641}
3642
3643fn codegen_mutual_tce_pair<'a>(
3648 func_a: Symbol,
3649 func_b: Symbol,
3650 stmts: &'a [Stmt<'a>],
3651 interner: &Interner,
3652 lww_fields: &HashSet<(String, String)>,
3653 mv_fields: &HashSet<(String, String)>,
3654 async_functions: &HashSet<Symbol>,
3655 boxed_fields: &HashSet<(String, String, String)>,
3656 registry: &TypeRegistry,
3657) -> String {
3658 let mut a_def = None;
3660 let mut b_def = None;
3661 for stmt in stmts {
3662 if let Stmt::FunctionDef { name, params, body, return_type, .. } = stmt {
3663 if *name == func_a {
3664 a_def = Some((params.as_slice(), *body, return_type.as_ref().copied()));
3665 } else if *name == func_b {
3666 b_def = Some((params.as_slice(), *body, return_type.as_ref().copied()));
3667 }
3668 }
3669 }
3670 let (a_params, a_body, a_ret) = a_def.expect("mutual TCE: func_a not found");
3671 let (b_params, b_body, _b_ret) = b_def.expect("mutual TCE: func_b not found");
3672
3673 let a_name = escape_rust_ident(interner.resolve(func_a));
3674 let b_name = escape_rust_ident(interner.resolve(func_b));
3675 let merged_name = format!("__mutual_{}_{}", a_name, b_name);
3676
3677 let params_str: Vec<String> = a_params.iter()
3679 .map(|(p, t)| format!("mut {}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
3680 .collect();
3681
3682 let ret_str = a_ret.map(|t| codegen_type_expr(t, interner));
3683
3684 let mut output = String::new();
3685
3686 let sig = if let Some(ref r) = ret_str {
3688 if r != "()" {
3689 format!("fn {}(mut __tag: u8, {}) -> {}", merged_name, params_str.join(", "), r)
3690 } else {
3691 format!("fn {}(mut __tag: u8, {})", merged_name, params_str.join(", "))
3692 }
3693 } else {
3694 format!("fn {}(mut __tag: u8, {})", merged_name, params_str.join(", "))
3695 };
3696
3697 writeln!(output, "{} {{", sig).unwrap();
3698 writeln!(output, " loop {{").unwrap();
3699 writeln!(output, " match __tag {{").unwrap();
3700
3701 writeln!(output, " 0 => {{").unwrap();
3703 let a_mutable = collect_mutable_vars(a_body);
3704 let mut a_ctx = RefinementContext::new();
3705 let mut a_synced = HashSet::new();
3706 let a_caps = HashMap::new();
3707 let a_pipes = HashSet::new();
3708 let a_param_syms: Vec<Symbol> = a_params.iter().map(|(s, _)| *s).collect();
3709 for s in a_body {
3710 output.push_str(&codegen_stmt_mutual_tce(s, func_a, func_b, &a_param_syms, 0, 1, interner, 4, &a_mutable, &mut a_ctx, lww_fields, mv_fields, &mut a_synced, &a_caps, async_functions, &a_pipes, boxed_fields, registry));
3711 }
3712 writeln!(output, " }}").unwrap();
3713
3714 writeln!(output, " 1 => {{").unwrap();
3716 let b_mutable = collect_mutable_vars(b_body);
3717 let mut b_ctx = RefinementContext::new();
3718 let mut b_synced = HashSet::new();
3719 let b_caps = HashMap::new();
3720 let b_pipes = HashSet::new();
3721 let b_param_syms: Vec<Symbol> = b_params.iter().map(|(s, _)| *s).collect();
3722 for s in b_body {
3724 output.push_str(&codegen_stmt_mutual_tce(s, func_b, func_a, &b_param_syms, 1, 0, interner, 4, &b_mutable, &mut b_ctx, lww_fields, mv_fields, &mut b_synced, &b_caps, async_functions, &b_pipes, boxed_fields, registry));
3725 }
3726 writeln!(output, " }}").unwrap();
3727
3728 writeln!(output, " _ => unreachable!()").unwrap();
3729 writeln!(output, " }}").unwrap();
3730 writeln!(output, " }}").unwrap();
3731 writeln!(output, "}}\n").unwrap();
3732
3733 let wrapper_params_a: Vec<String> = a_params.iter()
3735 .map(|(p, t)| format!("{}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
3736 .collect();
3737 let wrapper_args_a: Vec<String> = a_params.iter()
3738 .map(|(p, _)| interner.resolve(*p).to_string())
3739 .collect();
3740 writeln!(output, "#[inline]").unwrap();
3741 if let Some(ref r) = ret_str {
3742 if r != "()" {
3743 writeln!(output, "fn {}({}) -> {} {{ {}(0, {}) }}\n", a_name, wrapper_params_a.join(", "), r, merged_name, wrapper_args_a.join(", ")).unwrap();
3744 } else {
3745 writeln!(output, "fn {}({}) {{ {}(0, {}) }}\n", a_name, wrapper_params_a.join(", "), merged_name, wrapper_args_a.join(", ")).unwrap();
3746 }
3747 } else {
3748 writeln!(output, "fn {}({}) {{ {}(0, {}) }}\n", a_name, wrapper_params_a.join(", "), merged_name, wrapper_args_a.join(", ")).unwrap();
3749 }
3750
3751 let wrapper_params_b: Vec<String> = b_params.iter()
3753 .map(|(p, t)| format!("{}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
3754 .collect();
3755 let wrapper_args_b: Vec<String> = b_params.iter()
3756 .map(|(p, _)| interner.resolve(*p).to_string())
3757 .collect();
3758 writeln!(output, "#[inline]").unwrap();
3759 if let Some(ref r) = ret_str {
3760 if r != "()" {
3761 writeln!(output, "fn {}({}) -> {} {{ {}(1, {}) }}\n", b_name, wrapper_params_b.join(", "), r, merged_name, wrapper_args_b.join(", ")).unwrap();
3762 } else {
3763 writeln!(output, "fn {}({}) {{ {}(1, {}) }}\n", b_name, wrapper_params_b.join(", "), merged_name, wrapper_args_b.join(", ")).unwrap();
3764 }
3765 } else {
3766 writeln!(output, "fn {}({}) {{ {}(1, {}) }}\n", b_name, wrapper_params_b.join(", "), merged_name, wrapper_args_b.join(", ")).unwrap();
3767 }
3768
3769 output
3770}
3771
3772fn codegen_stmt_mutual_tce<'a>(
3773 stmt: &Stmt<'a>,
3774 self_name: Symbol,
3775 partner_name: Symbol,
3776 param_names: &[Symbol],
3777 self_tag: u8,
3778 partner_tag: u8,
3779 interner: &Interner,
3780 indent: usize,
3781 mutable_vars: &HashSet<Symbol>,
3782 ctx: &mut RefinementContext<'a>,
3783 lww_fields: &HashSet<(String, String)>,
3784 mv_fields: &HashSet<(String, String)>,
3785 synced_vars: &mut HashSet<Symbol>,
3786 var_caps: &HashMap<Symbol, VariableCapabilities>,
3787 async_functions: &HashSet<Symbol>,
3788 pipe_vars: &HashSet<Symbol>,
3789 boxed_fields: &HashSet<(String, String, String)>,
3790 registry: &TypeRegistry,
3791) -> String {
3792 let indent_str = " ".repeat(indent);
3793
3794 match stmt {
3795 Stmt::Return { value: Some(expr) } if expr_is_call_to(partner_name, expr) => {
3797 if let Expr::Call { args, .. } = expr {
3798 let mut output = String::new();
3799 writeln!(output, "{}{{", indent_str).unwrap();
3800 for (i, arg) in args.iter().enumerate() {
3801 let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3802 writeln!(output, "{} let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3803 }
3804 for (i, param_sym) in param_names.iter().enumerate() {
3805 let param_name = interner.resolve(*param_sym);
3806 writeln!(output, "{} {} = __tce_{};", indent_str, param_name, i).unwrap();
3807 }
3808 writeln!(output, "{} __tag = {};", indent_str, partner_tag).unwrap();
3809 writeln!(output, "{} continue;", indent_str).unwrap();
3810 writeln!(output, "{}}}", indent_str).unwrap();
3811 return output;
3812 }
3813 codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3814 }
3815
3816 Stmt::Return { value: Some(expr) } if expr_is_call_to(self_name, expr) => {
3818 if let Expr::Call { args, .. } = expr {
3819 let mut output = String::new();
3820 writeln!(output, "{}{{", indent_str).unwrap();
3821 for (i, arg) in args.iter().enumerate() {
3822 let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3823 writeln!(output, "{} let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3824 }
3825 for (i, param_sym) in param_names.iter().enumerate() {
3826 let param_name = interner.resolve(*param_sym);
3827 writeln!(output, "{} {} = __tce_{};", indent_str, param_name, i).unwrap();
3828 }
3829 writeln!(output, "{} continue;", indent_str).unwrap();
3830 writeln!(output, "{}}}", indent_str).unwrap();
3831 return output;
3832 }
3833 codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3834 }
3835
3836 Stmt::If { cond, then_block, else_block } => {
3838 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
3839 let mut output = String::new();
3840 writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
3841 ctx.push_scope();
3842 for s in *then_block {
3843 output.push_str(&codegen_stmt_mutual_tce(s, self_name, partner_name, param_names, self_tag, partner_tag, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3844 }
3845 ctx.pop_scope();
3846 if let Some(else_stmts) = else_block {
3847 writeln!(output, "{}}} else {{", indent_str).unwrap();
3848 ctx.push_scope();
3849 for s in *else_stmts {
3850 output.push_str(&codegen_stmt_mutual_tce(s, self_name, partner_name, param_names, self_tag, partner_tag, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3851 }
3852 ctx.pop_scope();
3853 }
3854 writeln!(output, "{}}}", indent_str).unwrap();
3855 output
3856 }
3857
3858 _ => codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry),
3860 }
3861}
3862
3863fn expr_is_call_to(target: Symbol, expr: &Expr) -> bool {
3864 matches!(expr, Expr::Call { function, .. } if *function == target)
3865}
3866
3867fn codegen_stmt_tce<'a>(
3872 stmt: &Stmt<'a>,
3873 func_name: Symbol,
3874 param_names: &[Symbol],
3875 interner: &Interner,
3876 indent: usize,
3877 mutable_vars: &HashSet<Symbol>,
3878 ctx: &mut RefinementContext<'a>,
3879 lww_fields: &HashSet<(String, String)>,
3880 mv_fields: &HashSet<(String, String)>,
3881 synced_vars: &mut HashSet<Symbol>,
3882 var_caps: &HashMap<Symbol, VariableCapabilities>,
3883 async_functions: &HashSet<Symbol>,
3884 pipe_vars: &HashSet<Symbol>,
3885 boxed_fields: &HashSet<(String, String, String)>,
3886 registry: &TypeRegistry,
3887) -> String {
3888 let indent_str = " ".repeat(indent);
3889
3890 match stmt {
3891 Stmt::Return { value: Some(expr) } if expr_is_self_call(func_name, expr) => {
3893 if let Expr::Call { args, .. } = expr {
3894 let mut output = String::new();
3895 writeln!(output, "{}{{", indent_str).unwrap();
3896 for (i, arg) in args.iter().enumerate() {
3898 let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3899 writeln!(output, "{} let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3900 }
3901 for (i, param_sym) in param_names.iter().enumerate() {
3903 let param_name = interner.resolve(*param_sym);
3904 writeln!(output, "{} {} = __tce_{};", indent_str, param_name, i).unwrap();
3905 }
3906 writeln!(output, "{} continue;", indent_str).unwrap();
3907 writeln!(output, "{}}}", indent_str).unwrap();
3908 return output;
3909 }
3910 codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3912 }
3913
3914 Stmt::Return { value: Some(expr) } => {
3916 if let Expr::Call { function, args } = expr {
3917 if *function == func_name {
3918 let mut output = String::new();
3919 writeln!(output, "{}{{", indent_str).unwrap();
3920 for (i, arg) in args.iter().enumerate() {
3923 if expr_is_self_call(func_name, arg) {
3924 let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3926 writeln!(output, "{} let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3927 } else {
3928 let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3929 writeln!(output, "{} let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3930 }
3931 }
3932 for (i, param_sym) in param_names.iter().enumerate() {
3934 let param_name = interner.resolve(*param_sym);
3935 writeln!(output, "{} {} = __tce_{};", indent_str, param_name, i).unwrap();
3936 }
3937 writeln!(output, "{} continue;", indent_str).unwrap();
3938 writeln!(output, "{}}}", indent_str).unwrap();
3939 return output;
3940 }
3941 }
3942 codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3944 }
3945
3946 Stmt::If { cond, then_block, else_block } => {
3948 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
3949 let mut output = String::new();
3950 writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
3951 ctx.push_scope();
3952 for s in *then_block {
3953 output.push_str(&codegen_stmt_tce(s, func_name, param_names, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3954 }
3955 ctx.pop_scope();
3956 if let Some(else_stmts) = else_block {
3957 writeln!(output, "{}}} else {{", indent_str).unwrap();
3958 ctx.push_scope();
3959 for s in *else_stmts {
3960 output.push_str(&codegen_stmt_tce(s, func_name, param_names, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3961 }
3962 ctx.pop_scope();
3963 }
3964 writeln!(output, "{}}}", indent_str).unwrap();
3965 output
3966 }
3967
3968 _ => codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry),
3970 }
3971}
3972
3973pub fn collect_pipe_sender_params(body: &[Stmt]) -> HashSet<Symbol> {
3976 let mut senders = HashSet::new();
3977 for stmt in body {
3978 collect_pipe_sender_params_stmt(stmt, &mut senders);
3979 }
3980 senders
3981}
3982
3983fn collect_pipe_sender_params_stmt(stmt: &Stmt, senders: &mut HashSet<Symbol>) {
3984 match stmt {
3985 Stmt::SendPipe { pipe, .. } | Stmt::TrySendPipe { pipe, .. } => {
3986 if let Expr::Identifier(sym) = pipe {
3987 senders.insert(*sym);
3988 }
3989 }
3990 Stmt::If { then_block, else_block, .. } => {
3991 for s in *then_block {
3992 collect_pipe_sender_params_stmt(s, senders);
3993 }
3994 if let Some(else_stmts) = else_block {
3995 for s in *else_stmts {
3996 collect_pipe_sender_params_stmt(s, senders);
3997 }
3998 }
3999 }
4000 Stmt::While { body, .. } | Stmt::Repeat { body, .. } | Stmt::Zone { body, .. } => {
4001 for s in *body {
4002 collect_pipe_sender_params_stmt(s, senders);
4003 }
4004 }
4005 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
4006 for s in *tasks {
4007 collect_pipe_sender_params_stmt(s, senders);
4008 }
4009 }
4010 _ => {}
4011 }
4012}
4013
4014pub fn collect_pipe_vars(stmts: &[Stmt]) -> HashSet<Symbol> {
4017 let mut pipe_vars = HashSet::new();
4018 for stmt in stmts {
4019 collect_pipe_vars_stmt(stmt, &mut pipe_vars);
4020 }
4021 pipe_vars
4022}
4023
4024fn collect_pipe_vars_stmt(stmt: &Stmt, pipe_vars: &mut HashSet<Symbol>) {
4025 match stmt {
4026 Stmt::CreatePipe { var, .. } => {
4027 pipe_vars.insert(*var);
4028 }
4029 Stmt::If { then_block, else_block, .. } => {
4030 for s in *then_block {
4031 collect_pipe_vars_stmt(s, pipe_vars);
4032 }
4033 if let Some(else_stmts) = else_block {
4034 for s in *else_stmts {
4035 collect_pipe_vars_stmt(s, pipe_vars);
4036 }
4037 }
4038 }
4039 Stmt::While { body, .. } | Stmt::Repeat { body, .. } | Stmt::Zone { body, .. } => {
4040 for s in *body {
4041 collect_pipe_vars_stmt(s, pipe_vars);
4042 }
4043 }
4044 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
4045 for s in *tasks {
4046 collect_pipe_vars_stmt(s, pipe_vars);
4047 }
4048 }
4049 _ => {}
4050 }
4051}
4052
4053fn collect_expr_identifiers(expr: &Expr, identifiers: &mut HashSet<Symbol>) {
4056 match expr {
4057 Expr::Identifier(sym) => {
4058 identifiers.insert(*sym);
4059 }
4060 Expr::BinaryOp { left, right, .. } => {
4061 collect_expr_identifiers(left, identifiers);
4062 collect_expr_identifiers(right, identifiers);
4063 }
4064 Expr::Call { args, .. } => {
4065 for arg in args {
4066 collect_expr_identifiers(arg, identifiers);
4067 }
4068 }
4069 Expr::Index { collection, index } => {
4070 collect_expr_identifiers(collection, identifiers);
4071 collect_expr_identifiers(index, identifiers);
4072 }
4073 Expr::Slice { collection, start, end } => {
4074 collect_expr_identifiers(collection, identifiers);
4075 collect_expr_identifiers(start, identifiers);
4076 collect_expr_identifiers(end, identifiers);
4077 }
4078 Expr::Copy { expr: inner } | Expr::Give { value: inner } | Expr::Length { collection: inner } => {
4079 collect_expr_identifiers(inner, identifiers);
4080 }
4081 Expr::Contains { collection, value } | Expr::Union { left: collection, right: value } | Expr::Intersection { left: collection, right: value } => {
4082 collect_expr_identifiers(collection, identifiers);
4083 collect_expr_identifiers(value, identifiers);
4084 }
4085 Expr::ManifestOf { zone } | Expr::ChunkAt { zone, .. } => {
4086 collect_expr_identifiers(zone, identifiers);
4087 }
4088 Expr::List(items) | Expr::Tuple(items) => {
4089 for item in items {
4090 collect_expr_identifiers(item, identifiers);
4091 }
4092 }
4093 Expr::Range { start, end } => {
4094 collect_expr_identifiers(start, identifiers);
4095 collect_expr_identifiers(end, identifiers);
4096 }
4097 Expr::FieldAccess { object, .. } => {
4098 collect_expr_identifiers(object, identifiers);
4099 }
4100 Expr::New { init_fields, .. } => {
4101 for (_, value) in init_fields {
4102 collect_expr_identifiers(value, identifiers);
4103 }
4104 }
4105 Expr::NewVariant { fields, .. } => {
4106 for (_, value) in fields {
4107 collect_expr_identifiers(value, identifiers);
4108 }
4109 }
4110 Expr::OptionSome { value } => {
4111 collect_expr_identifiers(value, identifiers);
4112 }
4113 Expr::WithCapacity { value, capacity } => {
4114 collect_expr_identifiers(value, identifiers);
4115 collect_expr_identifiers(capacity, identifiers);
4116 }
4117 Expr::Closure { body, .. } => {
4118 match body {
4119 crate::ast::stmt::ClosureBody::Expression(expr) => collect_expr_identifiers(expr, identifiers),
4120 crate::ast::stmt::ClosureBody::Block(_) => {}
4121 }
4122 }
4123 Expr::CallExpr { callee, args } => {
4124 collect_expr_identifiers(callee, identifiers);
4125 for arg in args {
4126 collect_expr_identifiers(arg, identifiers);
4127 }
4128 }
4129 Expr::OptionNone => {}
4130 Expr::Escape { .. } => {}
4131 Expr::Literal(_) => {}
4132 }
4133}
4134
4135fn collect_stmt_identifiers(stmt: &Stmt, identifiers: &mut HashSet<Symbol>) {
4137 match stmt {
4138 Stmt::Let { value, .. } => {
4139 collect_expr_identifiers(value, identifiers);
4140 }
4141 Stmt::Call { args, .. } => {
4142 for arg in args {
4143 collect_expr_identifiers(arg, identifiers);
4144 }
4145 }
4146 _ => {}
4147 }
4148}
4149
4150pub fn codegen_program(stmts: &[Stmt], registry: &TypeRegistry, policies: &PolicyRegistry, interner: &Interner) -> String {
4203 let mut output = String::new();
4204
4205 writeln!(output, "#[allow(unused_imports)]").unwrap();
4208 writeln!(output, "use std::fmt::Write as _;").unwrap();
4209 writeln!(output, "use logicaffeine_data::*;").unwrap();
4210 writeln!(output, "use logicaffeine_system::*;\n").unwrap();
4211
4212 if has_wasm_exports(stmts, interner) {
4214 writeln!(output, "use wasm_bindgen::prelude::*;\n").unwrap();
4215 }
4216
4217 if has_c_exports_with_text(stmts, interner) {
4219 writeln!(output, "use std::ffi::{{CStr, CString}};\n").unwrap();
4220 }
4221
4222 let c_exports_exist = has_c_exports(stmts, interner);
4224 if c_exports_exist {
4225 output.push_str(&codegen_logos_runtime_preamble());
4226 }
4227
4228 let (lww_fields, mv_fields) = collect_crdt_register_fields(registry, interner);
4231
4232 let async_functions = collect_async_functions(stmts);
4234
4235 let pure_functions = collect_pure_functions(stmts);
4237
4238 let main_pipe_vars = collect_pipe_vars(stmts);
4240
4241 let boxed_fields = collect_boxed_fields(registry, interner);
4243
4244 let c_abi_value_structs: HashSet<Symbol> = if c_exports_exist {
4246 collect_c_export_value_type_structs(stmts, interner, registry)
4247 } else {
4248 HashSet::new()
4249 };
4250
4251 let c_abi_ref_structs: HashSet<Symbol> = if c_exports_exist {
4253 collect_c_export_ref_structs(stmts, interner, registry)
4254 } else {
4255 HashSet::new()
4256 };
4257
4258 let structs: Vec<_> = registry.iter_types()
4260 .filter_map(|(name, def)| {
4261 if let TypeDef::Struct { fields, generics, is_portable, is_shared } = def {
4262 if !fields.is_empty() || !generics.is_empty() {
4263 Some((*name, fields.clone(), generics.clone(), *is_portable, *is_shared))
4264 } else {
4265 None
4266 }
4267 } else {
4268 None
4269 }
4270 })
4271 .collect();
4272
4273 let enums: Vec<_> = registry.iter_types()
4275 .filter_map(|(name, def)| {
4276 if let TypeDef::Enum { variants, generics, is_portable, is_shared } = def {
4277 if !variants.is_empty() || !generics.is_empty() {
4278 Some((*name, variants.clone(), generics.clone(), *is_portable, *is_shared))
4279 } else {
4280 None
4281 }
4282 } else {
4283 None
4284 }
4285 })
4286 .collect();
4287
4288 if !structs.is_empty() || !enums.is_empty() {
4290 writeln!(output, "pub mod user_types {{").unwrap();
4291 writeln!(output, " use super::*;\n").unwrap();
4292
4293 for (name, fields, generics, is_portable, is_shared) in &structs {
4294 output.push_str(&codegen_struct_def(*name, fields, generics, *is_portable, *is_shared, interner, 4, &c_abi_value_structs, &c_abi_ref_structs));
4295 }
4296
4297 for (name, variants, generics, is_portable, is_shared) in &enums {
4298 output.push_str(&codegen_enum_def(*name, variants, generics, *is_portable, *is_shared, interner, 4));
4299 }
4300
4301 writeln!(output, "}}\n").unwrap();
4302 writeln!(output, "use user_types::*;\n").unwrap();
4303 }
4304
4305 output.push_str(&codegen_policy_impls(policies, interner));
4307
4308 let mutual_tce_pairs = detect_mutual_tce_pairs(stmts, interner);
4310 let mut mutual_tce_members: HashSet<Symbol> = HashSet::new();
4311 for (a, b) in &mutual_tce_pairs {
4312 mutual_tce_members.insert(*a);
4313 mutual_tce_members.insert(*b);
4314 }
4315 let mut mutual_tce_emitted: HashSet<Symbol> = HashSet::new();
4316
4317 for stmt in stmts {
4319 if let Stmt::FunctionDef { name, params, body, return_type, is_native, native_path, is_exported, export_target } = stmt {
4320 if mutual_tce_members.contains(name) {
4321 if !mutual_tce_emitted.contains(name) {
4323 if let Some((a, b)) = mutual_tce_pairs.iter().find(|(a, b)| *a == *name || *b == *name) {
4325 output.push_str(&codegen_mutual_tce_pair(*a, *b, stmts, interner, &lww_fields, &mv_fields, &async_functions, &boxed_fields, registry));
4326 mutual_tce_emitted.insert(*a);
4327 mutual_tce_emitted.insert(*b);
4328 }
4329 }
4330 } else {
4332 output.push_str(&codegen_function_def(*name, params, body, return_type.as_ref().copied(), *is_native, *native_path, *is_exported, *export_target, interner, &lww_fields, &mv_fields, &async_functions, &boxed_fields, registry, &pure_functions));
4333 }
4334 }
4335 }
4336
4337 if c_exports_exist {
4339 let ref_types = collect_c_export_reference_types(stmts, interner, registry);
4340 for ref_ty in &ref_types {
4341 output.push_str(&codegen_c_accessors(ref_ty, interner, registry));
4342 }
4343 }
4344
4345 let main_stmts: Vec<&Stmt> = stmts.iter()
4347 .filter(|s| !matches!(s, Stmt::FunctionDef { .. }))
4348 .collect();
4349 let mut main_mutable_vars = HashSet::new();
4350 for stmt in &main_stmts {
4351 collect_mutable_vars_stmt(stmt, &mut main_mutable_vars);
4352 }
4353
4354 if requires_async(stmts) {
4357 writeln!(output, "#[tokio::main]").unwrap();
4358 writeln!(output, "async fn main() {{").unwrap();
4359 } else {
4360 writeln!(output, "fn main() {{").unwrap();
4361 }
4362 if requires_vfs(stmts) {
4364 writeln!(output, " let vfs = logicaffeine_system::fs::NativeVfs::new(\".\");").unwrap();
4365 }
4366 let mut main_ctx = RefinementContext::new();
4367 let mut main_synced_vars = HashSet::new(); let main_var_caps = analyze_variable_capabilities(stmts, interner);
4370 {
4371 let stmt_refs: Vec<&Stmt> = stmts.iter().collect();
4372 let mut i = 0;
4373 while i < stmt_refs.len() {
4374 if matches!(stmt_refs[i], Stmt::FunctionDef { .. }) {
4376 i += 1;
4377 continue;
4378 }
4379 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, i, interner, 1) {
4381 output.push_str(&code);
4382 i += 1 + skip;
4383 continue;
4384 }
4385 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry) {
4387 output.push_str(&code);
4388 i += 1 + skip;
4389 continue;
4390 }
4391 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
4393 output.push_str(&code);
4394 i += 1 + skip;
4395 continue;
4396 }
4397 output.push_str(&codegen_stmt(stmt_refs[i], interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry));
4398 i += 1;
4399 }
4400 }
4401 writeln!(output, "}}").unwrap();
4402 output
4403}
4404
4405fn codegen_function_def(
4410 name: Symbol,
4411 params: &[(Symbol, &TypeExpr)],
4412 body: &[Stmt],
4413 return_type: Option<&TypeExpr>,
4414 is_native: bool,
4415 native_path: Option<Symbol>,
4416 is_exported: bool,
4417 export_target: Option<Symbol>,
4418 interner: &Interner,
4419 lww_fields: &HashSet<(String, String)>,
4420 mv_fields: &HashSet<(String, String)>, async_functions: &HashSet<Symbol>, boxed_fields: &HashSet<(String, String, String)>, registry: &TypeRegistry, pure_functions: &HashSet<Symbol>,
4425) -> String {
4426 let mut output = String::new();
4427 let raw_name = interner.resolve(name);
4428 let func_name = escape_rust_ident(raw_name);
4429 let export_target_lower = export_target.map(|s| interner.resolve(s).to_lowercase());
4430
4431 let pipe_sender_params = collect_pipe_sender_params(body);
4433
4434 let is_c_export_early = is_exported && matches!(export_target_lower.as_deref(), None | Some("c"));
4436
4437 let is_tce = !is_native && !is_c_export_early && is_tail_recursive(name, body);
4439 let param_syms: Vec<Symbol> = params.iter().map(|(s, _)| *s).collect();
4440
4441 let acc_info = if !is_tce && !is_native && !is_c_export_early {
4443 detect_accumulator_pattern(name, body)
4444 } else {
4445 None
4446 };
4447 let is_acc = acc_info.is_some();
4448
4449 let is_memo = !is_tce && !is_acc && !is_native && !is_c_export_early
4451 && should_memoize(name, body, params, return_type, pure_functions.contains(&name), interner);
4452
4453 let needs_mut_params = is_tce || is_acc;
4454
4455 let params_str: Vec<String> = params.iter()
4457 .map(|(param_name, param_type)| {
4458 let pname = interner.resolve(*param_name);
4459 let ty = codegen_type_expr(param_type, interner);
4460 if pipe_sender_params.contains(param_name) {
4462 format!("{}: tokio::sync::mpsc::Sender<{}>", pname, ty)
4463 } else if needs_mut_params {
4464 format!("mut {}: {}", pname, ty)
4465 } else {
4466 format!("{}: {}", pname, ty)
4467 }
4468 })
4469 .collect();
4470
4471 let return_type_str = return_type
4473 .map(|t| codegen_type_expr(t, interner))
4474 .or_else(|| infer_return_type_from_body(body, interner));
4475
4476 let is_async = async_functions.contains(&name);
4478 let fn_keyword = if is_async { "async fn" } else { "fn" };
4479
4480 let is_c_export = is_c_export_early;
4482
4483 let needs_c_marshaling = is_c_export && {
4486 let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
4487 let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
4488 let has_ref_param = params.iter().any(|(_, ty)| {
4489 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4490 });
4491 let has_ref_return = return_type.map_or(false, |ty| {
4492 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4493 });
4494 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4495 let has_refinement_param = params.iter().any(|(_, ty)| {
4496 matches!(ty, TypeExpr::Refinement { .. })
4497 });
4498 has_text_param || has_text_return || has_ref_param || has_ref_return
4499 || has_result_return || has_refinement_param
4500 };
4501
4502 if needs_c_marshaling {
4503 return codegen_c_export_with_marshaling(
4505 name, params, body, return_type, interner,
4506 lww_fields, mv_fields, async_functions, boxed_fields, registry,
4507 );
4508 }
4509
4510 let (vis_prefix, abi_prefix) = if is_exported {
4512 match export_target_lower.as_deref() {
4513 None | Some("c") => ("pub ", "extern \"C\" "),
4514 Some("wasm") => ("pub ", ""),
4515 _ => ("pub ", ""),
4516 }
4517 } else {
4518 ("", "")
4519 };
4520
4521 let signature = if let Some(ref ret_ty) = return_type_str {
4522 if ret_ty != "()" {
4523 format!("{}{}{} {}({}) -> {}", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "), ret_ty)
4524 } else {
4525 format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4526 }
4527 } else {
4528 format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4529 };
4530
4531 if !is_tce && !is_acc && should_inline(name, body, is_native, is_exported, is_async) {
4533 writeln!(output, "#[inline]").unwrap();
4534 }
4535
4536 if is_exported {
4538 match export_target_lower.as_deref() {
4539 None | Some("c") => {
4540 writeln!(output, "#[export_name = \"logos_{}\"]", raw_name).unwrap();
4541 }
4542 Some("wasm") => {
4543 writeln!(output, "#[wasm_bindgen]").unwrap();
4544 }
4545 _ => {}
4546 }
4547 }
4548
4549 if is_native {
4551 let arg_names: Vec<&str> = params.iter()
4552 .map(|(n, _)| interner.resolve(*n))
4553 .collect();
4554
4555 if let Some(path_sym) = native_path {
4556 let path = interner.resolve(path_sym);
4558 let is_valid_path = !path.is_empty() && path.split("::").all(|seg| {
4560 !seg.is_empty() && seg.chars().all(|c| c.is_alphanumeric() || c == '_')
4561 });
4562 if is_valid_path {
4563 writeln!(output, "{} {{", signature).unwrap();
4564 writeln!(output, " {}({})", path, arg_names.join(", ")).unwrap();
4565 writeln!(output, "}}\n").unwrap();
4566 } else {
4567 writeln!(output, "{} {{", signature).unwrap();
4568 writeln!(output, " compile_error!(\"Invalid native function path: '{}'. Path must be a valid Rust path like \\\"crate::module::function\\\".\")", path).unwrap();
4569 writeln!(output, "}}\n").unwrap();
4570 }
4571 } else {
4572 if let Some((module, core_fn)) = map_native_function(raw_name) {
4574 writeln!(output, "{} {{", signature).unwrap();
4575 writeln!(output, " logicaffeine_system::{}::{}({})", module, core_fn, arg_names.join(", ")).unwrap();
4576 writeln!(output, "}}\n").unwrap();
4577 } else {
4578 writeln!(output, "{} {{", signature).unwrap();
4579 writeln!(output, " compile_error!(\"Unknown system native function: '{}'. Use `is native \\\"crate::path\\\"` syntax for user-defined native functions.\")", raw_name).unwrap();
4580 writeln!(output, "}}\n").unwrap();
4581 }
4582 }
4583 } else {
4584 let func_mutable_vars = collect_mutable_vars(body);
4587 writeln!(output, "{} {{", signature).unwrap();
4588
4589 let wrap_catch_unwind = is_c_export;
4591 if wrap_catch_unwind {
4592 writeln!(output, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
4593 }
4594
4595 let mut func_ctx = RefinementContext::new();
4596 let mut func_synced_vars = HashSet::new(); let func_var_caps = analyze_variable_capabilities(body, interner);
4599
4600 for (param_name, param_type) in params {
4602 let type_name = codegen_type_expr(param_type, interner);
4603 func_ctx.register_variable_type(*param_name, type_name);
4604 }
4605
4606 let func_pipe_vars = HashSet::new();
4608
4609 if is_tce {
4610 writeln!(output, " loop {{").unwrap();
4612 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4613 let mut si = 0;
4614 while si < stmt_refs.len() {
4615 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4616 output.push_str(&code);
4617 si += 1 + skip;
4618 continue;
4619 }
4620 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4621 output.push_str(&code);
4622 si += 1 + skip;
4623 continue;
4624 }
4625 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4626 output.push_str(&code);
4627 si += 1 + skip;
4628 continue;
4629 }
4630 output.push_str(&codegen_stmt_tce(stmt_refs[si], name, ¶m_syms, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4631 si += 1;
4632 }
4633 writeln!(output, " }}").unwrap();
4634 } else if let Some(ref acc) = acc_info {
4635 writeln!(output, " let mut __acc: i64 = {};", acc.identity).unwrap();
4637 writeln!(output, " loop {{").unwrap();
4638 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4639 let mut si = 0;
4640 while si < stmt_refs.len() {
4641 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4642 output.push_str(&code);
4643 si += 1 + skip;
4644 continue;
4645 }
4646 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4647 output.push_str(&code);
4648 si += 1 + skip;
4649 continue;
4650 }
4651 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4652 output.push_str(&code);
4653 si += 1 + skip;
4654 continue;
4655 }
4656 output.push_str(&codegen_stmt_acc(stmt_refs[si], name, ¶m_syms, acc, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4657 si += 1;
4658 }
4659 writeln!(output, " }}").unwrap();
4660 } else if is_memo {
4661 let ret_ty = return_type_str.as_deref().unwrap_or("i64");
4663 let memo_name = format!("__MEMO_{}", func_name.to_uppercase());
4664
4665 let (key_type, key_expr, copy_method) = if params.len() == 1 {
4667 let ty = codegen_type_expr(params[0].1, interner);
4668 let pname = interner.resolve(params[0].0).to_string();
4669 let copy = if is_copy_type_expr(params[0].1, interner) { "copied" } else { "cloned" };
4670 (ty, pname, copy)
4671 } else {
4672 let types: Vec<String> = params.iter().map(|(_, t)| codegen_type_expr(t, interner)).collect();
4673 let names: Vec<String> = params.iter().map(|(n, _)| interner.resolve(*n).to_string()).collect();
4674 let copy = if params.iter().all(|(_, t)| is_copy_type_expr(t, interner)) { "copied" } else { "cloned" };
4675 (format!("({})", types.join(", ")), format!("({})", names.join(", ")), copy)
4676 };
4677
4678 writeln!(output, " use std::cell::RefCell;").unwrap();
4679 writeln!(output, " use std::collections::HashMap;").unwrap();
4680 writeln!(output, " thread_local! {{").unwrap();
4681 writeln!(output, " static {}: RefCell<HashMap<{}, {}>> = RefCell::new(HashMap::new());", memo_name, key_type, ret_ty).unwrap();
4682 writeln!(output, " }}").unwrap();
4683 writeln!(output, " if let Some(__v) = {}.with(|c| c.borrow().get(&{}).{}()) {{", memo_name, key_expr, copy_method).unwrap();
4684 writeln!(output, " return __v;").unwrap();
4685 writeln!(output, " }}").unwrap();
4686 writeln!(output, " let __memo_result = (|| -> {} {{", ret_ty).unwrap();
4687 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4688 let mut si = 0;
4689 while si < stmt_refs.len() {
4690 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4691 output.push_str(&code);
4692 si += 1 + skip;
4693 continue;
4694 }
4695 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4696 output.push_str(&code);
4697 si += 1 + skip;
4698 continue;
4699 }
4700 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4701 output.push_str(&code);
4702 si += 1 + skip;
4703 continue;
4704 }
4705 output.push_str(&codegen_stmt(stmt_refs[si], interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4706 si += 1;
4707 }
4708 writeln!(output, " }})();").unwrap();
4709 writeln!(output, " {}.with(|c| c.borrow_mut().insert({}, __memo_result));", memo_name, key_expr).unwrap();
4710 writeln!(output, " __memo_result").unwrap();
4711 } else {
4712 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4713 let mut si = 0;
4714 while si < stmt_refs.len() {
4715 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4716 output.push_str(&code);
4717 si += 1 + skip;
4718 continue;
4719 }
4720 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4721 output.push_str(&code);
4722 si += 1 + skip;
4723 continue;
4724 }
4725 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4726 output.push_str(&code);
4727 si += 1 + skip;
4728 continue;
4729 }
4730 output.push_str(&codegen_stmt(stmt_refs[si], interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4731 si += 1;
4732 }
4733 }
4734
4735 if wrap_catch_unwind {
4736 writeln!(output, " }})) {{").unwrap();
4737 writeln!(output, " Ok(__v) => __v,").unwrap();
4738 writeln!(output, " Err(__panic) => {{").unwrap();
4739 writeln!(output, " let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
4740 writeln!(output, " logos_set_last_error(__msg);").unwrap();
4741 if let Some(ref ret_str) = return_type_str {
4743 if ret_str != "()" {
4744 writeln!(output, " Default::default()").unwrap();
4745 }
4746 }
4747 writeln!(output, " }}").unwrap();
4748 writeln!(output, " }}").unwrap();
4749 }
4750
4751 writeln!(output, "}}\n").unwrap();
4752 }
4753
4754 output
4755}
4756
4757fn map_native_function(name: &str) -> Option<(&'static str, &'static str)> {
4761 match name {
4762 "read" => Some(("file", "read")),
4763 "write" => Some(("file", "write")),
4764 "now" => Some(("time", "now")),
4765 "sleep" => Some(("time", "sleep")),
4766 "randomInt" => Some(("random", "randomInt")),
4767 "randomFloat" => Some(("random", "randomFloat")),
4768 "get" => Some(("env", "get")),
4769 "args" => Some(("env", "args")),
4770 "parseInt" => Some(("text", "parseInt")),
4771 "parseFloat" => Some(("text", "parseFloat")),
4772 "format" => Some(("fmt", "format")),
4773 _ => None,
4774 }
4775}
4776
4777fn is_text_type(ty: &TypeExpr, interner: &Interner) -> bool {
4779 match ty {
4780 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
4781 matches!(interner.resolve(*sym), "Text" | "String")
4782 }
4783 TypeExpr::Refinement { base, .. } => is_text_type(base, interner),
4784 _ => false,
4785 }
4786}
4787
4788fn map_type_to_c_abi(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
4791 if is_text_type(ty, interner) {
4792 if is_return {
4793 "*mut std::os::raw::c_char".to_string()
4794 } else {
4795 "*const std::os::raw::c_char".to_string()
4796 }
4797 } else {
4798 codegen_type_expr(ty, interner)
4799 }
4800}
4801
4802fn codegen_c_export_with_marshaling(
4812 name: Symbol,
4813 params: &[(Symbol, &TypeExpr)],
4814 body: &[Stmt],
4815 return_type: Option<&TypeExpr>,
4816 interner: &Interner,
4817 lww_fields: &HashSet<(String, String)>,
4818 mv_fields: &HashSet<(String, String)>,
4819 async_functions: &HashSet<Symbol>,
4820 boxed_fields: &HashSet<(String, String, String)>,
4821 registry: &crate::analysis::registry::TypeRegistry,
4822) -> String {
4823 let mut output = String::new();
4824 let raw_name = interner.resolve(name);
4825 let func_name = format!("logos_{}", raw_name);
4829 let inner_name = escape_rust_ident(raw_name);
4830
4831 let has_ref_return = return_type.map_or(false, |ty| {
4833 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4834 });
4835 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4836 let has_text_return = return_type.map_or(false, |t| is_text_type(t, interner));
4837
4838 let uses_status_code = has_ref_return || has_result_return || has_text_return
4843 || params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
4844
4845 let inner_params: Vec<String> = params.iter()
4847 .map(|(pname, ptype)| {
4848 format!("{}: {}", interner.resolve(*pname), codegen_type_expr(ptype, interner))
4849 })
4850 .collect();
4851 let inner_ret = return_type.map(|t| codegen_type_expr(t, interner));
4852
4853 let inner_sig = if let Some(ref ret) = inner_ret {
4854 if ret != "()" {
4855 format!("fn {}({}) -> {}", inner_name, inner_params.join(", "), ret)
4856 } else {
4857 format!("fn {}({})", inner_name, inner_params.join(", "))
4858 }
4859 } else {
4860 format!("fn {}({})", inner_name, inner_params.join(", "))
4861 };
4862
4863 writeln!(output, "{} {{", inner_sig).unwrap();
4864 let func_mutable_vars = collect_mutable_vars(body);
4865 let mut func_ctx = RefinementContext::new();
4866 let mut func_synced_vars = HashSet::new();
4867 let func_var_caps = analyze_variable_capabilities(body, interner);
4868 for (param_name, param_type) in params {
4869 let type_name = codegen_type_expr(param_type, interner);
4870 func_ctx.register_variable_type(*param_name, type_name);
4871 }
4872 let func_pipe_vars = HashSet::new();
4873 {
4874 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4875 let mut si = 0;
4876 while si < stmt_refs.len() {
4877 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4878 output.push_str(&code);
4879 si += 1 + skip;
4880 continue;
4881 }
4882 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4883 output.push_str(&code);
4884 si += 1 + skip;
4885 continue;
4886 }
4887 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4888 output.push_str(&code);
4889 si += 1 + skip;
4890 continue;
4891 }
4892 output.push_str(&codegen_stmt(stmt_refs[si], interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4893 si += 1;
4894 }
4895 }
4896 writeln!(output, "}}\n").unwrap();
4897
4898 let mut c_params: Vec<String> = Vec::new();
4900
4901 for (pname, ptype) in params.iter() {
4902 let pn = interner.resolve(*pname);
4903 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4904 c_params.push(format!("{}: LogosHandle", pn));
4905 } else if is_text_type(ptype, interner) {
4906 c_params.push(format!("{}: *const std::os::raw::c_char", pn));
4907 } else {
4908 c_params.push(format!("{}: {}", pn, codegen_type_expr(ptype, interner)));
4909 }
4910 }
4911
4912 if uses_status_code {
4914 if let Some(ret_ty) = return_type {
4915 if has_result_return {
4916 if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
4918 if !rparams.is_empty() {
4919 let ok_ty = &rparams[0];
4920 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
4921 c_params.push("out: *mut LogosHandle".to_string());
4922 } else if is_text_type(ok_ty, interner) {
4923 c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4924 } else {
4925 let ty_str = codegen_type_expr(ok_ty, interner);
4926 c_params.push(format!("out: *mut {}", ty_str));
4927 }
4928 }
4929 }
4930 } else if has_ref_return {
4931 c_params.push("out: *mut LogosHandle".to_string());
4932 } else if has_text_return {
4933 c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4934 }
4935 }
4936 }
4937
4938 let c_sig = if uses_status_code {
4940 format!("pub extern \"C\" fn {}({}) -> LogosStatus", func_name, c_params.join(", "))
4941 } else if has_text_return {
4942 format!("pub extern \"C\" fn {}({}) -> *mut std::os::raw::c_char", func_name, c_params.join(", "))
4943 } else if let Some(ret_ty) = return_type {
4944 let ret_str = codegen_type_expr(ret_ty, interner);
4945 if ret_str != "()" {
4946 format!("pub extern \"C\" fn {}({}) -> {}", func_name, c_params.join(", "), ret_str)
4947 } else {
4948 format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4949 }
4950 } else {
4951 format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4952 };
4953
4954 writeln!(output, "#[no_mangle]").unwrap();
4955 writeln!(output, "{} {{", c_sig).unwrap();
4956
4957 let call_args: Vec<String> = params.iter()
4959 .map(|(pname, ptype)| {
4960 let pname_str = interner.resolve(*pname);
4961 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4962 let rust_ty = codegen_type_expr(ptype, interner);
4964 writeln!(output, " let {pn} = {{", pn = pname_str).unwrap();
4965 writeln!(output, " let __id = {pn} as u64;", pn = pname_str).unwrap();
4966 writeln!(output, " let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
4967 writeln!(output, " let __ptr = __reg.deref(__id).expect(\"InvalidHandle: handle not found in registry\");").unwrap();
4968 writeln!(output, " drop(__reg);").unwrap();
4969 writeln!(output, " unsafe {{ &*(__ptr as *const {ty}) }}.clone()", ty = rust_ty).unwrap();
4970 writeln!(output, " }};").unwrap();
4971 } else if is_text_type(ptype, interner) {
4972 if uses_status_code {
4974 writeln!(output, " if {pn}.is_null() {{ logos_set_last_error(\"NullPointer: text parameter '{pn}' is null\".to_string()); return LogosStatus::NullPointer; }}",
4975 pn = pname_str).unwrap();
4976 writeln!(output, " let {pn} = unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }};",
4977 pn = pname_str).unwrap();
4978 } else {
4979 writeln!(output, " let {pn} = if {pn}.is_null() {{ String::new() }} else {{ unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }} }};",
4981 pn = pname_str).unwrap();
4982 }
4983 }
4984 pname_str.to_string()
4985 })
4986 .collect();
4987
4988 for (pname, ptype) in params.iter() {
4990 if let TypeExpr::Refinement { base: _, var, predicate } = ptype {
4991 let pname_str = interner.resolve(*pname);
4992 let bound = interner.resolve(*var);
4993 let assertion = codegen_assertion(predicate, interner);
4994 let check = if bound == pname_str {
4995 assertion
4996 } else {
4997 replace_word(&assertion, bound, pname_str)
4998 };
4999 writeln!(output, " if !({}) {{", check).unwrap();
5000 writeln!(output, " logos_set_last_error(format!(\"Refinement violation: expected {check}, got {pn} = {{}}\", {pn}));",
5001 check = check, pn = pname_str).unwrap();
5002 writeln!(output, " return LogosStatus::RefinementViolation;").unwrap();
5003 writeln!(output, " }}").unwrap();
5004 }
5005 }
5006
5007 if uses_status_code && (has_ref_return || has_text_return || has_result_return) {
5009 writeln!(output, " if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
5010 }
5011
5012 let panic_default = if uses_status_code {
5014 "LogosStatus::ThreadPanic"
5015 } else if has_text_return {
5016 "std::ptr::null_mut()"
5017 } else if return_type.map_or(false, |t| codegen_type_expr(t, interner) != "()") {
5018 "Default::default()"
5019 } else {
5020 "" };
5022
5023 writeln!(output, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
5025
5026 if uses_status_code {
5028 if has_result_return {
5029 writeln!(output, " match {}({}) {{", inner_name, call_args.join(", ")).unwrap();
5031 writeln!(output, " Ok(val) => {{").unwrap();
5032
5033 if let Some(TypeExpr::Generic { params: ref rparams, .. }) = return_type {
5034 if !rparams.is_empty() {
5035 let ok_ty = &rparams[0];
5036 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
5037 writeln!(output, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
5038 writeln!(output, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5039 writeln!(output, " let (__id, _) = __reg.register(__ptr);").unwrap();
5040 writeln!(output, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5041 } else if is_text_type(ok_ty, interner) {
5042 writeln!(output, " match std::ffi::CString::new(val) {{").unwrap();
5043 writeln!(output, " Ok(cstr) => unsafe {{ *out = cstr.into_raw(); }},").unwrap();
5044 writeln!(output, " Err(_) => {{").unwrap();
5045 writeln!(output, " logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5046 writeln!(output, " return LogosStatus::ContainsNullByte;").unwrap();
5047 writeln!(output, " }}").unwrap();
5048 writeln!(output, " }}").unwrap();
5049 } else {
5050 writeln!(output, " unsafe {{ *out = val; }}").unwrap();
5051 }
5052 }
5053 }
5054
5055 writeln!(output, " LogosStatus::Ok").unwrap();
5056 writeln!(output, " }}").unwrap();
5057 writeln!(output, " Err(e) => {{").unwrap();
5058 writeln!(output, " logos_set_last_error(format!(\"{{}}\", e));").unwrap();
5059 writeln!(output, " LogosStatus::Error").unwrap();
5060 writeln!(output, " }}").unwrap();
5061 writeln!(output, " }}").unwrap();
5062 } else if has_ref_return {
5063 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5065 writeln!(output, " let __ptr = Box::into_raw(Box::new(result)) as usize;").unwrap();
5066 writeln!(output, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5067 writeln!(output, " let (__id, _) = __reg.register(__ptr);").unwrap();
5068 writeln!(output, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5069 writeln!(output, " LogosStatus::Ok").unwrap();
5070 } else if has_text_return {
5071 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5073 writeln!(output, " match std::ffi::CString::new(result) {{").unwrap();
5074 writeln!(output, " Ok(cstr) => {{").unwrap();
5075 writeln!(output, " unsafe {{ *out = cstr.into_raw(); }}").unwrap();
5076 writeln!(output, " LogosStatus::Ok").unwrap();
5077 writeln!(output, " }}").unwrap();
5078 writeln!(output, " Err(_) => {{").unwrap();
5079 writeln!(output, " logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5080 writeln!(output, " LogosStatus::ContainsNullByte").unwrap();
5081 writeln!(output, " }}").unwrap();
5082 writeln!(output, " }}").unwrap();
5083 } else {
5084 writeln!(output, " {}({});", inner_name, call_args.join(", ")).unwrap();
5086 writeln!(output, " LogosStatus::Ok").unwrap();
5087 }
5088 } else if has_text_return {
5089 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5091 writeln!(output, " match std::ffi::CString::new(result) {{").unwrap();
5092 writeln!(output, " Ok(cstr) => cstr.into_raw(),").unwrap();
5093 writeln!(output, " Err(_) => {{ logos_set_last_error(\"Return value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
5094 writeln!(output, " }}").unwrap();
5095 } else if return_type.is_some() {
5096 writeln!(output, " {}({})", inner_name, call_args.join(", ")).unwrap();
5097 } else {
5098 writeln!(output, " {}({})", inner_name, call_args.join(", ")).unwrap();
5099 }
5100
5101 writeln!(output, " }})) {{").unwrap();
5103 writeln!(output, " Ok(__v) => __v,").unwrap();
5104 writeln!(output, " Err(__panic) => {{").unwrap();
5105 writeln!(output, " let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
5106 writeln!(output, " logos_set_last_error(__msg);").unwrap();
5107 if !panic_default.is_empty() {
5108 writeln!(output, " {}", panic_default).unwrap();
5109 }
5110 writeln!(output, " }}").unwrap();
5111 writeln!(output, " }}").unwrap();
5112
5113 writeln!(output, "}}\n").unwrap();
5114
5115 output
5116}
5117
5118fn codegen_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
5120 match ty {
5121 TypeExpr::Primitive(sym) => {
5122 map_type_to_rust(interner.resolve(*sym))
5123 }
5124 TypeExpr::Named(sym) => {
5125 let name = interner.resolve(*sym);
5126 map_type_to_rust(name)
5128 }
5129 TypeExpr::Generic { base, params } => {
5130 let base_name = interner.resolve(*base);
5131 let params_str: Vec<String> = params.iter()
5132 .map(|p| codegen_type_expr(p, interner))
5133 .collect();
5134
5135 match base_name {
5136 "Result" => {
5137 if params_str.len() == 2 {
5138 format!("Result<{}, {}>", params_str[0], params_str[1])
5139 } else if params_str.len() == 1 {
5140 format!("Result<{}, String>", params_str[0])
5141 } else {
5142 "Result<(), String>".to_string()
5143 }
5144 }
5145 "Option" | "Maybe" => {
5146 if !params_str.is_empty() {
5147 format!("Option<{}>", params_str[0])
5148 } else {
5149 "Option<()>".to_string()
5150 }
5151 }
5152 "Seq" | "List" | "Vec" => {
5153 if !params_str.is_empty() {
5154 format!("Vec<{}>", params_str[0])
5155 } else {
5156 "Vec<()>".to_string()
5157 }
5158 }
5159 "Map" | "HashMap" => {
5160 if params_str.len() >= 2 {
5161 format!("std::collections::HashMap<{}, {}>", params_str[0], params_str[1])
5162 } else {
5163 "std::collections::HashMap<String, String>".to_string()
5164 }
5165 }
5166 "Set" | "HashSet" => {
5167 if !params_str.is_empty() {
5168 format!("std::collections::HashSet<{}>", params_str[0])
5169 } else {
5170 "std::collections::HashSet<()>".to_string()
5171 }
5172 }
5173 other => {
5174 if params_str.is_empty() {
5175 other.to_string()
5176 } else {
5177 format!("{}<{}>", other, params_str.join(", "))
5178 }
5179 }
5180 }
5181 }
5182 TypeExpr::Function { inputs, output } => {
5183 let inputs_str: Vec<String> = inputs.iter()
5184 .map(|i| codegen_type_expr(i, interner))
5185 .collect();
5186 let output_str = codegen_type_expr(output, interner);
5187 format!("impl Fn({}) -> {}", inputs_str.join(", "), output_str)
5188 }
5189 TypeExpr::Refinement { base, .. } => {
5192 codegen_type_expr(base, interner)
5193 }
5194 TypeExpr::Persistent { inner } => {
5196 let inner_type = codegen_type_expr(inner, interner);
5197 format!("logicaffeine_system::storage::Persistent<{}>", inner_type)
5198 }
5199 }
5200}
5201
5202fn infer_return_type_from_body(body: &[Stmt], _interner: &Interner) -> Option<String> {
5204 for stmt in body {
5205 if let Stmt::Return { value: Some(_) } = stmt {
5206 return Some("i64".to_string());
5209 }
5210 }
5211 None
5212}
5213
5214fn map_type_to_rust(ty: &str) -> String {
5216 match ty {
5217 "Int" => "i64".to_string(),
5218 "Nat" => "u64".to_string(),
5219 "Text" => "String".to_string(),
5220 "Bool" | "Boolean" => "bool".to_string(),
5221 "Real" | "Float" => "f64".to_string(),
5222 "Char" => "char".to_string(),
5223 "Byte" => "u8".to_string(),
5224 "Unit" | "()" => "()".to_string(),
5225 "Duration" => "std::time::Duration".to_string(),
5226 other => other.to_string(),
5227 }
5228}
5229
5230fn codegen_struct_def(name: Symbol, fields: &[FieldDef], generics: &[Symbol], is_portable: bool, is_shared: bool, interner: &Interner, indent: usize, c_abi_value_structs: &HashSet<Symbol>, c_abi_ref_structs: &HashSet<Symbol>) -> String {
5235 let ind = " ".repeat(indent);
5236 let mut output = String::new();
5237
5238 let generic_str = if generics.is_empty() {
5240 String::new()
5241 } else {
5242 let params: Vec<&str> = generics.iter()
5243 .map(|g| interner.resolve(*g))
5244 .collect();
5245 format!("<{}>", params.join(", "))
5246 };
5247
5248 if c_abi_value_structs.contains(&name) {
5250 writeln!(output, "{}#[repr(C)]", ind).unwrap();
5251 }
5252
5253 if is_portable || is_shared || c_abi_ref_structs.contains(&name) {
5258 writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5259 } else {
5260 writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq)]", ind).unwrap();
5261 }
5262 writeln!(output, "{}pub struct {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5263
5264 for field in fields {
5265 let vis = if field.is_public { "pub " } else { "" };
5266 let rust_type = codegen_field_type(&field.ty, interner);
5267 writeln!(output, "{} {}{}: {},", ind, vis, interner.resolve(field.name), rust_type).unwrap();
5268 }
5269
5270 writeln!(output, "{}}}\n", ind).unwrap();
5271
5272 if is_shared {
5274 output.push_str(&codegen_merge_impl(name, fields, generics, interner, indent));
5275 }
5276
5277 output
5278}
5279
5280fn codegen_merge_impl(name: Symbol, fields: &[FieldDef], generics: &[Symbol], interner: &Interner, indent: usize) -> String {
5282 let ind = " ".repeat(indent);
5283 let name_str = interner.resolve(name);
5284 let mut output = String::new();
5285
5286 let generic_str = if generics.is_empty() {
5288 String::new()
5289 } else {
5290 let params: Vec<&str> = generics.iter()
5291 .map(|g| interner.resolve(*g))
5292 .collect();
5293 format!("<{}>", params.join(", "))
5294 };
5295
5296 writeln!(output, "{}impl{} logicaffeine_data::crdt::Merge for {}{} {{", ind, generic_str, name_str, generic_str).unwrap();
5297 writeln!(output, "{} fn merge(&mut self, other: &Self) {{", ind).unwrap();
5298
5299 for field in fields {
5300 let field_name = interner.resolve(field.name);
5301 if is_crdt_field_type(&field.ty, interner) {
5303 writeln!(output, "{} self.{}.merge(&other.{});", ind, field_name, field_name).unwrap();
5304 }
5305 }
5306
5307 writeln!(output, "{} }}", ind).unwrap();
5308 writeln!(output, "{}}}\n", ind).unwrap();
5309
5310 output
5311}
5312
5313fn is_crdt_field_type(ty: &FieldType, interner: &Interner) -> bool {
5315 match ty {
5316 FieldType::Named(sym) => {
5317 let name = interner.resolve(*sym);
5318 matches!(name,
5319 "ConvergentCount" | "GCounter" |
5320 "Tally" | "PNCounter"
5321 )
5322 }
5323 FieldType::Generic { base, .. } => {
5324 let name = interner.resolve(*base);
5325 matches!(name,
5326 "LastWriteWins" | "LWWRegister" |
5327 "SharedSet" | "ORSet" | "SharedSet_AddWins" | "SharedSet_RemoveWins" |
5328 "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" |
5329 "SharedMap" | "ORMap" |
5330 "Divergent" | "MVRegister"
5331 )
5332 }
5333 _ => false,
5334 }
5335}
5336
5337fn codegen_enum_def(name: Symbol, variants: &[VariantDef], generics: &[Symbol], is_portable: bool, _is_shared: bool, interner: &Interner, indent: usize) -> String {
5341 let ind = " ".repeat(indent);
5342 let mut output = String::new();
5343
5344 let generic_str = if generics.is_empty() {
5346 String::new()
5347 } else {
5348 let params: Vec<&str> = generics.iter()
5349 .map(|g| interner.resolve(*g))
5350 .collect();
5351 format!("<{}>", params.join(", "))
5352 };
5353
5354 if is_portable {
5356 writeln!(output, "{}#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5357 } else {
5358 writeln!(output, "{}#[derive(Debug, Clone, PartialEq)]", ind).unwrap();
5359 }
5360 writeln!(output, "{}pub enum {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5361
5362 for variant in variants {
5363 let variant_name = interner.resolve(variant.name);
5364 if variant.fields.is_empty() {
5365 writeln!(output, "{} {},", ind, variant_name).unwrap();
5367 } else {
5368 let enum_name_str = interner.resolve(name);
5371 let fields_str: Vec<String> = variant.fields.iter()
5372 .map(|f| {
5373 let rust_type = codegen_field_type(&f.ty, interner);
5374 let field_name = interner.resolve(f.name);
5375 if is_recursive_field(&f.ty, enum_name_str, interner) {
5377 format!("{}: Box<{}>", field_name, rust_type)
5378 } else {
5379 format!("{}: {}", field_name, rust_type)
5380 }
5381 })
5382 .collect();
5383 writeln!(output, "{} {} {{ {} }},", ind, variant_name, fields_str.join(", ")).unwrap();
5384 }
5385 }
5386
5387 writeln!(output, "{}}}\n", ind).unwrap();
5388
5389 if generics.is_empty() {
5393 if let Some(first_variant) = variants.first() {
5394 let enum_name_str = interner.resolve(name);
5395 let first_variant_name = interner.resolve(first_variant.name);
5396 writeln!(output, "{}impl{} Default for {}{} {{", ind, generic_str, enum_name_str, generic_str).unwrap();
5397 writeln!(output, "{} fn default() -> Self {{", ind).unwrap();
5398 if first_variant.fields.is_empty() {
5399 writeln!(output, "{} {}::{}", ind, enum_name_str, first_variant_name).unwrap();
5400 } else {
5401 let default_fields: Vec<String> = first_variant.fields.iter()
5403 .map(|f| {
5404 let field_name = interner.resolve(f.name);
5405 let enum_name_check = interner.resolve(name);
5406 if is_recursive_field(&f.ty, enum_name_check, interner) {
5407 format!("{}: Box::new(Default::default())", field_name)
5408 } else {
5409 format!("{}: Default::default()", field_name)
5410 }
5411 })
5412 .collect();
5413 writeln!(output, "{} {}::{} {{ {} }}", ind, enum_name_str, first_variant_name, default_fields.join(", ")).unwrap();
5414 }
5415 writeln!(output, "{} }}", ind).unwrap();
5416 writeln!(output, "{}}}\n", ind).unwrap();
5417 }
5418 }
5419
5420 output
5421}
5422
5423fn codegen_field_type(ty: &FieldType, interner: &Interner) -> String {
5425 match ty {
5426 FieldType::Primitive(sym) => {
5427 match interner.resolve(*sym) {
5428 "Int" => "i64".to_string(),
5429 "Nat" => "u64".to_string(),
5430 "Text" => "String".to_string(),
5431 "Bool" | "Boolean" => "bool".to_string(),
5432 "Real" | "Float" => "f64".to_string(),
5433 "Char" => "char".to_string(),
5434 "Byte" => "u8".to_string(),
5435 "Unit" => "()".to_string(),
5436 "Duration" => "std::time::Duration".to_string(),
5437 other => other.to_string(),
5438 }
5439 }
5440 FieldType::Named(sym) => {
5441 let name = interner.resolve(*sym);
5442 match name {
5443 "ConvergentCount" => "logicaffeine_data::crdt::GCounter".to_string(),
5445 "Tally" => "logicaffeine_data::crdt::PNCounter".to_string(),
5447 _ => name.to_string(),
5448 }
5449 }
5450 FieldType::Generic { base, params } => {
5451 let base_name = interner.resolve(*base);
5452 let param_strs: Vec<String> = params.iter()
5453 .map(|p| codegen_field_type(p, interner))
5454 .collect();
5455
5456 match base_name {
5458 "SharedSet_RemoveWins" => {
5460 return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::RemoveWins>", param_strs.join(", "));
5461 }
5462 "SharedSet_AddWins" => {
5463 return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::AddWins>", param_strs.join(", "));
5464 }
5465 "SharedSequence_YATA" | "CollaborativeSequence" => {
5467 return format!("logicaffeine_data::crdt::YATA<{}>", param_strs.join(", "));
5468 }
5469 _ => {}
5470 }
5471
5472 let base_str = match base_name {
5473 "List" | "Seq" => "Vec",
5474 "Set" => "std::collections::HashSet",
5475 "Map" => "std::collections::HashMap",
5476 "Option" | "Maybe" => "Option",
5477 "Result" => "Result",
5478 "LastWriteWins" => "logicaffeine_data::crdt::LWWRegister",
5480 "SharedSet" | "ORSet" => "logicaffeine_data::crdt::ORSet",
5482 "SharedSequence" | "RGA" => "logicaffeine_data::crdt::RGA",
5483 "SharedMap" | "ORMap" => "logicaffeine_data::crdt::ORMap",
5484 "Divergent" | "MVRegister" => "logicaffeine_data::crdt::MVRegister",
5485 other => other,
5486 };
5487 format!("{}<{}>", base_str, param_strs.join(", "))
5488 }
5489 FieldType::TypeParam(sym) => interner.resolve(*sym).to_string(),
5491 }
5492}
5493
5494fn is_recursive_field(ty: &FieldType, enum_name: &str, interner: &Interner) -> bool {
5497 match ty {
5498 FieldType::Primitive(sym) => interner.resolve(*sym) == enum_name,
5499 FieldType::Named(sym) => interner.resolve(*sym) == enum_name,
5500 FieldType::TypeParam(_) => false,
5501 FieldType::Generic { base, params } => {
5502 interner.resolve(*base) == enum_name ||
5504 params.iter().any(|p| is_recursive_field(p, enum_name, interner))
5505 }
5506 }
5507}
5508
5509fn infer_variant_type_annotation(
5512 expr: &Expr,
5513 registry: &TypeRegistry,
5514 interner: &Interner,
5515) -> Option<String> {
5516 let (enum_name, variant_name, field_values) = match expr {
5518 Expr::NewVariant { enum_name, variant, fields } => (*enum_name, *variant, fields),
5519 _ => return None,
5520 };
5521
5522 let enum_def = registry.get(enum_name)?;
5524 let (generics, variants) = match enum_def {
5525 TypeDef::Enum { generics, variants, .. } => (generics, variants),
5526 _ => return None,
5527 };
5528
5529 if generics.len() < 2 {
5531 return None;
5532 }
5533
5534 let variant_def = variants.iter().find(|v| v.name == variant_name)?;
5536
5537 let mut type_param_types: HashMap<Symbol, String> = HashMap::new();
5539 for (field_name, field_value) in field_values {
5540 if let Some(field_def) = variant_def.fields.iter().find(|f| f.name == *field_name) {
5542 if let FieldType::TypeParam(type_param) = &field_def.ty {
5544 let inferred = infer_rust_type_from_expr(field_value, interner);
5545 type_param_types.insert(*type_param, inferred);
5546 }
5547 }
5548 }
5549
5550 let enum_str = interner.resolve(enum_name);
5553 let param_strs: Vec<String> = generics.iter()
5554 .map(|g| {
5555 type_param_types.get(g)
5556 .cloned()
5557 .unwrap_or_else(|| "()".to_string())
5558 })
5559 .collect();
5560
5561 Some(format!("{}<{}>", enum_str, param_strs.join(", ")))
5562}
5563
5564fn infer_rust_type_from_expr(expr: &Expr, interner: &Interner) -> String {
5566 match expr {
5567 Expr::Literal(lit) => match lit {
5568 Literal::Number(_) => "i64".to_string(),
5569 Literal::Float(_) => "f64".to_string(),
5570 Literal::Text(_) => "String".to_string(),
5571 Literal::Boolean(_) => "bool".to_string(),
5572 Literal::Char(_) => "char".to_string(),
5573 Literal::Nothing => "()".to_string(),
5574 Literal::Duration(_) => "std::time::Duration".to_string(),
5575 Literal::Date(_) => "LogosDate".to_string(),
5576 Literal::Moment(_) => "LogosMoment".to_string(),
5577 Literal::Span { .. } => "LogosSpan".to_string(),
5578 Literal::Time(_) => "LogosTime".to_string(),
5579 },
5580 _ => "_".to_string(),
5582 }
5583}
5584
5585fn try_emit_for_range_pattern<'a>(
5590 stmts: &[&Stmt<'a>],
5591 idx: usize,
5592 interner: &Interner,
5593 indent: usize,
5594 mutable_vars: &HashSet<Symbol>,
5595 ctx: &mut RefinementContext<'a>,
5596 lww_fields: &HashSet<(String, String)>,
5597 mv_fields: &HashSet<(String, String)>,
5598 synced_vars: &mut HashSet<Symbol>,
5599 var_caps: &HashMap<Symbol, VariableCapabilities>,
5600 async_functions: &HashSet<Symbol>,
5601 pipe_vars: &HashSet<Symbol>,
5602 boxed_fields: &HashSet<(String, String, String)>,
5603 registry: &TypeRegistry,
5604) -> Option<(String, usize)> {
5605 if idx + 1 >= stmts.len() {
5606 return None;
5607 }
5608
5609 let (counter_sym, counter_start) = match stmts[idx] {
5613 Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), .. } => {
5614 (*var, *n)
5615 }
5616 _ => return None,
5617 };
5618
5619 let (body, limit_expr, is_exclusive) = match stmts[idx + 1] {
5621 Stmt::While { cond, body, .. } => {
5622 match cond {
5623 Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5624 if let Expr::Identifier(sym) = left {
5625 if *sym == counter_sym {
5626 (body, *right, false)
5627 } else {
5628 return None;
5629 }
5630 } else {
5631 return None;
5632 }
5633 }
5634 Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
5635 if let Expr::Identifier(sym) = left {
5636 if *sym == counter_sym {
5637 (body, *right, true)
5638 } else {
5639 return None;
5640 }
5641 } else {
5642 return None;
5643 }
5644 }
5645 _ => return None,
5646 }
5647 }
5648 _ => return None,
5649 };
5650
5651 if body.is_empty() {
5653 return None;
5654 }
5655
5656 let last = &body[body.len() - 1];
5658 match last {
5659 Stmt::Set { target, value, .. } => {
5660 if *target != counter_sym {
5661 return None;
5662 }
5663 match value {
5664 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5665 let is_counter_plus_1 = match (left, right) {
5666 (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5667 (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5668 _ => false,
5669 };
5670 if !is_counter_plus_1 {
5671 return None;
5672 }
5673 }
5674 _ => return None,
5675 }
5676 }
5677 _ => return None,
5678 }
5679
5680 let body_without_increment = &body[..body.len() - 1];
5683 if body_modifies_var(body_without_increment, counter_sym) {
5684 return None;
5685 }
5686
5687 let indent_str = " ".repeat(indent);
5689 let counter_name = interner.resolve(counter_sym);
5690 let limit_str = codegen_expr_simple(limit_expr, interner);
5691
5692 let range_str = if is_exclusive {
5697 format!("{}..{}", counter_start, limit_str)
5698 } else {
5699 if let Expr::Literal(Literal::Number(n)) = limit_expr {
5701 format!("{}..{}", counter_start, n + 1)
5702 } else {
5703 format!("{}..({} + 1)", counter_start, limit_str)
5704 }
5705 };
5706
5707 let mut output = String::new();
5708 writeln!(output, "{}for {} in {} {{", indent_str, counter_name, range_str).unwrap();
5709
5710 ctx.push_scope();
5712 let body_refs: Vec<&Stmt> = body_without_increment.iter().collect();
5713 let mut bi = 0;
5714 while bi < body_refs.len() {
5715 if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
5716 output.push_str(&code);
5717 bi += 1 + skip;
5718 continue;
5719 }
5720 output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
5721 bi += 1;
5722 }
5723 ctx.pop_scope();
5724 writeln!(output, "{}}}", indent_str).unwrap();
5725
5726 let post_value = if is_exclusive {
5730 limit_str
5731 } else {
5732 format!("({} + 1)", limit_str)
5733 };
5734 writeln!(output, "{}let mut {} = {};", indent_str, counter_name, post_value).unwrap();
5735
5736 Some((output, 1)) }
5738
5739fn body_modifies_var(stmts: &[Stmt], sym: Symbol) -> bool {
5742 for stmt in stmts {
5743 match stmt {
5744 Stmt::Set { target, .. } if *target == sym => return true,
5745 Stmt::If { then_block, else_block, .. } => {
5746 if body_modifies_var(then_block, sym) {
5747 return true;
5748 }
5749 if let Some(else_stmts) = else_block {
5750 if body_modifies_var(else_stmts, sym) {
5751 return true;
5752 }
5753 }
5754 }
5755 Stmt::While { body, .. } => {
5756 if body_modifies_var(body, sym) {
5757 return true;
5758 }
5759 }
5760 Stmt::Repeat { body, .. } => {
5761 if body_modifies_var(body, sym) {
5762 return true;
5763 }
5764 }
5765 Stmt::Zone { body, .. } => {
5766 if body_modifies_var(body, sym) {
5767 return true;
5768 }
5769 }
5770 _ => {}
5771 }
5772 }
5773 false
5774}
5775
5776fn body_mutates_collection(stmts: &[Stmt], coll_sym: Symbol) -> bool {
5780 for stmt in stmts {
5781 match stmt {
5782 Stmt::Push { collection, .. } | Stmt::Pop { collection, .. }
5783 | Stmt::Add { collection, .. } | Stmt::Remove { collection, .. } => {
5784 if let Expr::Identifier(sym) = collection {
5785 if *sym == coll_sym {
5786 return true;
5787 }
5788 }
5789 }
5790 Stmt::SetIndex { collection, .. } => {
5791 if let Expr::Identifier(sym) = collection {
5792 if *sym == coll_sym {
5793 return true;
5794 }
5795 }
5796 }
5797 Stmt::Set { target, .. } if *target == coll_sym => return true,
5798 Stmt::If { then_block, else_block, .. } => {
5799 if body_mutates_collection(then_block, coll_sym) {
5800 return true;
5801 }
5802 if let Some(else_stmts) = else_block {
5803 if body_mutates_collection(else_stmts, coll_sym) {
5804 return true;
5805 }
5806 }
5807 }
5808 Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
5809 if body_mutates_collection(body, coll_sym) {
5810 return true;
5811 }
5812 }
5813 Stmt::Zone { body, .. } => {
5814 if body_mutates_collection(body, coll_sym) {
5815 return true;
5816 }
5817 }
5818 _ => {}
5819 }
5820 }
5821 false
5822}
5823
5824fn try_emit_vec_fill_pattern<'a>(
5828 stmts: &[&Stmt<'a>],
5829 idx: usize,
5830 interner: &Interner,
5831 indent: usize,
5832) -> Option<(String, usize)> {
5833 if idx + 2 >= stmts.len() {
5834 return None;
5835 }
5836
5837 let (vec_sym, elem_type) = match stmts[idx] {
5839 Stmt::Let { var, value, mutable: true, ty, .. } => {
5840 let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
5842 let base_name = interner.resolve(*base);
5843 if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
5844 Some(codegen_type_expr(¶ms[0], interner))
5845 } else {
5846 None
5847 }
5848 } else {
5849 None
5850 };
5851
5852 let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
5854 let tn = interner.resolve(*type_name);
5855 if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
5856 if !type_args.is_empty() {
5857 Some(codegen_type_expr(&type_args[0], interner))
5858 } else {
5859 None
5860 }
5861 } else {
5862 None
5863 }
5864 } else {
5865 None
5866 };
5867
5868 match type_from_annotation.or(type_from_new) {
5869 Some(t) => (*var, t),
5870 None => return None,
5871 }
5872 }
5873 _ => return None,
5874 };
5875
5876 let (counter_sym, counter_start) = match stmts[idx + 1] {
5878 Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), mutable: true, .. } => {
5879 (*var, *n)
5880 }
5881 _ => return None,
5882 };
5883
5884 match stmts[idx + 2] {
5886 Stmt::While { cond, body, .. } => {
5887 let (limit_expr, is_exclusive) = match cond {
5889 Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5890 if let Expr::Identifier(sym) = left {
5891 if *sym == counter_sym {
5892 (Some(*right), false)
5893 } else {
5894 (None, false)
5895 }
5896 } else {
5897 (None, false)
5898 }
5899 }
5900 Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
5901 if let Expr::Identifier(sym) = left {
5902 if *sym == counter_sym {
5903 (Some(*right), true)
5904 } else {
5905 (None, false)
5906 }
5907 } else {
5908 (None, false)
5909 }
5910 }
5911 _ => (None, false),
5912 };
5913 let limit_expr = limit_expr?;
5914
5915 if body.len() != 2 {
5917 return None;
5918 }
5919
5920 let push_val = match &body[0] {
5922 Stmt::Push { value, collection } => {
5923 if let Expr::Identifier(sym) = collection {
5924 if *sym == vec_sym {
5925 Some(*value)
5926 } else {
5927 None
5928 }
5929 } else {
5930 None
5931 }
5932 }
5933 _ => None,
5934 }?;
5935
5936 let val_str = match push_val {
5938 Expr::Literal(Literal::Number(n)) => format!("{}", n),
5939 Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5940 Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5941 Expr::Literal(Literal::Char(c)) => format!("'{}'", c),
5942 Expr::Literal(Literal::Text(s)) => format!("{}.to_string()", interner.resolve(*s)),
5943 _ => return None,
5944 };
5945
5946 match &body[1] {
5948 Stmt::Set { target, value, .. } => {
5949 if *target != counter_sym {
5950 return None;
5951 }
5952 match value {
5954 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5955 let is_counter_plus_1 = match (left, right) {
5956 (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5957 (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5958 _ => false,
5959 };
5960 if !is_counter_plus_1 {
5961 return None;
5962 }
5963 }
5964 _ => return None,
5965 }
5966 }
5967 _ => return None,
5968 }
5969
5970 let indent_str = " ".repeat(indent);
5972 let vec_name = interner.resolve(vec_sym);
5973 let limit_str = codegen_expr_simple(limit_expr, interner);
5974
5975 let count_expr = if is_exclusive {
5979 if counter_start == 0 {
5981 format!("{} as usize", limit_str)
5982 } else {
5983 format!("({} - {}) as usize", limit_str, counter_start)
5984 }
5985 } else {
5986 if counter_start == 0 {
5988 format!("({} + 1) as usize", limit_str)
5989 } else if counter_start == 1 {
5990 format!("{} as usize", limit_str)
5991 } else {
5992 format!("({} - {} + 1) as usize", limit_str, counter_start)
5993 }
5994 };
5995
5996 let mut output = String::new();
5997 writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
5998 indent_str, vec_name, elem_type, val_str, count_expr).unwrap();
5999 let counter_name = interner.resolve(counter_sym);
6001 writeln!(output, "{}let mut {} = {};",
6002 indent_str, counter_name, counter_start).unwrap();
6003
6004 Some((output, 2)) }
6006 _ => None,
6007 }
6008}
6009
6010fn codegen_expr_simple(expr: &Expr, interner: &Interner) -> String {
6012 match expr {
6013 Expr::Literal(Literal::Number(n)) => format!("{}", n),
6014 Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
6015 Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
6016 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
6017 Expr::BinaryOp { op, left, right } => {
6018 let l = codegen_expr_simple(left, interner);
6019 let r = codegen_expr_simple(right, interner);
6020 let op_str = match op {
6021 BinaryOpKind::Add => "+",
6022 BinaryOpKind::Subtract => "-",
6023 BinaryOpKind::Multiply => "*",
6024 BinaryOpKind::Divide => "/",
6025 BinaryOpKind::Modulo => "%",
6026 _ => return format!("({})", l),
6027 };
6028 format!("({} {} {})", l, op_str, r)
6029 }
6030 _ => "_".to_string(),
6031 }
6032}
6033
6034fn exprs_equal(a: &Expr, b: &Expr) -> bool {
6036 match (a, b) {
6037 (Expr::Identifier(s1), Expr::Identifier(s2)) => s1 == s2,
6038 (Expr::Literal(Literal::Number(n1)), Expr::Literal(Literal::Number(n2))) => n1 == n2,
6039 (Expr::BinaryOp { op: op1, left: l1, right: r1 }, Expr::BinaryOp { op: op2, left: l2, right: r2 }) => {
6040 op1 == op2 && exprs_equal(l1, l2) && exprs_equal(r1, r2)
6041 }
6042 _ => false,
6043 }
6044}
6045
6046fn try_emit_swap_pattern<'a>(
6052 stmts: &[&Stmt<'a>],
6053 idx: usize,
6054 interner: &Interner,
6055 indent: usize,
6056 variable_types: &HashMap<Symbol, String>,
6057) -> Option<(String, usize)> {
6058 if idx + 2 >= stmts.len() {
6059 return None;
6060 }
6061
6062 let (a_sym, arr_sym_1, idx_expr_1) = match stmts[idx] {
6064 Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
6065 if let Expr::Identifier(coll_sym) = collection {
6066 (*var, *coll_sym, *index)
6067 } else {
6068 return None;
6069 }
6070 }
6071 _ => return None,
6072 };
6073
6074 if let Some(t) = variable_types.get(&arr_sym_1) {
6076 if !t.starts_with("Vec") {
6077 return None;
6078 }
6079 } else {
6080 return None;
6081 }
6082
6083 let (b_sym, arr_sym_2, idx_expr_2) = match stmts[idx + 1] {
6085 Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
6086 if let Expr::Identifier(coll_sym) = collection {
6087 (*var, *coll_sym, *index)
6088 } else {
6089 return None;
6090 }
6091 }
6092 _ => return None,
6093 };
6094
6095 if arr_sym_1 != arr_sym_2 {
6097 return None;
6098 }
6099
6100 let is_adjacent = match idx_expr_2 {
6102 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
6103 (exprs_equal(left, idx_expr_1) && matches!(right, Expr::Literal(Literal::Number(1))))
6104 || (matches!(left, Expr::Literal(Literal::Number(1))) && exprs_equal(right, idx_expr_1))
6105 }
6106 _ => false,
6107 };
6108 if !is_adjacent {
6109 return None;
6110 }
6111
6112 match stmts[idx + 2] {
6114 Stmt::If { cond, then_block, else_block } => {
6115 let compares_a_b = match cond {
6117 Expr::BinaryOp { op, left, right } => {
6118 matches!(op, BinaryOpKind::Gt | BinaryOpKind::Lt | BinaryOpKind::GtEq | BinaryOpKind::LtEq | BinaryOpKind::Eq | BinaryOpKind::NotEq) &&
6119 ((matches!(left, Expr::Identifier(s) if *s == a_sym) && matches!(right, Expr::Identifier(s) if *s == b_sym)) ||
6120 (matches!(left, Expr::Identifier(s) if *s == b_sym) && matches!(right, Expr::Identifier(s) if *s == a_sym)))
6121 }
6122 _ => false,
6123 };
6124 if !compares_a_b {
6125 return None;
6126 }
6127
6128 if else_block.is_some() {
6130 return None;
6131 }
6132
6133 if then_block.len() != 2 {
6135 return None;
6136 }
6137
6138 let swap_ok = match (&then_block[0], &then_block[1]) {
6140 (
6141 Stmt::SetIndex { collection: c1, index: i1, value: v1 },
6142 Stmt::SetIndex { collection: c2, index: i2, value: v2 },
6143 ) => {
6144 let same_arr = matches!((c1, c2), (Expr::Identifier(s1), Expr::Identifier(s2)) if *s1 == arr_sym_1 && *s2 == arr_sym_1);
6146 let cross = exprs_equal(i1, idx_expr_1) && exprs_equal(i2, idx_expr_2) &&
6148 matches!(v1, Expr::Identifier(s) if *s == b_sym) &&
6149 matches!(v2, Expr::Identifier(s) if *s == a_sym);
6150 let cross_rev = exprs_equal(i1, idx_expr_2) && exprs_equal(i2, idx_expr_1) &&
6152 matches!(v1, Expr::Identifier(s) if *s == a_sym) &&
6153 matches!(v2, Expr::Identifier(s) if *s == b_sym);
6154 same_arr && (cross || cross_rev)
6155 }
6156 _ => false,
6157 };
6158
6159 if !swap_ok {
6160 return None;
6161 }
6162
6163 let indent_str = " ".repeat(indent);
6165 let arr_name = interner.resolve(arr_sym_1);
6166 let idx1_str = codegen_expr_simple(idx_expr_1, interner);
6167 let idx2_str = codegen_expr_simple(idx_expr_2, interner);
6168
6169 let op_str = match cond {
6170 Expr::BinaryOp { op, .. } => match op {
6171 BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
6172 BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
6173 BinaryOpKind::Eq => "==", BinaryOpKind::NotEq => "!=",
6174 _ => unreachable!(),
6175 },
6176 _ => unreachable!(),
6177 };
6178
6179 let mut output = String::new();
6180 writeln!(output, "{}if {}[({} - 1) as usize] {} {}[({} - 1) as usize] {{",
6181 indent_str, arr_name, idx1_str, op_str, arr_name, idx2_str,
6182 ).unwrap();
6183 writeln!(output, "{} {}.swap(({} - 1) as usize, ({} - 1) as usize);",
6184 indent_str, arr_name, idx1_str, idx2_str).unwrap();
6185 writeln!(output, "{}}}", indent_str).unwrap();
6186
6187 Some((output, 2)) }
6189 _ => None,
6190 }
6191}
6192
6193pub fn codegen_stmt<'a>(
6194 stmt: &Stmt<'a>,
6195 interner: &Interner,
6196 indent: usize,
6197 mutable_vars: &HashSet<Symbol>,
6198 ctx: &mut RefinementContext<'a>,
6199 lww_fields: &HashSet<(String, String)>,
6200 mv_fields: &HashSet<(String, String)>, synced_vars: &mut HashSet<Symbol>, var_caps: &HashMap<Symbol, VariableCapabilities>, async_functions: &HashSet<Symbol>, pipe_vars: &HashSet<Symbol>, boxed_fields: &HashSet<(String, String, String)>, registry: &TypeRegistry, ) -> String {
6208 let indent_str = " ".repeat(indent);
6209 let mut output = String::new();
6210
6211 match stmt {
6212 Stmt::Let { var, ty, value, mutable } => {
6213 let var_name = interner.resolve(*var);
6214
6215 if let Some(TypeExpr::Generic { base, params }) = ty {
6218 let base_name = interner.resolve(*base);
6219 match base_name {
6220 "Seq" | "List" | "Vec" => {
6221 let rust_type = if !params.is_empty() {
6222 format!("Vec<{}>", codegen_type_expr(¶ms[0], interner))
6223 } else {
6224 "Vec<()>".to_string()
6225 };
6226 ctx.register_variable_type(*var, rust_type);
6227 }
6228 "Map" | "HashMap" => {
6229 let rust_type = if params.len() >= 2 {
6230 format!("std::collections::HashMap<{}, {}>", codegen_type_expr(¶ms[0], interner), codegen_type_expr(¶ms[1], interner))
6231 } else {
6232 "std::collections::HashMap<String, String>".to_string()
6233 };
6234 ctx.register_variable_type(*var, rust_type);
6235 }
6236 _ => {}
6237 }
6238 } else if let Expr::New { type_name, type_args, .. } = value {
6239 let type_str = interner.resolve(*type_name);
6240 match type_str {
6241 "Seq" | "List" | "Vec" => {
6242 let rust_type = if !type_args.is_empty() {
6243 format!("Vec<{}>", codegen_type_expr(&type_args[0], interner))
6244 } else {
6245 "Vec<()>".to_string()
6246 };
6247 ctx.register_variable_type(*var, rust_type);
6248 }
6249 "Map" | "HashMap" => {
6250 let rust_type = if type_args.len() >= 2 {
6251 format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&type_args[0], interner), codegen_type_expr(&type_args[1], interner))
6252 } else {
6253 "std::collections::HashMap<String, String>".to_string()
6254 };
6255 ctx.register_variable_type(*var, rust_type);
6256 }
6257 _ => {}
6258 }
6259 } else if let Expr::List(items) = value {
6260 let elem_type = items.first()
6262 .map(|e| infer_rust_type_from_expr(e, interner))
6263 .unwrap_or_else(|| "_".to_string());
6264 ctx.register_variable_type(*var, format!("Vec<{}>", elem_type));
6265 }
6266
6267 let value_str = codegen_expr_boxed_with_types(
6269 value, interner, synced_vars, boxed_fields, registry, async_functions,
6270 ctx.get_string_vars(), ctx.get_variable_types()
6271 );
6272
6273 let type_annotation = ty.map(|t| codegen_type_expr(t, interner))
6275 .or_else(|| infer_variant_type_annotation(value, registry, interner));
6276
6277 let is_mutable = *mutable || mutable_vars.contains(var);
6279
6280 match (is_mutable, type_annotation) {
6281 (true, Some(t)) => writeln!(output, "{}let mut {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
6282 (true, None) => writeln!(output, "{}let mut {} = {};", indent_str, var_name, value_str).unwrap(),
6283 (false, Some(t)) => writeln!(output, "{}let {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
6284 (false, None) => writeln!(output, "{}let {} = {};", indent_str, var_name, value_str).unwrap(),
6285 }
6286
6287 if is_definitely_string_expr_with_vars(value, ctx.get_string_vars()) {
6289 ctx.register_string_var(*var);
6290 }
6291
6292 if let Some(TypeExpr::Refinement { base: _, var: bound_var, predicate }) = ty {
6294 emit_refinement_check(var_name, *bound_var, predicate, interner, &indent_str, &mut output);
6295 ctx.register(*var, *bound_var, predicate);
6296 }
6297 }
6298
6299 Stmt::Set { target, value } => {
6300 let target_name = interner.resolve(*target);
6301 let string_vars = ctx.get_string_vars();
6302 let var_types = ctx.get_variable_types();
6303
6304 let used_write = if ctx.is_string_var(*target)
6308 && is_definitely_string_expr_with_vars(value, string_vars)
6309 {
6310 let mut operands = Vec::new();
6311 collect_string_concat_operands(value, string_vars, &mut operands);
6312
6313 if operands.len() >= 2 && matches!(operands[0], Expr::Identifier(sym) if *sym == *target) {
6315 let tail = &operands[1..];
6317 let mut tail_ids = HashSet::new();
6318 for op in tail {
6319 collect_expr_identifiers(op, &mut tail_ids);
6320 }
6321
6322 if !tail_ids.contains(target) {
6323 let placeholders: String = tail.iter().map(|_| "{}").collect::<Vec<_>>().join("");
6325 let values: Vec<String> = tail.iter().map(|e| {
6326 if let Expr::Literal(Literal::Text(sym)) = e {
6328 format!("\"{}\"", interner.resolve(*sym))
6329 } else {
6330 codegen_expr_boxed_with_types(
6331 e, interner, synced_vars, boxed_fields, registry, async_functions,
6332 string_vars, var_types
6333 )
6334 }
6335 }).collect();
6336 writeln!(output, "{}write!({}, \"{}\", {}).unwrap();",
6337 indent_str, target_name, placeholders, values.join(", ")).unwrap();
6338 true
6339 } else {
6340 false
6341 }
6342 } else {
6343 false
6344 }
6345 } else {
6346 false
6347 };
6348
6349 if !used_write {
6350 let value_str = codegen_expr_boxed_with_types(
6352 value, interner, synced_vars, boxed_fields, registry, async_functions,
6353 string_vars, var_types
6354 );
6355 writeln!(output, "{}{} = {};", indent_str, target_name, value_str).unwrap();
6356 }
6357
6358 if let Some((bound_var, predicate)) = ctx.get_constraint(*target) {
6360 emit_refinement_check(target_name, bound_var, predicate, interner, &indent_str, &mut output);
6361 }
6362 }
6363
6364 Stmt::Call { function, args } => {
6365 let func_name = escape_rust_ident(interner.resolve(*function));
6366 let args_str: Vec<String> = args.iter().map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())).collect();
6367 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6369 writeln!(output, "{}{}({}){};", indent_str, func_name, args_str.join(", "), await_suffix).unwrap();
6370 }
6371
6372 Stmt::If { cond, then_block, else_block } => {
6373 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6374 writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
6375 ctx.push_scope();
6376 for stmt in *then_block {
6377 output.push_str(&codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6378 }
6379 ctx.pop_scope();
6380 if let Some(else_stmts) = else_block {
6381 writeln!(output, "{}}} else {{", indent_str).unwrap();
6382 ctx.push_scope();
6383 for stmt in *else_stmts {
6384 output.push_str(&codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6385 }
6386 ctx.pop_scope();
6387 }
6388 writeln!(output, "{}}}", indent_str).unwrap();
6389 }
6390
6391 Stmt::While { cond, body, decreasing: _ } => {
6392 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6394 writeln!(output, "{}while {} {{", indent_str, cond_str).unwrap();
6395 ctx.push_scope();
6396 let body_refs: Vec<&Stmt> = body.iter().collect();
6398 let mut bi = 0;
6399 while bi < body_refs.len() {
6400 if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1) {
6401 output.push_str(&code);
6402 bi += 1 + skip;
6403 continue;
6404 }
6405 if let Some((code, skip)) = try_emit_for_range_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry) {
6406 output.push_str(&code);
6407 bi += 1 + skip;
6408 continue;
6409 }
6410 if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6411 output.push_str(&code);
6412 bi += 1 + skip;
6413 continue;
6414 }
6415 output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6416 bi += 1;
6417 }
6418 ctx.pop_scope();
6419 writeln!(output, "{}}}", indent_str).unwrap();
6420 }
6421
6422 Stmt::Repeat { pattern, iterable, body } => {
6423 use crate::ast::stmt::Pattern;
6424
6425 let pattern_str = match pattern {
6427 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
6428 Pattern::Tuple(syms) => {
6429 let names = syms.iter()
6430 .map(|s| interner.resolve(*s))
6431 .collect::<Vec<_>>()
6432 .join(", ");
6433 format!("({})", names)
6434 }
6435 };
6436
6437 let iter_str = codegen_expr_with_async(iterable, interner, synced_vars, async_functions, ctx.get_variable_types());
6438
6439 let body_has_async = body.iter().any(|s| {
6442 requires_async_stmt(s) || calls_async_function(s, async_functions)
6443 });
6444
6445 if body_has_async {
6446 writeln!(output, "{}let mut __iter = ({}).into_iter();", indent_str, iter_str).unwrap();
6448 writeln!(output, "{}while let Some({}) = __iter.next() {{", indent_str, pattern_str).unwrap();
6449 } else {
6450 let use_iter_copied = if let Expr::Identifier(coll_sym) = iterable {
6453 if let Some(coll_type) = ctx.get_variable_types().get(coll_sym) {
6454 coll_type.starts_with("Vec") && has_copy_element_type(coll_type)
6455 && !body_mutates_collection(body, *coll_sym)
6456 } else {
6457 false
6458 }
6459 } else {
6460 false
6461 };
6462
6463 if use_iter_copied {
6464 writeln!(output, "{}for {} in {}.iter().copied() {{", indent_str, pattern_str, iter_str).unwrap();
6465 } else {
6466 writeln!(output, "{}for {} in {}.clone() {{", indent_str, pattern_str, iter_str).unwrap();
6469 }
6470 }
6471 ctx.push_scope();
6472 {
6474 let body_refs: Vec<&Stmt> = body.iter().collect();
6475 let mut bi = 0;
6476 while bi < body_refs.len() {
6477 if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6478 output.push_str(&code);
6479 bi += 1 + skip;
6480 continue;
6481 }
6482 output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6483 bi += 1;
6484 }
6485 }
6486 ctx.pop_scope();
6487 writeln!(output, "{}}}", indent_str).unwrap();
6488 }
6489
6490 Stmt::Return { value } => {
6491 if let Some(v) = value {
6492 let value_str = codegen_expr_with_async(v, interner, synced_vars, async_functions, ctx.get_variable_types());
6493 writeln!(output, "{}return {};", indent_str, value_str).unwrap();
6494 } else {
6495 writeln!(output, "{}return;", indent_str).unwrap();
6496 }
6497 }
6498
6499 Stmt::Assert { proposition } => {
6500 let condition = codegen_assertion(proposition, interner);
6501 writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6502 }
6503
6504 Stmt::Trust { proposition, justification } => {
6506 let reason = interner.resolve(*justification);
6507 let reason_clean = reason.trim_matches('"');
6509 writeln!(output, "{}// TRUST: {}", indent_str, reason_clean).unwrap();
6510 let condition = codegen_assertion(proposition, interner);
6511 writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6512 }
6513
6514 Stmt::RuntimeAssert { condition } => {
6515 let cond_str = codegen_expr_with_async(condition, interner, synced_vars, async_functions, ctx.get_variable_types());
6516 writeln!(output, "{}debug_assert!({});", indent_str, cond_str).unwrap();
6517 }
6518
6519 Stmt::Check { subject, predicate, is_capability, object, source_text, span } => {
6521 let subj_name = interner.resolve(*subject);
6522 let pred_name = interner.resolve(*predicate).to_lowercase();
6523
6524 let call = if *is_capability {
6525 let obj_sym = object.expect("capability must have object");
6526 let obj_word = interner.resolve(obj_sym);
6527
6528 let obj_name = ctx.find_variable_by_type(obj_word, interner)
6532 .unwrap_or_else(|| obj_word.to_string());
6533
6534 format!("{}.can_{}(&{})", subj_name, pred_name, obj_name)
6535 } else {
6536 format!("{}.is_{}()", subj_name, pred_name)
6537 };
6538
6539 writeln!(output, "{}if !({}) {{", indent_str, call).unwrap();
6540 writeln!(output, "{} logicaffeine_system::panic_with(\"Security Check Failed at line {}: {}\");",
6541 indent_str, span.start, source_text).unwrap();
6542 writeln!(output, "{}}}", indent_str).unwrap();
6543 }
6544
6545 Stmt::Listen { address } => {
6547 let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6548 writeln!(output, "{}logicaffeine_system::network::listen(&{}).await.expect(\"Failed to listen\");",
6550 indent_str, addr_str).unwrap();
6551 }
6552
6553 Stmt::ConnectTo { address } => {
6555 let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6556 writeln!(output, "{}logicaffeine_system::network::connect(&{}).await.expect(\"Failed to connect\");",
6558 indent_str, addr_str).unwrap();
6559 }
6560
6561 Stmt::LetPeerAgent { var, address } => {
6563 let var_name = interner.resolve(*var);
6564 let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6565 writeln!(output, "{}let {} = logicaffeine_system::network::PeerAgent::new(&{}).expect(\"Invalid address\");",
6567 indent_str, var_name, addr_str).unwrap();
6568 }
6569
6570 Stmt::Sleep { milliseconds } => {
6572 let expr_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6573 let inferred_type = infer_rust_type_from_expr(milliseconds, interner);
6574
6575 if inferred_type == "std::time::Duration" {
6576 writeln!(output, "{}tokio::time::sleep({}).await;",
6578 indent_str, expr_str).unwrap();
6579 } else {
6580 writeln!(output, "{}tokio::time::sleep(std::time::Duration::from_millis({} as u64)).await;",
6582 indent_str, expr_str).unwrap();
6583 }
6584 }
6585
6586 Stmt::Sync { var, topic } => {
6588 let var_name = interner.resolve(*var);
6589 let topic_str = codegen_expr_with_async(topic, interner, synced_vars, async_functions, ctx.get_variable_types());
6590
6591 if let Some(caps) = var_caps.get(var) {
6593 if caps.mounted {
6594 synced_vars.insert(*var);
6598 return output; }
6600 }
6601
6602 writeln!(
6604 output,
6605 "{}let {} = logicaffeine_system::crdt::Synced::new({}, &{}).await;",
6606 indent_str, var_name, var_name, topic_str
6607 ).unwrap();
6608 synced_vars.insert(*var);
6609 }
6610
6611 Stmt::Mount { var, path } => {
6613 let var_name = interner.resolve(*var);
6614 let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
6615
6616 if let Some(caps) = var_caps.get(var) {
6618 if caps.synced {
6619 let topic_str = caps.sync_topic.as_ref()
6621 .map(|s| s.as_str())
6622 .unwrap_or("\"default\"");
6623 writeln!(
6624 output,
6625 "{}let {} = logicaffeine_system::distributed::Distributed::mount(std::sync::Arc::new(vfs.clone()), &{}, Some({}.to_string())).await.expect(\"Failed to mount\");",
6626 indent_str, var_name, path_str, topic_str
6627 ).unwrap();
6628 synced_vars.insert(*var);
6629 return output;
6630 }
6631 }
6632
6633 writeln!(
6635 output,
6636 "{}let {} = logicaffeine_system::storage::Persistent::mount(&vfs, &{}).await.expect(\"Failed to mount\");",
6637 indent_str, var_name, path_str
6638 ).unwrap();
6639 synced_vars.insert(*var);
6640 }
6641
6642 Stmt::LaunchTask { function, args } => {
6647 let fn_name = escape_rust_ident(interner.resolve(*function));
6648 let args_str: Vec<String> = args.iter()
6650 .map(|a| {
6651 if let Expr::Identifier(sym) = a {
6652 if pipe_vars.contains(sym) {
6653 return format!("{}_tx.clone()", interner.resolve(*sym));
6654 }
6655 }
6656 codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6657 })
6658 .collect();
6659 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6661 writeln!(
6662 output,
6663 "{}tokio::spawn(async move {{ {}({}){await_suffix}; }});",
6664 indent_str, fn_name, args_str.join(", ")
6665 ).unwrap();
6666 }
6667
6668 Stmt::LaunchTaskWithHandle { handle, function, args } => {
6669 let handle_name = interner.resolve(*handle);
6670 let fn_name = escape_rust_ident(interner.resolve(*function));
6671 let args_str: Vec<String> = args.iter()
6673 .map(|a| {
6674 if let Expr::Identifier(sym) = a {
6675 if pipe_vars.contains(sym) {
6676 return format!("{}_tx.clone()", interner.resolve(*sym));
6677 }
6678 }
6679 codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6680 })
6681 .collect();
6682 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6684 writeln!(
6685 output,
6686 "{}let {} = tokio::spawn(async move {{ {}({}){await_suffix} }});",
6687 indent_str, handle_name, fn_name, args_str.join(", ")
6688 ).unwrap();
6689 }
6690
6691 Stmt::CreatePipe { var, element_type, capacity } => {
6692 let var_name = interner.resolve(*var);
6693 let type_name = interner.resolve(*element_type);
6694 let cap = capacity.unwrap_or(32);
6695 let rust_type = match type_name {
6697 "Int" => "i64",
6698 "Nat" => "u64",
6699 "Text" => "String",
6700 "Bool" => "bool",
6701 _ => type_name,
6702 };
6703 writeln!(
6704 output,
6705 "{}let ({}_tx, mut {}_rx) = tokio::sync::mpsc::channel::<{}>({});",
6706 indent_str, var_name, var_name, rust_type, cap
6707 ).unwrap();
6708 }
6709
6710 Stmt::SendPipe { value, pipe } => {
6711 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6712 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6713 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6715 pipe_vars.contains(sym)
6716 } else {
6717 false
6718 };
6719 if is_local_pipe {
6720 writeln!(
6721 output,
6722 "{}{}_tx.send({}).await.expect(\"pipe send failed\");",
6723 indent_str, pipe_str, val_str
6724 ).unwrap();
6725 } else {
6726 writeln!(
6727 output,
6728 "{}{}.send({}).await.expect(\"pipe send failed\");",
6729 indent_str, pipe_str, val_str
6730 ).unwrap();
6731 }
6732 }
6733
6734 Stmt::ReceivePipe { var, pipe } => {
6735 let var_name = interner.resolve(*var);
6736 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6737 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6739 pipe_vars.contains(sym)
6740 } else {
6741 false
6742 };
6743 if is_local_pipe {
6744 writeln!(
6745 output,
6746 "{}let {} = {}_rx.recv().await.expect(\"pipe closed\");",
6747 indent_str, var_name, pipe_str
6748 ).unwrap();
6749 } else {
6750 writeln!(
6751 output,
6752 "{}let {} = {}.recv().await.expect(\"pipe closed\");",
6753 indent_str, var_name, pipe_str
6754 ).unwrap();
6755 }
6756 }
6757
6758 Stmt::TrySendPipe { value, pipe, result } => {
6759 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6760 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6761 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6763 pipe_vars.contains(sym)
6764 } else {
6765 false
6766 };
6767 let suffix = if is_local_pipe { "_tx" } else { "" };
6768 if let Some(res) = result {
6769 let res_name = interner.resolve(*res);
6770 writeln!(
6771 output,
6772 "{}let {} = {}{}.try_send({}).is_ok();",
6773 indent_str, res_name, pipe_str, suffix, val_str
6774 ).unwrap();
6775 } else {
6776 writeln!(
6777 output,
6778 "{}let _ = {}{}.try_send({});",
6779 indent_str, pipe_str, suffix, val_str
6780 ).unwrap();
6781 }
6782 }
6783
6784 Stmt::TryReceivePipe { var, pipe } => {
6785 let var_name = interner.resolve(*var);
6786 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6787 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6789 pipe_vars.contains(sym)
6790 } else {
6791 false
6792 };
6793 let suffix = if is_local_pipe { "_rx" } else { "" };
6794 writeln!(
6795 output,
6796 "{}let {} = {}{}.try_recv().ok();",
6797 indent_str, var_name, pipe_str, suffix
6798 ).unwrap();
6799 }
6800
6801 Stmt::StopTask { handle } => {
6802 let handle_str = codegen_expr_with_async(handle, interner, synced_vars, async_functions, ctx.get_variable_types());
6803 writeln!(output, "{}{}.abort();", indent_str, handle_str).unwrap();
6804 }
6805
6806 Stmt::Select { branches } => {
6807 use crate::ast::stmt::SelectBranch;
6808
6809 writeln!(output, "{}tokio::select! {{", indent_str).unwrap();
6810 for branch in branches {
6811 match branch {
6812 SelectBranch::Receive { var, pipe, body } => {
6813 let var_name = interner.resolve(*var);
6814 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6815 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6817 pipe_vars.contains(sym)
6818 } else {
6819 false
6820 };
6821 let suffix = if is_local_pipe { "_rx" } else { "" };
6822 writeln!(
6823 output,
6824 "{} {} = {}{}.recv() => {{",
6825 indent_str, var_name, pipe_str, suffix
6826 ).unwrap();
6827 writeln!(
6828 output,
6829 "{} if let Some({}) = {} {{",
6830 indent_str, var_name, var_name
6831 ).unwrap();
6832 for stmt in *body {
6833 let stmt_code = codegen_stmt(stmt, interner, indent + 3, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
6834 write!(output, "{}", stmt_code).unwrap();
6835 }
6836 writeln!(output, "{} }}", indent_str).unwrap();
6837 writeln!(output, "{} }}", indent_str).unwrap();
6838 }
6839 SelectBranch::Timeout { milliseconds, body } => {
6840 let ms_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6841 writeln!(
6843 output,
6844 "{} _ = tokio::time::sleep(std::time::Duration::from_secs({} as u64)) => {{",
6845 indent_str, ms_str
6846 ).unwrap();
6847 for stmt in *body {
6848 let stmt_code = codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
6849 write!(output, "{}", stmt_code).unwrap();
6850 }
6851 writeln!(output, "{} }}", indent_str).unwrap();
6852 }
6853 }
6854 }
6855 writeln!(output, "{}}}", indent_str).unwrap();
6856 }
6857
6858 Stmt::Give { object, recipient } => {
6859 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6861 let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6862 writeln!(output, "{}{}({});", indent_str, recv_str, obj_str).unwrap();
6863 }
6864
6865 Stmt::Show { object, recipient } => {
6866 let obj_str = codegen_expr_with_async_and_strings(object, interner, synced_vars, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6869 let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6870 writeln!(output, "{}{}(&{});", indent_str, recv_str, obj_str).unwrap();
6871 }
6872
6873 Stmt::SetField { object, field, value } => {
6874 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6875 let field_name = interner.resolve(*field);
6876 let value_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6877
6878 let is_lww = lww_fields.iter().any(|(_, f)| f == field_name);
6881 let is_mv = mv_fields.iter().any(|(_, f)| f == field_name);
6882 if is_lww {
6883 writeln!(output, "{}{}.{}.set({}, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_micros() as u64);", indent_str, obj_str, field_name, value_str).unwrap();
6885 } else if is_mv {
6886 writeln!(output, "{}{}.{}.set({});", indent_str, obj_str, field_name, value_str).unwrap();
6888 } else {
6889 writeln!(output, "{}{}.{} = {};", indent_str, obj_str, field_name, value_str).unwrap();
6890 }
6891 }
6892
6893 Stmt::StructDef { .. } => {
6894 }
6896
6897 Stmt::FunctionDef { .. } => {
6898 }
6900
6901 Stmt::Inspect { target, arms, .. } => {
6902 let target_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
6903
6904 let mut inner_boxed_binding_names: HashSet<String> = HashSet::new();
6908
6909 writeln!(output, "{}match {} {{", indent_str, target_str).unwrap();
6910
6911 for arm in arms {
6912 if let Some(variant) = arm.variant {
6913 let variant_name = interner.resolve(variant);
6914 let enum_name_str = arm.enum_name.map(|e| interner.resolve(e));
6916 let enum_prefix = enum_name_str
6917 .map(|e| format!("{}::", e))
6918 .unwrap_or_default();
6919
6920 if arm.bindings.is_empty() {
6921 writeln!(output, "{} {}{} => {{", indent_str, enum_prefix, variant_name).unwrap();
6923 } else {
6924 let bindings_str: Vec<String> = arm.bindings.iter()
6927 .map(|(field, binding)| {
6928 let field_name = interner.resolve(*field);
6929 let binding_name = interner.resolve(*binding);
6930
6931 if let Some(enum_name) = enum_name_str {
6933 let key = (enum_name.to_string(), variant_name.to_string(), field_name.to_string());
6934 if boxed_fields.contains(&key) {
6935 inner_boxed_binding_names.insert(binding_name.to_string());
6936 }
6937 }
6938
6939 if field_name == binding_name {
6940 field_name.to_string()
6941 } else {
6942 format!("{}: {}", field_name, binding_name)
6943 }
6944 })
6945 .collect();
6946 writeln!(output, "{} {}{} {{ {} }} => {{", indent_str, enum_prefix, variant_name, bindings_str.join(", ")).unwrap();
6947 }
6948 } else {
6949 writeln!(output, "{} _ => {{", indent_str).unwrap();
6951 }
6952
6953 ctx.push_scope();
6954
6955 for binding_name in &inner_boxed_binding_names {
6958 writeln!(output, "{} let {} = (*{}).clone();", indent_str, binding_name, binding_name).unwrap();
6959 }
6960
6961 for stmt in arm.body {
6962 let inner_stmt_code = if let Stmt::Inspect { target: inner_target, .. } = stmt {
6966 if let Expr::Identifier(sym) = inner_target {
6969 let target_name = interner.resolve(*sym);
6970 if inner_boxed_binding_names.contains(target_name) {
6971 let mut inner_output = String::new();
6973 writeln!(inner_output, "{}match {} {{", " ".repeat(indent + 2), target_name).unwrap();
6974
6975 if let Stmt::Inspect { arms: inner_arms, .. } = stmt {
6976 for inner_arm in inner_arms.iter() {
6977 if let Some(v) = inner_arm.variant {
6978 let v_name = interner.resolve(v);
6979 let inner_enum_prefix = inner_arm.enum_name
6980 .map(|e| format!("{}::", interner.resolve(e)))
6981 .unwrap_or_default();
6982
6983 if inner_arm.bindings.is_empty() {
6984 writeln!(inner_output, "{} {}{} => {{", " ".repeat(indent + 2), inner_enum_prefix, v_name).unwrap();
6985 } else {
6986 let bindings: Vec<String> = inner_arm.bindings.iter()
6987 .map(|(f, b)| {
6988 let fn_name = interner.resolve(*f);
6989 let bn_name = interner.resolve(*b);
6990 if fn_name == bn_name { fn_name.to_string() }
6991 else { format!("{}: {}", fn_name, bn_name) }
6992 })
6993 .collect();
6994 writeln!(inner_output, "{} {}{} {{ {} }} => {{", " ".repeat(indent + 2), inner_enum_prefix, v_name, bindings.join(", ")).unwrap();
6995 }
6996 } else {
6997 writeln!(inner_output, "{} _ => {{", " ".repeat(indent + 2)).unwrap();
6998 }
6999
7000 ctx.push_scope();
7001 for inner_stmt in inner_arm.body {
7002 inner_output.push_str(&codegen_stmt(inner_stmt, interner, indent + 4, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
7003 }
7004 ctx.pop_scope();
7005 writeln!(inner_output, "{} }}", " ".repeat(indent + 2)).unwrap();
7006 }
7007 }
7008 writeln!(inner_output, "{}}}", " ".repeat(indent + 2)).unwrap();
7009 inner_output
7010 } else {
7011 codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7012 }
7013 } else {
7014 codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7015 }
7016 } else {
7017 codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7018 };
7019 output.push_str(&inner_stmt_code);
7020 }
7021 ctx.pop_scope();
7022 writeln!(output, "{} }}", indent_str).unwrap();
7023 }
7024
7025 writeln!(output, "{}}}", indent_str).unwrap();
7026 }
7027
7028 Stmt::Push { value, collection } => {
7029 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7030 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7031 writeln!(output, "{}{}.push({});", indent_str, coll_str, val_str).unwrap();
7032 }
7033
7034 Stmt::Pop { collection, into } => {
7035 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7036 match into {
7037 Some(var) => {
7038 let var_name = interner.resolve(*var);
7039 writeln!(output, "{}let {} = {}.pop().expect(\"Pop from empty collection\");", indent_str, var_name, coll_str).unwrap();
7041 }
7042 None => {
7043 writeln!(output, "{}{}.pop();", indent_str, coll_str).unwrap();
7044 }
7045 }
7046 }
7047
7048 Stmt::Add { value, collection } => {
7049 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7050 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7051 writeln!(output, "{}{}.insert({});", indent_str, coll_str, val_str).unwrap();
7052 }
7053
7054 Stmt::Remove { value, collection } => {
7055 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7056 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7057 writeln!(output, "{}{}.remove(&{});", indent_str, coll_str, val_str).unwrap();
7058 }
7059
7060 Stmt::SetIndex { collection, index, value } => {
7061 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7062 let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
7063 let value_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7064
7065 let known_type = if let Expr::Identifier(sym) = collection {
7067 ctx.get_variable_types().get(sym).map(|s| s.as_str())
7068 } else {
7069 None
7070 };
7071
7072 match known_type {
7073 Some(t) if t.starts_with("Vec") => {
7074 if value_str.contains(&coll_str) {
7076 writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
7077 writeln!(output, "{}{}[({} - 1) as usize] = __set_tmp;", indent_str, coll_str, index_str).unwrap();
7078 } else {
7079 writeln!(output, "{}{}[({} - 1) as usize] = {};", indent_str, coll_str, index_str, value_str).unwrap();
7080 }
7081 }
7082 Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7083 writeln!(output, "{}{}.insert({}, {});", indent_str, coll_str, index_str, value_str).unwrap();
7084 }
7085 _ => {
7086 if value_str.contains("logos_get") && value_str.contains(&coll_str) {
7088 writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
7089 writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, __set_tmp);", indent_str, coll_str, index_str).unwrap();
7090 } else {
7091 writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, {});", indent_str, coll_str, index_str, value_str).unwrap();
7092 }
7093 }
7094 }
7095 }
7096
7097 Stmt::Zone { name, capacity, source_file, body } => {
7099 let zone_name = interner.resolve(*name);
7100
7101 if let Some(path_sym) = source_file {
7103 let path = interner.resolve(*path_sym);
7105 writeln!(
7106 output,
7107 "{}let {} = logicaffeine_system::memory::Zone::new_mapped(\"{}\").expect(\"Failed to map file\");",
7108 indent_str, zone_name, path
7109 ).unwrap();
7110 } else {
7111 let cap = capacity.unwrap_or(4096); writeln!(
7114 output,
7115 "{}let {} = logicaffeine_system::memory::Zone::new_heap({});",
7116 indent_str, zone_name, cap
7117 ).unwrap();
7118 }
7119
7120 writeln!(output, "{}{{", indent_str).unwrap();
7122 ctx.push_scope();
7123
7124 for stmt in *body {
7126 output.push_str(&codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
7127 }
7128
7129 ctx.pop_scope();
7130 writeln!(output, "{}}}", indent_str).unwrap();
7131 }
7132
7133 Stmt::Concurrent { tasks } => {
7137 let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
7139 if let Stmt::Let { var, .. } = s {
7140 Some(interner.resolve(*var).to_string())
7141 } else {
7142 None
7143 }
7144 }).collect();
7145
7146 let defined_vars: HashSet<Symbol> = tasks.iter().filter_map(|s| {
7148 if let Stmt::Let { var, .. } = s {
7149 Some(*var)
7150 } else {
7151 None
7152 }
7153 }).collect();
7154
7155 let mut has_intra_dependency = false;
7158 let mut seen_defs: HashSet<Symbol> = HashSet::new();
7159 for s in *tasks {
7160 let mut used_in_task: HashSet<Symbol> = HashSet::new();
7162 collect_stmt_identifiers(s, &mut used_in_task);
7163 for used_var in &used_in_task {
7164 if seen_defs.contains(used_var) {
7165 has_intra_dependency = true;
7166 break;
7167 }
7168 }
7169 if let Stmt::Let { var, .. } = s {
7171 seen_defs.insert(*var);
7172 }
7173 if has_intra_dependency {
7174 break;
7175 }
7176 }
7177
7178 let mut used_syms: HashSet<Symbol> = HashSet::new();
7181 for s in *tasks {
7182 collect_stmt_identifiers(s, &mut used_syms);
7183 }
7184 for def_var in &defined_vars {
7186 used_syms.remove(def_var);
7187 }
7188 let used_vars: HashSet<String> = used_syms.iter()
7189 .map(|sym| interner.resolve(*sym).to_string())
7190 .collect();
7191
7192 if has_intra_dependency {
7194 for stmt in *tasks {
7196 output.push_str(&codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
7197 }
7198 } else {
7199 if !let_bindings.is_empty() {
7201 writeln!(output, "{}let ({}) = tokio::join!(", indent_str, let_bindings.join(", ")).unwrap();
7203 } else {
7204 writeln!(output, "{}tokio::join!(", indent_str).unwrap();
7205 }
7206
7207 for (i, stmt) in tasks.iter().enumerate() {
7208 let inner_code = match stmt {
7211 Stmt::Let { value, .. } => {
7212 codegen_expr_with_async(value, interner, synced_vars, async_functions, ctx.get_variable_types())
7215 }
7216 Stmt::Call { function, args } => {
7217 let func_name = interner.resolve(*function);
7218 let args_str: Vec<String> = args.iter()
7219 .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7220 .collect();
7221 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
7223 format!("{}({}){}", func_name, args_str.join(", "), await_suffix)
7224 }
7225 _ => {
7226 let inner = codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
7228 inner.trim().to_string()
7229 }
7230 };
7231
7232 if !used_vars.is_empty() && i < tasks.len() - 1 {
7234 let clones: Vec<String> = used_vars.iter()
7236 .map(|v| format!("let {} = {}.clone();", v, v))
7237 .collect();
7238 write!(output, "{} {{ {} async move {{ {} }} }}",
7239 indent_str, clones.join(" "), inner_code).unwrap();
7240 } else {
7241 write!(output, "{} async {{ {} }}", indent_str, inner_code).unwrap();
7243 }
7244
7245 if i < tasks.len() - 1 {
7246 writeln!(output, ",").unwrap();
7247 } else {
7248 writeln!(output).unwrap();
7249 }
7250 }
7251
7252 writeln!(output, "{});", indent_str).unwrap();
7253 }
7254 }
7255
7256 Stmt::Parallel { tasks } => {
7259 let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
7261 if let Stmt::Let { var, .. } = s {
7262 Some(interner.resolve(*var).to_string())
7263 } else {
7264 None
7265 }
7266 }).collect();
7267
7268 if tasks.len() == 2 {
7269 if !let_bindings.is_empty() {
7271 writeln!(output, "{}let ({}) = rayon::join(", indent_str, let_bindings.join(", ")).unwrap();
7272 } else {
7273 writeln!(output, "{}rayon::join(", indent_str).unwrap();
7274 }
7275
7276 for (i, stmt) in tasks.iter().enumerate() {
7277 let inner_code = match stmt {
7279 Stmt::Let { value, .. } => {
7280 codegen_expr(value, interner, synced_vars)
7282 }
7283 Stmt::Call { function, args } => {
7284 let func_name = interner.resolve(*function);
7285 let args_str: Vec<String> = args.iter()
7286 .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7287 .collect();
7288 format!("{}({})", func_name, args_str.join(", "))
7289 }
7290 _ => {
7291 let inner = codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
7293 inner.trim().to_string()
7294 }
7295 };
7296 write!(output, "{} || {{ {} }}", indent_str, inner_code).unwrap();
7297 if i == 0 {
7298 writeln!(output, ",").unwrap();
7299 } else {
7300 writeln!(output).unwrap();
7301 }
7302 }
7303 writeln!(output, "{});", indent_str).unwrap();
7304 } else {
7305 writeln!(output, "{}{{", indent_str).unwrap();
7307 writeln!(output, "{} let handles: Vec<_> = vec![", indent_str).unwrap();
7308 for stmt in *tasks {
7309 let inner_code = match stmt {
7311 Stmt::Let { value, .. } => {
7312 codegen_expr(value, interner, synced_vars)
7313 }
7314 Stmt::Call { function, args } => {
7315 let func_name = interner.resolve(*function);
7316 let args_str: Vec<String> = args.iter()
7317 .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7318 .collect();
7319 format!("{}({})", func_name, args_str.join(", "))
7320 }
7321 _ => {
7322 let inner = codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
7323 inner.trim().to_string()
7324 }
7325 };
7326 writeln!(output, "{} std::thread::spawn(move || {{ {} }}),",
7327 indent_str, inner_code).unwrap();
7328 }
7329 writeln!(output, "{} ];", indent_str).unwrap();
7330 writeln!(output, "{} for h in handles {{ h.join().unwrap(); }}", indent_str).unwrap();
7331 writeln!(output, "{}}}", indent_str).unwrap();
7332 }
7333 }
7334
7335 Stmt::ReadFrom { var, source } => {
7338 let var_name = interner.resolve(*var);
7339 match source {
7340 ReadSource::Console => {
7341 writeln!(output, "{}let {} = logicaffeine_system::io::read_line();", indent_str, var_name).unwrap();
7342 }
7343 ReadSource::File(path_expr) => {
7344 let path_str = codegen_expr_with_async(path_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
7345 writeln!(
7347 output,
7348 "{}let {} = vfs.read_to_string(&{}).await.expect(\"Failed to read file\");",
7349 indent_str, var_name, path_str
7350 ).unwrap();
7351 }
7352 }
7353 }
7354
7355 Stmt::WriteFile { content, path } => {
7358 let content_str = codegen_expr_with_async(content, interner, synced_vars, async_functions, ctx.get_variable_types());
7359 let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
7360 writeln!(
7362 output,
7363 "{}vfs.write(&{}, {}.as_bytes()).await.expect(\"Failed to write file\");",
7364 indent_str, path_str, content_str
7365 ).unwrap();
7366 }
7367
7368 Stmt::Spawn { agent_type, name } => {
7370 let type_name = interner.resolve(*agent_type);
7371 let agent_name = interner.resolve(*name);
7372 writeln!(
7374 output,
7375 "{}let {} = tokio::spawn(async move {{ /* {} agent loop */ }});",
7376 indent_str, agent_name, type_name
7377 ).unwrap();
7378 }
7379
7380 Stmt::SendMessage { message, destination } => {
7382 let msg_str = codegen_expr_with_async(message, interner, synced_vars, async_functions, ctx.get_variable_types());
7383 let dest_str = codegen_expr_with_async(destination, interner, synced_vars, async_functions, ctx.get_variable_types());
7384 writeln!(
7385 output,
7386 "{}{}.send({}).await.expect(\"Failed to send message\");",
7387 indent_str, dest_str, msg_str
7388 ).unwrap();
7389 }
7390
7391 Stmt::AwaitMessage { source, into } => {
7393 let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7394 let var_name = interner.resolve(*into);
7395 writeln!(
7396 output,
7397 "{}let {} = {}.recv().await.expect(\"Failed to receive message\");",
7398 indent_str, var_name, src_str
7399 ).unwrap();
7400 }
7401
7402 Stmt::MergeCrdt { source, target } => {
7404 let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7405 let tgt_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
7406 writeln!(
7407 output,
7408 "{}{}.merge(&{});",
7409 indent_str, tgt_str, src_str
7410 ).unwrap();
7411 }
7412
7413 Stmt::IncreaseCrdt { object, field, amount } => {
7416 let field_name = interner.resolve(*field);
7417 let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7418
7419 let root_sym = get_root_identifier(object);
7421 if let Some(sym) = root_sym {
7422 if synced_vars.contains(&sym) {
7423 let obj_name = interner.resolve(sym);
7425 writeln!(
7426 output,
7427 "{}{}.mutate(|inner| inner.{}.increment({} as u64)).await;",
7428 indent_str, obj_name, field_name, amount_str
7429 ).unwrap();
7430 return output;
7431 }
7432 }
7433
7434 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7436 writeln!(
7437 output,
7438 "{}{}.{}.increment({} as u64);",
7439 indent_str, obj_str, field_name, amount_str
7440 ).unwrap();
7441 }
7442
7443 Stmt::DecreaseCrdt { object, field, amount } => {
7445 let field_name = interner.resolve(*field);
7446 let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7447
7448 let root_sym = get_root_identifier(object);
7450 if let Some(sym) = root_sym {
7451 if synced_vars.contains(&sym) {
7452 let obj_name = interner.resolve(sym);
7454 writeln!(
7455 output,
7456 "{}{}.mutate(|inner| inner.{}.decrement({} as u64)).await;",
7457 indent_str, obj_name, field_name, amount_str
7458 ).unwrap();
7459 return output;
7460 }
7461 }
7462
7463 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7465 writeln!(
7466 output,
7467 "{}{}.{}.decrement({} as u64);",
7468 indent_str, obj_str, field_name, amount_str
7469 ).unwrap();
7470 }
7471
7472 Stmt::AppendToSequence { sequence, value } => {
7474 let seq_str = codegen_expr_with_async(sequence, interner, synced_vars, async_functions, ctx.get_variable_types());
7475 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7476 writeln!(
7477 output,
7478 "{}{}.append({});",
7479 indent_str, seq_str, val_str
7480 ).unwrap();
7481 }
7482
7483 Stmt::ResolveConflict { object, field, value } => {
7485 let field_name = interner.resolve(*field);
7486 let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7487 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7488 writeln!(
7489 output,
7490 "{}{}.{}.resolve({});",
7491 indent_str, obj_str, field_name, val_str
7492 ).unwrap();
7493 }
7494
7495 Stmt::Escape { code, .. } => {
7497 let raw_code = interner.resolve(*code);
7498 write!(output, "{}{{\n", indent_str).unwrap();
7499 for line in raw_code.lines() {
7500 write!(output, "{} {}\n", indent_str, line).unwrap();
7501 }
7502 write!(output, "{}}}\n", indent_str).unwrap();
7503 }
7504
7505 Stmt::Require { .. } => {}
7507
7508 Stmt::Theorem(_) => {
7510 }
7513 }
7514
7515 output
7516}
7517
7518fn get_root_identifier(expr: &Expr) -> Option<Symbol> {
7521 match expr {
7522 Expr::Identifier(sym) => Some(*sym),
7523 Expr::FieldAccess { object, .. } => get_root_identifier(object),
7524 _ => None,
7525 }
7526}
7527
7528fn is_copy_type(ty: &str) -> bool {
7530 matches!(ty, "i64" | "u64" | "f64" | "i32" | "u32" | "f32" | "bool" | "char" | "u8" | "i8" | "()")
7531}
7532
7533fn has_copy_element_type(vec_type: &str) -> bool {
7535 if let Some(inner) = vec_type.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
7536 is_copy_type(inner)
7537 } else {
7538 false
7539 }
7540}
7541
7542fn has_copy_value_type(map_type: &str) -> bool {
7544 let inner = map_type.strip_prefix("std::collections::HashMap<")
7545 .or_else(|| map_type.strip_prefix("HashMap<"));
7546 if let Some(inner) = inner.and_then(|s| s.strip_suffix('>')) {
7547 if let Some((_key, value)) = inner.split_once(", ") {
7549 return is_copy_type(value);
7550 }
7551 }
7552 false
7553}
7554
7555pub fn codegen_expr(expr: &Expr, interner: &Interner, synced_vars: &HashSet<Symbol>) -> String {
7556 let empty_registry = TypeRegistry::new();
7558 let empty_async = HashSet::new();
7559 codegen_expr_boxed(expr, interner, synced_vars, &HashSet::new(), &empty_registry, &empty_async)
7560}
7561
7562pub fn codegen_expr_with_async(
7565 expr: &Expr,
7566 interner: &Interner,
7567 synced_vars: &HashSet<Symbol>,
7568 async_functions: &HashSet<Symbol>,
7569 variable_types: &HashMap<Symbol, String>,
7570) -> String {
7571 let empty_registry = TypeRegistry::new();
7572 let empty_strings = HashSet::new();
7573 codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), &empty_strings, variable_types)
7574}
7575
7576fn codegen_expr_with_async_and_strings(
7578 expr: &Expr,
7579 interner: &Interner,
7580 synced_vars: &HashSet<Symbol>,
7581 async_functions: &HashSet<Symbol>,
7582 string_vars: &HashSet<Symbol>,
7583 variable_types: &HashMap<Symbol, String>,
7584) -> String {
7585 let empty_registry = TypeRegistry::new();
7586 codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), string_vars, variable_types)
7587}
7588
7589fn is_definitely_numeric_expr(expr: &Expr) -> bool {
7593 match expr {
7594 Expr::Literal(Literal::Number(_)) => true,
7595 Expr::Literal(Literal::Float(_)) => true,
7596 Expr::Literal(Literal::Duration(_)) => true,
7597 Expr::Identifier(_) => true,
7600 Expr::BinaryOp { op: BinaryOpKind::Subtract, .. } => true,
7602 Expr::BinaryOp { op: BinaryOpKind::Multiply, .. } => true,
7603 Expr::BinaryOp { op: BinaryOpKind::Divide, .. } => true,
7604 Expr::BinaryOp { op: BinaryOpKind::Modulo, .. } => true,
7605 Expr::Length { .. } => true,
7607 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7609 is_definitely_numeric_expr(left) && is_definitely_numeric_expr(right)
7610 }
7611 Expr::Call { .. } => true,
7613 Expr::Index { .. } => true,
7615 _ => true,
7616 }
7617}
7618
7619fn is_definitely_string_expr_with_vars(expr: &Expr, string_vars: &HashSet<Symbol>) -> bool {
7622 match expr {
7623 Expr::Literal(Literal::Text(_)) => true,
7625 Expr::Identifier(sym) => string_vars.contains(sym),
7627 Expr::BinaryOp { op: BinaryOpKind::Concat, .. } => true,
7629 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7631 is_definitely_string_expr_with_vars(left, string_vars)
7632 || is_definitely_string_expr_with_vars(right, string_vars)
7633 }
7634 Expr::WithCapacity { value, .. } => is_definitely_string_expr_with_vars(value, string_vars),
7636 _ => false,
7637 }
7638}
7639
7640fn is_definitely_string_expr(expr: &Expr) -> bool {
7643 let empty = HashSet::new();
7644 is_definitely_string_expr_with_vars(expr, &empty)
7645}
7646
7647fn collect_string_concat_operands<'a, 'b>(
7654 expr: &'b Expr<'a>,
7655 string_vars: &HashSet<Symbol>,
7656 operands: &mut Vec<&'b Expr<'a>>,
7657) {
7658 match expr {
7659 Expr::BinaryOp { op: BinaryOpKind::Concat, left, right } => {
7660 collect_string_concat_operands(left, string_vars, operands);
7661 collect_string_concat_operands(right, string_vars, operands);
7662 }
7663 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7664 let has_string = is_definitely_string_expr_with_vars(left, string_vars)
7665 || is_definitely_string_expr_with_vars(right, string_vars);
7666 if has_string {
7667 collect_string_concat_operands(left, string_vars, operands);
7668 collect_string_concat_operands(right, string_vars, operands);
7669 } else {
7670 operands.push(expr);
7671 }
7672 }
7673 _ => {
7674 operands.push(expr);
7675 }
7676 }
7677}
7678
7679fn codegen_expr_boxed(
7683 expr: &Expr,
7684 interner: &Interner,
7685 synced_vars: &HashSet<Symbol>,
7686 boxed_fields: &HashSet<(String, String, String)>, registry: &TypeRegistry, async_functions: &HashSet<Symbol>, ) -> String {
7690 let empty_boxed = HashSet::new();
7692 let empty_strings = HashSet::new();
7693 let empty_types = HashMap::new();
7694 codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, &empty_strings, &empty_types)
7695}
7696
7697fn codegen_expr_boxed_with_strings(
7699 expr: &Expr,
7700 interner: &Interner,
7701 synced_vars: &HashSet<Symbol>,
7702 boxed_fields: &HashSet<(String, String, String)>,
7703 registry: &TypeRegistry,
7704 async_functions: &HashSet<Symbol>,
7705 string_vars: &HashSet<Symbol>,
7706) -> String {
7707 let empty_boxed = HashSet::new();
7708 let empty_types = HashMap::new();
7709 codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, &empty_types)
7710}
7711
7712fn codegen_expr_boxed_with_types(
7714 expr: &Expr,
7715 interner: &Interner,
7716 synced_vars: &HashSet<Symbol>,
7717 boxed_fields: &HashSet<(String, String, String)>,
7718 registry: &TypeRegistry,
7719 async_functions: &HashSet<Symbol>,
7720 string_vars: &HashSet<Symbol>,
7721 variable_types: &HashMap<Symbol, String>,
7722) -> String {
7723 let empty_boxed = HashSet::new();
7724 codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, variable_types)
7725}
7726
7727fn codegen_expr_boxed_internal(
7729 expr: &Expr,
7730 interner: &Interner,
7731 synced_vars: &HashSet<Symbol>,
7732 boxed_fields: &HashSet<(String, String, String)>,
7733 registry: &TypeRegistry,
7734 async_functions: &HashSet<Symbol>,
7735 boxed_bindings: &HashSet<Symbol>,
7736 string_vars: &HashSet<Symbol>,
7737 variable_types: &HashMap<Symbol, String>,
7738) -> String {
7739 macro_rules! recurse {
7741 ($e:expr) => {
7742 codegen_expr_boxed_internal($e, interner, synced_vars, boxed_fields, registry, async_functions, boxed_bindings, string_vars, variable_types)
7743 };
7744 }
7745
7746 match expr {
7747 Expr::Literal(lit) => codegen_literal(lit, interner),
7748
7749 Expr::Identifier(sym) => {
7750 let name = interner.resolve(*sym).to_string();
7751 if boxed_bindings.contains(sym) {
7753 format!("(*{})", name)
7754 } else {
7755 name
7756 }
7757 }
7758
7759 Expr::BinaryOp { op, left, right } => {
7760 let is_string_concat = matches!(op, BinaryOpKind::Concat)
7763 || (matches!(op, BinaryOpKind::Add)
7764 && (is_definitely_string_expr_with_vars(left, string_vars)
7765 || is_definitely_string_expr_with_vars(right, string_vars)));
7766
7767 if is_string_concat {
7768 let mut operands = Vec::new();
7769 collect_string_concat_operands(expr, string_vars, &mut operands);
7770 let placeholders: String = operands.iter().map(|_| "{}").collect::<Vec<_>>().join("");
7771 let values: Vec<String> = operands.iter().map(|e| {
7772 if let Expr::Literal(Literal::Text(sym)) = e {
7774 format!("\"{}\"", interner.resolve(*sym))
7775 } else {
7776 recurse!(e)
7777 }
7778 }).collect();
7779 return format!("format!(\"{}\", {})", placeholders, values.join(", "));
7780 }
7781
7782 if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq) {
7784 let neg = matches!(op, BinaryOpKind::NotEq);
7785 if let Expr::Index { collection, index } = left {
7787 if let Expr::Identifier(sym) = collection {
7788 if let Some(t) = variable_types.get(sym) {
7789 if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7790 let coll_str = recurse!(collection);
7791 let key_str = recurse!(index);
7792 let val_str = recurse!(right);
7793 let cmp = if neg { "!=" } else { "==" };
7794 if has_copy_value_type(t) {
7795 return format!("({}.get(&({})).copied() {} Some({}))", coll_str, key_str, cmp, val_str);
7796 } else {
7797 return format!("({}.get(&({})) {} Some(&({})))", coll_str, key_str, cmp, val_str);
7798 }
7799 }
7800 }
7801 }
7802 }
7803 if let Expr::Index { collection, index } = right {
7805 if let Expr::Identifier(sym) = collection {
7806 if let Some(t) = variable_types.get(sym) {
7807 if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7808 let coll_str = recurse!(collection);
7809 let key_str = recurse!(index);
7810 let val_str = recurse!(left);
7811 let cmp = if neg { "!=" } else { "==" };
7812 if has_copy_value_type(t) {
7813 return format!("(Some({}) {} {}.get(&({})).copied())", val_str, cmp, coll_str, key_str);
7814 } else {
7815 return format!("(Some(&({})) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
7816 }
7817 }
7818 }
7819 }
7820 }
7821 }
7822
7823 let left_str = recurse!(left);
7824 let right_str = recurse!(right);
7825 let op_str = match op {
7826 BinaryOpKind::Add => "+",
7827 BinaryOpKind::Subtract => "-",
7828 BinaryOpKind::Multiply => "*",
7829 BinaryOpKind::Divide => "/",
7830 BinaryOpKind::Modulo => "%",
7831 BinaryOpKind::Eq => "==",
7832 BinaryOpKind::NotEq => "!=",
7833 BinaryOpKind::Lt => "<",
7834 BinaryOpKind::Gt => ">",
7835 BinaryOpKind::LtEq => "<=",
7836 BinaryOpKind::GtEq => ">=",
7837 BinaryOpKind::And => "&&",
7838 BinaryOpKind::Or => "||",
7839 BinaryOpKind::Concat => unreachable!(), };
7841 format!("({} {} {})", left_str, op_str, right_str)
7842 }
7843
7844 Expr::Call { function, args } => {
7845 let func_name = escape_rust_ident(interner.resolve(*function));
7846 let args_str: Vec<String> = args.iter()
7848 .map(|a| recurse!(a))
7849 .collect();
7850 if async_functions.contains(function) {
7852 format!("{}({}).await", func_name, args_str.join(", "))
7853 } else {
7854 format!("{}({})", func_name, args_str.join(", "))
7855 }
7856 }
7857
7858 Expr::Index { collection, index } => {
7859 let coll_str = recurse!(collection);
7860 let index_str = recurse!(index);
7861 let known_type = if let Expr::Identifier(sym) = collection {
7863 variable_types.get(sym).map(|s| s.as_str())
7864 } else {
7865 None
7866 };
7867 match known_type {
7868 Some(t) if t.starts_with("Vec") => {
7869 let suffix = if has_copy_element_type(t) { "" } else { ".clone()" };
7870 format!("{}[({} - 1) as usize]{}", coll_str, index_str, suffix)
7871 }
7872 Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7873 let suffix = if has_copy_value_type(t) { "" } else { ".clone()" };
7874 format!("{}[&({})]{}", coll_str, index_str, suffix)
7875 }
7876 _ => {
7877 format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
7879 }
7880 }
7881 }
7882
7883 Expr::Slice { collection, start, end } => {
7884 let coll_str = recurse!(collection);
7885 let start_str = recurse!(start);
7886 let end_str = recurse!(end);
7887 format!("&{}[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
7890 }
7891
7892 Expr::Copy { expr: inner } => {
7893 let expr_str = recurse!(inner);
7894 format!("{}.to_owned()", expr_str)
7898 }
7899
7900 Expr::Give { value } => {
7901 recurse!(value)
7904 }
7905
7906 Expr::Length { collection } => {
7907 let coll_str = recurse!(collection);
7908 format!("({}.len() as i64)", coll_str)
7910 }
7911
7912 Expr::Contains { collection, value } => {
7913 let coll_str = recurse!(collection);
7914 let val_str = recurse!(value);
7915 format!("{}.logos_contains(&{})", coll_str, val_str)
7917 }
7918
7919 Expr::Union { left, right } => {
7920 let left_str = recurse!(left);
7921 let right_str = recurse!(right);
7922 format!("{}.union(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7923 }
7924
7925 Expr::Intersection { left, right } => {
7926 let left_str = recurse!(left);
7927 let right_str = recurse!(right);
7928 format!("{}.intersection(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7929 }
7930
7931 Expr::ManifestOf { zone } => {
7933 let zone_str = recurse!(zone);
7934 format!("logicaffeine_system::network::FileSipper::from_zone(&{}).manifest()", zone_str)
7935 }
7936
7937 Expr::ChunkAt { index, zone } => {
7938 let zone_str = recurse!(zone);
7939 let index_str = recurse!(index);
7940 format!("logicaffeine_system::network::FileSipper::from_zone(&{}).get_chunk(({} - 1) as usize)", zone_str, index_str)
7942 }
7943
7944 Expr::List(ref items) => {
7945 let item_strs: Vec<String> = items.iter()
7946 .map(|i| recurse!(i))
7947 .collect();
7948 format!("vec![{}]", item_strs.join(", "))
7949 }
7950
7951 Expr::Tuple(ref items) => {
7952 let item_strs: Vec<String> = items.iter()
7953 .map(|i| format!("Value::from({})", recurse!(i)))
7954 .collect();
7955 format!("vec![{}]", item_strs.join(", "))
7957 }
7958
7959 Expr::Range { start, end } => {
7960 let start_str = recurse!(start);
7961 let end_str = recurse!(end);
7962 format!("({}..={})", start_str, end_str)
7963 }
7964
7965 Expr::FieldAccess { object, field } => {
7966 let field_name = interner.resolve(*field);
7967
7968 let root_sym = get_root_identifier(object);
7970 if let Some(sym) = root_sym {
7971 if synced_vars.contains(&sym) {
7972 let obj_name = interner.resolve(sym);
7973 return format!("{}.get().await.{}", obj_name, field_name);
7974 }
7975 }
7976
7977 let obj_str = recurse!(object);
7978 format!("{}.{}", obj_str, field_name)
7979 }
7980
7981 Expr::New { type_name, type_args, init_fields } => {
7982 let type_str = interner.resolve(*type_name);
7983 if !init_fields.is_empty() {
7984 let fields_str = init_fields.iter()
7987 .map(|(name, value)| {
7988 let field_name = interner.resolve(*name);
7989 let value_str = recurse!(value);
7990 format!("{}: {}", field_name, value_str)
7991 })
7992 .collect::<Vec<_>>()
7993 .join(", ");
7994 format!("{} {{ {}, ..Default::default() }}", type_str, fields_str)
7995 } else if type_args.is_empty() {
7996 format!("{}::default()", type_str)
7997 } else {
7998 let args_str = type_args.iter()
8001 .map(|t| codegen_type_expr(t, interner))
8002 .collect::<Vec<_>>()
8003 .join(", ");
8004 format!("{}::<{}>::default()", type_str, args_str)
8005 }
8006 }
8007
8008 Expr::NewVariant { enum_name, variant, fields } => {
8009 let enum_str = interner.resolve(*enum_name);
8010 let variant_str = interner.resolve(*variant);
8011 if fields.is_empty() {
8012 format!("{}::{}", enum_str, variant_str)
8014 } else {
8015 let mut identifier_counts: HashMap<Symbol, usize> = HashMap::new();
8018 for (_, value) in fields.iter() {
8019 if let Expr::Identifier(sym) = value {
8020 *identifier_counts.entry(*sym).or_insert(0) += 1;
8021 }
8022 }
8023
8024 let mut remaining_uses: HashMap<Symbol, usize> = identifier_counts.clone();
8026
8027 let fields_str: Vec<String> = fields.iter()
8030 .map(|(field_name, value)| {
8031 let name = interner.resolve(*field_name);
8032
8033 let val = if let Expr::Identifier(sym) = value {
8036 let total = identifier_counts.get(sym).copied().unwrap_or(0);
8037 let remaining = remaining_uses.get_mut(sym);
8038 let base_name = if boxed_bindings.contains(sym) {
8039 format!("(*{})", interner.resolve(*sym))
8040 } else {
8041 interner.resolve(*sym).to_string()
8042 };
8043 if total > 1 {
8044 if let Some(r) = remaining {
8045 *r -= 1;
8046 if *r > 0 {
8047 format!("{}.clone()", base_name)
8049 } else {
8050 base_name
8052 }
8053 } else {
8054 base_name
8055 }
8056 } else {
8057 base_name
8058 }
8059 } else {
8060 recurse!(value)
8061 };
8062
8063 let key = (enum_str.to_string(), variant_str.to_string(), name.to_string());
8065 if boxed_fields.contains(&key) {
8066 format!("{}: Box::new({})", name, val)
8067 } else {
8068 format!("{}: {}", name, val)
8069 }
8070 })
8071 .collect();
8072 format!("{}::{} {{ {} }}", enum_str, variant_str, fields_str.join(", "))
8073 }
8074 }
8075
8076 Expr::OptionSome { value } => {
8077 format!("Some({})", recurse!(value))
8078 }
8079
8080 Expr::OptionNone => {
8081 "None".to_string()
8082 }
8083
8084 Expr::Escape { code, .. } => {
8085 let raw_code = interner.resolve(*code);
8086 let mut block = String::from("{\n");
8087 for line in raw_code.lines() {
8088 block.push_str(" ");
8089 block.push_str(line);
8090 block.push('\n');
8091 }
8092 block.push('}');
8093 block
8094 }
8095
8096 Expr::WithCapacity { value, capacity } => {
8097 let cap_str = recurse!(capacity);
8098 match value {
8099 Expr::Literal(Literal::Text(sym)) if interner.resolve(*sym).is_empty() => {
8101 format!("String::with_capacity(({}) as usize)", cap_str)
8102 }
8103 Expr::Literal(Literal::Text(sym)) => {
8105 let text = interner.resolve(*sym);
8106 format!("{{ let mut __s = String::with_capacity(({}) as usize); __s.push_str(\"{}\"); __s }}", cap_str, text)
8107 }
8108 Expr::New { type_name, type_args, .. } => {
8110 let type_str = interner.resolve(*type_name);
8111 match type_str {
8112 "Seq" | "List" | "Vec" => {
8113 let elem = if !type_args.is_empty() {
8114 codegen_type_expr(&type_args[0], interner)
8115 } else { "()".to_string() };
8116 format!("{{ let __v: Vec<{}> = Vec::with_capacity(({}) as usize); __v }}", elem, cap_str)
8117 }
8118 "Map" | "HashMap" => {
8119 let (k, v) = if type_args.len() >= 2 {
8120 (codegen_type_expr(&type_args[0], interner),
8121 codegen_type_expr(&type_args[1], interner))
8122 } else { ("String".to_string(), "String".to_string()) };
8123 format!("{{ let __m: std::collections::HashMap<{}, {}> = std::collections::HashMap::with_capacity(({}) as usize); __m }}", k, v, cap_str)
8124 }
8125 "Set" | "HashSet" => {
8126 let elem = if !type_args.is_empty() {
8127 codegen_type_expr(&type_args[0], interner)
8128 } else { "()".to_string() };
8129 format!("{{ let __s: std::collections::HashSet<{}> = std::collections::HashSet::with_capacity(({}) as usize); __s }}", elem, cap_str)
8130 }
8131 _ => recurse!(value) }
8133 }
8134 _ => recurse!(value)
8136 }
8137 }
8138
8139 Expr::Closure { params, body, .. } => {
8140 use crate::ast::stmt::ClosureBody;
8141 let params_str: Vec<String> = params.iter()
8142 .map(|(name, ty)| {
8143 let param_name = escape_rust_ident(interner.resolve(*name));
8144 let param_type = codegen_type_expr(ty, interner);
8145 format!("{}: {}", param_name, param_type)
8146 })
8147 .collect();
8148
8149 match body {
8150 ClosureBody::Expression(expr) => {
8151 let body_str = recurse!(expr);
8152 format!("move |{}| {{ {} }}", params_str.join(", "), body_str)
8153 }
8154 ClosureBody::Block(stmts) => {
8155 let mut body_str = String::new();
8156 let mut ctx = RefinementContext::new();
8157 let empty_mutable = collect_mutable_vars(stmts);
8158 let empty_lww = HashSet::new();
8159 let empty_mv = HashSet::new();
8160 let mut empty_synced = HashSet::new();
8161 let empty_caps = HashMap::new();
8162 let empty_pipes = HashSet::new();
8163 let empty_boxed = HashSet::new();
8164 let empty_registry = TypeRegistry::new();
8165 for stmt in stmts.iter() {
8166 body_str.push_str(&codegen_stmt(
8167 stmt, interner, 2, &empty_mutable, &mut ctx,
8168 &empty_lww, &empty_mv, &mut empty_synced, &empty_caps,
8169 async_functions, &empty_pipes, &empty_boxed, &empty_registry,
8170 ));
8171 }
8172 format!("move |{}| {{\n{}{}}}", params_str.join(", "), body_str, " ")
8173 }
8174 }
8175 }
8176
8177 Expr::CallExpr { callee, args } => {
8178 let callee_str = recurse!(callee);
8179 let args_str: Vec<String> = args.iter().map(|a| recurse!(a)).collect();
8180 format!("({})({})", callee_str, args_str.join(", "))
8181 }
8182 }
8183}
8184
8185fn codegen_literal(lit: &Literal, interner: &Interner) -> String {
8186 match lit {
8187 Literal::Number(n) => n.to_string(),
8188 Literal::Float(f) => format!("{}f64", f),
8189 Literal::Text(sym) => format!("String::from(\"{}\")", interner.resolve(*sym)),
8191 Literal::Boolean(b) => b.to_string(),
8192 Literal::Nothing => "()".to_string(),
8193 Literal::Char(c) => {
8195 match c {
8197 '\n' => "'\\n'".to_string(),
8198 '\t' => "'\\t'".to_string(),
8199 '\r' => "'\\r'".to_string(),
8200 '\\' => "'\\\\'".to_string(),
8201 '\'' => "'\\''".to_string(),
8202 '\0' => "'\\0'".to_string(),
8203 c => format!("'{}'", c),
8204 }
8205 }
8206 Literal::Duration(nanos) => format!("std::time::Duration::from_nanos({}u64)", nanos),
8208 Literal::Date(days) => format!("LogosDate({})", days),
8210 Literal::Moment(nanos) => format!("LogosMoment({})", nanos),
8212 Literal::Span { months, days } => format!("LogosSpan::new({}, {})", months, days),
8214 Literal::Time(nanos) => format!("LogosTime({})", nanos),
8216 }
8217}
8218
8219pub fn codegen_assertion(expr: &LogicExpr, interner: &Interner) -> String {
8222 let mut registry = SymbolRegistry::new();
8223 let formatter = RustFormatter;
8224 let mut buf = String::new();
8225
8226 match expr.write_logic(&mut buf, &mut registry, interner, &formatter) {
8227 Ok(_) => buf,
8228 Err(_) => "/* error generating assertion */ false".to_string(),
8229 }
8230}
8231
8232pub fn codegen_term(term: &Term, interner: &Interner) -> String {
8233 match term {
8234 Term::Constant(sym) => interner.resolve(*sym).to_string(),
8235 Term::Variable(sym) => interner.resolve(*sym).to_string(),
8236 Term::Value { kind, .. } => match kind {
8237 NumberKind::Integer(n) => n.to_string(),
8238 NumberKind::Real(f) => f.to_string(),
8239 NumberKind::Symbolic(sym) => interner.resolve(*sym).to_string(),
8240 },
8241 Term::Function(name, args) => {
8242 let args_str: Vec<String> = args.iter()
8243 .map(|a| codegen_term(a, interner))
8244 .collect();
8245 format!("{}({})", interner.resolve(*name), args_str.join(", "))
8246 }
8247 Term::Possessed { possessor, possessed } => {
8248 let poss_str = codegen_term(possessor, interner);
8249 format!("{}.{}", poss_str, interner.resolve(*possessed))
8250 }
8251 Term::Group(members) => {
8252 let members_str: Vec<String> = members.iter()
8253 .map(|m| codegen_term(m, interner))
8254 .collect();
8255 format!("({})", members_str.join(", "))
8256 }
8257 _ => "/* unsupported Term */".to_string(),
8258 }
8259}
8260
8261#[cfg(test)]
8262mod tests {
8263 use super::*;
8264
8265 #[test]
8266 fn test_literal_number() {
8267 let interner = Interner::new();
8268 let synced_vars = HashSet::new();
8269 let expr = Expr::Literal(Literal::Number(42));
8270 assert_eq!(codegen_expr(&expr, &interner, &synced_vars), "42");
8271 }
8272
8273 #[test]
8274 fn test_literal_boolean() {
8275 let interner = Interner::new();
8276 let synced_vars = HashSet::new();
8277 assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(true)), &interner, &synced_vars), "true");
8278 assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(false)), &interner, &synced_vars), "false");
8279 }
8280
8281 #[test]
8282 fn test_literal_nothing() {
8283 let interner = Interner::new();
8284 let synced_vars = HashSet::new();
8285 assert_eq!(codegen_expr(&Expr::Literal(Literal::Nothing), &interner, &synced_vars), "()");
8286 }
8287}