|
| 1 | +use std::ops::Deref; |
| 2 | + |
| 3 | +#[derive(Debug, Default)] |
| 4 | +/// A Rust to C/Cxx translator. |
| 5 | +pub(crate) struct Translator {} |
| 6 | + |
| 7 | +impl Translator { |
| 8 | + /// Create a new translator. |
| 9 | + #[expect(unused)] |
| 10 | + pub(crate) fn new() -> Self { |
| 11 | + Self::default() |
| 12 | + } |
| 13 | + |
| 14 | + /// Return whether a type is a Rust primitive type. |
| 15 | + #[expect(unused)] |
| 16 | + fn is_rust_primitive(&self, ty: &str) -> bool { |
| 17 | + let rustc_types = [ |
| 18 | + "usize", "u8", "u16", "u32", "u64", "isize", "i8", "i16", "i32", "i64", "f32", "f64", |
| 19 | + ]; |
| 20 | + ty.starts_with("c_") || rustc_types.contains(&ty) |
| 21 | + } |
| 22 | + |
| 23 | + /// Translate mutability from Rust to C. |
| 24 | + #[expect(unused)] |
| 25 | + fn translate_mut(&self, mutability: Option<syn::Token![mut]>) -> String { |
| 26 | + mutability.map(|_| "const ").unwrap_or("").to_string() |
| 27 | + } |
| 28 | + |
| 29 | + /// Translate a Rust type into its equivalent C type. |
| 30 | + pub(crate) fn translate_type(&self, ty: &syn::Type) -> String { |
| 31 | + match ty { |
| 32 | + syn::Type::Ptr(ptr) => self.translate_ptr(ptr), |
| 33 | + syn::Type::Path(path) => self.translate_path(path), |
| 34 | + syn::Type::Tuple(tuple) if tuple.elems.is_empty() => "void".to_string(), |
| 35 | + syn::Type::Array(array) => self.translate_array(array), |
| 36 | + syn::Type::Reference(reference) => self.translate_reference(reference), |
| 37 | + syn::Type::BareFn(function) => self.translate_bare_fn(function), |
| 38 | + syn::Type::Never(_) => "void".to_string(), |
| 39 | + _ => unimplemented!(), |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + /// Translate a Rust reference to its C equivalent. |
| 44 | + fn translate_reference(&self, reference: &syn::TypeReference) -> String { |
| 45 | + let path = match reference.elem.deref() { |
| 46 | + syn::Type::Path(path) => path.path.segments.last().unwrap(), |
| 47 | + syn::Type::Array(array) => { |
| 48 | + return format!( |
| 49 | + "{}{}*", |
| 50 | + self.translate_mut(reference.mutability), |
| 51 | + self.translate_type(&array.elem), |
| 52 | + ) |
| 53 | + } |
| 54 | + _ => panic!("Unknown type! {:?}", reference.elem), |
| 55 | + }; |
| 56 | + |
| 57 | + let ident = path.ident.to_string(); |
| 58 | + match ident.as_str() { |
| 59 | + "str" => { |
| 60 | + if reference.mutability.is_some() { |
| 61 | + panic!("Unknown type, &mut str"); |
| 62 | + } |
| 63 | + |
| 64 | + "char*".to_string() |
| 65 | + } |
| 66 | + c if self.is_rust_primitive(c) => format!( |
| 67 | + "{}{}*", |
| 68 | + self.translate_mut(reference.mutability), |
| 69 | + self.translate_primitive_type(&path.ident) |
| 70 | + ), |
| 71 | + _ => unimplemented!("References to non primitive types are not implemented."), |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + /// Translate a Rust function pointer type to its C equivalent. |
| 76 | + fn translate_bare_fn(&self, function: &syn::TypeBareFn) -> String { |
| 77 | + assert!(function.lifetimes.is_none(), "No lifetimes allowed."); |
| 78 | + assert!(function.variadic.is_none(), "No variadics allowed."); |
| 79 | + |
| 80 | + let mut parameters = function |
| 81 | + .inputs |
| 82 | + .iter() |
| 83 | + .map(|arg| self.translate_type(&arg.ty)) |
| 84 | + .collect::<Vec<_>>(); |
| 85 | + let return_type = match &function.output { |
| 86 | + syn::ReturnType::Default => "void".to_string(), |
| 87 | + syn::ReturnType::Type(_, ty) => self.translate_type(ty), |
| 88 | + }; |
| 89 | + |
| 90 | + if parameters.is_empty() { |
| 91 | + parameters.push("void".to_string()); |
| 92 | + } |
| 93 | + |
| 94 | + if return_type.contains("(*)") { |
| 95 | + return_type.replace("(*)", &format!("(*(*)({}))", parameters.join(", "))) |
| 96 | + } else { |
| 97 | + format!("{}(*)({})", return_type, parameters.join(", ")) |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + /// Translate a Rust primitve type into its C equivalent. |
| 102 | + fn translate_primitive_type(&self, ty: &syn::Ident) -> String { |
| 103 | + let ty = ty.to_string(); |
| 104 | + match ty.as_str() { |
| 105 | + "usize" => "size_t".to_string(), |
| 106 | + "isize" => "ssize_t".to_string(), |
| 107 | + "u8" => "uint8_t".to_string(), |
| 108 | + "u16" => "uint16_t".to_string(), |
| 109 | + "u32" => "uint32_t".to_string(), |
| 110 | + "u64" => "uint64_t".to_string(), |
| 111 | + "i8" => "int8_t".to_string(), |
| 112 | + "i16" => "int16_t".to_string(), |
| 113 | + "i32" => "int32_t".to_string(), |
| 114 | + "i64" => "int64_t".to_string(), |
| 115 | + "f32" => "float".to_string(), |
| 116 | + "f64" => "double".to_string(), |
| 117 | + "()" => "void".to_string(), |
| 118 | + |
| 119 | + "c_longdouble" | "c_long_double" => "long double".to_string(), |
| 120 | + ty if ty.starts_with("c_") => { |
| 121 | + let ty = &ty[2..].replace("long", " long")[..]; |
| 122 | + match ty { |
| 123 | + "short" => "short".to_string(), |
| 124 | + s if s.starts_with('u') => format!("unsigned {}", &s[1..]), |
| 125 | + s if s.starts_with('s') => format!("signed {}", &s[1..]), |
| 126 | + s => s.to_string(), |
| 127 | + } |
| 128 | + } |
| 129 | + // Overriding type names not yet implemented. |
| 130 | + s => s.to_string(), |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + /// Translate a Rust path into its C equivalent. |
| 135 | + fn translate_path(&self, path: &syn::TypePath) -> String { |
| 136 | + // Paths should be fully qualified otherwise they won't properly be translated. |
| 137 | + let last = path.path.segments.last().unwrap(); |
| 138 | + if last.ident == "Option" { |
| 139 | + if let syn::PathArguments::AngleBracketed(p) = &last.arguments { |
| 140 | + if let syn::GenericArgument::Type(ty) = p.args.first().unwrap() { |
| 141 | + self.translate_type(ty) |
| 142 | + } else { |
| 143 | + unimplemented!("Only simple generic types are supported!") |
| 144 | + } |
| 145 | + } else { |
| 146 | + unreachable!("Option<T> cannot have parentheses.") |
| 147 | + } |
| 148 | + } else { |
| 149 | + self.translate_primitive_type(&last.ident) |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + /// Translate a Rust array declaration into its C equivalent. |
| 154 | + fn translate_array(&self, array: &syn::TypeArray) -> String { |
| 155 | + format!( |
| 156 | + "{}[{}]", |
| 157 | + self.translate_type(array.elem.deref()), |
| 158 | + self.translate_expr(&array.len) |
| 159 | + ) |
| 160 | + } |
| 161 | + |
| 162 | + /// Translate a Rust pointer into its equivalent C pointer. |
| 163 | + fn translate_ptr(&self, ptr: &syn::TypePtr) -> String { |
| 164 | + let modifier = ptr.mutability.map(|_| "").unwrap_or("const"); |
| 165 | + let inner = ptr.elem.deref(); |
| 166 | + match inner { |
| 167 | + syn::Type::BareFn(_) => self.translate_type(inner), |
| 168 | + syn::Type::Ptr(_) => format!("{} {}*", self.translate_type(inner), modifier), |
| 169 | + syn::Type::Array(arr) => { |
| 170 | + let len = self.translate_expr(&arr.len); |
| 171 | + let ty = self.translate_type(inner); |
| 172 | + format!("{} {} [{}]", modifier, ty, len) |
| 173 | + } |
| 174 | + _ => format!("{} {}*", modifier, self.translate_type(inner)), |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + /// Translate a simple Rust expression to C. |
| 179 | + /// |
| 180 | + /// This method is only used for translating expressions inside of |
| 181 | + /// array brackets, and will fail for expressions not allowed inside of |
| 182 | + /// those brackets. |
| 183 | + fn translate_expr(&self, expr: &syn::Expr) -> String { |
| 184 | + match expr { |
| 185 | + syn::Expr::Lit(l) => match &l.lit { |
| 186 | + syn::Lit::Int(i) => i.to_string(), |
| 187 | + _ => panic!("Invalid Syntax! Cannot have non integer literal in array expression."), |
| 188 | + }, |
| 189 | + syn::Expr::Path(p) => p.path.segments.last().unwrap().ident.to_string(), |
| 190 | + syn::Expr::Cast(c) => self.translate_expr(c.expr.deref()), |
| 191 | + syn::Expr::Binary(b) => { |
| 192 | + let left = self.translate_expr(b.left.deref()); |
| 193 | + let right = self.translate_expr(b.right.deref()); |
| 194 | + |
| 195 | + match b.op { |
| 196 | + syn::BinOp::Add(_) => format!("{} + {}", left, right), |
| 197 | + syn::BinOp::Sub(_) => format!("{} - {}", left, right), |
| 198 | + // Some operators have not been implemented, such as |
| 199 | + // shift left, shift right etc. Some other operators cannot be |
| 200 | + // placed inside array brackets. |
| 201 | + _ => unimplemented!("Unknown Operator! {:?}", b.op), |
| 202 | + } |
| 203 | + } |
| 204 | + // Some expressions have not been implemented, such as |
| 205 | + // braces eg: [u8; { expr }], constant functions, etc. |
| 206 | + _ => unimplemented!("Unknown Expression! {:?}", expr), |
| 207 | + } |
| 208 | + } |
| 209 | +} |
0 commit comments