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 | CType::Short | CType::Int | CType::Long | CType::LongLong |
115            CType::SSize | CType::PtrDiff |
116            CType::Int8 | CType::Int16 | CType::Int32 | CType::Int64
117        )
118    }
119
120    /// Parse a C type name to its enum variant.
121    pub fn from_name(name: &str) -> Option<CType> {
122        match name {
123            "c_void" | "void" => Some(CType::Void),
124            "c_char" | "char" => Some(CType::Char),
125            "c_schar" => Some(CType::SChar),
126            "c_uchar" => Some(CType::UChar),
127            "c_short" | "short" => Some(CType::Short),
128            "c_ushort" => Some(CType::UShort),
129            "c_int" | "int" => Some(CType::Int),
130            "c_uint" => Some(CType::UInt),
131            "c_long" | "long" => Some(CType::Long),
132            "c_ulong" => Some(CType::ULong),
133            "c_longlong" => Some(CType::LongLong),
134            "c_ulonglong" => Some(CType::ULongLong),
135            "c_float" | "float" => Some(CType::Float),
136            "c_double" | "double" => Some(CType::Double),
137            "size_t" | "usize" => Some(CType::Size),
138            "ssize_t" | "isize" => Some(CType::SSize),
139            "ptrdiff_t" => Some(CType::PtrDiff),
140            "i8" | "int8_t" => Some(CType::Int8),
141            "i16" | "int16_t" => Some(CType::Int16),
142            "i32" | "int32_t" => Some(CType::Int32),
143            "i64" | "int64_t" => Some(CType::Int64),
144            "u8" | "uint8_t" => Some(CType::UInt8),
145            "u16" | "uint16_t" => Some(CType::UInt16),
146            "u32" | "uint32_t" => Some(CType::UInt32),
147            "u64" | "uint64_t" => Some(CType::UInt64),
148            _ => None,
149        }
150    }
151}
152
153/// C type aliases as they appear in Sigil code.
154pub const C_TYPE_ALIASES: &[(&str, &str)] = &[
155    // Void
156    ("c_void", "()"),
157
158    // Character types
159    ("c_char", "i8"),   // Platform-dependent, assume signed
160    ("c_schar", "i8"),
161    ("c_uchar", "u8"),
162
163    // Integer types (LP64 model)
164    ("c_short", "i16"),
165    ("c_ushort", "u16"),
166    ("c_int", "i32"),
167    ("c_uint", "u32"),
168    ("c_long", "i64"),      // LP64: long is 64-bit
169    ("c_ulong", "u64"),
170    ("c_longlong", "i64"),
171    ("c_ulonglong", "u64"),
172
173    // Floating point
174    ("c_float", "f32"),
175    ("c_double", "f64"),
176
177    // Size types
178    ("size_t", "usize"),
179    ("ssize_t", "isize"),
180    ("ptrdiff_t", "isize"),
181
182    // Fixed-width (these are just aliases)
183    ("int8_t", "i8"),
184    ("int16_t", "i16"),
185    ("int32_t", "i32"),
186    ("int64_t", "i64"),
187    ("uint8_t", "u8"),
188    ("uint16_t", "u16"),
189    ("uint32_t", "u32"),
190    ("uint64_t", "u64"),
191];
192
193/// Check if a type name is a C type.
194pub fn is_c_type(name: &str) -> bool {
195    CType::from_name(name).is_some()
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_c_type_parsing() {
204        assert_eq!(CType::from_name("c_int"), Some(CType::Int));
205        assert_eq!(CType::from_name("c_void"), Some(CType::Void));
206        assert_eq!(CType::from_name("size_t"), Some(CType::Size));
207        assert_eq!(CType::from_name("unknown"), None);
208    }
209
210    #[test]
211    fn test_c_type_sizes() {
212        assert_eq!(CType::Int.size_bytes(), 4);
213        assert_eq!(CType::Long.size_bytes(), 8);
214        assert_eq!(CType::Char.size_bytes(), 1);
215        assert_eq!(CType::Double.size_bytes(), 8);
216    }
217
218    #[test]
219    fn test_c_type_signedness() {
220        assert!(CType::Int.is_signed());
221        assert!(!CType::UInt.is_signed());
222        assert!(CType::SSize.is_signed());
223        assert!(!CType::Size.is_signed());
224    }
225}