sigil_parser/ffi/
ctypes.rs

1//! C type aliases for FFI.
2//!
3//! These types map to their C equivalents and are used in extern blocks
4//! for declaring foreign functions.
5//!
6//! # Example
7//! ```sigil
8//! use ffi::c::*;
9//!
10//! extern "C" {
11//!     fn strlen(s: *const c_char) -> usize;
12//!     fn malloc(size: usize) -> *mut c_void;
13//!     fn free(ptr: *mut c_void);
14//!     fn printf(fmt: *const c_char, ...) -> c_int;
15//! }
16//! ```
17
18/// C type information for type checking and code generation.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum CType {
21    // Void type
22    Void,
23
24    // Character types
25    Char,  // c_char (signed or unsigned depending on platform)
26    SChar, // c_schar (signed char)
27    UChar, // c_uchar (unsigned char)
28
29    // Integer types
30    Short,     // c_short
31    UShort,    // c_ushort
32    Int,       // c_int
33    UInt,      // c_uint
34    Long,      // c_long
35    ULong,     // c_ulong
36    LongLong,  // c_longlong
37    ULongLong, // c_ulonglong
38
39    // Floating point
40    Float,  // c_float
41    Double, // c_double
42
43    // Size types
44    Size,    // size_t (usize in Sigil)
45    SSize,   // ssize_t (isize in Sigil)
46    PtrDiff, // ptrdiff_t
47
48    // Fixed-width integers (from stdint.h)
49    Int8,
50    Int16,
51    Int32,
52    Int64,
53    UInt8,
54    UInt16,
55    UInt32,
56    UInt64,
57}
58
59impl CType {
60    /// Get the Sigil type name for this C type.
61    pub fn sigil_name(&self) -> &'static str {
62        match self {
63            CType::Void => "c_void",
64            CType::Char => "c_char",
65            CType::SChar => "c_schar",
66            CType::UChar => "c_uchar",
67            CType::Short => "c_short",
68            CType::UShort => "c_ushort",
69            CType::Int => "c_int",
70            CType::UInt => "c_uint",
71            CType::Long => "c_long",
72            CType::ULong => "c_ulong",
73            CType::LongLong => "c_longlong",
74            CType::ULongLong => "c_ulonglong",
75            CType::Float => "c_float",
76            CType::Double => "c_double",
77            CType::Size => "size_t",
78            CType::SSize => "ssize_t",
79            CType::PtrDiff => "ptrdiff_t",
80            CType::Int8 => "i8",
81            CType::Int16 => "i16",
82            CType::Int32 => "i32",
83            CType::Int64 => "i64",
84            CType::UInt8 => "u8",
85            CType::UInt16 => "u16",
86            CType::UInt32 => "u32",
87            CType::UInt64 => "u64",
88        }
89    }
90
91    /// Get the size in bytes on the target platform (assuming LP64).
92    pub fn size_bytes(&self) -> usize {
93        match self {
94            CType::Void => 0,
95            CType::Char | CType::SChar | CType::UChar => 1,
96            CType::Short | CType::UShort => 2,
97            CType::Int | CType::UInt => 4,
98            CType::Long | CType::ULong => 8, // LP64
99            CType::LongLong | CType::ULongLong => 8,
100            CType::Float => 4,
101            CType::Double => 8,
102            CType::Size | CType::SSize | CType::PtrDiff => 8, // 64-bit
103            CType::Int8 | CType::UInt8 => 1,
104            CType::Int16 | CType::UInt16 => 2,
105            CType::Int32 | CType::UInt32 => 4,
106            CType::Int64 | CType::UInt64 => 8,
107        }
108    }
109
110    /// Check if this is a signed integer type.
111    pub fn is_signed(&self) -> bool {
112        matches!(
113            self,
114            CType::SChar
115                | CType::Short
116                | CType::Int
117                | CType::Long
118                | CType::LongLong
119                | CType::SSize
120                | CType::PtrDiff
121                | CType::Int8
122                | CType::Int16
123                | CType::Int32
124                | CType::Int64
125        )
126    }
127
128    /// Parse a C type name to its enum variant.
129    pub fn from_name(name: &str) -> Option<CType> {
130        match name {
131            "c_void" | "void" => Some(CType::Void),
132            "c_char" | "char" => Some(CType::Char),
133            "c_schar" => Some(CType::SChar),
134            "c_uchar" => Some(CType::UChar),
135            "c_short" | "short" => Some(CType::Short),
136            "c_ushort" => Some(CType::UShort),
137            "c_int" | "int" => Some(CType::Int),
138            "c_uint" => Some(CType::UInt),
139            "c_long" | "long" => Some(CType::Long),
140            "c_ulong" => Some(CType::ULong),
141            "c_longlong" => Some(CType::LongLong),
142            "c_ulonglong" => Some(CType::ULongLong),
143            "c_float" | "float" => Some(CType::Float),
144            "c_double" | "double" => Some(CType::Double),
145            "size_t" | "usize" => Some(CType::Size),
146            "ssize_t" | "isize" => Some(CType::SSize),
147            "ptrdiff_t" => Some(CType::PtrDiff),
148            "i8" | "int8_t" => Some(CType::Int8),
149            "i16" | "int16_t" => Some(CType::Int16),
150            "i32" | "int32_t" => Some(CType::Int32),
151            "i64" | "int64_t" => Some(CType::Int64),
152            "u8" | "uint8_t" => Some(CType::UInt8),
153            "u16" | "uint16_t" => Some(CType::UInt16),
154            "u32" | "uint32_t" => Some(CType::UInt32),
155            "u64" | "uint64_t" => Some(CType::UInt64),
156            _ => None,
157        }
158    }
159}
160
161/// C type aliases as they appear in Sigil code.
162pub const C_TYPE_ALIASES: &[(&str, &str)] = &[
163    // Void
164    ("c_void", "()"),
165    // Character types
166    ("c_char", "i8"), // Platform-dependent, assume signed
167    ("c_schar", "i8"),
168    ("c_uchar", "u8"),
169    // Integer types (LP64 model)
170    ("c_short", "i16"),
171    ("c_ushort", "u16"),
172    ("c_int", "i32"),
173    ("c_uint", "u32"),
174    ("c_long", "i64"), // LP64: long is 64-bit
175    ("c_ulong", "u64"),
176    ("c_longlong", "i64"),
177    ("c_ulonglong", "u64"),
178    // Floating point
179    ("c_float", "f32"),
180    ("c_double", "f64"),
181    // Size types
182    ("size_t", "usize"),
183    ("ssize_t", "isize"),
184    ("ptrdiff_t", "isize"),
185    // Fixed-width (these are just aliases)
186    ("int8_t", "i8"),
187    ("int16_t", "i16"),
188    ("int32_t", "i32"),
189    ("int64_t", "i64"),
190    ("uint8_t", "u8"),
191    ("uint16_t", "u16"),
192    ("uint32_t", "u32"),
193    ("uint64_t", "u64"),
194];
195
196/// Check if a type name is a C type.
197pub fn is_c_type(name: &str) -> bool {
198    CType::from_name(name).is_some()
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_c_type_parsing() {
207        assert_eq!(CType::from_name("c_int"), Some(CType::Int));
208        assert_eq!(CType::from_name("c_void"), Some(CType::Void));
209        assert_eq!(CType::from_name("size_t"), Some(CType::Size));
210        assert_eq!(CType::from_name("unknown"), None);
211    }
212
213    #[test]
214    fn test_c_type_sizes() {
215        assert_eq!(CType::Int.size_bytes(), 4);
216        assert_eq!(CType::Long.size_bytes(), 8);
217        assert_eq!(CType::Char.size_bytes(), 1);
218        assert_eq!(CType::Double.size_bytes(), 8);
219    }
220
221    #[test]
222    fn test_c_type_signedness() {
223        assert!(CType::Int.is_signed());
224        assert!(!CType::UInt.is_signed());
225        assert!(CType::SSize.is_signed());
226        assert!(!CType::Size.is_signed());
227    }
228}