use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use crate::analysis::registry::{FieldDef, FieldType, TypeDef, TypeRegistry, VariantDef};
use crate::ast::stmt::{Expr, Stmt, TypeExpr};
use crate::intern::{Interner, Symbol};
use super::detection::is_result_type;
use super::marshal::is_text_type;
use super::types::codegen_type_expr;
pub(crate) fn has_wasm_exports(stmts: &[Stmt], interner: &Interner) -> bool {
stmts.iter().any(|stmt| {
if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
interner.resolve(*target).eq_ignore_ascii_case("wasm")
} else {
false
}
})
}
pub(crate) fn has_c_exports(stmts: &[Stmt], interner: &Interner) -> bool {
stmts.iter().any(|stmt| {
if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
match export_target {
None => true,
Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
}
} else {
false
}
})
}
pub(crate) fn has_c_exports_with_text(stmts: &[Stmt], interner: &Interner) -> bool {
stmts.iter().any(|stmt| {
if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
let is_c = match export_target {
None => true,
Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
};
if !is_c { return false; }
let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
let has_text_return = return_type.as_ref().map_or(false, |ty| is_text_type(ty, interner));
has_text_param || has_text_return
} else {
false
}
})
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CAbiClass {
ValueType,
ReferenceType,
}
pub(crate) fn classify_type_for_c_abi(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> CAbiClass {
match ty {
TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
let name = interner.resolve(*sym);
match name {
"Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
| "Byte" | "Char" | "Unit" => CAbiClass::ValueType,
"Text" | "String" => CAbiClass::ValueType,
_ => {
if let Some(type_def) = registry.get(*sym) {
match type_def {
TypeDef::Struct { fields, .. } => {
let all_value = fields.iter().all(|f| {
is_value_type_field(&f.ty, interner)
});
if all_value && fields.len() <= 4 {
CAbiClass::ValueType
} else {
CAbiClass::ReferenceType
}
}
TypeDef::Enum { .. } => CAbiClass::ReferenceType,
TypeDef::Primitive => CAbiClass::ValueType,
TypeDef::Generic { .. } => CAbiClass::ReferenceType,
TypeDef::Alias { .. } => CAbiClass::ValueType,
}
} else {
CAbiClass::ValueType }
}
}
}
TypeExpr::Refinement { base, .. } => classify_type_for_c_abi(base, interner, registry),
TypeExpr::Generic { base, .. } => {
let base_name = interner.resolve(*base);
match base_name {
"Option" | "Maybe" => {
CAbiClass::ReferenceType
}
"Result" | "Seq" | "List" | "Vec" | "Map" | "HashMap"
| "Set" | "HashSet" => CAbiClass::ReferenceType,
_ => CAbiClass::ReferenceType,
}
}
TypeExpr::Function { .. } => CAbiClass::ReferenceType,
TypeExpr::Persistent { .. } => CAbiClass::ReferenceType,
}
}
fn is_value_type_field(ft: &FieldType, interner: &Interner) -> bool {
match ft {
FieldType::Primitive(sym) | FieldType::Named(sym) => {
let name = interner.resolve(*sym);
matches!(name, "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
| "Byte" | "Char" | "Unit")
}
FieldType::Generic { .. } => false, FieldType::TypeParam(_) => false,
}
}
pub(crate) fn mangle_type_for_c(ty: &TypeExpr, interner: &Interner) -> String {
match ty {
TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
let name = interner.resolve(*sym);
match name {
"Int" => "i64".to_string(),
"Nat" => "u64".to_string(),
"Real" | "Float" => "f64".to_string(),
"Bool" | "Boolean" => "bool".to_string(),
"Byte" => "u8".to_string(),
"Char" => "char".to_string(),
"Text" | "String" => "string".to_string(),
other => other.to_lowercase(),
}
}
TypeExpr::Refinement { base, .. } => mangle_type_for_c(base, interner),
TypeExpr::Generic { base, params } => {
let base_name = interner.resolve(*base);
let param_strs: Vec<String> = params.iter()
.map(|p| mangle_type_for_c(p, interner))
.collect();
match base_name {
"Seq" | "List" | "Vec" => format!("seq_{}", param_strs.join("_")),
"Map" | "HashMap" => format!("map_{}", param_strs.join("_")),
"Set" | "HashSet" => format!("set_{}", param_strs.join("_")),
"Option" | "Maybe" => format!("option_{}", param_strs.join("_")),
"Result" => format!("result_{}", param_strs.join("_")),
other => format!("{}_{}", other.to_lowercase(), param_strs.join("_")),
}
}
TypeExpr::Function { .. } => "fn".to_string(),
TypeExpr::Persistent { inner } => mangle_type_for_c(inner, interner),
}
}
pub(crate) fn codegen_logos_runtime_preamble() -> String {
let mut out = String::new();
writeln!(out, "// ═══ LogicAffeine Universal ABI Runtime ═══\n").unwrap();
writeln!(out, "#[repr(C)]").unwrap();
writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq)]").unwrap();
writeln!(out, "pub enum LogosStatus {{").unwrap();
writeln!(out, " Ok = 0,").unwrap();
writeln!(out, " Error = 1,").unwrap();
writeln!(out, " RefinementViolation = 2,").unwrap();
writeln!(out, " NullPointer = 3,").unwrap();
writeln!(out, " OutOfBounds = 4,").unwrap();
writeln!(out, " DeserializationFailed = 5,").unwrap();
writeln!(out, " InvalidHandle = 6,").unwrap();
writeln!(out, " ContainsNullByte = 7,").unwrap();
writeln!(out, " ThreadPanic = 8,").unwrap();
writeln!(out, " MemoryExhausted = 9,").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "pub type LogosHandle = *mut std::ffi::c_void;\n").unwrap();
writeln!(out, "fn logos_error_store() -> &'static std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>> {{").unwrap();
writeln!(out, " use std::sync::OnceLock;").unwrap();
writeln!(out, " static STORE: OnceLock<std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>>> = OnceLock::new();").unwrap();
writeln!(out, " STORE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "fn logos_set_last_error(msg: String) {{").unwrap();
writeln!(out, " let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " store.insert(std::thread::current().id(), msg);").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "thread_local! {{").unwrap();
writeln!(out, " static LOGOS_ERROR_CACHE: std::cell::RefCell<Option<std::ffi::CString>> = std::cell::RefCell::new(None);").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_last_error() -> *const std::os::raw::c_char {{").unwrap();
writeln!(out, " let msg = logos_error_store().lock().unwrap_or_else(|e| e.into_inner())").unwrap();
writeln!(out, " .get(&std::thread::current().id()).cloned();").unwrap();
writeln!(out, " match msg {{").unwrap();
writeln!(out, " Some(s) => match std::ffi::CString::new(s) {{").unwrap();
writeln!(out, " Ok(cstr) => {{").unwrap();
writeln!(out, " let ptr = cstr.as_ptr();").unwrap();
writeln!(out, " LOGOS_ERROR_CACHE.with(|cache| {{ cache.borrow_mut().replace(cstr); }});").unwrap();
writeln!(out, " LOGOS_ERROR_CACHE.with(|cache| {{").unwrap();
writeln!(out, " cache.borrow().as_ref().map_or(std::ptr::null(), |c| c.as_ptr())").unwrap();
writeln!(out, " }})").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " Err(_) => std::ptr::null(),").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " None => std::ptr::null(),").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_get_last_error() -> *const std::os::raw::c_char {{").unwrap();
writeln!(out, " logos_last_error()").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_clear_error() {{").unwrap();
writeln!(out, " let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " store.remove(&std::thread::current().id());").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_free_string(ptr: *mut std::os::raw::c_char) {{").unwrap();
writeln!(out, " if !ptr.is_null() {{").unwrap();
writeln!(out, " unsafe {{ drop(std::ffi::CString::from_raw(ptr)); }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "pub const LOGOS_ABI_VERSION: u32 = 1;\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_version() -> *const std::os::raw::c_char {{").unwrap();
writeln!(out, " concat!(env!(\"CARGO_PKG_VERSION\"), \"\\0\").as_ptr() as *const std::os::raw::c_char").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_abi_version() -> u32 {{").unwrap();
writeln!(out, " LOGOS_ABI_VERSION").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "struct HandleEntry {{").unwrap();
writeln!(out, " data: usize,").unwrap();
writeln!(out, " generation: u64,").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "struct HandleRegistry {{").unwrap();
writeln!(out, " entries: std::collections::HashMap<u64, HandleEntry>,").unwrap();
writeln!(out, " counter: u64,").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "impl HandleRegistry {{").unwrap();
writeln!(out, " fn new() -> Self {{").unwrap();
writeln!(out, " HandleRegistry {{ entries: std::collections::HashMap::new(), counter: 0 }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " fn register(&mut self, ptr: usize) -> (u64, u64) {{").unwrap();
writeln!(out, " self.counter += 1;").unwrap();
writeln!(out, " let id = self.counter;").unwrap();
writeln!(out, " let generation = id;").unwrap();
writeln!(out, " self.entries.insert(id, HandleEntry {{ data: ptr, generation }});").unwrap();
writeln!(out, " (id, generation)").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " fn validate_handle(&self, id: u64, generation: u64) -> bool {{").unwrap();
writeln!(out, " self.entries.get(&id).map_or(false, |e| e.generation == generation)").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " fn deref(&self, id: u64) -> Option<usize> {{").unwrap();
writeln!(out, " self.entries.get(&id).map(|e| e.data)").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " fn free(&mut self, id: u64) -> Result<usize, ()> {{").unwrap();
writeln!(out, " if let Some(entry) = self.entries.remove(&id) {{ Ok(entry.data) }} else {{ Err(()) }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, "}}\n").unwrap();
writeln!(out, "fn logos_handle_registry() -> &'static std::sync::Mutex<HandleRegistry> {{").unwrap();
writeln!(out, " use std::sync::OnceLock;").unwrap();
writeln!(out, " static REGISTRY: OnceLock<std::sync::Mutex<HandleRegistry>> = OnceLock::new();").unwrap();
writeln!(out, " REGISTRY.get_or_init(|| std::sync::Mutex::new(HandleRegistry::new()))").unwrap();
writeln!(out, "}}\n").unwrap();
out
}
fn emit_catch_unwind_open(out: &mut String) {
writeln!(out, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
}
fn emit_catch_unwind_close(out: &mut String, default_expr: &str) {
writeln!(out, " }})) {{").unwrap();
writeln!(out, " Ok(__v) => __v,").unwrap();
writeln!(out, " Err(__panic) => {{").unwrap();
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();
writeln!(out, " logos_set_last_error(__msg);").unwrap();
writeln!(out, " {}", default_expr).unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }}").unwrap();
}
fn emit_null_handle_check(out: &mut String, default_expr: &str) {
writeln!(out, " if handle.is_null() {{ logos_set_last_error(\"NullPointer: handle is null\".to_string()); return {}; }}", default_expr).unwrap();
}
fn emit_null_out_check(out: &mut String, default_expr: &str) {
writeln!(out, " if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return {}; }}", default_expr).unwrap();
}
fn emit_registry_deref(out: &mut String, default_expr: &str) {
writeln!(out, " let __id = handle as u64;").unwrap();
writeln!(out, " let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let __ptr = match __reg.deref(__id) {{").unwrap();
writeln!(out, " Some(p) => p,").unwrap();
writeln!(out, " None => {{ logos_set_last_error(\"InvalidHandle: handle not found in registry\".to_string()); return {}; }}", default_expr).unwrap();
writeln!(out, " }};").unwrap();
writeln!(out, " drop(__reg);").unwrap();
}
fn emit_registry_create(out: &mut String, alloc_expr: &str, _rust_type: &str) {
writeln!(out, " let __data = {};", alloc_expr).unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(__data)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " __id as LogosHandle").unwrap();
}
fn emit_registry_free(out: &mut String, rust_type: &str) {
writeln!(out, " if handle.is_null() {{ return; }}").unwrap();
writeln!(out, " let __id = handle as u64;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " match __reg.free(__id) {{").unwrap();
writeln!(out, " Ok(__ptr) => {{ unsafe {{ drop(Box::from_raw(__ptr as *mut {})); }} }}", rust_type).unwrap();
writeln!(out, " Err(()) => {{ logos_set_last_error(\"InvalidHandle: handle already freed or not found\".to_string()); }}").unwrap();
writeln!(out, " }}").unwrap();
}
pub(crate) fn codegen_c_accessors(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
let mut out = String::new();
let mangled = mangle_type_for_c(ty, interner);
match ty {
TypeExpr::Generic { base, params } => {
let base_name = interner.resolve(*base);
match base_name {
"Seq" | "List" | "Vec" if !params.is_empty() => {
let inner_rust_type = codegen_type_expr(¶ms[0], interner);
let _inner_mangled = mangle_type_for_c(¶ms[0], interner);
let is_inner_text = is_text_type(¶ms[0], interner);
let vec_type = format!("Vec<{}>", inner_rust_type);
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "0");
emit_registry_deref(&mut out, "0");
writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
writeln!(out, " seq.len()").unwrap();
emit_catch_unwind_close(&mut out, "0");
writeln!(out, "}}\n").unwrap();
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize) -> *mut std::os::raw::c_char {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
writeln!(out, " if index >= seq.len() {{").unwrap();
writeln!(out, " logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
writeln!(out, " return std::ptr::null_mut();").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " match std::ffi::CString::new(seq[index].clone()) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"String contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
writeln!(out, " if index >= seq.len() {{").unwrap();
writeln!(out, " logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
writeln!(out, " return LogosStatus::OutOfBounds;").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " unsafe {{ *out = seq[index].clone(); }}").unwrap();
writeln!(out, " LogosStatus::Ok").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_create(&mut out, &format!("Vec::<{}>::new()", inner_rust_type), &vec_type);
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "()");
emit_registry_deref(&mut out, "()");
writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " seq.push(val_str);").unwrap();
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "()");
emit_registry_deref(&mut out, "()");
writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
writeln!(out, " seq.push(value);").unwrap();
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
writeln!(out, " match seq.pop() {{").unwrap();
writeln!(out, " Some(val) => match std::ffi::CString::new(val) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
writeln!(out, " let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
writeln!(out, " match seq.pop() {{").unwrap();
writeln!(out, " Some(val) => {{ unsafe {{ *out = val; }} LogosStatus::Ok }}").unwrap();
writeln!(out, " None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); LogosStatus::Error }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
writeln!(out, " match serde_json::to_string(seq) {{").unwrap();
writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", vec_type).unwrap();
writeln!(out, " Ok(val) => {{").unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
writeln!(out, " LogosStatus::Ok").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_free(&mut out, &vec_type);
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
"Map" | "HashMap" if params.len() >= 2 => {
let key_rust = codegen_type_expr(¶ms[0], interner);
let val_rust = codegen_type_expr(¶ms[1], interner);
let is_key_text = is_text_type(¶ms[0], interner);
let is_val_text = is_text_type(¶ms[1], interner);
let map_type = format!("FxHashMap<{}, {}>", key_rust, val_rust);
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "0");
emit_registry_deref(&mut out, "0");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " map.len()").unwrap();
emit_catch_unwind_close(&mut out, "0");
writeln!(out, "}}\n").unwrap();
if is_key_text {
if is_val_text {
writeln!(out, "#[no_mangle]").unwrap();
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();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " match map.get(&key_str) {{").unwrap();
writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " None => std::ptr::null_mut(),").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: *const std::os::raw::c_char, out: *mut {}) -> LogosStatus {{", mangled, val_rust).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " match map.get(&key_str) {{").unwrap();
writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
writeln!(out, " None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key_str)); LogosStatus::Error }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
}
} else {
if is_val_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}) -> *mut std::os::raw::c_char {{", mangled, key_rust).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " match map.get(&key) {{").unwrap();
writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " None => std::ptr::null_mut(),").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}, out: *mut {}) -> LogosStatus {{", mangled, key_rust, val_rust).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " match map.get(&key) {{").unwrap();
writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
writeln!(out, " None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key)); LogosStatus::Error }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
}
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_keys(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " let keys: Vec<{}> = map.keys().cloned().collect();", key_rust).unwrap();
writeln!(out, " let __kptr = Box::into_raw(Box::new(keys)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__kptr);").unwrap();
writeln!(out, " __id as LogosHandle").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_values(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " let values: Vec<{}> = map.values().cloned().collect();", val_rust).unwrap();
writeln!(out, " let __vptr = Box::into_raw(Box::new(values)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__vptr);").unwrap();
writeln!(out, " __id as LogosHandle").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_create(&mut out, &format!("FxHashMap::<{}, {}>::default()", key_rust, val_rust), &map_type);
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
if is_key_text {
let val_param = if is_val_text {
"value: *const std::os::raw::c_char".to_string()
} else {
format!("value: {}", val_rust)
};
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: *const std::os::raw::c_char, {}) {{", mangled, val_param).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "()");
emit_registry_deref(&mut out, "()");
writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
if is_val_text {
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " map.insert(key_str, val_str);").unwrap();
} else {
writeln!(out, " map.insert(key_str, value);").unwrap();
}
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
} else {
let val_param = if is_val_text {
"value: *const std::os::raw::c_char".to_string()
} else {
format!("value: {}", val_rust)
};
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: {}, {}) {{", mangled, key_rust, val_param).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "()");
emit_registry_deref(&mut out, "()");
writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
if is_val_text {
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " map.insert(key, val_str);").unwrap();
} else {
writeln!(out, " map.insert(key, value);").unwrap();
}
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
if is_key_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
writeln!(out, " let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " map.remove(&key_str).is_some()").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: {}) -> bool {{", mangled, key_rust).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
writeln!(out, " map.remove(&key).is_some()").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
writeln!(out, " match serde_json::to_string(map) {{").unwrap();
writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", map_type).unwrap();
writeln!(out, " Ok(val) => {{").unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
writeln!(out, " LogosStatus::Ok").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_free(&mut out, &map_type);
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
"Set" | "HashSet" if !params.is_empty() => {
let inner_rust_type = codegen_type_expr(¶ms[0], interner);
let is_inner_text = is_text_type(¶ms[0], interner);
let set_type = format!("FxHashSet<{}>", inner_rust_type);
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "0");
emit_registry_deref(&mut out, "0");
writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
writeln!(out, " set.len()").unwrap();
emit_catch_unwind_close(&mut out, "0");
writeln!(out, "}}\n").unwrap();
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " set.contains(&val_str)").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
writeln!(out, " set.contains(&value)").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_create(&mut out, &format!("FxHashSet::<{}>::default()", inner_rust_type), &set_type);
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "()");
emit_registry_deref(&mut out, "()");
writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " set.insert(val_str);").unwrap();
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "()");
emit_registry_deref(&mut out, "()");
writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
writeln!(out, " set.insert(value);").unwrap();
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " set.remove(&val_str)").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
writeln!(out, " set.remove(&value)").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
writeln!(out, " match serde_json::to_string(set) {{").unwrap();
writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_free(&mut out, &set_type);
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
"Option" | "Maybe" if !params.is_empty() => {
let inner_rust_type = codegen_type_expr(¶ms[0], interner);
let is_inner_text = is_text_type(¶ms[0], interner);
let opt_type = format!("Option<{}>", inner_rust_type);
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_is_some(handle: LogosHandle) -> bool {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "false");
emit_registry_deref(&mut out, "false");
writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
writeln!(out, " opt.is_some()").unwrap();
emit_catch_unwind_close(&mut out, "false");
writeln!(out, "}}\n").unwrap();
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
writeln!(out, " match opt {{").unwrap();
writeln!(out, " Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
writeln!(out, " let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
writeln!(out, " match opt {{").unwrap();
writeln!(out, " Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
writeln!(out, " None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); LogosStatus::Error }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
}
if is_inner_text {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_some(value: *const std::os::raw::c_char) -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
writeln!(out, " if value.is_null() {{ logos_set_last_error(\"NullPointer: value is null\".to_string()); return std::ptr::null_mut() as LogosHandle; }}").unwrap();
writeln!(out, " let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
writeln!(out, " let opt: {} = Some(val_str);", opt_type).unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " __id as LogosHandle").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
} else {
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_some(value: {}) -> LogosHandle {{", mangled, inner_rust_type).unwrap();
emit_catch_unwind_open(&mut out);
writeln!(out, " let opt: {} = Some(value);", opt_type).unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " __id as LogosHandle").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_none() -> LogosHandle {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
writeln!(out, " let opt: {} = None;", opt_type).unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " __id as LogosHandle").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_free(&mut out, &opt_type);
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
_ => {}
}
}
TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
let type_name = interner.resolve(*sym);
let type_def = registry.get(*sym);
match type_def {
Some(TypeDef::Struct { fields, is_portable, .. }) => {
let mangled_struct = type_name.to_lowercase();
let rust_struct_name = type_name.to_string();
for field in fields {
let field_name = interner.resolve(field.name);
let is_field_text = match &field.ty {
FieldType::Primitive(s) | FieldType::Named(s) => {
let n = interner.resolve(*s);
n == "Text" || n == "String"
}
_ => false,
};
writeln!(out, "#[no_mangle]").unwrap();
if is_field_text {
writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
mangled_struct, field = field_name).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
writeln!(out, " match std::ffi::CString::new(obj.{}.clone()) {{", field_name).unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
let (field_rust_type, is_char) = match &field.ty {
FieldType::Primitive(s) | FieldType::Named(s) => {
let n = interner.resolve(*s);
match n {
"Int" => ("i64", false),
"Nat" => ("u64", false),
"Real" | "Float" => ("f64", false),
"Bool" | "Boolean" => ("bool", false),
"Byte" => ("u8", false),
"Char" => ("u32", true),
_ => (n, false),
}
}
_ => ("LogosHandle", false),
};
writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> {} {{",
mangled_struct, field_rust_type, field = field_name).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "Default::default()");
emit_registry_deref(&mut out, "Default::default()");
writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
if is_char {
writeln!(out, " obj.{}.clone() as u32", field_name).unwrap();
} else {
writeln!(out, " obj.{}.clone()", field_name).unwrap();
}
emit_catch_unwind_close(&mut out, "Default::default()");
writeln!(out, "}}\n").unwrap();
}
}
{
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled_struct).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
writeln!(out, " match serde_json::to_string(obj) {{").unwrap();
writeln!(out, " Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }},").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled_struct).unwrap();
emit_catch_unwind_open(&mut out);
writeln!(out, " if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
emit_null_out_check(&mut out, "LogosStatus::NullPointer");
writeln!(out, " let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
writeln!(out, " match serde_json::from_str::<{}>(&json_str) {{", rust_struct_name).unwrap();
writeln!(out, " Ok(val) => {{").unwrap();
writeln!(out, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
writeln!(out, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
writeln!(out, " let (__id, _) = __reg.register(__ptr);").unwrap();
writeln!(out, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
writeln!(out, " LogosStatus::Ok").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
writeln!(out, "}}\n").unwrap();
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_struct).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_free(&mut out, &rust_struct_name);
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
Some(TypeDef::Enum { variants, .. }) => {
let mangled_enum = type_name.to_lowercase();
let rust_enum_name = type_name.to_string();
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_tag(handle: LogosHandle) -> i32 {{", mangled_enum).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "-1");
emit_registry_deref(&mut out, "-1");
writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
writeln!(out, " match obj {{").unwrap();
for (i, variant) in variants.iter().enumerate() {
let vname = interner.resolve(variant.name);
if variant.fields.is_empty() {
writeln!(out, " {}::{} => {},", rust_enum_name, vname, i).unwrap();
} else {
writeln!(out, " {}::{}{{ .. }} => {},", rust_enum_name, vname, i).unwrap();
}
}
writeln!(out, " }}").unwrap();
emit_catch_unwind_close(&mut out, "-1");
writeln!(out, "}}\n").unwrap();
for variant in variants {
let vname = interner.resolve(variant.name);
let vname_lower = vname.to_lowercase();
for field in &variant.fields {
let fname = interner.resolve(field.name);
let is_field_text = match &field.ty {
FieldType::Primitive(s) | FieldType::Named(s) => {
let n = interner.resolve(*s);
n == "Text" || n == "String"
}
_ => false,
};
writeln!(out, "#[no_mangle]").unwrap();
if is_field_text {
writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
mangled_enum, vname_lower, fname = fname).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "std::ptr::null_mut()");
emit_registry_deref(&mut out, "std::ptr::null_mut()");
writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
writeln!(out, " if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
writeln!(out, " match std::ffi::CString::new({fname}.clone()) {{", fname = fname).unwrap();
writeln!(out, " Ok(cstr) => cstr.into_raw(),").unwrap();
writeln!(out, " Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
writeln!(out, " }}").unwrap();
writeln!(out, " }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); std::ptr::null_mut() }}", vname).unwrap();
emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
writeln!(out, "}}\n").unwrap();
} else {
let field_rust_type = match &field.ty {
FieldType::Primitive(s) | FieldType::Named(s) => {
let n = interner.resolve(*s);
match n {
"Int" => "i64",
"Nat" => "u64",
"Real" | "Float" => "f64",
"Bool" | "Boolean" => "bool",
"Byte" => "u8",
"Char" => "u32",
_ => n,
}
}
_ => "LogosHandle",
};
writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> {} {{",
mangled_enum, vname_lower, field_rust_type, fname = fname).unwrap();
emit_catch_unwind_open(&mut out);
emit_null_handle_check(&mut out, "Default::default()");
emit_registry_deref(&mut out, "Default::default()");
writeln!(out, " let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
writeln!(out, " if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
writeln!(out, " {fname}.clone()", fname = fname).unwrap();
writeln!(out, " }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); Default::default() }}", vname).unwrap();
emit_catch_unwind_close(&mut out, "Default::default()");
writeln!(out, "}}\n").unwrap();
}
}
}
writeln!(out, "#[no_mangle]").unwrap();
writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_enum).unwrap();
emit_catch_unwind_open(&mut out);
emit_registry_free(&mut out, &rust_enum_name);
emit_catch_unwind_close(&mut out, "()");
writeln!(out, "}}\n").unwrap();
}
_ => {}
}
}
_ => {}
}
out
}
pub(crate) fn collect_c_export_reference_types<'a>(
stmts: &'a [Stmt<'a>],
interner: &Interner,
registry: &TypeRegistry,
) -> Vec<&'a TypeExpr<'a>> {
let mut seen = HashSet::new();
let mut types = Vec::new();
for stmt in stmts {
if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
let is_c = match export_target {
None => true,
Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
};
if !is_c { continue; }
for (_, ty) in params.iter() {
if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
let mangled = mangle_type_for_c(ty, interner);
if seen.insert(mangled) {
types.push(*ty);
}
}
}
if let Some(ty) = return_type {
if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
let mangled = mangle_type_for_c(ty, interner);
if seen.insert(mangled) {
types.push(*ty);
}
}
}
}
}
types
}
pub(crate) fn collect_c_export_value_type_structs(
stmts: &[Stmt],
interner: &Interner,
registry: &TypeRegistry,
) -> HashSet<Symbol> {
let mut value_structs = HashSet::new();
for stmt in stmts {
if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
let is_c = match export_target {
None => true,
Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
};
if !is_c { continue; }
let all_types: Vec<&TypeExpr> = params.iter()
.map(|(_, ty)| *ty)
.chain(return_type.iter().copied())
.collect();
for ty in all_types {
if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
if registry.get(*sym).is_some() {
value_structs.insert(*sym);
}
}
}
}
}
}
value_structs
}
pub(crate) fn collect_c_export_ref_structs(
stmts: &[Stmt],
interner: &Interner,
registry: &TypeRegistry,
) -> HashSet<Symbol> {
let mut ref_structs = HashSet::new();
let ref_types = collect_c_export_reference_types(stmts, interner, registry);
for ty in ref_types {
if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
if registry.get(*sym).map_or(false, |d| matches!(d, TypeDef::Struct { .. })) {
ref_structs.insert(*sym);
}
}
}
ref_structs
}
pub fn generate_c_header(
stmts: &[Stmt],
module_name: &str,
interner: &Interner,
registry: &TypeRegistry,
) -> String {
let mut out = String::new();
let guard = module_name.to_uppercase().replace('-', "_");
writeln!(out, "// Generated from {}.lg — LogicAffeine Universal ABI", module_name).unwrap();
writeln!(out, "#ifndef {}_H", guard).unwrap();
writeln!(out, "#define {}_H\n", guard).unwrap();
writeln!(out, "#include <stdint.h>").unwrap();
writeln!(out, "#include <stdbool.h>").unwrap();
writeln!(out, "#include <stddef.h>\n").unwrap();
writeln!(out, "#ifdef __cplusplus").unwrap();
writeln!(out, "extern \"C\" {{").unwrap();
writeln!(out, "#endif\n").unwrap();
writeln!(out, "// ═══ Runtime ═══").unwrap();
writeln!(out, "typedef enum {{").unwrap();
writeln!(out, " LOGOS_STATUS_OK = 0,").unwrap();
writeln!(out, " LOGOS_STATUS_ERROR = 1,").unwrap();
writeln!(out, " LOGOS_STATUS_REFINEMENT_VIOLATION = 2,").unwrap();
writeln!(out, " LOGOS_STATUS_NULL_POINTER = 3,").unwrap();
writeln!(out, " LOGOS_STATUS_OUT_OF_BOUNDS = 4,").unwrap();
writeln!(out, " LOGOS_STATUS_DESERIALIZATION_FAILED = 5,").unwrap();
writeln!(out, " LOGOS_STATUS_INVALID_HANDLE = 6,").unwrap();
writeln!(out, " LOGOS_STATUS_CONTAINS_NULL_BYTE = 7,").unwrap();
writeln!(out, " LOGOS_STATUS_THREAD_PANIC = 8,").unwrap();
writeln!(out, " LOGOS_STATUS_MEMORY_EXHAUSTED = 9,").unwrap();
writeln!(out, "}} logos_status_t;\n").unwrap();
writeln!(out, "typedef void* logos_handle_t;\n").unwrap();
writeln!(out, "const char* logos_last_error(void);").unwrap();
writeln!(out, "const char* logos_get_last_error(void);").unwrap();
writeln!(out, "void logos_clear_error(void);").unwrap();
writeln!(out, "void logos_free_string(char* str);\n").unwrap();
writeln!(out, "#define LOGOS_ABI_VERSION 1").unwrap();
writeln!(out, "const char* logos_version(void);").unwrap();
writeln!(out, "uint32_t logos_abi_version(void);\n").unwrap();
let mut emitted_structs = HashSet::new();
for stmt in stmts {
if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
let is_c = match export_target {
None => true,
Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
};
if !is_c { continue; }
let all_types: Vec<&TypeExpr> = params.iter()
.map(|(_, ty)| *ty)
.chain(return_type.iter().copied())
.collect();
for ty in all_types {
if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
if let Some(TypeDef::Struct { fields, .. }) = registry.get(*sym) {
let name = interner.resolve(*sym);
if emitted_structs.insert(name.to_string()) {
writeln!(out, "// ═══ Value Types ═══").unwrap();
writeln!(out, "typedef struct {{").unwrap();
for field in fields {
let c_type = map_field_type_to_c(&field.ty, interner);
writeln!(out, " {} {};", c_type, interner.resolve(field.name)).unwrap();
}
writeln!(out, "}} {};\n", name).unwrap();
}
}
}
}
}
}
}
writeln!(out, "// ═══ Exported Functions ═══").unwrap();
for stmt in stmts {
if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
let is_c = match export_target {
None => true,
Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
};
if !is_c { continue; }
let func_name = format!("logos_{}", interner.resolve(*name));
let has_ref_return = return_type.map_or(false, |ty| {
classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
});
let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
let has_refinement_param = params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
let uses_status_code = has_ref_return || has_result_return || has_text_return || has_refinement_param;
let mut c_params = Vec::new();
for (pname, ptype) in params.iter() {
let pn = interner.resolve(*pname);
if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
c_params.push(format!("logos_handle_t {}", pn));
} else {
c_params.push(format!("{} {}", map_type_to_c_header(ptype, interner, false), pn));
}
}
if uses_status_code {
if let Some(ret_ty) = return_type {
if is_result_type(ret_ty, interner) {
if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
if !rparams.is_empty() {
let ok_ty = &rparams[0];
if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
c_params.push("logos_handle_t* out".to_string());
} else {
c_params.push(format!("{}* out", map_type_to_c_header(ok_ty, interner, false)));
}
}
}
} else if classify_type_for_c_abi(ret_ty, interner, registry) == CAbiClass::ReferenceType {
c_params.push("logos_handle_t* out".to_string());
} else if has_text_return {
c_params.push("char** out".to_string());
}
}
writeln!(out, "logos_status_t {}({});", func_name, c_params.join(", ")).unwrap();
} else {
let ret = return_type
.map(|ty| map_type_to_c_header(ty, interner, true))
.unwrap_or_else(|| "void".to_string());
writeln!(out, "{} {}({});", ret, func_name, c_params.join(", ")).unwrap();
}
}
}
writeln!(out).unwrap();
let ref_types = collect_c_export_reference_types(stmts, interner, registry);
if !ref_types.is_empty() {
for ref_ty in &ref_types {
let mangled = mangle_type_for_c(ref_ty, interner);
writeln!(out, "// ═══ {} Accessors ═══", mangled).unwrap();
match ref_ty {
TypeExpr::Generic { base, params } => {
let base_name = interner.resolve(*base);
match base_name {
"Seq" | "List" | "Vec" if !params.is_empty() => {
let is_inner_text = is_text_type(¶ms[0], interner);
writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
if is_inner_text {
writeln!(out, "char* logos_{}_at(logos_handle_t handle, size_t index);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "logos_status_t logos_{}_at(logos_handle_t handle, size_t index, {}* out);", mangled, inner_c).unwrap();
}
writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
if is_inner_text {
writeln!(out, "void logos_{}_push(logos_handle_t handle, const char* value);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "void logos_{}_push(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
}
if is_inner_text {
writeln!(out, "char* logos_{}_pop(logos_handle_t handle);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "logos_status_t logos_{}_pop(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
}
writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
}
"Map" | "HashMap" if params.len() >= 2 => {
let is_key_text = is_text_type(¶ms[0], interner);
let is_val_text = is_text_type(¶ms[1], interner);
writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
if is_key_text {
if is_val_text {
writeln!(out, "char* logos_{}_get(logos_handle_t handle, const char* key);", mangled).unwrap();
} else {
let val_c = map_type_to_c_header(¶ms[1], interner, false);
writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, const char* key, {}* out);", mangled, val_c).unwrap();
}
} else {
let key_c = map_type_to_c_header(¶ms[0], interner, false);
if is_val_text {
writeln!(out, "char* logos_{}_get(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
} else {
let val_c = map_type_to_c_header(¶ms[1], interner, false);
writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, {} key, {}* out);", mangled, key_c, val_c).unwrap();
}
}
writeln!(out, "logos_handle_t logos_{}_keys(logos_handle_t handle);", mangled).unwrap();
writeln!(out, "logos_handle_t logos_{}_values(logos_handle_t handle);", mangled).unwrap();
writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
if is_key_text {
let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(¶ms[1], interner, false) };
writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* key, {} value);", mangled, val_c).unwrap();
} else {
let key_c = map_type_to_c_header(¶ms[0], interner, false);
let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(¶ms[1], interner, false) };
writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} key, {} value);", mangled, key_c, val_c).unwrap();
}
if is_key_text {
writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* key);", mangled).unwrap();
} else {
let key_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
}
writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
}
"Set" | "HashSet" if !params.is_empty() => {
let is_inner_text = is_text_type(¶ms[0], interner);
writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
if is_inner_text {
writeln!(out, "bool logos_{}_contains(logos_handle_t handle, const char* value);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "bool logos_{}_contains(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
}
writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
if is_inner_text {
writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* value);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
}
if is_inner_text {
writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* value);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
}
writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
}
"Option" | "Maybe" if !params.is_empty() => {
let is_inner_text = is_text_type(¶ms[0], interner);
writeln!(out, "bool logos_{}_is_some(logos_handle_t handle);", mangled).unwrap();
if is_inner_text {
writeln!(out, "char* logos_{}_unwrap(logos_handle_t handle);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "logos_status_t logos_{}_unwrap(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
}
if is_inner_text {
writeln!(out, "logos_handle_t logos_{}_some(const char* value);", mangled).unwrap();
} else {
let inner_c = map_type_to_c_header(¶ms[0], interner, false);
writeln!(out, "logos_handle_t logos_{}_some({} value);", mangled, inner_c).unwrap();
}
writeln!(out, "logos_handle_t logos_{}_none(void);", mangled).unwrap();
writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
}
_ => {}
}
}
TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
let type_name = interner.resolve(*sym);
match registry.get(*sym) {
Some(TypeDef::Struct { fields, is_portable, .. }) => {
let struct_lower = type_name.to_lowercase();
for field in fields {
let field_name = interner.resolve(field.name);
let is_field_text = match &field.ty {
FieldType::Primitive(s) | FieldType::Named(s) => {
let n = interner.resolve(*s);
n == "Text" || n == "String"
}
_ => false,
};
if is_field_text {
writeln!(out, "char* logos_{}_{}(logos_handle_t handle);", struct_lower, field_name).unwrap();
} else {
let c_type = map_field_type_to_c(&field.ty, interner);
writeln!(out, "{} logos_{}_{}(logos_handle_t handle);", c_type, struct_lower, field_name).unwrap();
}
}
writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", struct_lower).unwrap();
writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", struct_lower).unwrap();
writeln!(out, "void logos_{}_free(logos_handle_t handle);", struct_lower).unwrap();
}
Some(TypeDef::Enum { variants, .. }) => {
let enum_lower = type_name.to_lowercase();
writeln!(out, "typedef enum {{").unwrap();
for (i, variant) in variants.iter().enumerate() {
let vname = interner.resolve(variant.name).to_uppercase();
writeln!(out, " LOGOS_{}_{} = {},", type_name.to_uppercase(), vname, i).unwrap();
}
writeln!(out, "}} logos_{}_tag_t;", enum_lower).unwrap();
writeln!(out, "int32_t logos_{}_tag(logos_handle_t handle);", enum_lower).unwrap();
for variant in variants {
let vname = interner.resolve(variant.name);
let vname_lower = vname.to_lowercase();
for field in &variant.fields {
let fname = interner.resolve(field.name);
let is_field_text = match &field.ty {
FieldType::Primitive(s) | FieldType::Named(s) => {
let n = interner.resolve(*s);
n == "Text" || n == "String"
}
_ => false,
};
if is_field_text {
writeln!(out, "char* logos_{}_{}_{fname}(logos_handle_t handle);", enum_lower, vname_lower, fname = fname).unwrap();
} else {
let c_type = map_field_type_to_c(&field.ty, interner);
writeln!(out, "{} logos_{}_{}_{fname}(logos_handle_t handle);", c_type, enum_lower, vname_lower, fname = fname).unwrap();
}
}
}
writeln!(out, "void logos_{}_free(logos_handle_t handle);", enum_lower).unwrap();
}
_ => {}
}
}
_ => {}
}
writeln!(out).unwrap();
}
}
writeln!(out, "#ifdef __cplusplus").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out, "#endif\n").unwrap();
writeln!(out, "#endif // {}_H", guard).unwrap();
out
}
pub(crate) fn map_type_to_c_header(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
match ty {
TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
let name = interner.resolve(*sym);
match name {
"Int" => "int64_t".to_string(),
"Nat" => "uint64_t".to_string(),
"Real" | "Float" => "double".to_string(),
"Bool" | "Boolean" => "bool".to_string(),
"Byte" => "uint8_t".to_string(),
"Char" => "uint32_t".to_string(), "Text" | "String" => {
if is_return { "char*".to_string() } else { "const char*".to_string() }
}
"Unit" => "void".to_string(),
other => other.to_string(), }
}
TypeExpr::Refinement { base, .. } => map_type_to_c_header(base, interner, is_return),
TypeExpr::Generic { .. } => "logos_handle_t".to_string(),
_ => "logos_handle_t".to_string(),
}
}
pub(crate) fn map_field_type_to_c(ft: &FieldType, interner: &Interner) -> String {
match ft {
FieldType::Primitive(sym) | FieldType::Named(sym) => {
let name = interner.resolve(*sym);
match name {
"Int" => "int64_t".to_string(),
"Nat" => "uint64_t".to_string(),
"Real" | "Float" => "double".to_string(),
"Bool" | "Boolean" => "bool".to_string(),
"Byte" => "uint8_t".to_string(),
"Char" => "uint32_t".to_string(),
"Text" | "String" => "const char*".to_string(),
other => other.to_string(),
}
}
FieldType::Generic { .. } => "logos_handle_t".to_string(),
FieldType::TypeParam(_) => "logos_handle_t".to_string(),
}
}