1use std::collections::{HashMap, HashSet};
2use std::fmt::Write;
3
4use crate::analysis::registry::{FieldDef, FieldType, TypeDef, TypeRegistry, VariantDef};
5use crate::ast::stmt::{Expr, Stmt, TypeExpr};
6use crate::intern::{Interner, Symbol};
7
8use super::detection::is_result_type;
9use super::marshal::is_text_type;
10use super::types::codegen_type_expr;
11
12pub(crate) fn has_wasm_exports(stmts: &[Stmt], interner: &Interner) -> bool {
15 stmts.iter().any(|stmt| {
16 if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
17 interner.resolve(*target).eq_ignore_ascii_case("wasm")
18 } else {
19 false
20 }
21 })
22}
23
24pub(crate) fn has_c_exports(stmts: &[Stmt], interner: &Interner) -> bool {
27 stmts.iter().any(|stmt| {
28 if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
29 match export_target {
30 None => true,
31 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
32 }
33 } else {
34 false
35 }
36 })
37}
38
39pub(crate) fn has_c_exports_with_text(stmts: &[Stmt], interner: &Interner) -> bool {
42 stmts.iter().any(|stmt| {
43 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
44 let is_c = match export_target {
45 None => true,
46 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
47 };
48 if !is_c { return false; }
49 let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
50 let has_text_return = return_type.as_ref().map_or(false, |ty| is_text_type(ty, interner));
51 has_text_param || has_text_return
52 } else {
53 false
54 }
55 })
56}
57
58#[derive(Debug, Clone, Copy, PartialEq)]
67pub enum CAbiClass {
68 ValueType,
70 ReferenceType,
72}
73
74pub(crate) fn classify_type_for_c_abi(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> CAbiClass {
79 match ty {
80 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
81 let name = interner.resolve(*sym);
82 match name {
83 "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
84 | "Byte" | "Char" | "Unit" => CAbiClass::ValueType,
85 "Text" | "String" => CAbiClass::ValueType,
86 _ => {
87 if let Some(type_def) = registry.get(*sym) {
89 match type_def {
90 TypeDef::Struct { fields, .. } => {
91 let all_value = fields.iter().all(|f| {
93 is_value_type_field(&f.ty, interner)
94 });
95 if all_value && fields.len() <= 4 {
96 CAbiClass::ValueType
97 } else {
98 CAbiClass::ReferenceType
99 }
100 }
101 TypeDef::Enum { .. } => CAbiClass::ReferenceType,
102 TypeDef::Primitive => CAbiClass::ValueType,
103 TypeDef::Generic { .. } => CAbiClass::ReferenceType,
104 TypeDef::Alias { .. } => CAbiClass::ValueType,
105 }
106 } else {
107 CAbiClass::ValueType }
109 }
110 }
111 }
112 TypeExpr::Refinement { base, .. } => classify_type_for_c_abi(base, interner, registry),
113 TypeExpr::Generic { base, .. } => {
114 let base_name = interner.resolve(*base);
115 match base_name {
116 "Option" | "Maybe" => {
117 CAbiClass::ReferenceType
121 }
122 "Result" | "Seq" | "List" | "Vec" | "Map" | "HashMap"
123 | "Set" | "HashSet" => CAbiClass::ReferenceType,
124 _ => CAbiClass::ReferenceType,
125 }
126 }
127 TypeExpr::Function { .. } => CAbiClass::ReferenceType,
128 TypeExpr::Persistent { .. } => CAbiClass::ReferenceType,
129 TypeExpr::Mutable { inner } => classify_type_for_c_abi(inner, interner, registry),
130 }
131}
132
133fn is_value_type_field(ft: &FieldType, interner: &Interner) -> bool {
135 match ft {
136 FieldType::Primitive(sym) | FieldType::Named(sym) => {
137 let name = interner.resolve(*sym);
138 matches!(name, "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
139 | "Byte" | "Char" | "Unit")
140 }
142 FieldType::Generic { .. } => false, FieldType::TypeParam(_) => false,
144 }
145}
146
147pub(crate) fn mangle_type_for_c(ty: &TypeExpr, interner: &Interner) -> String {
148 match ty {
149 TypeExpr::Mutable { inner } => mangle_type_for_c(inner, interner),
150 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
151 let name = interner.resolve(*sym);
152 match name {
153 "Int" => "i64".to_string(),
154 "Nat" => "u64".to_string(),
155 "Real" | "Float" => "f64".to_string(),
156 "Bool" | "Boolean" => "bool".to_string(),
157 "Byte" => "u8".to_string(),
158 "Char" => "char".to_string(),
159 "Text" | "String" => "string".to_string(),
160 other => other.to_lowercase(),
161 }
162 }
163 TypeExpr::Refinement { base, .. } => mangle_type_for_c(base, interner),
164 TypeExpr::Generic { base, params } => {
165 let base_name = interner.resolve(*base);
166 let param_strs: Vec<String> = params.iter()
167 .map(|p| mangle_type_for_c(p, interner))
168 .collect();
169 match base_name {
170 "Seq" | "List" | "Vec" => format!("seq_{}", param_strs.join("_")),
171 "Map" | "HashMap" => format!("map_{}", param_strs.join("_")),
172 "Set" | "HashSet" => format!("set_{}", param_strs.join("_")),
173 "Option" | "Maybe" => format!("option_{}", param_strs.join("_")),
174 "Result" => format!("result_{}", param_strs.join("_")),
175 other => format!("{}_{}", other.to_lowercase(), param_strs.join("_")),
176 }
177 }
178 TypeExpr::Function { .. } => "fn".to_string(),
179 TypeExpr::Persistent { inner } => mangle_type_for_c(inner, interner),
180 }
181}
182
183pub(crate) fn codegen_logos_runtime_preamble() -> String {
191 let mut out = String::new();
192
193 writeln!(out, "// ═══ LogicAffeine Universal ABI Runtime ═══\n").unwrap();
194
195 writeln!(out, "#[repr(C)]").unwrap();
197 writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq)]").unwrap();
198 writeln!(out, "pub enum LogosStatus {{").unwrap();
199 writeln!(out, " Ok = 0,").unwrap();
200 writeln!(out, " Error = 1,").unwrap();
201 writeln!(out, " RefinementViolation = 2,").unwrap();
202 writeln!(out, " NullPointer = 3,").unwrap();
203 writeln!(out, " OutOfBounds = 4,").unwrap();
204 writeln!(out, " DeserializationFailed = 5,").unwrap();
205 writeln!(out, " InvalidHandle = 6,").unwrap();
206 writeln!(out, " ContainsNullByte = 7,").unwrap();
207 writeln!(out, " ThreadPanic = 8,").unwrap();
208 writeln!(out, " MemoryExhausted = 9,").unwrap();
209 writeln!(out, "}}\n").unwrap();
210
211 writeln!(out, "pub type LogosHandle = *mut std::ffi::c_void;\n").unwrap();
213
214 writeln!(out, "fn logos_error_store() -> &'static std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>> {{").unwrap();
216 writeln!(out, " use std::sync::OnceLock;").unwrap();
217 writeln!(out, " static STORE: OnceLock<std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>>> = OnceLock::new();").unwrap();
218 writeln!(out, " STORE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))").unwrap();
219 writeln!(out, "}}\n").unwrap();
220
221 writeln!(out, "fn logos_set_last_error(msg: String) {{").unwrap();
223 writeln!(out, " let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
224 writeln!(out, " store.insert(std::thread::current().id(), msg);").unwrap();
225 writeln!(out, "}}\n").unwrap();
226
227 writeln!(out, "thread_local! {{").unwrap();
231 writeln!(out, " static LOGOS_ERROR_CACHE: std::cell::RefCell<Option<std::ffi::CString>> = std::cell::RefCell::new(None);").unwrap();
232 writeln!(out, "}}\n").unwrap();
233
234 writeln!(out, "#[no_mangle]").unwrap();
235 writeln!(out, "pub extern \"C\" fn logos_last_error() -> *const std::os::raw::c_char {{").unwrap();
236 writeln!(out, " let msg = logos_error_store().lock().unwrap_or_else(|e| e.into_inner())").unwrap();
237 writeln!(out, " .get(&std::thread::current().id()).cloned();").unwrap();
238 writeln!(out, " match msg {{").unwrap();
239 writeln!(out, " Some(s) => match std::ffi::CString::new(s) {{").unwrap();
240 writeln!(out, " Ok(cstr) => {{").unwrap();
241 writeln!(out, " let ptr = cstr.as_ptr();").unwrap();
242 writeln!(out, " LOGOS_ERROR_CACHE.with(|cache| {{ cache.borrow_mut().replace(cstr); }});").unwrap();
243 writeln!(out, " LOGOS_ERROR_CACHE.with(|cache| {{").unwrap();
244 writeln!(out, " cache.borrow().as_ref().map_or(std::ptr::null(), |c| c.as_ptr())").unwrap();
245 writeln!(out, " }})").unwrap();
246 writeln!(out, " }}").unwrap();
247 writeln!(out, " Err(_) => std::ptr::null(),").unwrap();
248 writeln!(out, " }}").unwrap();
249 writeln!(out, " None => std::ptr::null(),").unwrap();
250 writeln!(out, " }}").unwrap();
251 writeln!(out, "}}\n").unwrap();
252
253 writeln!(out, "#[no_mangle]").unwrap();
255 writeln!(out, "pub extern \"C\" fn logos_get_last_error() -> *const std::os::raw::c_char {{").unwrap();
256 writeln!(out, " logos_last_error()").unwrap();
257 writeln!(out, "}}\n").unwrap();
258
259 writeln!(out, "#[no_mangle]").unwrap();
261 writeln!(out, "pub extern \"C\" fn logos_clear_error() {{").unwrap();
262 writeln!(out, " let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
263 writeln!(out, " store.remove(&std::thread::current().id());").unwrap();
264 writeln!(out, "}}\n").unwrap();
265
266 writeln!(out, "#[no_mangle]").unwrap();
268 writeln!(out, "pub extern \"C\" fn logos_free_string(ptr: *mut std::os::raw::c_char) {{").unwrap();
269 writeln!(out, " if !ptr.is_null() {{").unwrap();
270 writeln!(out, " unsafe {{ drop(std::ffi::CString::from_raw(ptr)); }}").unwrap();
271 writeln!(out, " }}").unwrap();
272 writeln!(out, "}}\n").unwrap();
273
274 writeln!(out, "pub const LOGOS_ABI_VERSION: u32 = 1;\n").unwrap();
276
277 writeln!(out, "#[no_mangle]").unwrap();
278 writeln!(out, "pub extern \"C\" fn logos_version() -> *const std::os::raw::c_char {{").unwrap();
279 writeln!(out, " concat!(env!(\"CARGO_PKG_VERSION\"), \"\\0\").as_ptr() as *const std::os::raw::c_char").unwrap();
280 writeln!(out, "}}\n").unwrap();
281
282 writeln!(out, "#[no_mangle]").unwrap();
283 writeln!(out, "pub extern \"C\" fn logos_abi_version() -> u32 {{").unwrap();
284 writeln!(out, " LOGOS_ABI_VERSION").unwrap();
285 writeln!(out, "}}\n").unwrap();
286
287 writeln!(out, "struct HandleEntry {{").unwrap();
289 writeln!(out, " data: usize,").unwrap();
290 writeln!(out, " generation: u64,").unwrap();
291 writeln!(out, "}}\n").unwrap();
292
293 writeln!(out, "struct HandleRegistry {{").unwrap();
294 writeln!(out, " entries: std::collections::HashMap<u64, HandleEntry>,").unwrap();
295 writeln!(out, " counter: u64,").unwrap();
296 writeln!(out, "}}\n").unwrap();
297
298 writeln!(out, "impl HandleRegistry {{").unwrap();
299 writeln!(out, " fn new() -> Self {{").unwrap();
300 writeln!(out, " HandleRegistry {{ entries: std::collections::HashMap::new(), counter: 0 }}").unwrap();
301 writeln!(out, " }}").unwrap();
302 writeln!(out, " fn register(&mut self, ptr: usize) -> (u64, u64) {{").unwrap();
303 writeln!(out, " self.counter += 1;").unwrap();
304 writeln!(out, " let id = self.counter;").unwrap();
305 writeln!(out, " let generation = id;").unwrap();
306 writeln!(out, " self.entries.insert(id, HandleEntry {{ data: ptr, generation }});").unwrap();
307 writeln!(out, " (id, generation)").unwrap();
308 writeln!(out, " }}").unwrap();
309 writeln!(out, " fn validate_handle(&self, id: u64, generation: u64) -> bool {{").unwrap();
310 writeln!(out, " self.entries.get(&id).map_or(false, |e| e.generation == generation)").unwrap();
311 writeln!(out, " }}").unwrap();
312 writeln!(out, " fn deref(&self, id: u64) -> Option<usize> {{").unwrap();
313 writeln!(out, " self.entries.get(&id).map(|e| e.data)").unwrap();
314 writeln!(out, " }}").unwrap();
315 writeln!(out, " fn free(&mut self, id: u64) -> Result<usize, ()> {{").unwrap();
316 writeln!(out, " if let Some(entry) = self.entries.remove(&id) {{ Ok(entry.data) }} else {{ Err(()) }}").unwrap();
317 writeln!(out, " }}").unwrap();
318 writeln!(out, "}}\n").unwrap();
319
320 writeln!(out, "fn logos_handle_registry() -> &'static std::sync::Mutex<HandleRegistry> {{").unwrap();
321 writeln!(out, " use std::sync::OnceLock;").unwrap();
322 writeln!(out, " static REGISTRY: OnceLock<std::sync::Mutex<HandleRegistry>> = OnceLock::new();").unwrap();
323 writeln!(out, " REGISTRY.get_or_init(|| std::sync::Mutex::new(HandleRegistry::new()))").unwrap();
324 writeln!(out, "}}\n").unwrap();
325
326 out
327}
328
329fn emit_catch_unwind_open(out: &mut String) {
331 writeln!(out, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
332}
333
334fn emit_catch_unwind_close(out: &mut String, default_expr: &str) {
337 writeln!(out, " }})) {{").unwrap();
338 writeln!(out, " Ok(__v) => __v,").unwrap();
339 writeln!(out, " Err(__panic) => {{").unwrap();
340 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();
341 writeln!(out, " logos_set_last_error(__msg);").unwrap();
342 writeln!(out, " {}", default_expr).unwrap();
343 writeln!(out, " }}").unwrap();
344 writeln!(out, " }}").unwrap();
345}
346
347fn emit_null_handle_check(out: &mut String, default_expr: &str) {
349 writeln!(out, " if handle.is_null() {{ logos_set_last_error(\"NullPointer: handle is null\".to_string()); return {}; }}", default_expr).unwrap();
350}
351
352fn emit_null_out_check(out: &mut String, default_expr: &str) {
354 writeln!(out, " if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return {}; }}", default_expr).unwrap();
355}
356
357fn emit_registry_deref(out: &mut String, default_expr: &str) {
359 writeln!(out, " let __id = handle as u64;").unwrap();
360 writeln!(out, " let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
361 writeln!(out, " let __ptr = match __reg.deref(__id) {{").unwrap();
362 writeln!(out, " Some(p) => p,").unwrap();
363 writeln!(out, " None => {{ logos_set_last_error(\"InvalidHandle: handle not found in registry\".to_string()); return {}; }}", default_expr).unwrap();
364 writeln!(out, " }};").unwrap();
365 writeln!(out, " drop(__reg);").unwrap();
366}
367
368fn emit_registry_create(out: &mut String, alloc_expr: &str, _rust_type: &str) {
371 writeln!(out, " let __data = {};", alloc_expr).unwrap();
372 writeln!(out, " let __ptr = Box::into_raw(Box::new(__data)) as usize;").unwrap();
373 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
374 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
375 writeln!(out, " __id as LogosHandle").unwrap();
376}
377
378fn emit_registry_free(out: &mut String, rust_type: &str) {
381 writeln!(out, " if handle.is_null() {{ return; }}").unwrap();
382 writeln!(out, " let __id = handle as u64;").unwrap();
383 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
384 writeln!(out, " match __reg.free(__id) {{").unwrap();
385 writeln!(out, " Ok(__ptr) => {{ unsafe {{ drop(Box::from_raw(__ptr as *mut {})); }} }}", rust_type).unwrap();
386 writeln!(out, " Err(()) => {{ logos_set_last_error(\"InvalidHandle: handle already freed or not found\".to_string()); }}").unwrap();
387 writeln!(out, " }}").unwrap();
388}
389
390pub(crate) fn codegen_c_accessors(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
393 let mut out = String::new();
394 let mangled = mangle_type_for_c(ty, interner);
395
396 match ty {
397 TypeExpr::Generic { base, params } => {
398 let base_name = interner.resolve(*base);
399 match base_name {
400 "Seq" | "List" | "Vec" if !params.is_empty() => {
401 let inner_rust_type = codegen_type_expr(¶ms[0], interner);
402 let _inner_mangled = mangle_type_for_c(¶ms[0], interner);
403 let is_inner_text = is_text_type(¶ms[0], interner);
404 let vec_type = format!("LogosSeq<{}>", inner_rust_type);
405
406 writeln!(out, "#[no_mangle]").unwrap();
408 writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
409 emit_catch_unwind_open(&mut out);
410 emit_null_handle_check(&mut out, "0");
411 emit_registry_deref(&mut out, "0");
412 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
413 writeln!(out, " seq.len()").unwrap();
414 emit_catch_unwind_close(&mut out, "0");
415 writeln!(out, "}}\n").unwrap();
416
417 if is_inner_text {
419 writeln!(out, "#[no_mangle]").unwrap();
420 writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize) -> *mut std::os::raw::c_char {{", mangled).unwrap();
421 emit_catch_unwind_open(&mut out);
422 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
423 emit_registry_deref(&mut out, "std::ptr::null_mut()");
424 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
425 writeln!(out, " if index >= seq.len() {{").unwrap();
426 writeln!(out, " logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
427 writeln!(out, " return std::ptr::null_mut();").unwrap();
428 writeln!(out, " }}").unwrap();
429 writeln!(out, " match std::ffi::CString::new(seq.borrow()[index].clone()) {{").unwrap();
430 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
431 writeln!(out, " Err(_) => {{ logos_set_last_error(\"String contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
432 writeln!(out, " }}").unwrap();
433 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
434 writeln!(out, "}}\n").unwrap();
435 } else {
436 writeln!(out, "#[no_mangle]").unwrap();
437 writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
438 emit_catch_unwind_open(&mut out);
439 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
440 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
441 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
442 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
443 writeln!(out, " if index >= seq.len() {{").unwrap();
444 writeln!(out, " logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
445 writeln!(out, " return LogosStatus::OutOfBounds;").unwrap();
446 writeln!(out, " }}").unwrap();
447 writeln!(out, " unsafe {{ *out = seq.borrow()[index].clone(); }}").unwrap();
448 writeln!(out, " LogosStatus::Ok").unwrap();
449 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
450 writeln!(out, "}}\n").unwrap();
451 }
452
453 writeln!(out, "#[no_mangle]").unwrap();
455 writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
456 emit_catch_unwind_open(&mut out);
457 emit_registry_create(&mut out, &format!("LogosSeq::<{}>::new()", inner_rust_type), &vec_type);
458 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
459 writeln!(out, "}}\n").unwrap();
460
461 if is_inner_text {
463 writeln!(out, "#[no_mangle]").unwrap();
464 writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
465 emit_catch_unwind_open(&mut out);
466 emit_null_handle_check(&mut out, "()");
467 emit_registry_deref(&mut out, "()");
468 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
469 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
470 writeln!(out, " seq.push(val_str);").unwrap();
471 emit_catch_unwind_close(&mut out, "()");
472 writeln!(out, "}}\n").unwrap();
473 } else {
474 writeln!(out, "#[no_mangle]").unwrap();
475 writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
476 emit_catch_unwind_open(&mut out);
477 emit_null_handle_check(&mut out, "()");
478 emit_registry_deref(&mut out, "()");
479 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
480 writeln!(out, " seq.push(value);").unwrap();
481 emit_catch_unwind_close(&mut out, "()");
482 writeln!(out, "}}\n").unwrap();
483 }
484
485 if is_inner_text {
487 writeln!(out, "#[no_mangle]").unwrap();
488 writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
489 emit_catch_unwind_open(&mut out);
490 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
491 emit_registry_deref(&mut out, "std::ptr::null_mut()");
492 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
493 writeln!(out, " match seq.pop() {{").unwrap();
494 writeln!(out, " Some(val) => match std::ffi::CString::new(val) {{").unwrap();
495 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
496 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
497 writeln!(out, " }},").unwrap();
498 writeln!(out, " None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); std::ptr::null_mut() }}").unwrap();
499 writeln!(out, " }}").unwrap();
500 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
501 writeln!(out, "}}\n").unwrap();
502 } else {
503 writeln!(out, "#[no_mangle]").unwrap();
504 writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
505 emit_catch_unwind_open(&mut out);
506 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
507 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
508 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
509 writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
510 writeln!(out, " match seq.pop() {{").unwrap();
511 writeln!(out, " Some(val) => {{ unsafe {{ *out = val; }} LogosStatus::Ok }}").unwrap();
512 writeln!(out, " None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); LogosStatus::Error }}").unwrap();
513 writeln!(out, " }}").unwrap();
514 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
515 writeln!(out, "}}\n").unwrap();
516 }
517
518 writeln!(out, "#[no_mangle]").unwrap();
520 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
521 emit_catch_unwind_open(&mut out);
522 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
523 emit_registry_deref(&mut out, "std::ptr::null_mut()");
524 writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
525 writeln!(out, " match serde_json::to_string(seq) {{").unwrap();
526 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
527 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
528 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
529 writeln!(out, " }},").unwrap();
530 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
531 writeln!(out, " }}").unwrap();
532 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
533 writeln!(out, "}}\n").unwrap();
534
535 writeln!(out, "#[no_mangle]").unwrap();
537 writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
538 emit_catch_unwind_open(&mut out);
539 writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
540 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
541 writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
542 writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", vec_type).unwrap();
543 writeln!(out, " Ok(val) => {{").unwrap();
544 writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
545 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
546 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
547 writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
548 writeln!(out, " LogosStatus::Ok").unwrap();
549 writeln!(out, " }}").unwrap();
550 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
551 writeln!(out, " }}").unwrap();
552 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
553 writeln!(out, "}}\n").unwrap();
554
555 writeln!(out, "#[no_mangle]").unwrap();
557 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
558 emit_catch_unwind_open(&mut out);
559 emit_registry_free(&mut out, &vec_type);
560 emit_catch_unwind_close(&mut out, "()");
561 writeln!(out, "}}\n").unwrap();
562 }
563
564 "Map" | "HashMap" if params.len() >= 2 => {
565 let key_rust = codegen_type_expr(¶ms[0], interner);
566 let val_rust = codegen_type_expr(¶ms[1], interner);
567 let is_key_text = is_text_type(¶ms[0], interner);
568 let is_val_text = is_text_type(¶ms[1], interner);
569 let map_type = format!("LogosMap<{}, {}>", key_rust, val_rust);
570
571 writeln!(out, "#[no_mangle]").unwrap();
573 writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
574 emit_catch_unwind_open(&mut out);
575 emit_null_handle_check(&mut out, "0");
576 emit_registry_deref(&mut out, "0");
577 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
578 writeln!(out, " map.len()").unwrap();
579 emit_catch_unwind_close(&mut out, "0");
580 writeln!(out, "}}\n").unwrap();
581
582 if is_key_text {
584 if is_val_text {
585 writeln!(out, "#[no_mangle]").unwrap();
586 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();
587 emit_catch_unwind_open(&mut out);
588 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
589 emit_registry_deref(&mut out, "std::ptr::null_mut()");
590 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
591 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
592 writeln!(out, " match map.get(&key_str) {{").unwrap();
593 writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
594 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
595 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
596 writeln!(out, " }},").unwrap();
597 writeln!(out, " None => std::ptr::null_mut(),").unwrap();
598 writeln!(out, " }}").unwrap();
599 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
600 writeln!(out, "}}\n").unwrap();
601 } else {
602 writeln!(out, "#[no_mangle]").unwrap();
603 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: *const std::os::raw::c_char, out: *mut {}) -> LogosStatus {{", mangled, val_rust).unwrap();
604 emit_catch_unwind_open(&mut out);
605 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
606 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
607 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
608 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
609 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
610 writeln!(out, " match map.get(&key_str) {{").unwrap();
611 writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
612 writeln!(out, " None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key_str)); LogosStatus::Error }}").unwrap();
613 writeln!(out, " }}").unwrap();
614 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
615 writeln!(out, "}}\n").unwrap();
616 }
617 } else {
618 if is_val_text {
619 writeln!(out, "#[no_mangle]").unwrap();
620 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}) -> *mut std::os::raw::c_char {{", mangled, key_rust).unwrap();
621 emit_catch_unwind_open(&mut out);
622 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
623 emit_registry_deref(&mut out, "std::ptr::null_mut()");
624 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
625 writeln!(out, " match map.get(&key) {{").unwrap();
626 writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
627 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
628 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
629 writeln!(out, " }},").unwrap();
630 writeln!(out, " None => std::ptr::null_mut(),").unwrap();
631 writeln!(out, " }}").unwrap();
632 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
633 writeln!(out, "}}\n").unwrap();
634 } else {
635 writeln!(out, "#[no_mangle]").unwrap();
636 writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}, out: *mut {}) -> LogosStatus {{", mangled, key_rust, val_rust).unwrap();
637 emit_catch_unwind_open(&mut out);
638 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
639 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
640 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
641 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
642 writeln!(out, " match map.get(&key) {{").unwrap();
643 writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
644 writeln!(out, " None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key)); LogosStatus::Error }}").unwrap();
645 writeln!(out, " }}").unwrap();
646 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
647 writeln!(out, "}}\n").unwrap();
648 }
649 }
650
651 writeln!(out, "#[no_mangle]").unwrap();
653 writeln!(out, "pub extern \"C\" fn logos_{}_keys(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
654 emit_catch_unwind_open(&mut out);
655 emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
656 emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
657 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
658 writeln!(out, " let keys = LogosSeq::from_vec(map.borrow().keys().cloned().collect::<Vec<{}>>());", key_rust).unwrap();
659 writeln!(out, " let __kptr = Box::into_raw(Box::new(keys)) as usize;").unwrap();
660 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
661 writeln!(out, " let (__id, _) = __reg.register(__kptr);").unwrap();
662 writeln!(out, " __id as LogosHandle").unwrap();
663 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
664 writeln!(out, "}}\n").unwrap();
665
666 writeln!(out, "#[no_mangle]").unwrap();
668 writeln!(out, "pub extern \"C\" fn logos_{}_values(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
669 emit_catch_unwind_open(&mut out);
670 emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
671 emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
672 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
673 writeln!(out, " let values = LogosSeq::from_vec(map.borrow().values().cloned().collect::<Vec<{}>>());", val_rust).unwrap();
674 writeln!(out, " let __vptr = Box::into_raw(Box::new(values)) as usize;").unwrap();
675 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
676 writeln!(out, " let (__id, _) = __reg.register(__vptr);").unwrap();
677 writeln!(out, " __id as LogosHandle").unwrap();
678 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
679 writeln!(out, "}}\n").unwrap();
680
681 writeln!(out, "#[no_mangle]").unwrap();
683 writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
684 emit_catch_unwind_open(&mut out);
685 emit_registry_create(&mut out, &format!("LogosMap::<{}, {}>::new()", key_rust, val_rust), &map_type);
686 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
687 writeln!(out, "}}\n").unwrap();
688
689 if is_key_text {
691 let val_param = if is_val_text {
692 "value: *const std::os::raw::c_char".to_string()
693 } else {
694 format!("value: {}", val_rust)
695 };
696 writeln!(out, "#[no_mangle]").unwrap();
697 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: *const std::os::raw::c_char, {}) {{", mangled, val_param).unwrap();
698 emit_catch_unwind_open(&mut out);
699 emit_null_handle_check(&mut out, "()");
700 emit_registry_deref(&mut out, "()");
701 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
702 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
703 if is_val_text {
704 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
705 writeln!(out, " map.insert(key_str, val_str);").unwrap();
706 } else {
707 writeln!(out, " map.insert(key_str, value);").unwrap();
708 }
709 emit_catch_unwind_close(&mut out, "()");
710 writeln!(out, "}}\n").unwrap();
711 } else {
712 let val_param = if is_val_text {
713 "value: *const std::os::raw::c_char".to_string()
714 } else {
715 format!("value: {}", val_rust)
716 };
717 writeln!(out, "#[no_mangle]").unwrap();
718 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: {}, {}) {{", mangled, key_rust, val_param).unwrap();
719 emit_catch_unwind_open(&mut out);
720 emit_null_handle_check(&mut out, "()");
721 emit_registry_deref(&mut out, "()");
722 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
723 if is_val_text {
724 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
725 writeln!(out, " map.insert(key, val_str);").unwrap();
726 } else {
727 writeln!(out, " map.insert(key, value);").unwrap();
728 }
729 emit_catch_unwind_close(&mut out, "()");
730 writeln!(out, "}}\n").unwrap();
731 }
732
733 if is_key_text {
735 writeln!(out, "#[no_mangle]").unwrap();
736 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
737 emit_catch_unwind_open(&mut out);
738 emit_null_handle_check(&mut out, "false");
739 emit_registry_deref(&mut out, "false");
740 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
741 writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
742 writeln!(out, " map.remove(&key_str).is_some()").unwrap();
743 emit_catch_unwind_close(&mut out, "false");
744 writeln!(out, "}}\n").unwrap();
745 } else {
746 writeln!(out, "#[no_mangle]").unwrap();
747 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: {}) -> bool {{", mangled, key_rust).unwrap();
748 emit_catch_unwind_open(&mut out);
749 emit_null_handle_check(&mut out, "false");
750 emit_registry_deref(&mut out, "false");
751 writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
752 writeln!(out, " map.remove(&key).is_some()").unwrap();
753 emit_catch_unwind_close(&mut out, "false");
754 writeln!(out, "}}\n").unwrap();
755 }
756
757 writeln!(out, "#[no_mangle]").unwrap();
759 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
760 emit_catch_unwind_open(&mut out);
761 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
762 emit_registry_deref(&mut out, "std::ptr::null_mut()");
763 writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
764 writeln!(out, " match serde_json::to_string(map) {{").unwrap();
765 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
766 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
767 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
768 writeln!(out, " }},").unwrap();
769 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
770 writeln!(out, " }}").unwrap();
771 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
772 writeln!(out, "}}\n").unwrap();
773
774 writeln!(out, "#[no_mangle]").unwrap();
776 writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
777 emit_catch_unwind_open(&mut out);
778 writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
779 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
780 writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
781 writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", map_type).unwrap();
782 writeln!(out, " Ok(val) => {{").unwrap();
783 writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
784 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
785 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
786 writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
787 writeln!(out, " LogosStatus::Ok").unwrap();
788 writeln!(out, " }}").unwrap();
789 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
790 writeln!(out, " }}").unwrap();
791 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
792 writeln!(out, "}}\n").unwrap();
793
794 writeln!(out, "#[no_mangle]").unwrap();
796 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
797 emit_catch_unwind_open(&mut out);
798 emit_registry_free(&mut out, &map_type);
799 emit_catch_unwind_close(&mut out, "()");
800 writeln!(out, "}}\n").unwrap();
801 }
802
803 "Set" | "HashSet" if !params.is_empty() => {
804 let inner_rust_type = codegen_type_expr(¶ms[0], interner);
805 let is_inner_text = is_text_type(¶ms[0], interner);
806 let set_type = format!("Set<{}>", inner_rust_type);
807
808 writeln!(out, "#[no_mangle]").unwrap();
810 writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
811 emit_catch_unwind_open(&mut out);
812 emit_null_handle_check(&mut out, "0");
813 emit_registry_deref(&mut out, "0");
814 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
815 writeln!(out, " set.len()").unwrap();
816 emit_catch_unwind_close(&mut out, "0");
817 writeln!(out, "}}\n").unwrap();
818
819 if is_inner_text {
821 writeln!(out, "#[no_mangle]").unwrap();
822 writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
823 emit_catch_unwind_open(&mut out);
824 emit_null_handle_check(&mut out, "false");
825 emit_registry_deref(&mut out, "false");
826 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
827 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
828 writeln!(out, " set.contains(&val_str)").unwrap();
829 emit_catch_unwind_close(&mut out, "false");
830 writeln!(out, "}}\n").unwrap();
831 } else {
832 writeln!(out, "#[no_mangle]").unwrap();
833 writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
834 emit_catch_unwind_open(&mut out);
835 emit_null_handle_check(&mut out, "false");
836 emit_registry_deref(&mut out, "false");
837 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
838 writeln!(out, " set.contains(&value)").unwrap();
839 emit_catch_unwind_close(&mut out, "false");
840 writeln!(out, "}}\n").unwrap();
841 }
842
843 writeln!(out, "#[no_mangle]").unwrap();
845 writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
846 emit_catch_unwind_open(&mut out);
847 emit_registry_create(&mut out, &format!("Set::<{}>::default()", inner_rust_type), &set_type);
848 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
849 writeln!(out, "}}\n").unwrap();
850
851 if is_inner_text {
853 writeln!(out, "#[no_mangle]").unwrap();
854 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
855 emit_catch_unwind_open(&mut out);
856 emit_null_handle_check(&mut out, "()");
857 emit_registry_deref(&mut out, "()");
858 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
859 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
860 writeln!(out, " set.insert(val_str);").unwrap();
861 emit_catch_unwind_close(&mut out, "()");
862 writeln!(out, "}}\n").unwrap();
863 } else {
864 writeln!(out, "#[no_mangle]").unwrap();
865 writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
866 emit_catch_unwind_open(&mut out);
867 emit_null_handle_check(&mut out, "()");
868 emit_registry_deref(&mut out, "()");
869 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
870 writeln!(out, " set.insert(value);").unwrap();
871 emit_catch_unwind_close(&mut out, "()");
872 writeln!(out, "}}\n").unwrap();
873 }
874
875 if is_inner_text {
877 writeln!(out, "#[no_mangle]").unwrap();
878 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
879 emit_catch_unwind_open(&mut out);
880 emit_null_handle_check(&mut out, "false");
881 emit_registry_deref(&mut out, "false");
882 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
883 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
884 writeln!(out, " set.remove(&val_str)").unwrap();
885 emit_catch_unwind_close(&mut out, "false");
886 writeln!(out, "}}\n").unwrap();
887 } else {
888 writeln!(out, "#[no_mangle]").unwrap();
889 writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
890 emit_catch_unwind_open(&mut out);
891 emit_null_handle_check(&mut out, "false");
892 emit_registry_deref(&mut out, "false");
893 writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
894 writeln!(out, " set.remove(&value)").unwrap();
895 emit_catch_unwind_close(&mut out, "false");
896 writeln!(out, "}}\n").unwrap();
897 }
898
899 writeln!(out, "#[no_mangle]").unwrap();
901 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
902 emit_catch_unwind_open(&mut out);
903 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
904 emit_registry_deref(&mut out, "std::ptr::null_mut()");
905 writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
906 writeln!(out, " match serde_json::to_string(set) {{").unwrap();
907 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
908 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
909 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
910 writeln!(out, " }},").unwrap();
911 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
912 writeln!(out, " }}").unwrap();
913 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
914 writeln!(out, "}}\n").unwrap();
915
916 writeln!(out, "#[no_mangle]").unwrap();
918 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
919 emit_catch_unwind_open(&mut out);
920 emit_registry_free(&mut out, &set_type);
921 emit_catch_unwind_close(&mut out, "()");
922 writeln!(out, "}}\n").unwrap();
923 }
924
925 "Option" | "Maybe" if !params.is_empty() => {
926 let inner_rust_type = codegen_type_expr(¶ms[0], interner);
927 let is_inner_text = is_text_type(¶ms[0], interner);
928 let opt_type = format!("Option<{}>", inner_rust_type);
929
930 writeln!(out, "#[no_mangle]").unwrap();
932 writeln!(out, "pub extern \"C\" fn logos_{}_is_some(handle: LogosHandle) -> bool {{", mangled).unwrap();
933 emit_catch_unwind_open(&mut out);
934 emit_null_handle_check(&mut out, "false");
935 emit_registry_deref(&mut out, "false");
936 writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
937 writeln!(out, " opt.is_some()").unwrap();
938 emit_catch_unwind_close(&mut out, "false");
939 writeln!(out, "}}\n").unwrap();
940
941 if is_inner_text {
943 writeln!(out, "#[no_mangle]").unwrap();
944 writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
945 emit_catch_unwind_open(&mut out);
946 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
947 emit_registry_deref(&mut out, "std::ptr::null_mut()");
948 writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
949 writeln!(out, " match opt {{").unwrap();
950 writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
951 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
952 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
953 writeln!(out, " }},").unwrap();
954 writeln!(out, " None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); std::ptr::null_mut() }}").unwrap();
955 writeln!(out, " }}").unwrap();
956 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
957 writeln!(out, "}}\n").unwrap();
958 } else {
959 writeln!(out, "#[no_mangle]").unwrap();
960 writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
961 emit_catch_unwind_open(&mut out);
962 emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
963 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
964 emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
965 writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
966 writeln!(out, " match opt {{").unwrap();
967 writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
968 writeln!(out, " None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); LogosStatus::Error }}").unwrap();
969 writeln!(out, " }}").unwrap();
970 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
971 writeln!(out, "}}\n").unwrap();
972 }
973
974 if is_inner_text {
976 writeln!(out, "#[no_mangle]").unwrap();
977 writeln!(out, "pub extern \"C\" fn logos_{}_some(value: *const std::os::raw::c_char) -> LogosHandle {{", mangled).unwrap();
978 emit_catch_unwind_open(&mut out);
979 writeln!(out, " if value.is_null() {{ logos_set_last_error(\"NullPointer: value is null\".to_string()); return std::ptr::null_mut() as LogosHandle; }}").unwrap();
980 writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
981 writeln!(out, " let opt: {} = Some(val_str);", opt_type).unwrap();
982 writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
983 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
984 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
985 writeln!(out, " __id as LogosHandle").unwrap();
986 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
987 writeln!(out, "}}\n").unwrap();
988 } else {
989 writeln!(out, "#[no_mangle]").unwrap();
990 writeln!(out, "pub extern \"C\" fn logos_{}_some(value: {}) -> LogosHandle {{", mangled, inner_rust_type).unwrap();
991 emit_catch_unwind_open(&mut out);
992 writeln!(out, " let opt: {} = Some(value);", opt_type).unwrap();
993 writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
994 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
995 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
996 writeln!(out, " __id as LogosHandle").unwrap();
997 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
998 writeln!(out, "}}\n").unwrap();
999 }
1000
1001 writeln!(out, "#[no_mangle]").unwrap();
1003 writeln!(out, "pub extern \"C\" fn logos_{}_none() -> LogosHandle {{", mangled).unwrap();
1004 emit_catch_unwind_open(&mut out);
1005 writeln!(out, " let opt: {} = None;", opt_type).unwrap();
1006 writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1007 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1008 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1009 writeln!(out, " __id as LogosHandle").unwrap();
1010 emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1011 writeln!(out, "}}\n").unwrap();
1012
1013 writeln!(out, "#[no_mangle]").unwrap();
1015 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1016 emit_catch_unwind_open(&mut out);
1017 emit_registry_free(&mut out, &opt_type);
1018 emit_catch_unwind_close(&mut out, "()");
1019 writeln!(out, "}}\n").unwrap();
1020 }
1021
1022 _ => {}
1023 }
1024 }
1025 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1026 let type_name = interner.resolve(*sym);
1027 let type_def = registry.get(*sym);
1028
1029 match type_def {
1030 Some(TypeDef::Struct { fields, is_portable, .. }) => {
1031 let mangled_struct = type_name.to_lowercase();
1032 let rust_struct_name = type_name.to_string();
1033
1034 for field in fields {
1035 let field_name = interner.resolve(field.name);
1036 let is_field_text = match &field.ty {
1037 FieldType::Primitive(s) | FieldType::Named(s) => {
1038 let n = interner.resolve(*s);
1039 n == "Text" || n == "String"
1040 }
1041 _ => false,
1042 };
1043
1044 writeln!(out, "#[no_mangle]").unwrap();
1045 if is_field_text {
1046 writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
1047 mangled_struct, field = field_name).unwrap();
1048 emit_catch_unwind_open(&mut out);
1049 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1050 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1051 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1052 writeln!(out, " match std::ffi::CString::new(obj.{}.clone()) {{", field_name).unwrap();
1053 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1054 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1055 writeln!(out, " }}").unwrap();
1056 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1057 writeln!(out, "}}\n").unwrap();
1058 } else {
1059 let (field_rust_type, is_char) = match &field.ty {
1060 FieldType::Primitive(s) | FieldType::Named(s) => {
1061 let n = interner.resolve(*s);
1062 match n {
1063 "Int" => ("i64", false),
1064 "Nat" => ("u64", false),
1065 "Real" | "Float" => ("f64", false),
1066 "Bool" | "Boolean" => ("bool", false),
1067 "Byte" => ("u8", false),
1068 "Char" => ("u32", true),
1069 _ => (n, false),
1070 }
1071 }
1072 _ => ("LogosHandle", false),
1073 };
1074 writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> {} {{",
1075 mangled_struct, field_rust_type, field = field_name).unwrap();
1076 emit_catch_unwind_open(&mut out);
1077 emit_null_handle_check(&mut out, "Default::default()");
1078 emit_registry_deref(&mut out, "Default::default()");
1079 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1080 if is_char {
1081 writeln!(out, " obj.{}.clone() as u32", field_name).unwrap();
1082 } else {
1083 writeln!(out, " obj.{}.clone()", field_name).unwrap();
1084 }
1085 emit_catch_unwind_close(&mut out, "Default::default()");
1086 writeln!(out, "}}\n").unwrap();
1087 }
1088 }
1089
1090 {
1091 writeln!(out, "#[no_mangle]").unwrap();
1093 writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled_struct).unwrap();
1094 emit_catch_unwind_open(&mut out);
1095 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1096 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1097 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1098 writeln!(out, " match serde_json::to_string(obj) {{").unwrap();
1099 writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1100 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1101 writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1102 writeln!(out, " }},").unwrap();
1103 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1104 writeln!(out, " }}").unwrap();
1105 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1106 writeln!(out, "}}\n").unwrap();
1107
1108 writeln!(out, "#[no_mangle]").unwrap();
1110 writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled_struct).unwrap();
1111 emit_catch_unwind_open(&mut out);
1112 writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
1113 emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1114 writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
1115 writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", rust_struct_name).unwrap();
1116 writeln!(out, " Ok(val) => {{").unwrap();
1117 writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
1118 writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1119 writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
1120 writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
1121 writeln!(out, " LogosStatus::Ok").unwrap();
1122 writeln!(out, " }}").unwrap();
1123 writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
1124 writeln!(out, " }}").unwrap();
1125 emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1126 writeln!(out, "}}\n").unwrap();
1127 }
1128
1129 writeln!(out, "#[no_mangle]").unwrap();
1131 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_struct).unwrap();
1132 emit_catch_unwind_open(&mut out);
1133 emit_registry_free(&mut out, &rust_struct_name);
1134 emit_catch_unwind_close(&mut out, "()");
1135 writeln!(out, "}}\n").unwrap();
1136 }
1137 Some(TypeDef::Enum { variants, .. }) => {
1138 let mangled_enum = type_name.to_lowercase();
1139 let rust_enum_name = type_name.to_string();
1140
1141 writeln!(out, "#[no_mangle]").unwrap();
1143 writeln!(out, "pub extern \"C\" fn logos_{}_tag(handle: LogosHandle) -> i32 {{", mangled_enum).unwrap();
1144 emit_catch_unwind_open(&mut out);
1145 emit_null_handle_check(&mut out, "-1");
1146 emit_registry_deref(&mut out, "-1");
1147 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1148 writeln!(out, " match obj {{").unwrap();
1149 for (i, variant) in variants.iter().enumerate() {
1150 let vname = interner.resolve(variant.name);
1151 if variant.fields.is_empty() {
1152 writeln!(out, " {}::{} => {},", rust_enum_name, vname, i).unwrap();
1153 } else {
1154 writeln!(out, " {}::{}{{ .. }} => {},", rust_enum_name, vname, i).unwrap();
1155 }
1156 }
1157 writeln!(out, " }}").unwrap();
1158 emit_catch_unwind_close(&mut out, "-1");
1159 writeln!(out, "}}\n").unwrap();
1160
1161 for variant in variants {
1162 let vname = interner.resolve(variant.name);
1163 let vname_lower = vname.to_lowercase();
1164 for field in &variant.fields {
1165 let fname = interner.resolve(field.name);
1166 let is_field_text = match &field.ty {
1167 FieldType::Primitive(s) | FieldType::Named(s) => {
1168 let n = interner.resolve(*s);
1169 n == "Text" || n == "String"
1170 }
1171 _ => false,
1172 };
1173
1174 writeln!(out, "#[no_mangle]").unwrap();
1175 if is_field_text {
1176 writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
1177 mangled_enum, vname_lower, fname = fname).unwrap();
1178 emit_catch_unwind_open(&mut out);
1179 emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1180 emit_registry_deref(&mut out, "std::ptr::null_mut()");
1181 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1182 writeln!(out, " if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
1183 writeln!(out, " match std::ffi::CString::new({fname}.clone()) {{", fname = fname).unwrap();
1184 writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
1185 writeln!(out, " Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1186 writeln!(out, " }}").unwrap();
1187 writeln!(out, " }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); std::ptr::null_mut() }}", vname).unwrap();
1188 emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1189 writeln!(out, "}}\n").unwrap();
1190 } else {
1191 let field_rust_type = match &field.ty {
1192 FieldType::Primitive(s) | FieldType::Named(s) => {
1193 let n = interner.resolve(*s);
1194 match n {
1195 "Int" => "i64",
1196 "Nat" => "u64",
1197 "Real" | "Float" => "f64",
1198 "Bool" | "Boolean" => "bool",
1199 "Byte" => "u8",
1200 "Char" => "u32",
1201 _ => n,
1202 }
1203 }
1204 _ => "LogosHandle",
1205 };
1206 writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> {} {{",
1207 mangled_enum, vname_lower, field_rust_type, fname = fname).unwrap();
1208 emit_catch_unwind_open(&mut out);
1209 emit_null_handle_check(&mut out, "Default::default()");
1210 emit_registry_deref(&mut out, "Default::default()");
1211 writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1212 writeln!(out, " if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
1213 writeln!(out, " {fname}.clone()", fname = fname).unwrap();
1214 writeln!(out, " }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); Default::default() }}", vname).unwrap();
1215 emit_catch_unwind_close(&mut out, "Default::default()");
1216 writeln!(out, "}}\n").unwrap();
1217 }
1218 }
1219 }
1220
1221 writeln!(out, "#[no_mangle]").unwrap();
1223 writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_enum).unwrap();
1224 emit_catch_unwind_open(&mut out);
1225 emit_registry_free(&mut out, &rust_enum_name);
1226 emit_catch_unwind_close(&mut out, "()");
1227 writeln!(out, "}}\n").unwrap();
1228 }
1229 _ => {}
1230 }
1231 }
1232 _ => {}
1233 }
1234
1235 out
1236}
1237
1238pub(crate) fn collect_c_export_reference_types<'a>(
1241 stmts: &'a [Stmt<'a>],
1242 interner: &Interner,
1243 registry: &TypeRegistry,
1244) -> Vec<&'a TypeExpr<'a>> {
1245 let mut seen = HashSet::new();
1246 let mut types = Vec::new();
1247
1248 for stmt in stmts {
1249 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1250 let is_c = match export_target {
1251 None => true,
1252 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1253 };
1254 if !is_c { continue; }
1255
1256 for (_, ty) in params.iter() {
1257 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
1258 let mangled = mangle_type_for_c(ty, interner);
1259 if seen.insert(mangled) {
1260 types.push(*ty);
1261 }
1262 }
1263 }
1264 if let Some(ty) = return_type {
1265 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
1266 let mangled = mangle_type_for_c(ty, interner);
1267 if seen.insert(mangled) {
1268 types.push(*ty);
1269 }
1270 }
1271 }
1272 }
1273 }
1274
1275 types
1276}
1277
1278pub(crate) fn collect_c_export_value_type_structs(
1281 stmts: &[Stmt],
1282 interner: &Interner,
1283 registry: &TypeRegistry,
1284) -> HashSet<Symbol> {
1285 let mut value_structs = HashSet::new();
1286
1287 for stmt in stmts {
1288 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1289 let is_c = match export_target {
1290 None => true,
1291 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1292 };
1293 if !is_c { continue; }
1294
1295 let all_types: Vec<&TypeExpr> = params.iter()
1296 .map(|(_, ty)| *ty)
1297 .chain(return_type.iter().copied())
1298 .collect();
1299
1300 for ty in all_types {
1301 if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1302 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
1303 if registry.get(*sym).is_some() {
1304 value_structs.insert(*sym);
1305 }
1306 }
1307 }
1308 }
1309 }
1310 }
1311
1312 value_structs
1313}
1314
1315pub(crate) fn collect_c_export_ref_structs(
1318 stmts: &[Stmt],
1319 interner: &Interner,
1320 registry: &TypeRegistry,
1321) -> HashSet<Symbol> {
1322 let mut ref_structs = HashSet::new();
1323 let ref_types = collect_c_export_reference_types(stmts, interner, registry);
1324 for ty in ref_types {
1325 if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1326 if registry.get(*sym).map_or(false, |d| matches!(d, TypeDef::Struct { .. })) {
1327 ref_structs.insert(*sym);
1328 }
1329 }
1330 }
1331 ref_structs
1332}
1333
1334pub fn generate_c_header(
1343 stmts: &[Stmt],
1344 module_name: &str,
1345 interner: &Interner,
1346 registry: &TypeRegistry,
1347) -> String {
1348 let mut out = String::new();
1349 let guard = module_name.to_uppercase().replace('-', "_");
1350
1351 writeln!(out, "// Generated from {}.lg — LogicAffeine Universal ABI", module_name).unwrap();
1352 writeln!(out, "#ifndef {}_H", guard).unwrap();
1353 writeln!(out, "#define {}_H\n", guard).unwrap();
1354 writeln!(out, "#include <stdint.h>").unwrap();
1355 writeln!(out, "#include <stdbool.h>").unwrap();
1356 writeln!(out, "#include <stddef.h>\n").unwrap();
1357
1358 writeln!(out, "#ifdef __cplusplus").unwrap();
1359 writeln!(out, "extern \"C\" {{").unwrap();
1360 writeln!(out, "#endif\n").unwrap();
1361
1362 writeln!(out, "// ═══ Runtime ═══").unwrap();
1364 writeln!(out, "typedef enum {{").unwrap();
1365 writeln!(out, " LOGOS_STATUS_OK = 0,").unwrap();
1366 writeln!(out, " LOGOS_STATUS_ERROR = 1,").unwrap();
1367 writeln!(out, " LOGOS_STATUS_REFINEMENT_VIOLATION = 2,").unwrap();
1368 writeln!(out, " LOGOS_STATUS_NULL_POINTER = 3,").unwrap();
1369 writeln!(out, " LOGOS_STATUS_OUT_OF_BOUNDS = 4,").unwrap();
1370 writeln!(out, " LOGOS_STATUS_DESERIALIZATION_FAILED = 5,").unwrap();
1371 writeln!(out, " LOGOS_STATUS_INVALID_HANDLE = 6,").unwrap();
1372 writeln!(out, " LOGOS_STATUS_CONTAINS_NULL_BYTE = 7,").unwrap();
1373 writeln!(out, " LOGOS_STATUS_THREAD_PANIC = 8,").unwrap();
1374 writeln!(out, " LOGOS_STATUS_MEMORY_EXHAUSTED = 9,").unwrap();
1375 writeln!(out, "}} logos_status_t;\n").unwrap();
1376 writeln!(out, "typedef void* logos_handle_t;\n").unwrap();
1377 writeln!(out, "const char* logos_last_error(void);").unwrap();
1378 writeln!(out, "const char* logos_get_last_error(void);").unwrap();
1379 writeln!(out, "void logos_clear_error(void);").unwrap();
1380 writeln!(out, "void logos_free_string(char* str);\n").unwrap();
1381
1382 writeln!(out, "#define LOGOS_ABI_VERSION 1").unwrap();
1383 writeln!(out, "const char* logos_version(void);").unwrap();
1384 writeln!(out, "uint32_t logos_abi_version(void);\n").unwrap();
1385
1386 let mut emitted_structs = HashSet::new();
1388 for stmt in stmts {
1389 if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1390 let is_c = match export_target {
1391 None => true,
1392 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1393 };
1394 if !is_c { continue; }
1395
1396 let all_types: Vec<&TypeExpr> = params.iter()
1398 .map(|(_, ty)| *ty)
1399 .chain(return_type.iter().copied())
1400 .collect();
1401
1402 for ty in all_types {
1403 if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1404 if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
1405 if let Some(TypeDef::Struct { fields, .. }) = registry.get(*sym) {
1406 let name = interner.resolve(*sym);
1407 if emitted_structs.insert(name.to_string()) {
1408 writeln!(out, "// ═══ Value Types ═══").unwrap();
1409 writeln!(out, "typedef struct {{").unwrap();
1410 for field in fields {
1411 let c_type = map_field_type_to_c(&field.ty, interner);
1412 writeln!(out, " {} {};", c_type, interner.resolve(field.name)).unwrap();
1413 }
1414 writeln!(out, "}} {};\n", name).unwrap();
1415 }
1416 }
1417 }
1418 }
1419 }
1420 }
1421 }
1422
1423 writeln!(out, "// ═══ Exported Functions ═══").unwrap();
1425 for stmt in stmts {
1426 if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
1427 let is_c = match export_target {
1428 None => true,
1429 Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1430 };
1431 if !is_c { continue; }
1432
1433 let func_name = format!("logos_{}", interner.resolve(*name));
1434 let has_ref_return = return_type.map_or(false, |ty| {
1435 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
1436 });
1437 let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
1438 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
1439 let has_refinement_param = params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
1440
1441 let uses_status_code = has_ref_return || has_result_return || has_text_return || has_refinement_param;
1443
1444 let mut c_params = Vec::new();
1446 for (pname, ptype) in params.iter() {
1447 let pn = interner.resolve(*pname);
1448 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
1449 c_params.push(format!("logos_handle_t {}", pn));
1450 } else {
1451 c_params.push(format!("{} {}", map_type_to_c_header(ptype, interner, false), pn));
1452 }
1453 }
1454
1455 if uses_status_code {
1456 if let Some(ret_ty) = return_type {
1458 if is_result_type(ret_ty, interner) {
1459 if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
1460 if !rparams.is_empty() {
1461 let ok_ty = &rparams[0];
1462 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
1463 c_params.push("logos_handle_t* out".to_string());
1464 } else {
1465 c_params.push(format!("{}* out", map_type_to_c_header(ok_ty, interner, false)));
1466 }
1467 }
1468 }
1469 } else if classify_type_for_c_abi(ret_ty, interner, registry) == CAbiClass::ReferenceType {
1470 c_params.push("logos_handle_t* out".to_string());
1471 } else if has_text_return {
1472 c_params.push("char** out".to_string());
1473 }
1474 }
1475 writeln!(out, "logos_status_t {}({});", func_name, c_params.join(", ")).unwrap();
1476 } else {
1477 let ret = return_type
1479 .map(|ty| map_type_to_c_header(ty, interner, true))
1480 .unwrap_or_else(|| "void".to_string());
1481 writeln!(out, "{} {}({});", ret, func_name, c_params.join(", ")).unwrap();
1482 }
1483 }
1484 }
1485 writeln!(out).unwrap();
1486
1487 let ref_types = collect_c_export_reference_types(stmts, interner, registry);
1489 if !ref_types.is_empty() {
1490 for ref_ty in &ref_types {
1491 let mangled = mangle_type_for_c(ref_ty, interner);
1492 writeln!(out, "// ═══ {} Accessors ═══", mangled).unwrap();
1493
1494 match ref_ty {
1495 TypeExpr::Generic { base, params } => {
1496 let base_name = interner.resolve(*base);
1497 match base_name {
1498 "Seq" | "List" | "Vec" if !params.is_empty() => {
1499 let is_inner_text = is_text_type(¶ms[0], interner);
1500 writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1501 if is_inner_text {
1502 writeln!(out, "char* logos_{}_at(logos_handle_t handle, size_t index);", mangled).unwrap();
1503 } else {
1504 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1505 writeln!(out, "logos_status_t logos_{}_at(logos_handle_t handle, size_t index, {}* out);", mangled, inner_c).unwrap();
1506 }
1507 writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1508 if is_inner_text {
1509 writeln!(out, "void logos_{}_push(logos_handle_t handle, const char* value);", mangled).unwrap();
1510 } else {
1511 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1512 writeln!(out, "void logos_{}_push(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1513 }
1514 if is_inner_text {
1515 writeln!(out, "char* logos_{}_pop(logos_handle_t handle);", mangled).unwrap();
1516 } else {
1517 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1518 writeln!(out, "logos_status_t logos_{}_pop(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
1519 }
1520 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1521 writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
1522 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1523 }
1524 "Map" | "HashMap" if params.len() >= 2 => {
1525 let is_key_text = is_text_type(¶ms[0], interner);
1526 let is_val_text = is_text_type(¶ms[1], interner);
1527 writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1528 if is_key_text {
1529 if is_val_text {
1530 writeln!(out, "char* logos_{}_get(logos_handle_t handle, const char* key);", mangled).unwrap();
1531 } else {
1532 let val_c = map_type_to_c_header(¶ms[1], interner, false);
1533 writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, const char* key, {}* out);", mangled, val_c).unwrap();
1534 }
1535 } else {
1536 let key_c = map_type_to_c_header(¶ms[0], interner, false);
1537 if is_val_text {
1538 writeln!(out, "char* logos_{}_get(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
1539 } else {
1540 let val_c = map_type_to_c_header(¶ms[1], interner, false);
1541 writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, {} key, {}* out);", mangled, key_c, val_c).unwrap();
1542 }
1543 }
1544 writeln!(out, "logos_handle_t logos_{}_keys(logos_handle_t handle);", mangled).unwrap();
1545 writeln!(out, "logos_handle_t logos_{}_values(logos_handle_t handle);", mangled).unwrap();
1546 writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1547 if is_key_text {
1548 let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(¶ms[1], interner, false) };
1549 writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* key, {} value);", mangled, val_c).unwrap();
1550 } else {
1551 let key_c = map_type_to_c_header(¶ms[0], interner, false);
1552 let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(¶ms[1], interner, false) };
1553 writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} key, {} value);", mangled, key_c, val_c).unwrap();
1554 }
1555 if is_key_text {
1556 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* key);", mangled).unwrap();
1557 } else {
1558 let key_c = map_type_to_c_header(¶ms[0], interner, false);
1559 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
1560 }
1561 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1562 writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
1563 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1564 }
1565 "Set" | "HashSet" if !params.is_empty() => {
1566 let is_inner_text = is_text_type(¶ms[0], interner);
1567 writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1568 if is_inner_text {
1569 writeln!(out, "bool logos_{}_contains(logos_handle_t handle, const char* value);", mangled).unwrap();
1570 } else {
1571 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1572 writeln!(out, "bool logos_{}_contains(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1573 }
1574 writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1575 if is_inner_text {
1576 writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* value);", mangled).unwrap();
1577 } else {
1578 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1579 writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1580 }
1581 if is_inner_text {
1582 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* value);", mangled).unwrap();
1583 } else {
1584 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1585 writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1586 }
1587 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1588 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1589 }
1590 "Option" | "Maybe" if !params.is_empty() => {
1591 let is_inner_text = is_text_type(¶ms[0], interner);
1592 writeln!(out, "bool logos_{}_is_some(logos_handle_t handle);", mangled).unwrap();
1593 if is_inner_text {
1594 writeln!(out, "char* logos_{}_unwrap(logos_handle_t handle);", mangled).unwrap();
1595 } else {
1596 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1597 writeln!(out, "logos_status_t logos_{}_unwrap(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
1598 }
1599 if is_inner_text {
1600 writeln!(out, "logos_handle_t logos_{}_some(const char* value);", mangled).unwrap();
1601 } else {
1602 let inner_c = map_type_to_c_header(¶ms[0], interner, false);
1603 writeln!(out, "logos_handle_t logos_{}_some({} value);", mangled, inner_c).unwrap();
1604 }
1605 writeln!(out, "logos_handle_t logos_{}_none(void);", mangled).unwrap();
1606 writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1607 }
1608 _ => {}
1609 }
1610 }
1611 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1612 let type_name = interner.resolve(*sym);
1613 match registry.get(*sym) {
1614 Some(TypeDef::Struct { fields, is_portable, .. }) => {
1615 let struct_lower = type_name.to_lowercase();
1616 for field in fields {
1617 let field_name = interner.resolve(field.name);
1618 let is_field_text = match &field.ty {
1619 FieldType::Primitive(s) | FieldType::Named(s) => {
1620 let n = interner.resolve(*s);
1621 n == "Text" || n == "String"
1622 }
1623 _ => false,
1624 };
1625 if is_field_text {
1626 writeln!(out, "char* logos_{}_{}(logos_handle_t handle);", struct_lower, field_name).unwrap();
1627 } else {
1628 let c_type = map_field_type_to_c(&field.ty, interner);
1629 writeln!(out, "{} logos_{}_{}(logos_handle_t handle);", c_type, struct_lower, field_name).unwrap();
1630 }
1631 }
1632 writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", struct_lower).unwrap();
1634 writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", struct_lower).unwrap();
1635 writeln!(out, "void logos_{}_free(logos_handle_t handle);", struct_lower).unwrap();
1636 }
1637 Some(TypeDef::Enum { variants, .. }) => {
1638 let enum_lower = type_name.to_lowercase();
1639 writeln!(out, "typedef enum {{").unwrap();
1641 for (i, variant) in variants.iter().enumerate() {
1642 let vname = interner.resolve(variant.name).to_uppercase();
1643 writeln!(out, " LOGOS_{}_{} = {},", type_name.to_uppercase(), vname, i).unwrap();
1644 }
1645 writeln!(out, "}} logos_{}_tag_t;", enum_lower).unwrap();
1646 writeln!(out, "int32_t logos_{}_tag(logos_handle_t handle);", enum_lower).unwrap();
1647 for variant in variants {
1649 let vname = interner.resolve(variant.name);
1650 let vname_lower = vname.to_lowercase();
1651 for field in &variant.fields {
1652 let fname = interner.resolve(field.name);
1653 let is_field_text = match &field.ty {
1654 FieldType::Primitive(s) | FieldType::Named(s) => {
1655 let n = interner.resolve(*s);
1656 n == "Text" || n == "String"
1657 }
1658 _ => false,
1659 };
1660 if is_field_text {
1661 writeln!(out, "char* logos_{}_{}_{fname}(logos_handle_t handle);", enum_lower, vname_lower, fname = fname).unwrap();
1662 } else {
1663 let c_type = map_field_type_to_c(&field.ty, interner);
1664 writeln!(out, "{} logos_{}_{}_{fname}(logos_handle_t handle);", c_type, enum_lower, vname_lower, fname = fname).unwrap();
1665 }
1666 }
1667 }
1668 writeln!(out, "void logos_{}_free(logos_handle_t handle);", enum_lower).unwrap();
1669 }
1670 _ => {}
1671 }
1672 }
1673 _ => {}
1674 }
1675 writeln!(out).unwrap();
1676 }
1677 }
1678
1679 writeln!(out, "#ifdef __cplusplus").unwrap();
1680 writeln!(out, "}}").unwrap();
1681 writeln!(out, "#endif\n").unwrap();
1682 writeln!(out, "#endif // {}_H", guard).unwrap();
1683
1684 out
1685}
1686pub(crate) fn map_type_to_c_header(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
1687 match ty {
1688 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1689 let name = interner.resolve(*sym);
1690 match name {
1691 "Int" => "int64_t".to_string(),
1692 "Nat" => "uint64_t".to_string(),
1693 "Real" | "Float" => "double".to_string(),
1694 "Bool" | "Boolean" => "bool".to_string(),
1695 "Byte" => "uint8_t".to_string(),
1696 "Char" => "uint32_t".to_string(), "Text" | "String" => {
1698 if is_return { "char*".to_string() } else { "const char*".to_string() }
1699 }
1700 "Unit" => "void".to_string(),
1701 other => other.to_string(), }
1703 }
1704 TypeExpr::Refinement { base, .. } => map_type_to_c_header(base, interner, is_return),
1705 TypeExpr::Generic { .. } => "logos_handle_t".to_string(),
1706 _ => "logos_handle_t".to_string(),
1707 }
1708}
1709
1710pub(crate) fn map_field_type_to_c(ft: &FieldType, interner: &Interner) -> String {
1712 match ft {
1713 FieldType::Primitive(sym) | FieldType::Named(sym) => {
1714 let name = interner.resolve(*sym);
1715 match name {
1716 "Int" => "int64_t".to_string(),
1717 "Nat" => "uint64_t".to_string(),
1718 "Real" | "Float" => "double".to_string(),
1719 "Bool" | "Boolean" => "bool".to_string(),
1720 "Byte" => "uint8_t".to_string(),
1721 "Char" => "uint32_t".to_string(),
1722 "Text" | "String" => "const char*".to_string(),
1723 other => other.to_string(),
1724 }
1725 }
1726 FieldType::Generic { .. } => "logos_handle_t".to_string(),
1727 FieldType::TypeParam(_) => "logos_handle_t".to_string(),
1728 }
1729}