1 unstable release
Uses new Rust 2024
new 0.1.0 | Mar 26, 2025 |
---|
#157 in Programming languages
300KB
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.1–2.9MB
~58K SLoC