3 releases
Uses new Rust 2024
| 0.1.2 | Apr 27, 2025 |
|---|---|
| 0.1.1 | Apr 7, 2025 |
| 0.1.0 | Mar 26, 2025 |
#333 in Programming languages
165 downloads per month
345KB
6.5K
SLoC
CCARP - (trans)Compile C And Rust Partially
CCARP is a command line utility made with the sole purpose of transcompiling or transpiling C programs into Rust.
Does it actually work?
Somewhat.
At the moment CCARP can be used to rustify simple C programs, but for more complex programs it will most likely run into an error. If it succeeds however, it will produce a Rust program which either compiles or almost compiles, in either way its meaning will be similar to the C code (if one manages to solve all conflicts within the generated code).
An example
Example C code:
int fib(int n) {
int a=0,b=1,c;
for (int i=0;i<n;i++) {
c=a+b;
a=b;
b=c;
}
return a;
}
int main() {
return fib(11);
}
Generated Rust code (after formatting and simplification):
pub fn fib(mut n: i32) -> i32 {
let mut a: i32 = (0);
let mut b: i32 = (1);
let mut c: i32;
let mut i: i32 = (0);
while (i < n) {
{
(c = (a + b));
(a = b);
(b = c);
(i += 1);
}
}
return (a);
}
pub fn main() -> std::process::ExitCode {
return std::process::ExitCode::from((fib(11) as u8));
}
How to use it?
CCARP can be both a library and an executable.
You can use it as a dependency
cargo add ccarp
in this case translate, translate_single_file and translate_project functions are available.
Or alternatively you can install it as an executable via
cargo install ccarp
After that a
ccarp --help
command will show you how it can be used.
What does CCARP implement from C?
CCARP mainly implements the core of C, as in the syntax approximate meaning of C without most macros and standard library. (Also without goto, labels and variadic functions amongst other things.)
What will most likely work:
- standard primitive types (
char,short,int,long,long long, etc.) - expressions with primitive types (e.g.
1 + 2,a + b,1 ? a << 2 : b & c * 2) - fixed size arrays (
int arr[5],{1,2,3}) - simple declarations (
int c=0;,typedef int int32;) - most statements
if (1==2) { a=1; } else { a=2; }for (int i=0;i<10;i++) { a+=2; }while (a<5) { a++; }
- switch with some restrictions
switch (num[i]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
res *= 10;
res += num[i] - '0';
}
break;
default:
return 0;
break;
}
in general:
switch ($EXPR) {
case $LITERAL:
case $LITERAL_2:
...
case $LITERAL_N:
$BODY
break;
...
default:
$BODY
break;
}
- function definitions (
int add_two(int n) { return n+2; })
How does CCARP deal with types?
Primitive types
Primitive types are translated to the following Rust types:
| c type | rust type |
|---|---|
| char | u8 |
| unsigned char | u8 |
| signed char | i8 |
| short | i16 |
| unsigned short | u16 |
| int | i32 |
| unsigned int | u32 |
| long | i64 |
| unsigned long | u64 |
| long long | i64 |
| unsigned long long | u64 |
| float | f32 |
| double | f64 |
| long double | f64 |
| bool | ~i8 |
| void | () |
Arrays and Pointers
Arrays and pointers are somewhat dependent on compiler flags in translation. Basic array and pointer types:
| c type | rust type |
|---|---|
| int * | *mut i32 |
| const int * | *const i32 |
| int *const | &mut i32 |
| const int *const | &i32 |
| int arr[N] | [i32;N] |
Conversions
CCARP applies a best-effort conversion between types, but not all types can be trivially converted into another. This is why for example void * does not compile to valid Rust at the moment.
How does CCARP work?
CCARP uses Pest for parsing its inputs into tokens; after tokenisation it parses tokens into C translation units and creates a C AST (Abstract Syntax Tree for short) from it; the next step after that is logically morphing the C AST into a Rust AST and from that the program can produce a Rust code (which might or might not work).
In fewer words:
- C Code
- Tokenisation
- Parsing tokens into structures
- C AST
- Rust AST
- Rust Code
Dependencies
~2–2.8MB
~56K SLoC