1#![allow(dead_code)]
5
6pub mod staticlib_header;
7
8pub type CIdent = String;
10
11#[derive(Debug, Clone, Default)]
13#[allow(dead_code)]
14pub enum CType {
15 #[default]
17 Void,
18 PointerTo {
20 is_const: bool,
22 inner: Box<CType>,
24 },
25 U8,
27 U16,
29 U32,
31 U64,
33 USize,
35 I8,
37 I16,
39 I32,
41 I64,
43 ISize,
45 Function {
47 arguments: Vec<CType>,
49 return_value: Option<Box<CType>>,
53 },
54 Array {
56 inner: Box<CType>,
58 },
59 TypeDef(String),
61}
62
63impl CType {
64 pub fn void_ptr() -> Self {
66 CType::PointerTo {
67 is_const: false,
68 inner: Box::new(CType::Void),
69 }
70 }
71
72 #[allow(dead_code)]
74 pub fn const_void_ptr() -> Self {
75 CType::PointerTo {
76 is_const: true,
77 inner: Box::new(CType::Void),
78 }
79 }
80
81 fn generate_c(&self, w: &mut String) {
83 match &self {
84 Self::Void => {
85 w.push_str("void");
86 }
87 Self::PointerTo { is_const, inner } => {
88 if *is_const {
89 w.push_str("const ");
90 }
91 inner.generate_c(w);
92 w.push('*');
93 }
94 Self::U8 => {
95 w.push_str("unsigned char");
96 }
97 Self::U16 => {
98 w.push_str("unsigned short");
99 }
100 Self::U32 => {
101 w.push_str("unsigned int");
102 }
103 Self::U64 => {
104 w.push_str("unsigned long long");
105 }
106 Self::USize => {
107 w.push_str("unsigned size_t");
108 }
109 Self::I8 => {
110 w.push_str("char");
111 }
112 Self::I16 => {
113 w.push_str("short");
114 }
115 Self::I32 => {
116 w.push_str("int");
117 }
118 Self::I64 => {
119 w.push_str("long long");
120 }
121 Self::ISize => {
122 w.push_str("size_t");
123 }
124 Self::Function {
125 arguments,
126 return_value,
127 } => {
128 #[allow(clippy::borrowed_box)]
130 let ret: CType = return_value
131 .as_ref()
132 .map(|i: &Box<CType>| (**i).clone())
133 .unwrap_or_default();
134 ret.generate_c(w);
135 w.push(' ');
136 w.push_str("(*)");
137 w.push('(');
138 match arguments.len() {
139 l if l > 1 => {
140 for arg in &arguments[..arguments.len() - 1] {
141 arg.generate_c(w);
142 w.push_str(", ");
143 }
144 arguments.last().unwrap().generate_c(w);
145 }
146 1 => {
147 arguments[0].generate_c(w);
148 }
149 _ => {}
150 }
151 w.push(')');
152 }
153 Self::Array { inner } => {
154 inner.generate_c(w);
155 w.push_str("[]");
156 }
157 Self::TypeDef(inner) => {
158 w.push_str(inner);
159 }
160 }
161 }
162
163 fn generate_c_with_name(&self, name: &str, w: &mut String) {
165 match &self {
166 Self::PointerTo { .. }
167 | Self::TypeDef { .. }
168 | Self::Void
169 | Self::U8
170 | Self::U16
171 | Self::U32
172 | Self::U64
173 | Self::USize
174 | Self::I8
175 | Self::I16
176 | Self::I32
177 | Self::I64
178 | Self::ISize => {
179 self.generate_c(w);
180 w.push(' ');
181 w.push_str(name);
182 }
183 Self::Function {
184 arguments,
185 return_value,
186 } => {
187 #[allow(clippy::borrowed_box)]
188 let ret: CType = return_value
189 .as_ref()
190 .map(|i: &Box<CType>| (**i).clone())
191 .unwrap_or_default();
192 ret.generate_c(w);
193 w.push(' ');
194 w.push_str(name);
195 w.push('(');
196 match arguments.len() {
197 l if l > 1 => {
198 for arg in &arguments[..arguments.len() - 1] {
199 arg.generate_c(w);
200 w.push_str(", ");
201 }
202 arguments.last().unwrap().generate_c(w);
203 }
204 1 => {
205 arguments[0].generate_c(w);
206 }
207 _ => {}
208 }
209 w.push(')');
210 }
211 Self::Array { inner } => {
212 inner.generate_c(w);
213 w.push(' ');
214 w.push_str(name);
215 w.push_str("[]");
216 }
217 }
218 }
219}
220
221#[derive(Debug, Clone)]
225pub enum CStatement {
226 Declaration {
228 name: CIdent,
230 is_extern: bool,
232 is_const: bool,
234 ctype: CType,
236 definition: Option<Box<CStatement>>,
240 },
241
242 LiteralArray {
244 items: Vec<CStatement>,
246 },
247
248 LiteralConstant {
250 value: String,
252 },
253
254 Cast {
256 target_type: CType,
258 expression: Box<CStatement>,
260 },
261
262 TypeDef {
264 source_type: CType,
266 new_name: CIdent,
268 },
269}
270
271impl CStatement {
272 fn generate_c(&self, w: &mut String) {
274 match &self {
275 Self::Declaration {
276 name,
277 is_extern,
278 is_const,
279 ctype,
280 definition,
281 } => {
282 if *is_const {
283 w.push_str("const ");
284 }
285 if *is_extern {
286 w.push_str("extern ");
287 }
288 ctype.generate_c_with_name(name, w);
289 if let Some(def) = definition {
290 w.push_str(" = ");
291 def.generate_c(w);
292 }
293 w.push(';');
294 w.push('\n');
295 }
296 Self::LiteralArray { items } => {
297 w.push('{');
298 if !items.is_empty() {
299 w.push('\n');
300 }
301 for item in items {
302 w.push('\t');
303 item.generate_c(w);
304 w.push(',');
305 w.push('\n');
306 }
307 w.push('}');
308 }
309 Self::LiteralConstant { value } => {
310 w.push_str(value);
311 }
312 Self::Cast {
313 target_type,
314 expression,
315 } => {
316 w.push('(');
317 target_type.generate_c(w);
318 w.push(')');
319 w.push(' ');
320 expression.generate_c(w);
321 }
322 Self::TypeDef {
323 source_type,
324 new_name,
325 } => {
326 w.push_str("typedef ");
327 if let CType::Function { .. } = source_type {
329 source_type.generate_c_with_name(&format!("(*{new_name})"), w);
330 } else {
331 source_type.generate_c(w);
332 w.push(' ');
333 w.push_str(new_name);
334 }
335 w.push(';');
336 w.push('\n');
337 }
338 }
339 }
340}
341
342pub fn generate_c(statements: &[CStatement]) -> String {
345 let mut out = String::new();
346 for statement in statements {
347 statement.generate_c(&mut out);
348 }
349 out
350}
351
352#[cfg(test)]
353mod test {
354 use super::*;
355
356 #[test]
357 fn generate_types() {
358 macro_rules! assert_c_type {
359 ($ctype:expr, $expected:expr) => {
360 let mut w = String::new();
361 let ctype = $ctype;
362 ctype.generate_c(&mut w);
363 assert_eq!(w, $expected);
364 };
365 }
366
367 assert_c_type!(CType::Void, "void");
368 assert_c_type!(CType::void_ptr(), "void*");
369 assert_c_type!(CType::const_void_ptr(), "const void*");
370 assert_c_type!(CType::U8, "unsigned char");
371 assert_c_type!(CType::U16, "unsigned short");
372 assert_c_type!(CType::U32, "unsigned int");
373 assert_c_type!(CType::U64, "unsigned long long");
374 assert_c_type!(CType::USize, "unsigned size_t");
375 assert_c_type!(CType::I8, "char");
376 assert_c_type!(CType::I16, "short");
377 assert_c_type!(CType::I32, "int");
378 assert_c_type!(CType::I64, "long long");
379 assert_c_type!(CType::ISize, "size_t");
380 assert_c_type!(CType::TypeDef("my_type".to_string()), "my_type");
381 assert_c_type!(
382 CType::Function {
383 arguments: vec![CType::U8, CType::ISize],
384 return_value: None
385 },
386 "void (*)(unsigned char, size_t)"
387 );
388 assert_c_type!(
389 CType::Function {
390 arguments: vec![],
391 return_value: Some(Box::new(CType::ISize))
392 },
393 "size_t (*)()"
394 );
395 assert_c_type!(
396 CType::PointerTo {
397 is_const: true,
398 inner: Box::new(CType::PointerTo {
399 is_const: false,
400 inner: Box::new(CType::U32)
401 })
402 },
403 "const unsigned int**"
404 );
405 }
407
408 #[test]
409 fn generate_types_with_names() {
410 macro_rules! assert_c_type {
411 ($ctype:expr, $name:literal, $expected:expr) => {
412 let mut w = String::new();
413 let ctype = $ctype;
414 ctype.generate_c_with_name($name, &mut w);
415 assert_eq!(w, $expected);
416 };
417 }
418
419 assert_c_type!(CType::Void, "main", "void main");
420 assert_c_type!(CType::void_ptr(), "data", "void* data");
421 assert_c_type!(CType::const_void_ptr(), "data", "const void* data");
422 assert_c_type!(CType::U8, "data", "unsigned char data");
423 assert_c_type!(CType::U16, "data", "unsigned short data");
424 assert_c_type!(CType::U32, "data", "unsigned int data");
425 assert_c_type!(CType::U64, "data", "unsigned long long data");
426 assert_c_type!(CType::USize, "data", "unsigned size_t data");
427 assert_c_type!(CType::I8, "data", "char data");
428 assert_c_type!(CType::I16, "data", "short data");
429 assert_c_type!(CType::I32, "data", "int data");
430 assert_c_type!(CType::I64, "data", "long long data");
431 assert_c_type!(CType::ISize, "data", "size_t data");
432 assert_c_type!(
433 CType::TypeDef("my_type".to_string()),
434 "data",
435 "my_type data"
436 );
437 assert_c_type!(
438 CType::Function {
439 arguments: vec![CType::U8, CType::ISize],
440 return_value: None
441 },
442 "my_func",
443 "void my_func(unsigned char, size_t)"
444 );
445 assert_c_type!(
446 CType::Function {
447 arguments: vec![],
448 return_value: Some(Box::new(CType::ISize))
449 },
450 "my_func",
451 "size_t my_func()"
452 );
453 assert_c_type!(
454 CType::PointerTo {
455 is_const: true,
456 inner: Box::new(CType::PointerTo {
457 is_const: false,
458 inner: Box::new(CType::U32)
459 })
460 },
461 "data",
462 "const unsigned int** data"
463 );
464 }
466
467 #[test]
468 fn generate_expressions_works() {
469 macro_rules! assert_c_expr {
470 ($cexpr:expr, $expected:expr) => {
471 let mut w = String::new();
472 let cexpr = $cexpr;
473 cexpr.generate_c(&mut w);
474 assert_eq!(w, $expected);
475 };
476 }
477
478 assert_c_expr!(
479 CStatement::LiteralConstant {
480 value: "\"Hello, world!\"".to_string()
481 },
482 "\"Hello, world!\""
483 );
484 assert_c_expr!(
485 CStatement::TypeDef {
486 source_type: CType::Function {
487 arguments: vec![CType::I32, CType::I32],
488 return_value: None,
489 },
490 new_name: "my_func_ptr".to_string(),
491 },
492 "typedef void (*my_func_ptr)(int, int);\n"
493 );
494 assert_c_expr!(
495 CStatement::LiteralArray {
496 items: vec![
497 CStatement::LiteralConstant {
498 value: "1".to_string()
499 },
500 CStatement::LiteralConstant {
501 value: "2".to_string()
502 },
503 CStatement::LiteralConstant {
504 value: "3".to_string()
505 },
506 ]
507 },
508 "{\n\t1,\n\t2,\n\t3,\n}"
509 );
510 assert_c_expr!(CStatement::LiteralArray { items: vec![] }, "{}");
511 assert_c_expr!(
512 CStatement::Declaration {
513 name: "my_array".to_string(),
514 is_extern: false,
515 is_const: true,
516 ctype: CType::Array {
517 inner: Box::new(CType::I32)
518 },
519 definition: Some(Box::new(CStatement::LiteralArray {
520 items: vec![
521 CStatement::LiteralConstant {
522 value: "1".to_string()
523 },
524 CStatement::LiteralConstant {
525 value: "2".to_string()
526 },
527 CStatement::LiteralConstant {
528 value: "3".to_string()
529 },
530 ]
531 }))
532 },
533 "const int my_array[] = {\n\t1,\n\t2,\n\t3,\n};\n"
534 );
535 assert_c_expr!(
536 CStatement::Declaration {
537 name: "my_array".to_string(),
538 is_extern: true,
539 is_const: true,
540 ctype: CType::Array {
541 inner: Box::new(CType::I32)
542 },
543 definition: None,
544 },
545 "const extern int my_array[];\n"
546 );
547 }
548}