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_swap_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
4387 output.push_str(&code);
4388 i += 1 + skip;
4389 continue;
4390 }
4391 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));
4392 i += 1;
4393 }
4394 }
4395 writeln!(output, "}}").unwrap();
4396 output
4397}
4398
4399fn codegen_function_def(
4404 name: Symbol,
4405 params: &[(Symbol, &TypeExpr)],
4406 body: &[Stmt],
4407 return_type: Option<&TypeExpr>,
4408 is_native: bool,
4409 native_path: Option<Symbol>,
4410 is_exported: bool,
4411 export_target: Option<Symbol>,
4412 interner: &Interner,
4413 lww_fields: &HashSet<(String, String)>,
4414 mv_fields: &HashSet<(String, String)>, async_functions: &HashSet<Symbol>, boxed_fields: &HashSet<(String, String, String)>, registry: &TypeRegistry, pure_functions: &HashSet<Symbol>,
4419) -> String {
4420 let mut output = String::new();
4421 let raw_name = interner.resolve(name);
4422 let func_name = escape_rust_ident(raw_name);
4423 let export_target_lower = export_target.map(|s| interner.resolve(s).to_lowercase());
4424
4425 let pipe_sender_params = collect_pipe_sender_params(body);
4427
4428 let is_c_export_early = is_exported && matches!(export_target_lower.as_deref(), None | Some("c"));
4430
4431 let is_tce = !is_native && !is_c_export_early && is_tail_recursive(name, body);
4433 let param_syms: Vec<Symbol> = params.iter().map(|(s, _)| *s).collect();
4434
4435 let acc_info = if !is_tce && !is_native && !is_c_export_early {
4437 detect_accumulator_pattern(name, body)
4438 } else {
4439 None
4440 };
4441 let is_acc = acc_info.is_some();
4442
4443 let is_memo = !is_tce && !is_acc && !is_native && !is_c_export_early
4445 && should_memoize(name, body, params, return_type, pure_functions.contains(&name), interner);
4446
4447 let needs_mut_params = is_tce || is_acc;
4448
4449 let params_str: Vec<String> = params.iter()
4451 .map(|(param_name, param_type)| {
4452 let pname = interner.resolve(*param_name);
4453 let ty = codegen_type_expr(param_type, interner);
4454 if pipe_sender_params.contains(param_name) {
4456 format!("{}: tokio::sync::mpsc::Sender<{}>", pname, ty)
4457 } else if needs_mut_params {
4458 format!("mut {}: {}", pname, ty)
4459 } else {
4460 format!("{}: {}", pname, ty)
4461 }
4462 })
4463 .collect();
4464
4465 let return_type_str = return_type
4467 .map(|t| codegen_type_expr(t, interner))
4468 .or_else(|| infer_return_type_from_body(body, interner));
4469
4470 let is_async = async_functions.contains(&name);
4472 let fn_keyword = if is_async { "async fn" } else { "fn" };
4473
4474 let is_c_export = is_c_export_early;
4476
4477 let needs_c_marshaling = is_c_export && {
4480 let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
4481 let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
4482 let has_ref_param = params.iter().any(|(_, ty)| {
4483 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4484 });
4485 let has_ref_return = return_type.map_or(false, |ty| {
4486 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4487 });
4488 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4489 let has_refinement_param = params.iter().any(|(_, ty)| {
4490 matches!(ty, TypeExpr::Refinement { .. })
4491 });
4492 has_text_param || has_text_return || has_ref_param || has_ref_return
4493 || has_result_return || has_refinement_param
4494 };
4495
4496 if needs_c_marshaling {
4497 return codegen_c_export_with_marshaling(
4499 name, params, body, return_type, interner,
4500 lww_fields, mv_fields, async_functions, boxed_fields, registry,
4501 );
4502 }
4503
4504 let (vis_prefix, abi_prefix) = if is_exported {
4506 match export_target_lower.as_deref() {
4507 None | Some("c") => ("pub ", "extern \"C\" "),
4508 Some("wasm") => ("pub ", ""),
4509 _ => ("pub ", ""),
4510 }
4511 } else {
4512 ("", "")
4513 };
4514
4515 let signature = if let Some(ref ret_ty) = return_type_str {
4516 if ret_ty != "()" {
4517 format!("{}{}{} {}({}) -> {}", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "), ret_ty)
4518 } else {
4519 format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4520 }
4521 } else {
4522 format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4523 };
4524
4525 if !is_tce && !is_acc && should_inline(name, body, is_native, is_exported, is_async) {
4527 writeln!(output, "#[inline]").unwrap();
4528 }
4529
4530 if is_exported {
4532 match export_target_lower.as_deref() {
4533 None | Some("c") => {
4534 writeln!(output, "#[export_name = \"logos_{}\"]", raw_name).unwrap();
4535 }
4536 Some("wasm") => {
4537 writeln!(output, "#[wasm_bindgen]").unwrap();
4538 }
4539 _ => {}
4540 }
4541 }
4542
4543 if is_native {
4545 let arg_names: Vec<&str> = params.iter()
4546 .map(|(n, _)| interner.resolve(*n))
4547 .collect();
4548
4549 if let Some(path_sym) = native_path {
4550 let path = interner.resolve(path_sym);
4552 let is_valid_path = !path.is_empty() && path.split("::").all(|seg| {
4554 !seg.is_empty() && seg.chars().all(|c| c.is_alphanumeric() || c == '_')
4555 });
4556 if is_valid_path {
4557 writeln!(output, "{} {{", signature).unwrap();
4558 writeln!(output, " {}({})", path, arg_names.join(", ")).unwrap();
4559 writeln!(output, "}}\n").unwrap();
4560 } else {
4561 writeln!(output, "{} {{", signature).unwrap();
4562 writeln!(output, " compile_error!(\"Invalid native function path: '{}'. Path must be a valid Rust path like \\\"crate::module::function\\\".\")", path).unwrap();
4563 writeln!(output, "}}\n").unwrap();
4564 }
4565 } else {
4566 if let Some((module, core_fn)) = map_native_function(raw_name) {
4568 writeln!(output, "{} {{", signature).unwrap();
4569 writeln!(output, " logicaffeine_system::{}::{}({})", module, core_fn, arg_names.join(", ")).unwrap();
4570 writeln!(output, "}}\n").unwrap();
4571 } else {
4572 writeln!(output, "{} {{", signature).unwrap();
4573 writeln!(output, " compile_error!(\"Unknown system native function: '{}'. Use `is native \\\"crate::path\\\"` syntax for user-defined native functions.\")", raw_name).unwrap();
4574 writeln!(output, "}}\n").unwrap();
4575 }
4576 }
4577 } else {
4578 let func_mutable_vars = collect_mutable_vars(body);
4581 writeln!(output, "{} {{", signature).unwrap();
4582
4583 let wrap_catch_unwind = is_c_export;
4585 if wrap_catch_unwind {
4586 writeln!(output, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
4587 }
4588
4589 let mut func_ctx = RefinementContext::new();
4590 let mut func_synced_vars = HashSet::new(); let func_var_caps = analyze_variable_capabilities(body, interner);
4593
4594 for (param_name, param_type) in params {
4596 let type_name = codegen_type_expr(param_type, interner);
4597 func_ctx.register_variable_type(*param_name, type_name);
4598 }
4599
4600 let func_pipe_vars = HashSet::new();
4602
4603 if is_tce {
4604 writeln!(output, " loop {{").unwrap();
4606 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4607 let mut si = 0;
4608 while si < stmt_refs.len() {
4609 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4610 output.push_str(&code);
4611 si += 1 + skip;
4612 continue;
4613 }
4614 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4615 output.push_str(&code);
4616 si += 1 + skip;
4617 continue;
4618 }
4619 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));
4620 si += 1;
4621 }
4622 writeln!(output, " }}").unwrap();
4623 } else if let Some(ref acc) = acc_info {
4624 writeln!(output, " let mut __acc: i64 = {};", acc.identity).unwrap();
4626 writeln!(output, " loop {{").unwrap();
4627 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4628 let mut si = 0;
4629 while si < stmt_refs.len() {
4630 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4631 output.push_str(&code);
4632 si += 1 + skip;
4633 continue;
4634 }
4635 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4636 output.push_str(&code);
4637 si += 1 + skip;
4638 continue;
4639 }
4640 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));
4641 si += 1;
4642 }
4643 writeln!(output, " }}").unwrap();
4644 } else if is_memo {
4645 let ret_ty = return_type_str.as_deref().unwrap_or("i64");
4647 let memo_name = format!("__MEMO_{}", func_name.to_uppercase());
4648
4649 let (key_type, key_expr, copy_method) = if params.len() == 1 {
4651 let ty = codegen_type_expr(params[0].1, interner);
4652 let pname = interner.resolve(params[0].0).to_string();
4653 let copy = if is_copy_type_expr(params[0].1, interner) { "copied" } else { "cloned" };
4654 (ty, pname, copy)
4655 } else {
4656 let types: Vec<String> = params.iter().map(|(_, t)| codegen_type_expr(t, interner)).collect();
4657 let names: Vec<String> = params.iter().map(|(n, _)| interner.resolve(*n).to_string()).collect();
4658 let copy = if params.iter().all(|(_, t)| is_copy_type_expr(t, interner)) { "copied" } else { "cloned" };
4659 (format!("({})", types.join(", ")), format!("({})", names.join(", ")), copy)
4660 };
4661
4662 writeln!(output, " use std::cell::RefCell;").unwrap();
4663 writeln!(output, " use std::collections::HashMap;").unwrap();
4664 writeln!(output, " thread_local! {{").unwrap();
4665 writeln!(output, " static {}: RefCell<HashMap<{}, {}>> = RefCell::new(HashMap::new());", memo_name, key_type, ret_ty).unwrap();
4666 writeln!(output, " }}").unwrap();
4667 writeln!(output, " if let Some(__v) = {}.with(|c| c.borrow().get(&{}).{}()) {{", memo_name, key_expr, copy_method).unwrap();
4668 writeln!(output, " return __v;").unwrap();
4669 writeln!(output, " }}").unwrap();
4670 writeln!(output, " let __memo_result = (|| -> {} {{", ret_ty).unwrap();
4671 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4672 let mut si = 0;
4673 while si < stmt_refs.len() {
4674 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4675 output.push_str(&code);
4676 si += 1 + skip;
4677 continue;
4678 }
4679 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4680 output.push_str(&code);
4681 si += 1 + skip;
4682 continue;
4683 }
4684 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));
4685 si += 1;
4686 }
4687 writeln!(output, " }})();").unwrap();
4688 writeln!(output, " {}.with(|c| c.borrow_mut().insert({}, __memo_result));", memo_name, key_expr).unwrap();
4689 writeln!(output, " __memo_result").unwrap();
4690 } else {
4691 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4692 let mut si = 0;
4693 while si < stmt_refs.len() {
4694 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4695 output.push_str(&code);
4696 si += 1 + skip;
4697 continue;
4698 }
4699 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4700 output.push_str(&code);
4701 si += 1 + skip;
4702 continue;
4703 }
4704 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));
4705 si += 1;
4706 }
4707 }
4708
4709 if wrap_catch_unwind {
4710 writeln!(output, " }})) {{").unwrap();
4711 writeln!(output, " Ok(__v) => __v,").unwrap();
4712 writeln!(output, " Err(__panic) => {{").unwrap();
4713 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();
4714 writeln!(output, " logos_set_last_error(__msg);").unwrap();
4715 if let Some(ref ret_str) = return_type_str {
4717 if ret_str != "()" {
4718 writeln!(output, " Default::default()").unwrap();
4719 }
4720 }
4721 writeln!(output, " }}").unwrap();
4722 writeln!(output, " }}").unwrap();
4723 }
4724
4725 writeln!(output, "}}\n").unwrap();
4726 }
4727
4728 output
4729}
4730
4731fn map_native_function(name: &str) -> Option<(&'static str, &'static str)> {
4735 match name {
4736 "read" => Some(("file", "read")),
4737 "write" => Some(("file", "write")),
4738 "now" => Some(("time", "now")),
4739 "sleep" => Some(("time", "sleep")),
4740 "randomInt" => Some(("random", "randomInt")),
4741 "randomFloat" => Some(("random", "randomFloat")),
4742 "get" => Some(("env", "get")),
4743 "args" => Some(("env", "args")),
4744 "parseInt" => Some(("text", "parseInt")),
4745 "parseFloat" => Some(("text", "parseFloat")),
4746 "format" => Some(("fmt", "format")),
4747 _ => None,
4748 }
4749}
4750
4751fn is_text_type(ty: &TypeExpr, interner: &Interner) -> bool {
4753 match ty {
4754 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
4755 matches!(interner.resolve(*sym), "Text" | "String")
4756 }
4757 TypeExpr::Refinement { base, .. } => is_text_type(base, interner),
4758 _ => false,
4759 }
4760}
4761
4762fn map_type_to_c_abi(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
4765 if is_text_type(ty, interner) {
4766 if is_return {
4767 "*mut std::os::raw::c_char".to_string()
4768 } else {
4769 "*const std::os::raw::c_char".to_string()
4770 }
4771 } else {
4772 codegen_type_expr(ty, interner)
4773 }
4774}
4775
4776fn codegen_c_export_with_marshaling(
4786 name: Symbol,
4787 params: &[(Symbol, &TypeExpr)],
4788 body: &[Stmt],
4789 return_type: Option<&TypeExpr>,
4790 interner: &Interner,
4791 lww_fields: &HashSet<(String, String)>,
4792 mv_fields: &HashSet<(String, String)>,
4793 async_functions: &HashSet<Symbol>,
4794 boxed_fields: &HashSet<(String, String, String)>,
4795 registry: &crate::analysis::registry::TypeRegistry,
4796) -> String {
4797 let mut output = String::new();
4798 let raw_name = interner.resolve(name);
4799 let func_name = format!("logos_{}", raw_name);
4803 let inner_name = escape_rust_ident(raw_name);
4804
4805 let has_ref_return = return_type.map_or(false, |ty| {
4807 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4808 });
4809 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4810 let has_text_return = return_type.map_or(false, |t| is_text_type(t, interner));
4811
4812 let uses_status_code = has_ref_return || has_result_return || has_text_return
4817 || params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
4818
4819 let inner_params: Vec<String> = params.iter()
4821 .map(|(pname, ptype)| {
4822 format!("{}: {}", interner.resolve(*pname), codegen_type_expr(ptype, interner))
4823 })
4824 .collect();
4825 let inner_ret = return_type.map(|t| codegen_type_expr(t, interner));
4826
4827 let inner_sig = if let Some(ref ret) = inner_ret {
4828 if ret != "()" {
4829 format!("fn {}({}) -> {}", inner_name, inner_params.join(", "), ret)
4830 } else {
4831 format!("fn {}({})", inner_name, inner_params.join(", "))
4832 }
4833 } else {
4834 format!("fn {}({})", inner_name, inner_params.join(", "))
4835 };
4836
4837 writeln!(output, "{} {{", inner_sig).unwrap();
4838 let func_mutable_vars = collect_mutable_vars(body);
4839 let mut func_ctx = RefinementContext::new();
4840 let mut func_synced_vars = HashSet::new();
4841 let func_var_caps = analyze_variable_capabilities(body, interner);
4842 for (param_name, param_type) in params {
4843 let type_name = codegen_type_expr(param_type, interner);
4844 func_ctx.register_variable_type(*param_name, type_name);
4845 }
4846 let func_pipe_vars = HashSet::new();
4847 {
4848 let stmt_refs: Vec<&Stmt> = body.iter().collect();
4849 let mut si = 0;
4850 while si < stmt_refs.len() {
4851 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4852 output.push_str(&code);
4853 si += 1 + skip;
4854 continue;
4855 }
4856 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4857 output.push_str(&code);
4858 si += 1 + skip;
4859 continue;
4860 }
4861 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));
4862 si += 1;
4863 }
4864 }
4865 writeln!(output, "}}\n").unwrap();
4866
4867 let mut c_params: Vec<String> = Vec::new();
4869
4870 for (pname, ptype) in params.iter() {
4871 let pn = interner.resolve(*pname);
4872 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4873 c_params.push(format!("{}: LogosHandle", pn));
4874 } else if is_text_type(ptype, interner) {
4875 c_params.push(format!("{}: *const std::os::raw::c_char", pn));
4876 } else {
4877 c_params.push(format!("{}: {}", pn, codegen_type_expr(ptype, interner)));
4878 }
4879 }
4880
4881 if uses_status_code {
4883 if let Some(ret_ty) = return_type {
4884 if has_result_return {
4885 if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
4887 if !rparams.is_empty() {
4888 let ok_ty = &rparams[0];
4889 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
4890 c_params.push("out: *mut LogosHandle".to_string());
4891 } else if is_text_type(ok_ty, interner) {
4892 c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4893 } else {
4894 let ty_str = codegen_type_expr(ok_ty, interner);
4895 c_params.push(format!("out: *mut {}", ty_str));
4896 }
4897 }
4898 }
4899 } else if has_ref_return {
4900 c_params.push("out: *mut LogosHandle".to_string());
4901 } else if has_text_return {
4902 c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4903 }
4904 }
4905 }
4906
4907 let c_sig = if uses_status_code {
4909 format!("pub extern \"C\" fn {}({}) -> LogosStatus", func_name, c_params.join(", "))
4910 } else if has_text_return {
4911 format!("pub extern \"C\" fn {}({}) -> *mut std::os::raw::c_char", func_name, c_params.join(", "))
4912 } else if let Some(ret_ty) = return_type {
4913 let ret_str = codegen_type_expr(ret_ty, interner);
4914 if ret_str != "()" {
4915 format!("pub extern \"C\" fn {}({}) -> {}", func_name, c_params.join(", "), ret_str)
4916 } else {
4917 format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4918 }
4919 } else {
4920 format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4921 };
4922
4923 writeln!(output, "#[no_mangle]").unwrap();
4924 writeln!(output, "{} {{", c_sig).unwrap();
4925
4926 let call_args: Vec<String> = params.iter()
4928 .map(|(pname, ptype)| {
4929 let pname_str = interner.resolve(*pname);
4930 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4931 let rust_ty = codegen_type_expr(ptype, interner);
4933 writeln!(output, " let {pn} = {{", pn = pname_str).unwrap();
4934 writeln!(output, " let __id = {pn} as u64;", pn = pname_str).unwrap();
4935 writeln!(output, " let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
4936 writeln!(output, " let __ptr = __reg.deref(__id).expect(\"InvalidHandle: handle not found in registry\");").unwrap();
4937 writeln!(output, " drop(__reg);").unwrap();
4938 writeln!(output, " unsafe {{ &*(__ptr as *const {ty}) }}.clone()", ty = rust_ty).unwrap();
4939 writeln!(output, " }};").unwrap();
4940 } else if is_text_type(ptype, interner) {
4941 if uses_status_code {
4943 writeln!(output, " if {pn}.is_null() {{ logos_set_last_error(\"NullPointer: text parameter '{pn}' is null\".to_string()); return LogosStatus::NullPointer; }}",
4944 pn = pname_str).unwrap();
4945 writeln!(output, " let {pn} = unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }};",
4946 pn = pname_str).unwrap();
4947 } else {
4948 writeln!(output, " let {pn} = if {pn}.is_null() {{ String::new() }} else {{ unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }} }};",
4950 pn = pname_str).unwrap();
4951 }
4952 }
4953 pname_str.to_string()
4954 })
4955 .collect();
4956
4957 for (pname, ptype) in params.iter() {
4959 if let TypeExpr::Refinement { base: _, var, predicate } = ptype {
4960 let pname_str = interner.resolve(*pname);
4961 let bound = interner.resolve(*var);
4962 let assertion = codegen_assertion(predicate, interner);
4963 let check = if bound == pname_str {
4964 assertion
4965 } else {
4966 replace_word(&assertion, bound, pname_str)
4967 };
4968 writeln!(output, " if !({}) {{", check).unwrap();
4969 writeln!(output, " logos_set_last_error(format!(\"Refinement violation: expected {check}, got {pn} = {{}}\", {pn}));",
4970 check = check, pn = pname_str).unwrap();
4971 writeln!(output, " return LogosStatus::RefinementViolation;").unwrap();
4972 writeln!(output, " }}").unwrap();
4973 }
4974 }
4975
4976 if uses_status_code && (has_ref_return || has_text_return || has_result_return) {
4978 writeln!(output, " if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
4979 }
4980
4981 let panic_default = if uses_status_code {
4983 "LogosStatus::ThreadPanic"
4984 } else if has_text_return {
4985 "std::ptr::null_mut()"
4986 } else if return_type.map_or(false, |t| codegen_type_expr(t, interner) != "()") {
4987 "Default::default()"
4988 } else {
4989 "" };
4991
4992 writeln!(output, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
4994
4995 if uses_status_code {
4997 if has_result_return {
4998 writeln!(output, " match {}({}) {{", inner_name, call_args.join(", ")).unwrap();
5000 writeln!(output, " Ok(val) => {{").unwrap();
5001
5002 if let Some(TypeExpr::Generic { params: ref rparams, .. }) = return_type {
5003 if !rparams.is_empty() {
5004 let ok_ty = &rparams[0];
5005 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
5006 writeln!(output, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
5007 writeln!(output, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5008 writeln!(output, " let (__id, _) = __reg.register(__ptr);").unwrap();
5009 writeln!(output, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5010 } else if is_text_type(ok_ty, interner) {
5011 writeln!(output, " match std::ffi::CString::new(val) {{").unwrap();
5012 writeln!(output, " Ok(cstr) => unsafe {{ *out = cstr.into_raw(); }},").unwrap();
5013 writeln!(output, " Err(_) => {{").unwrap();
5014 writeln!(output, " logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5015 writeln!(output, " return LogosStatus::ContainsNullByte;").unwrap();
5016 writeln!(output, " }}").unwrap();
5017 writeln!(output, " }}").unwrap();
5018 } else {
5019 writeln!(output, " unsafe {{ *out = val; }}").unwrap();
5020 }
5021 }
5022 }
5023
5024 writeln!(output, " LogosStatus::Ok").unwrap();
5025 writeln!(output, " }}").unwrap();
5026 writeln!(output, " Err(e) => {{").unwrap();
5027 writeln!(output, " logos_set_last_error(format!(\"{{}}\", e));").unwrap();
5028 writeln!(output, " LogosStatus::Error").unwrap();
5029 writeln!(output, " }}").unwrap();
5030 writeln!(output, " }}").unwrap();
5031 } else if has_ref_return {
5032 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5034 writeln!(output, " let __ptr = Box::into_raw(Box::new(result)) as usize;").unwrap();
5035 writeln!(output, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5036 writeln!(output, " let (__id, _) = __reg.register(__ptr);").unwrap();
5037 writeln!(output, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5038 writeln!(output, " LogosStatus::Ok").unwrap();
5039 } else if has_text_return {
5040 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5042 writeln!(output, " match std::ffi::CString::new(result) {{").unwrap();
5043 writeln!(output, " Ok(cstr) => {{").unwrap();
5044 writeln!(output, " unsafe {{ *out = cstr.into_raw(); }}").unwrap();
5045 writeln!(output, " LogosStatus::Ok").unwrap();
5046 writeln!(output, " }}").unwrap();
5047 writeln!(output, " Err(_) => {{").unwrap();
5048 writeln!(output, " logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5049 writeln!(output, " LogosStatus::ContainsNullByte").unwrap();
5050 writeln!(output, " }}").unwrap();
5051 writeln!(output, " }}").unwrap();
5052 } else {
5053 writeln!(output, " {}({});", inner_name, call_args.join(", ")).unwrap();
5055 writeln!(output, " LogosStatus::Ok").unwrap();
5056 }
5057 } else if has_text_return {
5058 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5060 writeln!(output, " match std::ffi::CString::new(result) {{").unwrap();
5061 writeln!(output, " Ok(cstr) => cstr.into_raw(),").unwrap();
5062 writeln!(output, " Err(_) => {{ logos_set_last_error(\"Return value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
5063 writeln!(output, " }}").unwrap();
5064 } else if return_type.is_some() {
5065 writeln!(output, " {}({})", inner_name, call_args.join(", ")).unwrap();
5066 } else {
5067 writeln!(output, " {}({})", inner_name, call_args.join(", ")).unwrap();
5068 }
5069
5070 writeln!(output, " }})) {{").unwrap();
5072 writeln!(output, " Ok(__v) => __v,").unwrap();
5073 writeln!(output, " Err(__panic) => {{").unwrap();
5074 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();
5075 writeln!(output, " logos_set_last_error(__msg);").unwrap();
5076 if !panic_default.is_empty() {
5077 writeln!(output, " {}", panic_default).unwrap();
5078 }
5079 writeln!(output, " }}").unwrap();
5080 writeln!(output, " }}").unwrap();
5081
5082 writeln!(output, "}}\n").unwrap();
5083
5084 output
5085}
5086
5087fn codegen_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
5089 match ty {
5090 TypeExpr::Primitive(sym) => {
5091 map_type_to_rust(interner.resolve(*sym))
5092 }
5093 TypeExpr::Named(sym) => {
5094 let name = interner.resolve(*sym);
5095 map_type_to_rust(name)
5097 }
5098 TypeExpr::Generic { base, params } => {
5099 let base_name = interner.resolve(*base);
5100 let params_str: Vec<String> = params.iter()
5101 .map(|p| codegen_type_expr(p, interner))
5102 .collect();
5103
5104 match base_name {
5105 "Result" => {
5106 if params_str.len() == 2 {
5107 format!("Result<{}, {}>", params_str[0], params_str[1])
5108 } else if params_str.len() == 1 {
5109 format!("Result<{}, String>", params_str[0])
5110 } else {
5111 "Result<(), String>".to_string()
5112 }
5113 }
5114 "Option" | "Maybe" => {
5115 if !params_str.is_empty() {
5116 format!("Option<{}>", params_str[0])
5117 } else {
5118 "Option<()>".to_string()
5119 }
5120 }
5121 "Seq" | "List" | "Vec" => {
5122 if !params_str.is_empty() {
5123 format!("Vec<{}>", params_str[0])
5124 } else {
5125 "Vec<()>".to_string()
5126 }
5127 }
5128 "Map" | "HashMap" => {
5129 if params_str.len() >= 2 {
5130 format!("std::collections::HashMap<{}, {}>", params_str[0], params_str[1])
5131 } else {
5132 "std::collections::HashMap<String, String>".to_string()
5133 }
5134 }
5135 "Set" | "HashSet" => {
5136 if !params_str.is_empty() {
5137 format!("std::collections::HashSet<{}>", params_str[0])
5138 } else {
5139 "std::collections::HashSet<()>".to_string()
5140 }
5141 }
5142 other => {
5143 if params_str.is_empty() {
5144 other.to_string()
5145 } else {
5146 format!("{}<{}>", other, params_str.join(", "))
5147 }
5148 }
5149 }
5150 }
5151 TypeExpr::Function { inputs, output } => {
5152 let inputs_str: Vec<String> = inputs.iter()
5153 .map(|i| codegen_type_expr(i, interner))
5154 .collect();
5155 let output_str = codegen_type_expr(output, interner);
5156 format!("impl Fn({}) -> {}", inputs_str.join(", "), output_str)
5157 }
5158 TypeExpr::Refinement { base, .. } => {
5161 codegen_type_expr(base, interner)
5162 }
5163 TypeExpr::Persistent { inner } => {
5165 let inner_type = codegen_type_expr(inner, interner);
5166 format!("logicaffeine_system::storage::Persistent<{}>", inner_type)
5167 }
5168 }
5169}
5170
5171fn infer_return_type_from_body(body: &[Stmt], _interner: &Interner) -> Option<String> {
5173 for stmt in body {
5174 if let Stmt::Return { value: Some(_) } = stmt {
5175 return Some("i64".to_string());
5178 }
5179 }
5180 None
5181}
5182
5183fn map_type_to_rust(ty: &str) -> String {
5185 match ty {
5186 "Int" => "i64".to_string(),
5187 "Nat" => "u64".to_string(),
5188 "Text" => "String".to_string(),
5189 "Bool" | "Boolean" => "bool".to_string(),
5190 "Real" | "Float" => "f64".to_string(),
5191 "Char" => "char".to_string(),
5192 "Byte" => "u8".to_string(),
5193 "Unit" | "()" => "()".to_string(),
5194 "Duration" => "std::time::Duration".to_string(),
5195 other => other.to_string(),
5196 }
5197}
5198
5199fn 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 {
5204 let ind = " ".repeat(indent);
5205 let mut output = String::new();
5206
5207 let generic_str = if generics.is_empty() {
5209 String::new()
5210 } else {
5211 let params: Vec<&str> = generics.iter()
5212 .map(|g| interner.resolve(*g))
5213 .collect();
5214 format!("<{}>", params.join(", "))
5215 };
5216
5217 if c_abi_value_structs.contains(&name) {
5219 writeln!(output, "{}#[repr(C)]", ind).unwrap();
5220 }
5221
5222 if is_portable || is_shared || c_abi_ref_structs.contains(&name) {
5227 writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5228 } else {
5229 writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq)]", ind).unwrap();
5230 }
5231 writeln!(output, "{}pub struct {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5232
5233 for field in fields {
5234 let vis = if field.is_public { "pub " } else { "" };
5235 let rust_type = codegen_field_type(&field.ty, interner);
5236 writeln!(output, "{} {}{}: {},", ind, vis, interner.resolve(field.name), rust_type).unwrap();
5237 }
5238
5239 writeln!(output, "{}}}\n", ind).unwrap();
5240
5241 if is_shared {
5243 output.push_str(&codegen_merge_impl(name, fields, generics, interner, indent));
5244 }
5245
5246 output
5247}
5248
5249fn codegen_merge_impl(name: Symbol, fields: &[FieldDef], generics: &[Symbol], interner: &Interner, indent: usize) -> String {
5251 let ind = " ".repeat(indent);
5252 let name_str = interner.resolve(name);
5253 let mut output = String::new();
5254
5255 let generic_str = if generics.is_empty() {
5257 String::new()
5258 } else {
5259 let params: Vec<&str> = generics.iter()
5260 .map(|g| interner.resolve(*g))
5261 .collect();
5262 format!("<{}>", params.join(", "))
5263 };
5264
5265 writeln!(output, "{}impl{} logicaffeine_data::crdt::Merge for {}{} {{", ind, generic_str, name_str, generic_str).unwrap();
5266 writeln!(output, "{} fn merge(&mut self, other: &Self) {{", ind).unwrap();
5267
5268 for field in fields {
5269 let field_name = interner.resolve(field.name);
5270 if is_crdt_field_type(&field.ty, interner) {
5272 writeln!(output, "{} self.{}.merge(&other.{});", ind, field_name, field_name).unwrap();
5273 }
5274 }
5275
5276 writeln!(output, "{} }}", ind).unwrap();
5277 writeln!(output, "{}}}\n", ind).unwrap();
5278
5279 output
5280}
5281
5282fn is_crdt_field_type(ty: &FieldType, interner: &Interner) -> bool {
5284 match ty {
5285 FieldType::Named(sym) => {
5286 let name = interner.resolve(*sym);
5287 matches!(name,
5288 "ConvergentCount" | "GCounter" |
5289 "Tally" | "PNCounter"
5290 )
5291 }
5292 FieldType::Generic { base, .. } => {
5293 let name = interner.resolve(*base);
5294 matches!(name,
5295 "LastWriteWins" | "LWWRegister" |
5296 "SharedSet" | "ORSet" | "SharedSet_AddWins" | "SharedSet_RemoveWins" |
5297 "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" |
5298 "SharedMap" | "ORMap" |
5299 "Divergent" | "MVRegister"
5300 )
5301 }
5302 _ => false,
5303 }
5304}
5305
5306fn codegen_enum_def(name: Symbol, variants: &[VariantDef], generics: &[Symbol], is_portable: bool, _is_shared: bool, interner: &Interner, indent: usize) -> String {
5310 let ind = " ".repeat(indent);
5311 let mut output = String::new();
5312
5313 let generic_str = if generics.is_empty() {
5315 String::new()
5316 } else {
5317 let params: Vec<&str> = generics.iter()
5318 .map(|g| interner.resolve(*g))
5319 .collect();
5320 format!("<{}>", params.join(", "))
5321 };
5322
5323 if is_portable {
5325 writeln!(output, "{}#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5326 } else {
5327 writeln!(output, "{}#[derive(Debug, Clone, PartialEq)]", ind).unwrap();
5328 }
5329 writeln!(output, "{}pub enum {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5330
5331 for variant in variants {
5332 let variant_name = interner.resolve(variant.name);
5333 if variant.fields.is_empty() {
5334 writeln!(output, "{} {},", ind, variant_name).unwrap();
5336 } else {
5337 let enum_name_str = interner.resolve(name);
5340 let fields_str: Vec<String> = variant.fields.iter()
5341 .map(|f| {
5342 let rust_type = codegen_field_type(&f.ty, interner);
5343 let field_name = interner.resolve(f.name);
5344 if is_recursive_field(&f.ty, enum_name_str, interner) {
5346 format!("{}: Box<{}>", field_name, rust_type)
5347 } else {
5348 format!("{}: {}", field_name, rust_type)
5349 }
5350 })
5351 .collect();
5352 writeln!(output, "{} {} {{ {} }},", ind, variant_name, fields_str.join(", ")).unwrap();
5353 }
5354 }
5355
5356 writeln!(output, "{}}}\n", ind).unwrap();
5357
5358 if generics.is_empty() {
5362 if let Some(first_variant) = variants.first() {
5363 let enum_name_str = interner.resolve(name);
5364 let first_variant_name = interner.resolve(first_variant.name);
5365 writeln!(output, "{}impl{} Default for {}{} {{", ind, generic_str, enum_name_str, generic_str).unwrap();
5366 writeln!(output, "{} fn default() -> Self {{", ind).unwrap();
5367 if first_variant.fields.is_empty() {
5368 writeln!(output, "{} {}::{}", ind, enum_name_str, first_variant_name).unwrap();
5369 } else {
5370 let default_fields: Vec<String> = first_variant.fields.iter()
5372 .map(|f| {
5373 let field_name = interner.resolve(f.name);
5374 let enum_name_check = interner.resolve(name);
5375 if is_recursive_field(&f.ty, enum_name_check, interner) {
5376 format!("{}: Box::new(Default::default())", field_name)
5377 } else {
5378 format!("{}: Default::default()", field_name)
5379 }
5380 })
5381 .collect();
5382 writeln!(output, "{} {}::{} {{ {} }}", ind, enum_name_str, first_variant_name, default_fields.join(", ")).unwrap();
5383 }
5384 writeln!(output, "{} }}", ind).unwrap();
5385 writeln!(output, "{}}}\n", ind).unwrap();
5386 }
5387 }
5388
5389 output
5390}
5391
5392fn codegen_field_type(ty: &FieldType, interner: &Interner) -> String {
5394 match ty {
5395 FieldType::Primitive(sym) => {
5396 match interner.resolve(*sym) {
5397 "Int" => "i64".to_string(),
5398 "Nat" => "u64".to_string(),
5399 "Text" => "String".to_string(),
5400 "Bool" | "Boolean" => "bool".to_string(),
5401 "Real" | "Float" => "f64".to_string(),
5402 "Char" => "char".to_string(),
5403 "Byte" => "u8".to_string(),
5404 "Unit" => "()".to_string(),
5405 "Duration" => "std::time::Duration".to_string(),
5406 other => other.to_string(),
5407 }
5408 }
5409 FieldType::Named(sym) => {
5410 let name = interner.resolve(*sym);
5411 match name {
5412 "ConvergentCount" => "logicaffeine_data::crdt::GCounter".to_string(),
5414 "Tally" => "logicaffeine_data::crdt::PNCounter".to_string(),
5416 _ => name.to_string(),
5417 }
5418 }
5419 FieldType::Generic { base, params } => {
5420 let base_name = interner.resolve(*base);
5421 let param_strs: Vec<String> = params.iter()
5422 .map(|p| codegen_field_type(p, interner))
5423 .collect();
5424
5425 match base_name {
5427 "SharedSet_RemoveWins" => {
5429 return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::RemoveWins>", param_strs.join(", "));
5430 }
5431 "SharedSet_AddWins" => {
5432 return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::AddWins>", param_strs.join(", "));
5433 }
5434 "SharedSequence_YATA" | "CollaborativeSequence" => {
5436 return format!("logicaffeine_data::crdt::YATA<{}>", param_strs.join(", "));
5437 }
5438 _ => {}
5439 }
5440
5441 let base_str = match base_name {
5442 "List" | "Seq" => "Vec",
5443 "Set" => "std::collections::HashSet",
5444 "Map" => "std::collections::HashMap",
5445 "Option" | "Maybe" => "Option",
5446 "Result" => "Result",
5447 "LastWriteWins" => "logicaffeine_data::crdt::LWWRegister",
5449 "SharedSet" | "ORSet" => "logicaffeine_data::crdt::ORSet",
5451 "SharedSequence" | "RGA" => "logicaffeine_data::crdt::RGA",
5452 "SharedMap" | "ORMap" => "logicaffeine_data::crdt::ORMap",
5453 "Divergent" | "MVRegister" => "logicaffeine_data::crdt::MVRegister",
5454 other => other,
5455 };
5456 format!("{}<{}>", base_str, param_strs.join(", "))
5457 }
5458 FieldType::TypeParam(sym) => interner.resolve(*sym).to_string(),
5460 }
5461}
5462
5463fn is_recursive_field(ty: &FieldType, enum_name: &str, interner: &Interner) -> bool {
5466 match ty {
5467 FieldType::Primitive(sym) => interner.resolve(*sym) == enum_name,
5468 FieldType::Named(sym) => interner.resolve(*sym) == enum_name,
5469 FieldType::TypeParam(_) => false,
5470 FieldType::Generic { base, params } => {
5471 interner.resolve(*base) == enum_name ||
5473 params.iter().any(|p| is_recursive_field(p, enum_name, interner))
5474 }
5475 }
5476}
5477
5478fn infer_variant_type_annotation(
5481 expr: &Expr,
5482 registry: &TypeRegistry,
5483 interner: &Interner,
5484) -> Option<String> {
5485 let (enum_name, variant_name, field_values) = match expr {
5487 Expr::NewVariant { enum_name, variant, fields } => (*enum_name, *variant, fields),
5488 _ => return None,
5489 };
5490
5491 let enum_def = registry.get(enum_name)?;
5493 let (generics, variants) = match enum_def {
5494 TypeDef::Enum { generics, variants, .. } => (generics, variants),
5495 _ => return None,
5496 };
5497
5498 if generics.len() < 2 {
5500 return None;
5501 }
5502
5503 let variant_def = variants.iter().find(|v| v.name == variant_name)?;
5505
5506 let mut type_param_types: HashMap<Symbol, String> = HashMap::new();
5508 for (field_name, field_value) in field_values {
5509 if let Some(field_def) = variant_def.fields.iter().find(|f| f.name == *field_name) {
5511 if let FieldType::TypeParam(type_param) = &field_def.ty {
5513 let inferred = infer_rust_type_from_expr(field_value, interner);
5514 type_param_types.insert(*type_param, inferred);
5515 }
5516 }
5517 }
5518
5519 let enum_str = interner.resolve(enum_name);
5522 let param_strs: Vec<String> = generics.iter()
5523 .map(|g| {
5524 type_param_types.get(g)
5525 .cloned()
5526 .unwrap_or_else(|| "()".to_string())
5527 })
5528 .collect();
5529
5530 Some(format!("{}<{}>", enum_str, param_strs.join(", ")))
5531}
5532
5533fn infer_rust_type_from_expr(expr: &Expr, interner: &Interner) -> String {
5535 match expr {
5536 Expr::Literal(lit) => match lit {
5537 Literal::Number(_) => "i64".to_string(),
5538 Literal::Float(_) => "f64".to_string(),
5539 Literal::Text(_) => "String".to_string(),
5540 Literal::Boolean(_) => "bool".to_string(),
5541 Literal::Char(_) => "char".to_string(),
5542 Literal::Nothing => "()".to_string(),
5543 Literal::Duration(_) => "std::time::Duration".to_string(),
5544 Literal::Date(_) => "LogosDate".to_string(),
5545 Literal::Moment(_) => "LogosMoment".to_string(),
5546 Literal::Span { .. } => "LogosSpan".to_string(),
5547 Literal::Time(_) => "LogosTime".to_string(),
5548 },
5549 _ => "_".to_string(),
5551 }
5552}
5553
5554fn try_emit_vec_fill_pattern<'a>(
5558 stmts: &[&Stmt<'a>],
5559 idx: usize,
5560 interner: &Interner,
5561 indent: usize,
5562) -> Option<(String, usize)> {
5563 if idx + 2 >= stmts.len() {
5564 return None;
5565 }
5566
5567 let (vec_sym, elem_type) = match stmts[idx] {
5569 Stmt::Let { var, value, mutable: true, ty, .. } => {
5570 let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
5572 let base_name = interner.resolve(*base);
5573 if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
5574 Some(codegen_type_expr(¶ms[0], interner))
5575 } else {
5576 None
5577 }
5578 } else {
5579 None
5580 };
5581
5582 let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
5584 let tn = interner.resolve(*type_name);
5585 if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
5586 if !type_args.is_empty() {
5587 Some(codegen_type_expr(&type_args[0], interner))
5588 } else {
5589 None
5590 }
5591 } else {
5592 None
5593 }
5594 } else {
5595 None
5596 };
5597
5598 match type_from_annotation.or(type_from_new) {
5599 Some(t) => (*var, t),
5600 None => return None,
5601 }
5602 }
5603 _ => return None,
5604 };
5605
5606 let (counter_sym, counter_start) = match stmts[idx + 1] {
5608 Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), mutable: true, .. } => {
5609 (*var, *n)
5610 }
5611 _ => return None,
5612 };
5613
5614 match stmts[idx + 2] {
5616 Stmt::While { cond, body, .. } => {
5617 let limit_expr = match cond {
5619 Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5620 if let Expr::Identifier(sym) = left {
5621 if *sym == counter_sym {
5622 Some(*right)
5623 } else {
5624 None
5625 }
5626 } else {
5627 None
5628 }
5629 }
5630 _ => None,
5631 }?;
5632
5633 if body.len() != 2 {
5635 return None;
5636 }
5637
5638 let push_val = match &body[0] {
5640 Stmt::Push { value, collection } => {
5641 if let Expr::Identifier(sym) = collection {
5642 if *sym == vec_sym {
5643 Some(*value)
5644 } else {
5645 None
5646 }
5647 } else {
5648 None
5649 }
5650 }
5651 _ => None,
5652 }?;
5653
5654 let val_str = match push_val {
5656 Expr::Literal(Literal::Number(n)) => format!("{}", n),
5657 Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5658 Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5659 Expr::Literal(Literal::Char(c)) => format!("'{}'", c),
5660 Expr::Literal(Literal::Text(s)) => format!("{}.to_string()", interner.resolve(*s)),
5661 _ => return None,
5662 };
5663
5664 match &body[1] {
5666 Stmt::Set { target, value, .. } => {
5667 if *target != counter_sym {
5668 return None;
5669 }
5670 match value {
5672 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5673 let is_counter_plus_1 = match (left, right) {
5674 (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5675 (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5676 _ => false,
5677 };
5678 if !is_counter_plus_1 {
5679 return None;
5680 }
5681 }
5682 _ => return None,
5683 }
5684 }
5685 _ => return None,
5686 }
5687
5688 let indent_str = " ".repeat(indent);
5690 let vec_name = interner.resolve(vec_sym);
5691 let limit_str = codegen_expr_simple(limit_expr, interner);
5692
5693 let count_expr = if counter_start == 0 {
5695 format!("({} + 1) as usize", limit_str)
5696 } else if counter_start == 1 {
5697 format!("{} as usize", limit_str)
5698 } else {
5699 format!("({} - {} + 1) as usize", limit_str, counter_start)
5700 };
5701
5702 let mut output = String::new();
5703 writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
5704 indent_str, vec_name, elem_type, val_str, count_expr).unwrap();
5705 let counter_name = interner.resolve(counter_sym);
5707 writeln!(output, "{}let mut {} = {};",
5708 indent_str, counter_name, counter_start).unwrap();
5709
5710 Some((output, 2)) }
5712 _ => None,
5713 }
5714}
5715
5716fn codegen_expr_simple(expr: &Expr, interner: &Interner) -> String {
5718 match expr {
5719 Expr::Literal(Literal::Number(n)) => format!("{}", n),
5720 Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5721 Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5722 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
5723 Expr::BinaryOp { op, left, right } => {
5724 let l = codegen_expr_simple(left, interner);
5725 let r = codegen_expr_simple(right, interner);
5726 let op_str = match op {
5727 BinaryOpKind::Add => "+",
5728 BinaryOpKind::Subtract => "-",
5729 BinaryOpKind::Multiply => "*",
5730 BinaryOpKind::Divide => "/",
5731 BinaryOpKind::Modulo => "%",
5732 _ => return format!("({})", l),
5733 };
5734 format!("({} {} {})", l, op_str, r)
5735 }
5736 _ => "_".to_string(),
5737 }
5738}
5739
5740fn exprs_equal(a: &Expr, b: &Expr) -> bool {
5742 match (a, b) {
5743 (Expr::Identifier(s1), Expr::Identifier(s2)) => s1 == s2,
5744 (Expr::Literal(Literal::Number(n1)), Expr::Literal(Literal::Number(n2))) => n1 == n2,
5745 (Expr::BinaryOp { op: op1, left: l1, right: r1 }, Expr::BinaryOp { op: op2, left: l2, right: r2 }) => {
5746 op1 == op2 && exprs_equal(l1, l2) && exprs_equal(r1, r2)
5747 }
5748 _ => false,
5749 }
5750}
5751
5752fn try_emit_swap_pattern<'a>(
5758 stmts: &[&Stmt<'a>],
5759 idx: usize,
5760 interner: &Interner,
5761 indent: usize,
5762 variable_types: &HashMap<Symbol, String>,
5763) -> Option<(String, usize)> {
5764 if idx + 2 >= stmts.len() {
5765 return None;
5766 }
5767
5768 let (a_sym, arr_sym_1, idx_expr_1) = match stmts[idx] {
5770 Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
5771 if let Expr::Identifier(coll_sym) = collection {
5772 (*var, *coll_sym, *index)
5773 } else {
5774 return None;
5775 }
5776 }
5777 _ => return None,
5778 };
5779
5780 if let Some(t) = variable_types.get(&arr_sym_1) {
5782 if !t.starts_with("Vec") {
5783 return None;
5784 }
5785 } else {
5786 return None;
5787 }
5788
5789 let (b_sym, arr_sym_2, idx_expr_2) = match stmts[idx + 1] {
5791 Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
5792 if let Expr::Identifier(coll_sym) = collection {
5793 (*var, *coll_sym, *index)
5794 } else {
5795 return None;
5796 }
5797 }
5798 _ => return None,
5799 };
5800
5801 if arr_sym_1 != arr_sym_2 {
5803 return None;
5804 }
5805
5806 let is_adjacent = match idx_expr_2 {
5808 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5809 (exprs_equal(left, idx_expr_1) && matches!(right, Expr::Literal(Literal::Number(1))))
5810 || (matches!(left, Expr::Literal(Literal::Number(1))) && exprs_equal(right, idx_expr_1))
5811 }
5812 _ => false,
5813 };
5814 if !is_adjacent {
5815 return None;
5816 }
5817
5818 match stmts[idx + 2] {
5820 Stmt::If { cond, then_block, else_block } => {
5821 let compares_a_b = match cond {
5823 Expr::BinaryOp { op, left, right } => {
5824 matches!(op, BinaryOpKind::Gt | BinaryOpKind::Lt | BinaryOpKind::GtEq | BinaryOpKind::LtEq) &&
5825 ((matches!(left, Expr::Identifier(s) if *s == a_sym) && matches!(right, Expr::Identifier(s) if *s == b_sym)) ||
5826 (matches!(left, Expr::Identifier(s) if *s == b_sym) && matches!(right, Expr::Identifier(s) if *s == a_sym)))
5827 }
5828 _ => false,
5829 };
5830 if !compares_a_b {
5831 return None;
5832 }
5833
5834 if else_block.is_some() {
5836 return None;
5837 }
5838
5839 if then_block.len() != 2 {
5841 return None;
5842 }
5843
5844 let swap_ok = match (&then_block[0], &then_block[1]) {
5846 (
5847 Stmt::SetIndex { collection: c1, index: i1, value: v1 },
5848 Stmt::SetIndex { collection: c2, index: i2, value: v2 },
5849 ) => {
5850 let same_arr = matches!((c1, c2), (Expr::Identifier(s1), Expr::Identifier(s2)) if *s1 == arr_sym_1 && *s2 == arr_sym_1);
5852 let cross = exprs_equal(i1, idx_expr_1) && exprs_equal(i2, idx_expr_2) &&
5854 matches!(v1, Expr::Identifier(s) if *s == b_sym) &&
5855 matches!(v2, Expr::Identifier(s) if *s == a_sym);
5856 let cross_rev = exprs_equal(i1, idx_expr_2) && exprs_equal(i2, idx_expr_1) &&
5858 matches!(v1, Expr::Identifier(s) if *s == a_sym) &&
5859 matches!(v2, Expr::Identifier(s) if *s == b_sym);
5860 same_arr && (cross || cross_rev)
5861 }
5862 _ => false,
5863 };
5864
5865 if !swap_ok {
5866 return None;
5867 }
5868
5869 let indent_str = " ".repeat(indent);
5871 let arr_name = interner.resolve(arr_sym_1);
5872 let idx1_str = codegen_expr_simple(idx_expr_1, interner);
5873 let idx2_str = codegen_expr_simple(idx_expr_2, interner);
5874
5875 let cond_str = format!("({} {} {})",
5877 codegen_expr_simple(match cond { Expr::BinaryOp { left, .. } => left, _ => unreachable!() }, interner),
5878 match cond { Expr::BinaryOp { op, .. } => match op {
5879 BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
5880 BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
5881 _ => unreachable!(),
5882 }, _ => unreachable!() },
5883 codegen_expr_simple(match cond { Expr::BinaryOp { right, .. } => right, _ => unreachable!() }, interner),
5884 );
5885
5886 let mut output = String::new();
5887 writeln!(output, "{}if {}[({} - 1) as usize] {} {}[({} - 1) as usize] {{",
5890 indent_str, arr_name, idx1_str,
5891 match cond { Expr::BinaryOp { op, .. } => match op {
5892 BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
5893 BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
5894 _ => unreachable!(),
5895 }, _ => unreachable!() },
5896 arr_name, idx2_str,
5897 ).unwrap();
5898 writeln!(output, "{} {}.swap(({} - 1) as usize, ({} - 1) as usize);",
5899 indent_str, arr_name, idx1_str, idx2_str).unwrap();
5900 writeln!(output, "{}}}", indent_str).unwrap();
5901
5902 Some((output, 2)) }
5904 _ => None,
5905 }
5906}
5907
5908pub fn codegen_stmt<'a>(
5909 stmt: &Stmt<'a>,
5910 interner: &Interner,
5911 indent: usize,
5912 mutable_vars: &HashSet<Symbol>,
5913 ctx: &mut RefinementContext<'a>,
5914 lww_fields: &HashSet<(String, String)>,
5915 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 {
5923 let indent_str = " ".repeat(indent);
5924 let mut output = String::new();
5925
5926 match stmt {
5927 Stmt::Let { var, ty, value, mutable } => {
5928 let var_name = interner.resolve(*var);
5929
5930 if let Some(TypeExpr::Generic { base, params }) = ty {
5933 let base_name = interner.resolve(*base);
5934 match base_name {
5935 "Seq" | "List" | "Vec" => {
5936 let rust_type = if !params.is_empty() {
5937 format!("Vec<{}>", codegen_type_expr(¶ms[0], interner))
5938 } else {
5939 "Vec<()>".to_string()
5940 };
5941 ctx.register_variable_type(*var, rust_type);
5942 }
5943 "Map" | "HashMap" => {
5944 let rust_type = if params.len() >= 2 {
5945 format!("std::collections::HashMap<{}, {}>", codegen_type_expr(¶ms[0], interner), codegen_type_expr(¶ms[1], interner))
5946 } else {
5947 "std::collections::HashMap<String, String>".to_string()
5948 };
5949 ctx.register_variable_type(*var, rust_type);
5950 }
5951 _ => {}
5952 }
5953 } else if let Expr::New { type_name, type_args, .. } = value {
5954 let type_str = interner.resolve(*type_name);
5955 match type_str {
5956 "Seq" | "List" | "Vec" => {
5957 let rust_type = if !type_args.is_empty() {
5958 format!("Vec<{}>", codegen_type_expr(&type_args[0], interner))
5959 } else {
5960 "Vec<()>".to_string()
5961 };
5962 ctx.register_variable_type(*var, rust_type);
5963 }
5964 "Map" | "HashMap" => {
5965 let rust_type = if type_args.len() >= 2 {
5966 format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&type_args[0], interner), codegen_type_expr(&type_args[1], interner))
5967 } else {
5968 "std::collections::HashMap<String, String>".to_string()
5969 };
5970 ctx.register_variable_type(*var, rust_type);
5971 }
5972 _ => {}
5973 }
5974 } else if matches!(value, Expr::List(_)) {
5975 ctx.register_variable_type(*var, "Vec<_>".to_string());
5976 }
5977
5978 let value_str = codegen_expr_boxed_with_types(
5980 value, interner, synced_vars, boxed_fields, registry, async_functions,
5981 ctx.get_string_vars(), ctx.get_variable_types()
5982 );
5983
5984 let type_annotation = ty.map(|t| codegen_type_expr(t, interner))
5986 .or_else(|| infer_variant_type_annotation(value, registry, interner));
5987
5988 let is_mutable = *mutable || mutable_vars.contains(var);
5990
5991 match (is_mutable, type_annotation) {
5992 (true, Some(t)) => writeln!(output, "{}let mut {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
5993 (true, None) => writeln!(output, "{}let mut {} = {};", indent_str, var_name, value_str).unwrap(),
5994 (false, Some(t)) => writeln!(output, "{}let {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
5995 (false, None) => writeln!(output, "{}let {} = {};", indent_str, var_name, value_str).unwrap(),
5996 }
5997
5998 if is_definitely_string_expr_with_vars(value, ctx.get_string_vars()) {
6000 ctx.register_string_var(*var);
6001 }
6002
6003 if let Some(TypeExpr::Refinement { base: _, var: bound_var, predicate }) = ty {
6005 emit_refinement_check(var_name, *bound_var, predicate, interner, &indent_str, &mut output);
6006 ctx.register(*var, *bound_var, predicate);
6007 }
6008 }
6009
6010 Stmt::Set { target, value } => {
6011 let target_name = interner.resolve(*target);
6012 let string_vars = ctx.get_string_vars();
6013 let var_types = ctx.get_variable_types();
6014
6015 let used_write = if ctx.is_string_var(*target)
6019 && is_definitely_string_expr_with_vars(value, string_vars)
6020 {
6021 let mut operands = Vec::new();
6022 collect_string_concat_operands(value, string_vars, &mut operands);
6023
6024 if operands.len() >= 2 && matches!(operands[0], Expr::Identifier(sym) if *sym == *target) {
6026 let tail = &operands[1..];
6028 let mut tail_ids = HashSet::new();
6029 for op in tail {
6030 collect_expr_identifiers(op, &mut tail_ids);
6031 }
6032
6033 if !tail_ids.contains(target) {
6034 let placeholders: String = tail.iter().map(|_| "{}").collect::<Vec<_>>().join("");
6036 let values: Vec<String> = tail.iter().map(|e| {
6037 if let Expr::Literal(Literal::Text(sym)) = e {
6039 format!("\"{}\"", interner.resolve(*sym))
6040 } else {
6041 codegen_expr_boxed_with_types(
6042 e, interner, synced_vars, boxed_fields, registry, async_functions,
6043 string_vars, var_types
6044 )
6045 }
6046 }).collect();
6047 writeln!(output, "{}write!({}, \"{}\", {}).unwrap();",
6048 indent_str, target_name, placeholders, values.join(", ")).unwrap();
6049 true
6050 } else {
6051 false
6052 }
6053 } else {
6054 false
6055 }
6056 } else {
6057 false
6058 };
6059
6060 if !used_write {
6061 let value_str = codegen_expr_boxed_with_types(
6063 value, interner, synced_vars, boxed_fields, registry, async_functions,
6064 string_vars, var_types
6065 );
6066 writeln!(output, "{}{} = {};", indent_str, target_name, value_str).unwrap();
6067 }
6068
6069 if let Some((bound_var, predicate)) = ctx.get_constraint(*target) {
6071 emit_refinement_check(target_name, bound_var, predicate, interner, &indent_str, &mut output);
6072 }
6073 }
6074
6075 Stmt::Call { function, args } => {
6076 let func_name = escape_rust_ident(interner.resolve(*function));
6077 let args_str: Vec<String> = args.iter().map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())).collect();
6078 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6080 writeln!(output, "{}{}({}){};", indent_str, func_name, args_str.join(", "), await_suffix).unwrap();
6081 }
6082
6083 Stmt::If { cond, then_block, else_block } => {
6084 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6085 writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
6086 ctx.push_scope();
6087 for stmt in *then_block {
6088 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));
6089 }
6090 ctx.pop_scope();
6091 if let Some(else_stmts) = else_block {
6092 writeln!(output, "{}}} else {{", indent_str).unwrap();
6093 ctx.push_scope();
6094 for stmt in *else_stmts {
6095 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));
6096 }
6097 ctx.pop_scope();
6098 }
6099 writeln!(output, "{}}}", indent_str).unwrap();
6100 }
6101
6102 Stmt::While { cond, body, decreasing: _ } => {
6103 let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6105 writeln!(output, "{}while {} {{", indent_str, cond_str).unwrap();
6106 ctx.push_scope();
6107 let body_refs: Vec<&Stmt> = body.iter().collect();
6109 let mut bi = 0;
6110 while bi < body_refs.len() {
6111 if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6112 output.push_str(&code);
6113 bi += 1 + skip;
6114 continue;
6115 }
6116 if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1) {
6117 output.push_str(&code);
6118 bi += 1 + skip;
6119 continue;
6120 }
6121 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));
6122 bi += 1;
6123 }
6124 ctx.pop_scope();
6125 writeln!(output, "{}}}", indent_str).unwrap();
6126 }
6127
6128 Stmt::Repeat { pattern, iterable, body } => {
6129 use crate::ast::stmt::Pattern;
6130
6131 let pattern_str = match pattern {
6133 Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
6134 Pattern::Tuple(syms) => {
6135 let names = syms.iter()
6136 .map(|s| interner.resolve(*s))
6137 .collect::<Vec<_>>()
6138 .join(", ");
6139 format!("({})", names)
6140 }
6141 };
6142
6143 let iter_str = codegen_expr_with_async(iterable, interner, synced_vars, async_functions, ctx.get_variable_types());
6144
6145 let body_has_async = body.iter().any(|s| {
6148 requires_async_stmt(s) || calls_async_function(s, async_functions)
6149 });
6150
6151 if body_has_async {
6152 writeln!(output, "{}let mut __iter = ({}).into_iter();", indent_str, iter_str).unwrap();
6154 writeln!(output, "{}while let Some({}) = __iter.next() {{", indent_str, pattern_str).unwrap();
6155 } else {
6156 writeln!(output, "{}for {} in {}.clone() {{", indent_str, pattern_str, iter_str).unwrap();
6160 }
6161 ctx.push_scope();
6162 {
6164 let body_refs: Vec<&Stmt> = body.iter().collect();
6165 let mut bi = 0;
6166 while bi < body_refs.len() {
6167 if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6168 output.push_str(&code);
6169 bi += 1 + skip;
6170 continue;
6171 }
6172 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));
6173 bi += 1;
6174 }
6175 }
6176 ctx.pop_scope();
6177 writeln!(output, "{}}}", indent_str).unwrap();
6178 }
6179
6180 Stmt::Return { value } => {
6181 if let Some(v) = value {
6182 let value_str = codegen_expr_with_async(v, interner, synced_vars, async_functions, ctx.get_variable_types());
6183 writeln!(output, "{}return {};", indent_str, value_str).unwrap();
6184 } else {
6185 writeln!(output, "{}return;", indent_str).unwrap();
6186 }
6187 }
6188
6189 Stmt::Assert { proposition } => {
6190 let condition = codegen_assertion(proposition, interner);
6191 writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6192 }
6193
6194 Stmt::Trust { proposition, justification } => {
6196 let reason = interner.resolve(*justification);
6197 let reason_clean = reason.trim_matches('"');
6199 writeln!(output, "{}// TRUST: {}", indent_str, reason_clean).unwrap();
6200 let condition = codegen_assertion(proposition, interner);
6201 writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6202 }
6203
6204 Stmt::RuntimeAssert { condition } => {
6205 let cond_str = codegen_expr_with_async(condition, interner, synced_vars, async_functions, ctx.get_variable_types());
6206 writeln!(output, "{}debug_assert!({});", indent_str, cond_str).unwrap();
6207 }
6208
6209 Stmt::Check { subject, predicate, is_capability, object, source_text, span } => {
6211 let subj_name = interner.resolve(*subject);
6212 let pred_name = interner.resolve(*predicate).to_lowercase();
6213
6214 let call = if *is_capability {
6215 let obj_sym = object.expect("capability must have object");
6216 let obj_word = interner.resolve(obj_sym);
6217
6218 let obj_name = ctx.find_variable_by_type(obj_word, interner)
6222 .unwrap_or_else(|| obj_word.to_string());
6223
6224 format!("{}.can_{}(&{})", subj_name, pred_name, obj_name)
6225 } else {
6226 format!("{}.is_{}()", subj_name, pred_name)
6227 };
6228
6229 writeln!(output, "{}if !({}) {{", indent_str, call).unwrap();
6230 writeln!(output, "{} logicaffeine_system::panic_with(\"Security Check Failed at line {}: {}\");",
6231 indent_str, span.start, source_text).unwrap();
6232 writeln!(output, "{}}}", indent_str).unwrap();
6233 }
6234
6235 Stmt::Listen { address } => {
6237 let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6238 writeln!(output, "{}logicaffeine_system::network::listen(&{}).await.expect(\"Failed to listen\");",
6240 indent_str, addr_str).unwrap();
6241 }
6242
6243 Stmt::ConnectTo { address } => {
6245 let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6246 writeln!(output, "{}logicaffeine_system::network::connect(&{}).await.expect(\"Failed to connect\");",
6248 indent_str, addr_str).unwrap();
6249 }
6250
6251 Stmt::LetPeerAgent { var, address } => {
6253 let var_name = interner.resolve(*var);
6254 let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6255 writeln!(output, "{}let {} = logicaffeine_system::network::PeerAgent::new(&{}).expect(\"Invalid address\");",
6257 indent_str, var_name, addr_str).unwrap();
6258 }
6259
6260 Stmt::Sleep { milliseconds } => {
6262 let expr_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6263 let inferred_type = infer_rust_type_from_expr(milliseconds, interner);
6264
6265 if inferred_type == "std::time::Duration" {
6266 writeln!(output, "{}tokio::time::sleep({}).await;",
6268 indent_str, expr_str).unwrap();
6269 } else {
6270 writeln!(output, "{}tokio::time::sleep(std::time::Duration::from_millis({} as u64)).await;",
6272 indent_str, expr_str).unwrap();
6273 }
6274 }
6275
6276 Stmt::Sync { var, topic } => {
6278 let var_name = interner.resolve(*var);
6279 let topic_str = codegen_expr_with_async(topic, interner, synced_vars, async_functions, ctx.get_variable_types());
6280
6281 if let Some(caps) = var_caps.get(var) {
6283 if caps.mounted {
6284 synced_vars.insert(*var);
6288 return output; }
6290 }
6291
6292 writeln!(
6294 output,
6295 "{}let {} = logicaffeine_system::crdt::Synced::new({}, &{}).await;",
6296 indent_str, var_name, var_name, topic_str
6297 ).unwrap();
6298 synced_vars.insert(*var);
6299 }
6300
6301 Stmt::Mount { var, path } => {
6303 let var_name = interner.resolve(*var);
6304 let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
6305
6306 if let Some(caps) = var_caps.get(var) {
6308 if caps.synced {
6309 let topic_str = caps.sync_topic.as_ref()
6311 .map(|s| s.as_str())
6312 .unwrap_or("\"default\"");
6313 writeln!(
6314 output,
6315 "{}let {} = logicaffeine_system::distributed::Distributed::mount(std::sync::Arc::new(vfs.clone()), &{}, Some({}.to_string())).await.expect(\"Failed to mount\");",
6316 indent_str, var_name, path_str, topic_str
6317 ).unwrap();
6318 synced_vars.insert(*var);
6319 return output;
6320 }
6321 }
6322
6323 writeln!(
6325 output,
6326 "{}let {} = logicaffeine_system::storage::Persistent::mount(&vfs, &{}).await.expect(\"Failed to mount\");",
6327 indent_str, var_name, path_str
6328 ).unwrap();
6329 synced_vars.insert(*var);
6330 }
6331
6332 Stmt::LaunchTask { function, args } => {
6337 let fn_name = escape_rust_ident(interner.resolve(*function));
6338 let args_str: Vec<String> = args.iter()
6340 .map(|a| {
6341 if let Expr::Identifier(sym) = a {
6342 if pipe_vars.contains(sym) {
6343 return format!("{}_tx.clone()", interner.resolve(*sym));
6344 }
6345 }
6346 codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6347 })
6348 .collect();
6349 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6351 writeln!(
6352 output,
6353 "{}tokio::spawn(async move {{ {}({}){await_suffix}; }});",
6354 indent_str, fn_name, args_str.join(", ")
6355 ).unwrap();
6356 }
6357
6358 Stmt::LaunchTaskWithHandle { handle, function, args } => {
6359 let handle_name = interner.resolve(*handle);
6360 let fn_name = escape_rust_ident(interner.resolve(*function));
6361 let args_str: Vec<String> = args.iter()
6363 .map(|a| {
6364 if let Expr::Identifier(sym) = a {
6365 if pipe_vars.contains(sym) {
6366 return format!("{}_tx.clone()", interner.resolve(*sym));
6367 }
6368 }
6369 codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6370 })
6371 .collect();
6372 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6374 writeln!(
6375 output,
6376 "{}let {} = tokio::spawn(async move {{ {}({}){await_suffix} }});",
6377 indent_str, handle_name, fn_name, args_str.join(", ")
6378 ).unwrap();
6379 }
6380
6381 Stmt::CreatePipe { var, element_type, capacity } => {
6382 let var_name = interner.resolve(*var);
6383 let type_name = interner.resolve(*element_type);
6384 let cap = capacity.unwrap_or(32);
6385 let rust_type = match type_name {
6387 "Int" => "i64",
6388 "Nat" => "u64",
6389 "Text" => "String",
6390 "Bool" => "bool",
6391 _ => type_name,
6392 };
6393 writeln!(
6394 output,
6395 "{}let ({}_tx, mut {}_rx) = tokio::sync::mpsc::channel::<{}>({});",
6396 indent_str, var_name, var_name, rust_type, cap
6397 ).unwrap();
6398 }
6399
6400 Stmt::SendPipe { value, pipe } => {
6401 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());
6402 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6403 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6405 pipe_vars.contains(sym)
6406 } else {
6407 false
6408 };
6409 if is_local_pipe {
6410 writeln!(
6411 output,
6412 "{}{}_tx.send({}).await.expect(\"pipe send failed\");",
6413 indent_str, pipe_str, val_str
6414 ).unwrap();
6415 } else {
6416 writeln!(
6417 output,
6418 "{}{}.send({}).await.expect(\"pipe send failed\");",
6419 indent_str, pipe_str, val_str
6420 ).unwrap();
6421 }
6422 }
6423
6424 Stmt::ReceivePipe { var, pipe } => {
6425 let var_name = interner.resolve(*var);
6426 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6427 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6429 pipe_vars.contains(sym)
6430 } else {
6431 false
6432 };
6433 if is_local_pipe {
6434 writeln!(
6435 output,
6436 "{}let {} = {}_rx.recv().await.expect(\"pipe closed\");",
6437 indent_str, var_name, pipe_str
6438 ).unwrap();
6439 } else {
6440 writeln!(
6441 output,
6442 "{}let {} = {}.recv().await.expect(\"pipe closed\");",
6443 indent_str, var_name, pipe_str
6444 ).unwrap();
6445 }
6446 }
6447
6448 Stmt::TrySendPipe { value, pipe, result } => {
6449 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());
6450 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6451 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6453 pipe_vars.contains(sym)
6454 } else {
6455 false
6456 };
6457 let suffix = if is_local_pipe { "_tx" } else { "" };
6458 if let Some(res) = result {
6459 let res_name = interner.resolve(*res);
6460 writeln!(
6461 output,
6462 "{}let {} = {}{}.try_send({}).is_ok();",
6463 indent_str, res_name, pipe_str, suffix, val_str
6464 ).unwrap();
6465 } else {
6466 writeln!(
6467 output,
6468 "{}let _ = {}{}.try_send({});",
6469 indent_str, pipe_str, suffix, val_str
6470 ).unwrap();
6471 }
6472 }
6473
6474 Stmt::TryReceivePipe { var, pipe } => {
6475 let var_name = interner.resolve(*var);
6476 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6477 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6479 pipe_vars.contains(sym)
6480 } else {
6481 false
6482 };
6483 let suffix = if is_local_pipe { "_rx" } else { "" };
6484 writeln!(
6485 output,
6486 "{}let {} = {}{}.try_recv().ok();",
6487 indent_str, var_name, pipe_str, suffix
6488 ).unwrap();
6489 }
6490
6491 Stmt::StopTask { handle } => {
6492 let handle_str = codegen_expr_with_async(handle, interner, synced_vars, async_functions, ctx.get_variable_types());
6493 writeln!(output, "{}{}.abort();", indent_str, handle_str).unwrap();
6494 }
6495
6496 Stmt::Select { branches } => {
6497 use crate::ast::stmt::SelectBranch;
6498
6499 writeln!(output, "{}tokio::select! {{", indent_str).unwrap();
6500 for branch in branches {
6501 match branch {
6502 SelectBranch::Receive { var, pipe, body } => {
6503 let var_name = interner.resolve(*var);
6504 let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6505 let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6507 pipe_vars.contains(sym)
6508 } else {
6509 false
6510 };
6511 let suffix = if is_local_pipe { "_rx" } else { "" };
6512 writeln!(
6513 output,
6514 "{} {} = {}{}.recv() => {{",
6515 indent_str, var_name, pipe_str, suffix
6516 ).unwrap();
6517 writeln!(
6518 output,
6519 "{} if let Some({}) = {} {{",
6520 indent_str, var_name, var_name
6521 ).unwrap();
6522 for stmt in *body {
6523 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);
6524 write!(output, "{}", stmt_code).unwrap();
6525 }
6526 writeln!(output, "{} }}", indent_str).unwrap();
6527 writeln!(output, "{} }}", indent_str).unwrap();
6528 }
6529 SelectBranch::Timeout { milliseconds, body } => {
6530 let ms_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6531 writeln!(
6533 output,
6534 "{} _ = tokio::time::sleep(std::time::Duration::from_secs({} as u64)) => {{",
6535 indent_str, ms_str
6536 ).unwrap();
6537 for stmt in *body {
6538 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);
6539 write!(output, "{}", stmt_code).unwrap();
6540 }
6541 writeln!(output, "{} }}", indent_str).unwrap();
6542 }
6543 }
6544 }
6545 writeln!(output, "{}}}", indent_str).unwrap();
6546 }
6547
6548 Stmt::Give { object, recipient } => {
6549 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6551 let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6552 writeln!(output, "{}{}({});", indent_str, recv_str, obj_str).unwrap();
6553 }
6554
6555 Stmt::Show { object, recipient } => {
6556 let obj_str = codegen_expr_with_async_and_strings(object, interner, synced_vars, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6559 let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6560 writeln!(output, "{}{}(&{});", indent_str, recv_str, obj_str).unwrap();
6561 }
6562
6563 Stmt::SetField { object, field, value } => {
6564 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6565 let field_name = interner.resolve(*field);
6566 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());
6567
6568 let is_lww = lww_fields.iter().any(|(_, f)| f == field_name);
6571 let is_mv = mv_fields.iter().any(|(_, f)| f == field_name);
6572 if is_lww {
6573 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();
6575 } else if is_mv {
6576 writeln!(output, "{}{}.{}.set({});", indent_str, obj_str, field_name, value_str).unwrap();
6578 } else {
6579 writeln!(output, "{}{}.{} = {};", indent_str, obj_str, field_name, value_str).unwrap();
6580 }
6581 }
6582
6583 Stmt::StructDef { .. } => {
6584 }
6586
6587 Stmt::FunctionDef { .. } => {
6588 }
6590
6591 Stmt::Inspect { target, arms, .. } => {
6592 let target_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
6593
6594 let mut inner_boxed_binding_names: HashSet<String> = HashSet::new();
6598
6599 writeln!(output, "{}match {} {{", indent_str, target_str).unwrap();
6600
6601 for arm in arms {
6602 if let Some(variant) = arm.variant {
6603 let variant_name = interner.resolve(variant);
6604 let enum_name_str = arm.enum_name.map(|e| interner.resolve(e));
6606 let enum_prefix = enum_name_str
6607 .map(|e| format!("{}::", e))
6608 .unwrap_or_default();
6609
6610 if arm.bindings.is_empty() {
6611 writeln!(output, "{} {}{} => {{", indent_str, enum_prefix, variant_name).unwrap();
6613 } else {
6614 let bindings_str: Vec<String> = arm.bindings.iter()
6617 .map(|(field, binding)| {
6618 let field_name = interner.resolve(*field);
6619 let binding_name = interner.resolve(*binding);
6620
6621 if let Some(enum_name) = enum_name_str {
6623 let key = (enum_name.to_string(), variant_name.to_string(), field_name.to_string());
6624 if boxed_fields.contains(&key) {
6625 inner_boxed_binding_names.insert(binding_name.to_string());
6626 }
6627 }
6628
6629 if field_name == binding_name {
6630 field_name.to_string()
6631 } else {
6632 format!("{}: {}", field_name, binding_name)
6633 }
6634 })
6635 .collect();
6636 writeln!(output, "{} {}{} {{ {} }} => {{", indent_str, enum_prefix, variant_name, bindings_str.join(", ")).unwrap();
6637 }
6638 } else {
6639 writeln!(output, "{} _ => {{", indent_str).unwrap();
6641 }
6642
6643 ctx.push_scope();
6644
6645 for binding_name in &inner_boxed_binding_names {
6648 writeln!(output, "{} let {} = (*{}).clone();", indent_str, binding_name, binding_name).unwrap();
6649 }
6650
6651 for stmt in arm.body {
6652 let inner_stmt_code = if let Stmt::Inspect { target: inner_target, .. } = stmt {
6656 if let Expr::Identifier(sym) = inner_target {
6659 let target_name = interner.resolve(*sym);
6660 if inner_boxed_binding_names.contains(target_name) {
6661 let mut inner_output = String::new();
6663 writeln!(inner_output, "{}match {} {{", " ".repeat(indent + 2), target_name).unwrap();
6664
6665 if let Stmt::Inspect { arms: inner_arms, .. } = stmt {
6666 for inner_arm in inner_arms.iter() {
6667 if let Some(v) = inner_arm.variant {
6668 let v_name = interner.resolve(v);
6669 let inner_enum_prefix = inner_arm.enum_name
6670 .map(|e| format!("{}::", interner.resolve(e)))
6671 .unwrap_or_default();
6672
6673 if inner_arm.bindings.is_empty() {
6674 writeln!(inner_output, "{} {}{} => {{", " ".repeat(indent + 2), inner_enum_prefix, v_name).unwrap();
6675 } else {
6676 let bindings: Vec<String> = inner_arm.bindings.iter()
6677 .map(|(f, b)| {
6678 let fn_name = interner.resolve(*f);
6679 let bn_name = interner.resolve(*b);
6680 if fn_name == bn_name { fn_name.to_string() }
6681 else { format!("{}: {}", fn_name, bn_name) }
6682 })
6683 .collect();
6684 writeln!(inner_output, "{} {}{} {{ {} }} => {{", " ".repeat(indent + 2), inner_enum_prefix, v_name, bindings.join(", ")).unwrap();
6685 }
6686 } else {
6687 writeln!(inner_output, "{} _ => {{", " ".repeat(indent + 2)).unwrap();
6688 }
6689
6690 ctx.push_scope();
6691 for inner_stmt in inner_arm.body {
6692 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));
6693 }
6694 ctx.pop_scope();
6695 writeln!(inner_output, "{} }}", " ".repeat(indent + 2)).unwrap();
6696 }
6697 }
6698 writeln!(inner_output, "{}}}", " ".repeat(indent + 2)).unwrap();
6699 inner_output
6700 } else {
6701 codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
6702 }
6703 } else {
6704 codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
6705 }
6706 } else {
6707 codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
6708 };
6709 output.push_str(&inner_stmt_code);
6710 }
6711 ctx.pop_scope();
6712 writeln!(output, "{} }}", indent_str).unwrap();
6713 }
6714
6715 writeln!(output, "{}}}", indent_str).unwrap();
6716 }
6717
6718 Stmt::Push { value, collection } => {
6719 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());
6720 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6721 writeln!(output, "{}{}.push({});", indent_str, coll_str, val_str).unwrap();
6722 }
6723
6724 Stmt::Pop { collection, into } => {
6725 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6726 match into {
6727 Some(var) => {
6728 let var_name = interner.resolve(*var);
6729 writeln!(output, "{}let {} = {}.pop().expect(\"Pop from empty collection\");", indent_str, var_name, coll_str).unwrap();
6731 }
6732 None => {
6733 writeln!(output, "{}{}.pop();", indent_str, coll_str).unwrap();
6734 }
6735 }
6736 }
6737
6738 Stmt::Add { value, collection } => {
6739 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());
6740 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6741 writeln!(output, "{}{}.insert({});", indent_str, coll_str, val_str).unwrap();
6742 }
6743
6744 Stmt::Remove { value, collection } => {
6745 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());
6746 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6747 writeln!(output, "{}{}.remove(&{});", indent_str, coll_str, val_str).unwrap();
6748 }
6749
6750 Stmt::SetIndex { collection, index, value } => {
6751 let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6752 let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
6753 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());
6754
6755 let known_type = if let Expr::Identifier(sym) = collection {
6757 ctx.get_variable_types().get(sym).map(|s| s.as_str())
6758 } else {
6759 None
6760 };
6761
6762 match known_type {
6763 Some(t) if t.starts_with("Vec") => {
6764 if value_str.contains(&coll_str) {
6766 writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
6767 writeln!(output, "{}{}[({} - 1) as usize] = __set_tmp;", indent_str, coll_str, index_str).unwrap();
6768 } else {
6769 writeln!(output, "{}{}[({} - 1) as usize] = {};", indent_str, coll_str, index_str, value_str).unwrap();
6770 }
6771 }
6772 Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
6773 writeln!(output, "{}{}.insert({}, {});", indent_str, coll_str, index_str, value_str).unwrap();
6774 }
6775 _ => {
6776 if value_str.contains("logos_get") && value_str.contains(&coll_str) {
6778 writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
6779 writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, __set_tmp);", indent_str, coll_str, index_str).unwrap();
6780 } else {
6781 writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, {});", indent_str, coll_str, index_str, value_str).unwrap();
6782 }
6783 }
6784 }
6785 }
6786
6787 Stmt::Zone { name, capacity, source_file, body } => {
6789 let zone_name = interner.resolve(*name);
6790
6791 if let Some(path_sym) = source_file {
6793 let path = interner.resolve(*path_sym);
6795 writeln!(
6796 output,
6797 "{}let {} = logicaffeine_system::memory::Zone::new_mapped(\"{}\").expect(\"Failed to map file\");",
6798 indent_str, zone_name, path
6799 ).unwrap();
6800 } else {
6801 let cap = capacity.unwrap_or(4096); writeln!(
6804 output,
6805 "{}let {} = logicaffeine_system::memory::Zone::new_heap({});",
6806 indent_str, zone_name, cap
6807 ).unwrap();
6808 }
6809
6810 writeln!(output, "{}{{", indent_str).unwrap();
6812 ctx.push_scope();
6813
6814 for stmt in *body {
6816 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));
6817 }
6818
6819 ctx.pop_scope();
6820 writeln!(output, "{}}}", indent_str).unwrap();
6821 }
6822
6823 Stmt::Concurrent { tasks } => {
6827 let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
6829 if let Stmt::Let { var, .. } = s {
6830 Some(interner.resolve(*var).to_string())
6831 } else {
6832 None
6833 }
6834 }).collect();
6835
6836 let defined_vars: HashSet<Symbol> = tasks.iter().filter_map(|s| {
6838 if let Stmt::Let { var, .. } = s {
6839 Some(*var)
6840 } else {
6841 None
6842 }
6843 }).collect();
6844
6845 let mut has_intra_dependency = false;
6848 let mut seen_defs: HashSet<Symbol> = HashSet::new();
6849 for s in *tasks {
6850 let mut used_in_task: HashSet<Symbol> = HashSet::new();
6852 collect_stmt_identifiers(s, &mut used_in_task);
6853 for used_var in &used_in_task {
6854 if seen_defs.contains(used_var) {
6855 has_intra_dependency = true;
6856 break;
6857 }
6858 }
6859 if let Stmt::Let { var, .. } = s {
6861 seen_defs.insert(*var);
6862 }
6863 if has_intra_dependency {
6864 break;
6865 }
6866 }
6867
6868 let mut used_syms: HashSet<Symbol> = HashSet::new();
6871 for s in *tasks {
6872 collect_stmt_identifiers(s, &mut used_syms);
6873 }
6874 for def_var in &defined_vars {
6876 used_syms.remove(def_var);
6877 }
6878 let used_vars: HashSet<String> = used_syms.iter()
6879 .map(|sym| interner.resolve(*sym).to_string())
6880 .collect();
6881
6882 if has_intra_dependency {
6884 for stmt in *tasks {
6886 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));
6887 }
6888 } else {
6889 if !let_bindings.is_empty() {
6891 writeln!(output, "{}let ({}) = tokio::join!(", indent_str, let_bindings.join(", ")).unwrap();
6893 } else {
6894 writeln!(output, "{}tokio::join!(", indent_str).unwrap();
6895 }
6896
6897 for (i, stmt) in tasks.iter().enumerate() {
6898 let inner_code = match stmt {
6901 Stmt::Let { value, .. } => {
6902 codegen_expr_with_async(value, interner, synced_vars, async_functions, ctx.get_variable_types())
6905 }
6906 Stmt::Call { function, args } => {
6907 let func_name = interner.resolve(*function);
6908 let args_str: Vec<String> = args.iter()
6909 .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
6910 .collect();
6911 let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6913 format!("{}({}){}", func_name, args_str.join(", "), await_suffix)
6914 }
6915 _ => {
6916 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);
6918 inner.trim().to_string()
6919 }
6920 };
6921
6922 if !used_vars.is_empty() && i < tasks.len() - 1 {
6924 let clones: Vec<String> = used_vars.iter()
6926 .map(|v| format!("let {} = {}.clone();", v, v))
6927 .collect();
6928 write!(output, "{} {{ {} async move {{ {} }} }}",
6929 indent_str, clones.join(" "), inner_code).unwrap();
6930 } else {
6931 write!(output, "{} async {{ {} }}", indent_str, inner_code).unwrap();
6933 }
6934
6935 if i < tasks.len() - 1 {
6936 writeln!(output, ",").unwrap();
6937 } else {
6938 writeln!(output).unwrap();
6939 }
6940 }
6941
6942 writeln!(output, "{});", indent_str).unwrap();
6943 }
6944 }
6945
6946 Stmt::Parallel { tasks } => {
6949 let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
6951 if let Stmt::Let { var, .. } = s {
6952 Some(interner.resolve(*var).to_string())
6953 } else {
6954 None
6955 }
6956 }).collect();
6957
6958 if tasks.len() == 2 {
6959 if !let_bindings.is_empty() {
6961 writeln!(output, "{}let ({}) = rayon::join(", indent_str, let_bindings.join(", ")).unwrap();
6962 } else {
6963 writeln!(output, "{}rayon::join(", indent_str).unwrap();
6964 }
6965
6966 for (i, stmt) in tasks.iter().enumerate() {
6967 let inner_code = match stmt {
6969 Stmt::Let { value, .. } => {
6970 codegen_expr(value, interner, synced_vars)
6972 }
6973 Stmt::Call { function, args } => {
6974 let func_name = interner.resolve(*function);
6975 let args_str: Vec<String> = args.iter()
6976 .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
6977 .collect();
6978 format!("{}({})", func_name, args_str.join(", "))
6979 }
6980 _ => {
6981 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);
6983 inner.trim().to_string()
6984 }
6985 };
6986 write!(output, "{} || {{ {} }}", indent_str, inner_code).unwrap();
6987 if i == 0 {
6988 writeln!(output, ",").unwrap();
6989 } else {
6990 writeln!(output).unwrap();
6991 }
6992 }
6993 writeln!(output, "{});", indent_str).unwrap();
6994 } else {
6995 writeln!(output, "{}{{", indent_str).unwrap();
6997 writeln!(output, "{} let handles: Vec<_> = vec![", indent_str).unwrap();
6998 for stmt in *tasks {
6999 let inner_code = match stmt {
7001 Stmt::Let { value, .. } => {
7002 codegen_expr(value, interner, synced_vars)
7003 }
7004 Stmt::Call { function, args } => {
7005 let func_name = interner.resolve(*function);
7006 let args_str: Vec<String> = args.iter()
7007 .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7008 .collect();
7009 format!("{}({})", func_name, args_str.join(", "))
7010 }
7011 _ => {
7012 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);
7013 inner.trim().to_string()
7014 }
7015 };
7016 writeln!(output, "{} std::thread::spawn(move || {{ {} }}),",
7017 indent_str, inner_code).unwrap();
7018 }
7019 writeln!(output, "{} ];", indent_str).unwrap();
7020 writeln!(output, "{} for h in handles {{ h.join().unwrap(); }}", indent_str).unwrap();
7021 writeln!(output, "{}}}", indent_str).unwrap();
7022 }
7023 }
7024
7025 Stmt::ReadFrom { var, source } => {
7028 let var_name = interner.resolve(*var);
7029 match source {
7030 ReadSource::Console => {
7031 writeln!(output, "{}let {} = logicaffeine_system::io::read_line();", indent_str, var_name).unwrap();
7032 }
7033 ReadSource::File(path_expr) => {
7034 let path_str = codegen_expr_with_async(path_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
7035 writeln!(
7037 output,
7038 "{}let {} = vfs.read_to_string(&{}).await.expect(\"Failed to read file\");",
7039 indent_str, var_name, path_str
7040 ).unwrap();
7041 }
7042 }
7043 }
7044
7045 Stmt::WriteFile { content, path } => {
7048 let content_str = codegen_expr_with_async(content, interner, synced_vars, async_functions, ctx.get_variable_types());
7049 let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
7050 writeln!(
7052 output,
7053 "{}vfs.write(&{}, {}.as_bytes()).await.expect(\"Failed to write file\");",
7054 indent_str, path_str, content_str
7055 ).unwrap();
7056 }
7057
7058 Stmt::Spawn { agent_type, name } => {
7060 let type_name = interner.resolve(*agent_type);
7061 let agent_name = interner.resolve(*name);
7062 writeln!(
7064 output,
7065 "{}let {} = tokio::spawn(async move {{ /* {} agent loop */ }});",
7066 indent_str, agent_name, type_name
7067 ).unwrap();
7068 }
7069
7070 Stmt::SendMessage { message, destination } => {
7072 let msg_str = codegen_expr_with_async(message, interner, synced_vars, async_functions, ctx.get_variable_types());
7073 let dest_str = codegen_expr_with_async(destination, interner, synced_vars, async_functions, ctx.get_variable_types());
7074 writeln!(
7075 output,
7076 "{}{}.send({}).await.expect(\"Failed to send message\");",
7077 indent_str, dest_str, msg_str
7078 ).unwrap();
7079 }
7080
7081 Stmt::AwaitMessage { source, into } => {
7083 let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7084 let var_name = interner.resolve(*into);
7085 writeln!(
7086 output,
7087 "{}let {} = {}.recv().await.expect(\"Failed to receive message\");",
7088 indent_str, var_name, src_str
7089 ).unwrap();
7090 }
7091
7092 Stmt::MergeCrdt { source, target } => {
7094 let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7095 let tgt_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
7096 writeln!(
7097 output,
7098 "{}{}.merge(&{});",
7099 indent_str, tgt_str, src_str
7100 ).unwrap();
7101 }
7102
7103 Stmt::IncreaseCrdt { object, field, amount } => {
7106 let field_name = interner.resolve(*field);
7107 let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7108
7109 let root_sym = get_root_identifier(object);
7111 if let Some(sym) = root_sym {
7112 if synced_vars.contains(&sym) {
7113 let obj_name = interner.resolve(sym);
7115 writeln!(
7116 output,
7117 "{}{}.mutate(|inner| inner.{}.increment({} as u64)).await;",
7118 indent_str, obj_name, field_name, amount_str
7119 ).unwrap();
7120 return output;
7121 }
7122 }
7123
7124 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7126 writeln!(
7127 output,
7128 "{}{}.{}.increment({} as u64);",
7129 indent_str, obj_str, field_name, amount_str
7130 ).unwrap();
7131 }
7132
7133 Stmt::DecreaseCrdt { object, field, amount } => {
7135 let field_name = interner.resolve(*field);
7136 let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7137
7138 let root_sym = get_root_identifier(object);
7140 if let Some(sym) = root_sym {
7141 if synced_vars.contains(&sym) {
7142 let obj_name = interner.resolve(sym);
7144 writeln!(
7145 output,
7146 "{}{}.mutate(|inner| inner.{}.decrement({} as u64)).await;",
7147 indent_str, obj_name, field_name, amount_str
7148 ).unwrap();
7149 return output;
7150 }
7151 }
7152
7153 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7155 writeln!(
7156 output,
7157 "{}{}.{}.decrement({} as u64);",
7158 indent_str, obj_str, field_name, amount_str
7159 ).unwrap();
7160 }
7161
7162 Stmt::AppendToSequence { sequence, value } => {
7164 let seq_str = codegen_expr_with_async(sequence, interner, synced_vars, async_functions, ctx.get_variable_types());
7165 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());
7166 writeln!(
7167 output,
7168 "{}{}.append({});",
7169 indent_str, seq_str, val_str
7170 ).unwrap();
7171 }
7172
7173 Stmt::ResolveConflict { object, field, value } => {
7175 let field_name = interner.resolve(*field);
7176 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());
7177 let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7178 writeln!(
7179 output,
7180 "{}{}.{}.resolve({});",
7181 indent_str, obj_str, field_name, val_str
7182 ).unwrap();
7183 }
7184
7185 Stmt::Escape { code, .. } => {
7187 let raw_code = interner.resolve(*code);
7188 write!(output, "{}{{\n", indent_str).unwrap();
7189 for line in raw_code.lines() {
7190 write!(output, "{} {}\n", indent_str, line).unwrap();
7191 }
7192 write!(output, "{}}}\n", indent_str).unwrap();
7193 }
7194
7195 Stmt::Require { .. } => {}
7197
7198 Stmt::Theorem(_) => {
7200 }
7203 }
7204
7205 output
7206}
7207
7208fn get_root_identifier(expr: &Expr) -> Option<Symbol> {
7211 match expr {
7212 Expr::Identifier(sym) => Some(*sym),
7213 Expr::FieldAccess { object, .. } => get_root_identifier(object),
7214 _ => None,
7215 }
7216}
7217
7218fn is_copy_type(ty: &str) -> bool {
7220 matches!(ty, "i64" | "u64" | "f64" | "i32" | "u32" | "f32" | "bool" | "char" | "u8" | "i8" | "()")
7221}
7222
7223fn has_copy_element_type(vec_type: &str) -> bool {
7225 if let Some(inner) = vec_type.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
7226 is_copy_type(inner)
7227 } else {
7228 false
7229 }
7230}
7231
7232fn has_copy_value_type(map_type: &str) -> bool {
7234 let inner = map_type.strip_prefix("std::collections::HashMap<")
7235 .or_else(|| map_type.strip_prefix("HashMap<"));
7236 if let Some(inner) = inner.and_then(|s| s.strip_suffix('>')) {
7237 if let Some((_key, value)) = inner.split_once(", ") {
7239 return is_copy_type(value);
7240 }
7241 }
7242 false
7243}
7244
7245pub fn codegen_expr(expr: &Expr, interner: &Interner, synced_vars: &HashSet<Symbol>) -> String {
7246 let empty_registry = TypeRegistry::new();
7248 let empty_async = HashSet::new();
7249 codegen_expr_boxed(expr, interner, synced_vars, &HashSet::new(), &empty_registry, &empty_async)
7250}
7251
7252pub fn codegen_expr_with_async(
7255 expr: &Expr,
7256 interner: &Interner,
7257 synced_vars: &HashSet<Symbol>,
7258 async_functions: &HashSet<Symbol>,
7259 variable_types: &HashMap<Symbol, String>,
7260) -> String {
7261 let empty_registry = TypeRegistry::new();
7262 let empty_strings = HashSet::new();
7263 codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), &empty_strings, variable_types)
7264}
7265
7266fn codegen_expr_with_async_and_strings(
7268 expr: &Expr,
7269 interner: &Interner,
7270 synced_vars: &HashSet<Symbol>,
7271 async_functions: &HashSet<Symbol>,
7272 string_vars: &HashSet<Symbol>,
7273 variable_types: &HashMap<Symbol, String>,
7274) -> String {
7275 let empty_registry = TypeRegistry::new();
7276 codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), string_vars, variable_types)
7277}
7278
7279fn is_definitely_numeric_expr(expr: &Expr) -> bool {
7283 match expr {
7284 Expr::Literal(Literal::Number(_)) => true,
7285 Expr::Literal(Literal::Float(_)) => true,
7286 Expr::Literal(Literal::Duration(_)) => true,
7287 Expr::Identifier(_) => true,
7290 Expr::BinaryOp { op: BinaryOpKind::Subtract, .. } => true,
7292 Expr::BinaryOp { op: BinaryOpKind::Multiply, .. } => true,
7293 Expr::BinaryOp { op: BinaryOpKind::Divide, .. } => true,
7294 Expr::BinaryOp { op: BinaryOpKind::Modulo, .. } => true,
7295 Expr::Length { .. } => true,
7297 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7299 is_definitely_numeric_expr(left) && is_definitely_numeric_expr(right)
7300 }
7301 Expr::Call { .. } => true,
7303 Expr::Index { .. } => true,
7305 _ => true,
7306 }
7307}
7308
7309fn is_definitely_string_expr_with_vars(expr: &Expr, string_vars: &HashSet<Symbol>) -> bool {
7312 match expr {
7313 Expr::Literal(Literal::Text(_)) => true,
7315 Expr::Identifier(sym) => string_vars.contains(sym),
7317 Expr::BinaryOp { op: BinaryOpKind::Concat, .. } => true,
7319 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7321 is_definitely_string_expr_with_vars(left, string_vars)
7322 || is_definitely_string_expr_with_vars(right, string_vars)
7323 }
7324 Expr::WithCapacity { value, .. } => is_definitely_string_expr_with_vars(value, string_vars),
7326 _ => false,
7327 }
7328}
7329
7330fn is_definitely_string_expr(expr: &Expr) -> bool {
7333 let empty = HashSet::new();
7334 is_definitely_string_expr_with_vars(expr, &empty)
7335}
7336
7337fn collect_string_concat_operands<'a, 'b>(
7344 expr: &'b Expr<'a>,
7345 string_vars: &HashSet<Symbol>,
7346 operands: &mut Vec<&'b Expr<'a>>,
7347) {
7348 match expr {
7349 Expr::BinaryOp { op: BinaryOpKind::Concat, left, right } => {
7350 collect_string_concat_operands(left, string_vars, operands);
7351 collect_string_concat_operands(right, string_vars, operands);
7352 }
7353 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7354 let has_string = is_definitely_string_expr_with_vars(left, string_vars)
7355 || is_definitely_string_expr_with_vars(right, string_vars);
7356 if has_string {
7357 collect_string_concat_operands(left, string_vars, operands);
7358 collect_string_concat_operands(right, string_vars, operands);
7359 } else {
7360 operands.push(expr);
7361 }
7362 }
7363 _ => {
7364 operands.push(expr);
7365 }
7366 }
7367}
7368
7369fn codegen_expr_boxed(
7373 expr: &Expr,
7374 interner: &Interner,
7375 synced_vars: &HashSet<Symbol>,
7376 boxed_fields: &HashSet<(String, String, String)>, registry: &TypeRegistry, async_functions: &HashSet<Symbol>, ) -> String {
7380 let empty_boxed = HashSet::new();
7382 let empty_strings = HashSet::new();
7383 let empty_types = HashMap::new();
7384 codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, &empty_strings, &empty_types)
7385}
7386
7387fn codegen_expr_boxed_with_strings(
7389 expr: &Expr,
7390 interner: &Interner,
7391 synced_vars: &HashSet<Symbol>,
7392 boxed_fields: &HashSet<(String, String, String)>,
7393 registry: &TypeRegistry,
7394 async_functions: &HashSet<Symbol>,
7395 string_vars: &HashSet<Symbol>,
7396) -> String {
7397 let empty_boxed = HashSet::new();
7398 let empty_types = HashMap::new();
7399 codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, &empty_types)
7400}
7401
7402fn codegen_expr_boxed_with_types(
7404 expr: &Expr,
7405 interner: &Interner,
7406 synced_vars: &HashSet<Symbol>,
7407 boxed_fields: &HashSet<(String, String, String)>,
7408 registry: &TypeRegistry,
7409 async_functions: &HashSet<Symbol>,
7410 string_vars: &HashSet<Symbol>,
7411 variable_types: &HashMap<Symbol, String>,
7412) -> String {
7413 let empty_boxed = HashSet::new();
7414 codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, variable_types)
7415}
7416
7417fn codegen_expr_boxed_internal(
7419 expr: &Expr,
7420 interner: &Interner,
7421 synced_vars: &HashSet<Symbol>,
7422 boxed_fields: &HashSet<(String, String, String)>,
7423 registry: &TypeRegistry,
7424 async_functions: &HashSet<Symbol>,
7425 boxed_bindings: &HashSet<Symbol>,
7426 string_vars: &HashSet<Symbol>,
7427 variable_types: &HashMap<Symbol, String>,
7428) -> String {
7429 macro_rules! recurse {
7431 ($e:expr) => {
7432 codegen_expr_boxed_internal($e, interner, synced_vars, boxed_fields, registry, async_functions, boxed_bindings, string_vars, variable_types)
7433 };
7434 }
7435
7436 match expr {
7437 Expr::Literal(lit) => codegen_literal(lit, interner),
7438
7439 Expr::Identifier(sym) => {
7440 let name = interner.resolve(*sym).to_string();
7441 if boxed_bindings.contains(sym) {
7443 format!("(*{})", name)
7444 } else {
7445 name
7446 }
7447 }
7448
7449 Expr::BinaryOp { op, left, right } => {
7450 let is_string_concat = matches!(op, BinaryOpKind::Concat)
7453 || (matches!(op, BinaryOpKind::Add)
7454 && (is_definitely_string_expr_with_vars(left, string_vars)
7455 || is_definitely_string_expr_with_vars(right, string_vars)));
7456
7457 if is_string_concat {
7458 let mut operands = Vec::new();
7459 collect_string_concat_operands(expr, string_vars, &mut operands);
7460 let placeholders: String = operands.iter().map(|_| "{}").collect::<Vec<_>>().join("");
7461 let values: Vec<String> = operands.iter().map(|e| {
7462 if let Expr::Literal(Literal::Text(sym)) = e {
7464 format!("\"{}\"", interner.resolve(*sym))
7465 } else {
7466 recurse!(e)
7467 }
7468 }).collect();
7469 return format!("format!(\"{}\", {})", placeholders, values.join(", "));
7470 }
7471
7472 if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq) {
7474 let neg = matches!(op, BinaryOpKind::NotEq);
7475 if let Expr::Index { collection, index } = left {
7477 if let Expr::Identifier(sym) = collection {
7478 if let Some(t) = variable_types.get(sym) {
7479 if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7480 let coll_str = recurse!(collection);
7481 let key_str = recurse!(index);
7482 let val_str = recurse!(right);
7483 let cmp = if neg { "!=" } else { "==" };
7484 if has_copy_value_type(t) {
7485 return format!("({}.get(&({})).copied() {} Some({}))", coll_str, key_str, cmp, val_str);
7486 } else {
7487 return format!("({}.get(&({})) {} Some(&({})))", coll_str, key_str, cmp, val_str);
7488 }
7489 }
7490 }
7491 }
7492 }
7493 if let Expr::Index { collection, index } = right {
7495 if let Expr::Identifier(sym) = collection {
7496 if let Some(t) = variable_types.get(sym) {
7497 if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7498 let coll_str = recurse!(collection);
7499 let key_str = recurse!(index);
7500 let val_str = recurse!(left);
7501 let cmp = if neg { "!=" } else { "==" };
7502 if has_copy_value_type(t) {
7503 return format!("(Some({}) {} {}.get(&({})).copied())", val_str, cmp, coll_str, key_str);
7504 } else {
7505 return format!("(Some(&({})) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
7506 }
7507 }
7508 }
7509 }
7510 }
7511 }
7512
7513 let left_str = recurse!(left);
7514 let right_str = recurse!(right);
7515 let op_str = match op {
7516 BinaryOpKind::Add => "+",
7517 BinaryOpKind::Subtract => "-",
7518 BinaryOpKind::Multiply => "*",
7519 BinaryOpKind::Divide => "/",
7520 BinaryOpKind::Modulo => "%",
7521 BinaryOpKind::Eq => "==",
7522 BinaryOpKind::NotEq => "!=",
7523 BinaryOpKind::Lt => "<",
7524 BinaryOpKind::Gt => ">",
7525 BinaryOpKind::LtEq => "<=",
7526 BinaryOpKind::GtEq => ">=",
7527 BinaryOpKind::And => "&&",
7528 BinaryOpKind::Or => "||",
7529 BinaryOpKind::Concat => unreachable!(), };
7531 format!("({} {} {})", left_str, op_str, right_str)
7532 }
7533
7534 Expr::Call { function, args } => {
7535 let func_name = escape_rust_ident(interner.resolve(*function));
7536 let args_str: Vec<String> = args.iter()
7538 .map(|a| recurse!(a))
7539 .collect();
7540 if async_functions.contains(function) {
7542 format!("{}({}).await", func_name, args_str.join(", "))
7543 } else {
7544 format!("{}({})", func_name, args_str.join(", "))
7545 }
7546 }
7547
7548 Expr::Index { collection, index } => {
7549 let coll_str = recurse!(collection);
7550 let index_str = recurse!(index);
7551 let known_type = if let Expr::Identifier(sym) = collection {
7553 variable_types.get(sym).map(|s| s.as_str())
7554 } else {
7555 None
7556 };
7557 match known_type {
7558 Some(t) if t.starts_with("Vec") => {
7559 let suffix = if has_copy_element_type(t) { "" } else { ".clone()" };
7560 format!("{}[({} - 1) as usize]{}", coll_str, index_str, suffix)
7561 }
7562 Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7563 let suffix = if has_copy_value_type(t) { "" } else { ".clone()" };
7564 format!("{}[&({})]{}", coll_str, index_str, suffix)
7565 }
7566 _ => {
7567 format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
7569 }
7570 }
7571 }
7572
7573 Expr::Slice { collection, start, end } => {
7574 let coll_str = recurse!(collection);
7575 let start_str = recurse!(start);
7576 let end_str = recurse!(end);
7577 format!("&{}[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
7580 }
7581
7582 Expr::Copy { expr: inner } => {
7583 let expr_str = recurse!(inner);
7584 format!("{}.to_owned()", expr_str)
7588 }
7589
7590 Expr::Give { value } => {
7591 recurse!(value)
7594 }
7595
7596 Expr::Length { collection } => {
7597 let coll_str = recurse!(collection);
7598 format!("({}.len() as i64)", coll_str)
7600 }
7601
7602 Expr::Contains { collection, value } => {
7603 let coll_str = recurse!(collection);
7604 let val_str = recurse!(value);
7605 format!("{}.logos_contains(&{})", coll_str, val_str)
7607 }
7608
7609 Expr::Union { left, right } => {
7610 let left_str = recurse!(left);
7611 let right_str = recurse!(right);
7612 format!("{}.union(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7613 }
7614
7615 Expr::Intersection { left, right } => {
7616 let left_str = recurse!(left);
7617 let right_str = recurse!(right);
7618 format!("{}.intersection(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7619 }
7620
7621 Expr::ManifestOf { zone } => {
7623 let zone_str = recurse!(zone);
7624 format!("logicaffeine_system::network::FileSipper::from_zone(&{}).manifest()", zone_str)
7625 }
7626
7627 Expr::ChunkAt { index, zone } => {
7628 let zone_str = recurse!(zone);
7629 let index_str = recurse!(index);
7630 format!("logicaffeine_system::network::FileSipper::from_zone(&{}).get_chunk(({} - 1) as usize)", zone_str, index_str)
7632 }
7633
7634 Expr::List(ref items) => {
7635 let item_strs: Vec<String> = items.iter()
7636 .map(|i| recurse!(i))
7637 .collect();
7638 format!("vec![{}]", item_strs.join(", "))
7639 }
7640
7641 Expr::Tuple(ref items) => {
7642 let item_strs: Vec<String> = items.iter()
7643 .map(|i| format!("Value::from({})", recurse!(i)))
7644 .collect();
7645 format!("vec![{}]", item_strs.join(", "))
7647 }
7648
7649 Expr::Range { start, end } => {
7650 let start_str = recurse!(start);
7651 let end_str = recurse!(end);
7652 format!("({}..={})", start_str, end_str)
7653 }
7654
7655 Expr::FieldAccess { object, field } => {
7656 let field_name = interner.resolve(*field);
7657
7658 let root_sym = get_root_identifier(object);
7660 if let Some(sym) = root_sym {
7661 if synced_vars.contains(&sym) {
7662 let obj_name = interner.resolve(sym);
7663 return format!("{}.get().await.{}", obj_name, field_name);
7664 }
7665 }
7666
7667 let obj_str = recurse!(object);
7668 format!("{}.{}", obj_str, field_name)
7669 }
7670
7671 Expr::New { type_name, type_args, init_fields } => {
7672 let type_str = interner.resolve(*type_name);
7673 if !init_fields.is_empty() {
7674 let fields_str = init_fields.iter()
7677 .map(|(name, value)| {
7678 let field_name = interner.resolve(*name);
7679 let value_str = recurse!(value);
7680 format!("{}: {}", field_name, value_str)
7681 })
7682 .collect::<Vec<_>>()
7683 .join(", ");
7684 format!("{} {{ {}, ..Default::default() }}", type_str, fields_str)
7685 } else if type_args.is_empty() {
7686 format!("{}::default()", type_str)
7687 } else {
7688 let args_str = type_args.iter()
7691 .map(|t| codegen_type_expr(t, interner))
7692 .collect::<Vec<_>>()
7693 .join(", ");
7694 format!("{}::<{}>::default()", type_str, args_str)
7695 }
7696 }
7697
7698 Expr::NewVariant { enum_name, variant, fields } => {
7699 let enum_str = interner.resolve(*enum_name);
7700 let variant_str = interner.resolve(*variant);
7701 if fields.is_empty() {
7702 format!("{}::{}", enum_str, variant_str)
7704 } else {
7705 let mut identifier_counts: HashMap<Symbol, usize> = HashMap::new();
7708 for (_, value) in fields.iter() {
7709 if let Expr::Identifier(sym) = value {
7710 *identifier_counts.entry(*sym).or_insert(0) += 1;
7711 }
7712 }
7713
7714 let mut remaining_uses: HashMap<Symbol, usize> = identifier_counts.clone();
7716
7717 let fields_str: Vec<String> = fields.iter()
7720 .map(|(field_name, value)| {
7721 let name = interner.resolve(*field_name);
7722
7723 let val = if let Expr::Identifier(sym) = value {
7726 let total = identifier_counts.get(sym).copied().unwrap_or(0);
7727 let remaining = remaining_uses.get_mut(sym);
7728 let base_name = if boxed_bindings.contains(sym) {
7729 format!("(*{})", interner.resolve(*sym))
7730 } else {
7731 interner.resolve(*sym).to_string()
7732 };
7733 if total > 1 {
7734 if let Some(r) = remaining {
7735 *r -= 1;
7736 if *r > 0 {
7737 format!("{}.clone()", base_name)
7739 } else {
7740 base_name
7742 }
7743 } else {
7744 base_name
7745 }
7746 } else {
7747 base_name
7748 }
7749 } else {
7750 recurse!(value)
7751 };
7752
7753 let key = (enum_str.to_string(), variant_str.to_string(), name.to_string());
7755 if boxed_fields.contains(&key) {
7756 format!("{}: Box::new({})", name, val)
7757 } else {
7758 format!("{}: {}", name, val)
7759 }
7760 })
7761 .collect();
7762 format!("{}::{} {{ {} }}", enum_str, variant_str, fields_str.join(", "))
7763 }
7764 }
7765
7766 Expr::OptionSome { value } => {
7767 format!("Some({})", recurse!(value))
7768 }
7769
7770 Expr::OptionNone => {
7771 "None".to_string()
7772 }
7773
7774 Expr::Escape { code, .. } => {
7775 let raw_code = interner.resolve(*code);
7776 let mut block = String::from("{\n");
7777 for line in raw_code.lines() {
7778 block.push_str(" ");
7779 block.push_str(line);
7780 block.push('\n');
7781 }
7782 block.push('}');
7783 block
7784 }
7785
7786 Expr::WithCapacity { value, capacity } => {
7787 let cap_str = recurse!(capacity);
7788 match value {
7789 Expr::Literal(Literal::Text(sym)) if interner.resolve(*sym).is_empty() => {
7791 format!("String::with_capacity(({}) as usize)", cap_str)
7792 }
7793 Expr::Literal(Literal::Text(sym)) => {
7795 let text = interner.resolve(*sym);
7796 format!("{{ let mut __s = String::with_capacity(({}) as usize); __s.push_str(\"{}\"); __s }}", cap_str, text)
7797 }
7798 Expr::New { type_name, type_args, .. } => {
7800 let type_str = interner.resolve(*type_name);
7801 match type_str {
7802 "Seq" | "List" | "Vec" => {
7803 let elem = if !type_args.is_empty() {
7804 codegen_type_expr(&type_args[0], interner)
7805 } else { "()".to_string() };
7806 format!("{{ let __v: Vec<{}> = Vec::with_capacity(({}) as usize); __v }}", elem, cap_str)
7807 }
7808 "Map" | "HashMap" => {
7809 let (k, v) = if type_args.len() >= 2 {
7810 (codegen_type_expr(&type_args[0], interner),
7811 codegen_type_expr(&type_args[1], interner))
7812 } else { ("String".to_string(), "String".to_string()) };
7813 format!("{{ let __m: std::collections::HashMap<{}, {}> = std::collections::HashMap::with_capacity(({}) as usize); __m }}", k, v, cap_str)
7814 }
7815 "Set" | "HashSet" => {
7816 let elem = if !type_args.is_empty() {
7817 codegen_type_expr(&type_args[0], interner)
7818 } else { "()".to_string() };
7819 format!("{{ let __s: std::collections::HashSet<{}> = std::collections::HashSet::with_capacity(({}) as usize); __s }}", elem, cap_str)
7820 }
7821 _ => recurse!(value) }
7823 }
7824 _ => recurse!(value)
7826 }
7827 }
7828
7829 Expr::Closure { params, body, .. } => {
7830 use crate::ast::stmt::ClosureBody;
7831 let params_str: Vec<String> = params.iter()
7832 .map(|(name, ty)| {
7833 let param_name = escape_rust_ident(interner.resolve(*name));
7834 let param_type = codegen_type_expr(ty, interner);
7835 format!("{}: {}", param_name, param_type)
7836 })
7837 .collect();
7838
7839 match body {
7840 ClosureBody::Expression(expr) => {
7841 let body_str = recurse!(expr);
7842 format!("move |{}| {{ {} }}", params_str.join(", "), body_str)
7843 }
7844 ClosureBody::Block(stmts) => {
7845 let mut body_str = String::new();
7846 let mut ctx = RefinementContext::new();
7847 let empty_mutable = collect_mutable_vars(stmts);
7848 let empty_lww = HashSet::new();
7849 let empty_mv = HashSet::new();
7850 let mut empty_synced = HashSet::new();
7851 let empty_caps = HashMap::new();
7852 let empty_pipes = HashSet::new();
7853 let empty_boxed = HashSet::new();
7854 let empty_registry = TypeRegistry::new();
7855 for stmt in stmts.iter() {
7856 body_str.push_str(&codegen_stmt(
7857 stmt, interner, 2, &empty_mutable, &mut ctx,
7858 &empty_lww, &empty_mv, &mut empty_synced, &empty_caps,
7859 async_functions, &empty_pipes, &empty_boxed, &empty_registry,
7860 ));
7861 }
7862 format!("move |{}| {{\n{}{}}}", params_str.join(", "), body_str, " ")
7863 }
7864 }
7865 }
7866
7867 Expr::CallExpr { callee, args } => {
7868 let callee_str = recurse!(callee);
7869 let args_str: Vec<String> = args.iter().map(|a| recurse!(a)).collect();
7870 format!("({})({})", callee_str, args_str.join(", "))
7871 }
7872 }
7873}
7874
7875fn codegen_literal(lit: &Literal, interner: &Interner) -> String {
7876 match lit {
7877 Literal::Number(n) => n.to_string(),
7878 Literal::Float(f) => format!("{}f64", f),
7879 Literal::Text(sym) => format!("String::from(\"{}\")", interner.resolve(*sym)),
7881 Literal::Boolean(b) => b.to_string(),
7882 Literal::Nothing => "()".to_string(),
7883 Literal::Char(c) => {
7885 match c {
7887 '\n' => "'\\n'".to_string(),
7888 '\t' => "'\\t'".to_string(),
7889 '\r' => "'\\r'".to_string(),
7890 '\\' => "'\\\\'".to_string(),
7891 '\'' => "'\\''".to_string(),
7892 '\0' => "'\\0'".to_string(),
7893 c => format!("'{}'", c),
7894 }
7895 }
7896 Literal::Duration(nanos) => format!("std::time::Duration::from_nanos({}u64)", nanos),
7898 Literal::Date(days) => format!("LogosDate({})", days),
7900 Literal::Moment(nanos) => format!("LogosMoment({})", nanos),
7902 Literal::Span { months, days } => format!("LogosSpan::new({}, {})", months, days),
7904 Literal::Time(nanos) => format!("LogosTime({})", nanos),
7906 }
7907}
7908
7909pub fn codegen_assertion(expr: &LogicExpr, interner: &Interner) -> String {
7912 let mut registry = SymbolRegistry::new();
7913 let formatter = RustFormatter;
7914 let mut buf = String::new();
7915
7916 match expr.write_logic(&mut buf, &mut registry, interner, &formatter) {
7917 Ok(_) => buf,
7918 Err(_) => "/* error generating assertion */ false".to_string(),
7919 }
7920}
7921
7922pub fn codegen_term(term: &Term, interner: &Interner) -> String {
7923 match term {
7924 Term::Constant(sym) => interner.resolve(*sym).to_string(),
7925 Term::Variable(sym) => interner.resolve(*sym).to_string(),
7926 Term::Value { kind, .. } => match kind {
7927 NumberKind::Integer(n) => n.to_string(),
7928 NumberKind::Real(f) => f.to_string(),
7929 NumberKind::Symbolic(sym) => interner.resolve(*sym).to_string(),
7930 },
7931 Term::Function(name, args) => {
7932 let args_str: Vec<String> = args.iter()
7933 .map(|a| codegen_term(a, interner))
7934 .collect();
7935 format!("{}({})", interner.resolve(*name), args_str.join(", "))
7936 }
7937 Term::Possessed { possessor, possessed } => {
7938 let poss_str = codegen_term(possessor, interner);
7939 format!("{}.{}", poss_str, interner.resolve(*possessed))
7940 }
7941 Term::Group(members) => {
7942 let members_str: Vec<String> = members.iter()
7943 .map(|m| codegen_term(m, interner))
7944 .collect();
7945 format!("({})", members_str.join(", "))
7946 }
7947 _ => "/* unsupported Term */".to_string(),
7948 }
7949}
7950
7951#[cfg(test)]
7952mod tests {
7953 use super::*;
7954
7955 #[test]
7956 fn test_literal_number() {
7957 let interner = Interner::new();
7958 let synced_vars = HashSet::new();
7959 let expr = Expr::Literal(Literal::Number(42));
7960 assert_eq!(codegen_expr(&expr, &interner, &synced_vars), "42");
7961 }
7962
7963 #[test]
7964 fn test_literal_boolean() {
7965 let interner = Interner::new();
7966 let synced_vars = HashSet::new();
7967 assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(true)), &interner, &synced_vars), "true");
7968 assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(false)), &interner, &synced_vars), "false");
7969 }
7970
7971 #[test]
7972 fn test_literal_nothing() {
7973 let interner = Interner::new();
7974 let synced_vars = HashSet::new();
7975 assert_eq!(codegen_expr(&Expr::Literal(Literal::Nothing), &interner, &synced_vars), "()");
7976 }
7977}