| Crates.io | ccarp |
| lib.rs | ccarp |
| version | 0.1.2 |
| created_at | 2025-03-26 14:56:49.422182+00 |
| updated_at | 2025-04-27 10:44:38.210054+00 |
| description | (trans)Compile C And Rust Partially |
| homepage | |
| repository | https://codeberg.org/ministry460/ccarp |
| max_upload_size | |
| id | 1606623 |
| size | 391,025 |
CCARP is a command line utility made with the sole purpose of transcompiling or transpiling C programs into Rust.
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).
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));
}
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.
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:
char, short, int, long, long long, etc.)1 + 2, a + b, 1 ? a << 2 : b & c * 2)int arr[5],{1,2,3})int c=0;,typedef int int32;)if (1==2) { a=1; } else { a=2; }for (int i=0;i<10;i++) { a+=2; }while (a<5) { a++; }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;
}
int add_two(int n) { return n+2; })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 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] |
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.
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: