Crusty is a C-like programming language that transpiles to Rust, providing familiar C syntax while guaranteeing Rust's safety and performance. The Crusty transpiler enables bidirectional translation between Crusty and Rust source code, allowing seamless integration with the Rust ecosystem.
- Familiar C syntax: Write code with C-style function declarations, control flow, and data structures
- Type-scoped calls: Use
@Typeprefix with dot notation:@Vec.new(),@Option.None - Macro system: Double-underscore naming for macros:
__println__("Hello"),__vec__[1, 2, 3] - Closures: Nested functions that capture outer scope (transpile to Rust closures)
- Escape hatch: Use
__rust__to embed raw Rust code for advanced features - Rust compatibility: All Crusty code transpiles to safe, idiomatic Rust
- Use Rust crates: Import and use any existing Rust crate or module
- Publish crates: Compile Crusty code into crates that native Rust projects can depend on
- Bidirectional transpilation: Convert between Crusty and Rust syntax as needed
- Build system integration: Works seamlessly with Cargo through build.rs scripts
- Memory safety: Rust's ownership and borrowing model prevents memory errors
- Type safety: Strong static typing catches errors at compile time
- No null pointers: Uses Rust's Option type for nullable values
- No data races: Rust's concurrency model prevents data races
- Rust toolchain (stable) - Install Rust
- Cargo package manager (included with Rust)
git clone https://github.com/major0/crusty.git
cd crusty
cargo build --release
cargo install --path .Create hello.crst:
void main() {
__println__("Hello, Crusty!");
}Transpile and run:
crustyc hello.crst --emit=binary -o hello
./hello// C-style function declarations
int add(int a, int b) {
return a + b;
}
// Void return type
void print_sum(int x, int y) {
__println__("Sum: {}", add(x, y));
}
// Static functions (private in Rust)
static int helper(int n) {
return n * 2;
}// Define a struct type
typedef struct {
int x;
int y;
} Point;
// Add implementation block with methods
typedef struct {
// Static method (constructor)
Point new(int x, int y) {
return Point { x: x, y: y };
}
// Instance method
int distance_squared(&self) {
return self.x * self.x + self.y * self.y;
}
} @Point;
// Implement Default trait
typedef default {
Point default() {
return Point { x: 0, y: 0 };
}
} @Point;
void main() {
// Type-scoped call with @ prefix and dot notation
// Dot (.) replaces Rust's :: for type-scoped access
let p1 = @Point.new(3, 4);
// Use Default trait
let origin = @Point.default();
// Instance method call (no @ prefix)
__println__("Distance²: {}", p1.distance_squared());
// Nested type paths: dot replaces :: for type-scoped access
// @std.collections.HashMap.new()
// Translates to: std::collections::HashMap::new()
// Method calls on type-scoped values use arrow
// @Foo.BAR->boo() where BAR is a constant, boo() is a method
// Translates to: Foo::BAR.boo()
}int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
void count_to_ten() {
for (int i = 0; i < 10; i++) {
__println__("{}", i);
}
}void main() {
// Macros use double-underscore naming (no ! suffix in Crusty)
__println__("Creating a vector...");
// Type-scoped calls use @ prefix with dot notation
let v = @Vec.new();
v.push(1);
v.push(2);
v.push(3);
// Macro with formatting
__println__("Vector: {:?}", v);
}// Import Rust standard library modules
// Dot notation in module paths (no @ prefix for imports)
#use std.collections.HashMap;
#use std.io.Write;
void main() {
// Type-scoped call with @ prefix uses dot notation
let map = @HashMap.new();
map.insert("key", "value");
}void main() {
// Explicit type parameters with parentheses/brackets syntax
let v = @Vec(i32).new();
v.push(42);
// Nested generics alternate parentheses and brackets
// Dot notation for type-scoped access
let opt = @Option(Result[String, std.io.Error]).None;
// Type inference when parameters omitted
let v2 = @Vec.new(); // Type inferred from usage
}// Define macros with double-underscore naming
#define __MAX__(a, b) ((a) > (b) ? (a) : (b))
#define __SQUARE__(x) ((x) * (x))
void main() {
let max_val = __MAX__(10, 20);
let squared = __SQUARE__(5);
__println__("Max: {}, Squared: {}", max_val, squared);
}void main() {
// Labels use dot prefix (. is not part of the label name)
.outer: loop {
.inner: loop {
if (condition) {
break outer; // Break to outer loop (no dot in break)
}
continue inner; // Continue inner loop (no dot in continue)
}
}
}void main() {
// Use __rust__ as an escape hatch for Rust-specific features
// The contents are passed directly to the Rust compiler
// In expression context
let result = __rust__{ Some(42) };
// In statement context
__rust__{
println!("This is raw Rust code");
let x = vec![1, 2, 3];
};
// For complex Rust patterns not yet supported in Crusty
__rust__{
match value {
Some(x) if x > 10 => println!("Large: {}", x),
Some(x) => println!("Small: {}", x),
None => println!("Nothing"),
}
};
// In type context (for complex Rust types)
let callback: __rust__{ Box<dyn Fn(i32) -> i32> } = __rust__{ Box::new(|x| x * 2) };
}Note: The __rust__ macro provides an escape hatch for using Rust features not yet supported by Crusty syntax. The contents are passed directly to rustc without validation by crustyc. Use this when you need access to advanced Rust features like pattern matching, closures, or complex trait bounds.
void main() {
// Crusty supports nested functions as closures
// Functions defined within functions can capture variables from outer scope
int outer_value = 42;
// Define a nested function that captures outer scope
// Can only capture variables defined BEFORE the nested function
int add_to_outer(int x) {
return x + outer_value; // Captures outer_value (defined above)
}
// Use the nested function
let result = add_to_outer(10); // Returns 52
__println__("Result: {}", result);
// Variables defined after the nested function are NOT accessible
int later_value = 100; // add_to_outer cannot access this
// Nested functions can be passed as function parameters
void apply_twice(int (*func)(int), int value) {
return func(func(value));
}
int double_it(int x) {
return x * 2;
}
let doubled = apply_twice(double_it, 5); // Returns 20
// Mutable captures work too
int counter = 0;
void increment() {
counter = counter + 1; // Mutably captures counter
}
increment();
increment();
__println__("Counter: {}", counter); // Prints 2
// Multiple nested functions can capture the same variables
void reset() {
counter = 0;
}
reset();
__println__("Counter after reset: {}", counter); // Prints 0
}Translation to Rust: Nested functions are translated to Rust closures:
pub fn main() {
let outer_value = 42;
// Becomes a closure
let add_to_outer = |x: i32| -> i32 {
x + outer_value
};
let result = add_to_outer(10);
println!("Result: {}", result);
}Scoping Rules:
- Nested functions can only capture variables defined before the nested function declaration
- Variables defined after a nested function are not accessible to that function
- Multiple nested functions can capture and share the same outer variables
- Captures can be immutable (read-only) or mutable (read-write)
Note: Nested functions provide a familiar C-style syntax for closures. They can capture variables from the enclosing scope and are translated to Rust closures (Fn, FnMut, or FnOnce depending on how they use captured variables).
Reference: GNU C supports nested functions as an extension: https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html
// Define a struct type
typedef struct {
int width;
int height;
} Rectangle;
// Add implementation block
typedef struct {
Rectangle new(int w, int h) {
return Rectangle { width: w, height: h };
}
int area(&self) {
return self.width * self.height;
}
} @Rectangle;
// Implement Default trait
typedef default {
Rectangle default() {
return Rectangle { width: 0, height: 0 };
}
} @Rectangle;
// Named implementation block (for organization)
typedef struct {
void print(&self) {
__println__("Rectangle: {}x{}", self.width, self.height);
}
} @Rectangle.display;
void main() {
// Type-scoped call with @ prefix and dot notation
let rect = @Rectangle.new(10, 20);
__println__("Area: {}", rect.area());
rect.print();
}Transpile Crusty to Rust:
crustyc input.crst -o output.rsTranspile and compile to binary:
crustyc input.crst --emit=binary -o programTranspile Rust to Crusty:
crustyc input.rs --from-lang=rust -o output.crstcrustyc [OPTIONS] <INPUT>
OPTIONS:
-o, --output <FILE> Output file path
--emit <MODE> Output mode: rust, binary, ast
--from-lang <LANG> Source language: crusty, rust
-v, --verbose Detailed output
--no-compile Generate Rust without invoking rustc
-h, --help Print help information
--version Print version information
Crusty integrates seamlessly with Cargo through build.rs scripts.
1. Add crustyc as a build dependency in Cargo.toml:
[package]
name = "my-project"
version = "0.1.0"
edition = "2021"
[build-dependencies]
# crustyc = "0.1" # When published to crates.io2. Create build.rs in your project root:
use std::process::Command;
use std::env;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
// Transpile all .crst files to Rust
Command::new("crustyc")
.args(&["src/", "--out-dir", &out_dir])
.status()
.expect("Failed to run crustyc");
// Rebuild if any .crst file changes
println!("cargo:rerun-if-changed=src/");
}3. Place your .crst files in src/:
my-project/
├── Cargo.toml
├── build.rs
└── src/
├── main.crst
├── lib.crst
└── utils.crst
4. Build normally with Cargo:
cargo build
cargo runThe build.rs script automatically transpiles your Crusty code to Rust during the build process.
The repository includes a complete working example demonstrating Crusty language features and build system integration. See the example/ directory for:
- example/Cargo.toml - Project configuration
- example/build.rs - Build script that transpiles .crst files
- example/src/ - Sample Crusty programs
- example/README.md - Build and run instructions
The example demonstrates:
- Function declarations and control flow
- Struct definitions with methods
- Type-scoped static method calls (
@Type.method()) - Macro usage with double-underscore naming (
__println__,__vec__) - Build system integration with Cargo
To run the example:
cd example
cargo build
cargo runThe example is automatically built and tested in the CI/CD pipeline to ensure the transpiler works correctly.
cargo testcargo fmtcargo clippyInstall pre-commit hooks for automatic code quality checks:
pip install pre-commit
pre-commit installThe hooks will automatically run:
- Crusty syntax validation on
.crstfiles - Rust formatting checks on
.rsfiles - Clippy linting on
.rsfiles
- Requirements - Detailed requirements and acceptance criteria
- Design - Architecture and component design
- Implementation Tasks - Development task breakdown and progress
- Function declarations and definitions
- Struct and enum types with typedef syntax
- Implementation blocks (typedef struct @Type)
- Trait implementations (typedef default @Type)
- Type-scoped calls with dot notation (
@Type.method()) - Macro invocations with double-underscore naming (
__macro_name__) - Raw Rust code embedding with
__rust__escape hatch - Closures with nested functions
- Control flow statements
- Memory management and ownership
- Module system and visibility
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create a feature branch
- Make your changes
- Ensure all tests pass:
cargo test - Format code:
cargo fmt - Check for warnings:
cargo clippy - Commit using Conventional Commits format
- Submit a pull request
type(scope): subject
body
footer
Types: feat, fix, docs, test, refactor, chore
Example:
feat(parser): add support for labeled loops
Implemented parsing for labeled loops with .label: syntax.
Translates to Rust's 'label: syntax.
Validates: Requirements 6.13, 6.14, 6.15
This project is licensed under the MIT License - see the LICENSE.txt file for details.
Phase 1: Core Transpiler - In Active Development
Current progress:
- ✅ Infrastructure (CI/CD, pre-commit hooks, licensing)
- ✅ Core transpiler (lexer, parser, AST, semantic analysis)
- ✅ Code generation (Crusty → Rust)
- ✅ Advanced parsing (structs, methods, generics, macros)
- ✅ Example directory with working samples (see example/)
- ✅ Build system integration (build.rs support)
- 🚧 Bidirectional transpilation (Rust → Crusty)
- 🚧 Documentation generator (crustydoc)
See tasks.md for detailed implementation progress.
- Core transpiler infrastructure
- Crusty → Rust transpilation
- Basic language features
- Build system integration
- Enhanced macro system
- Generic function definitions
- Trait definitions with ergonomic syntax
- Pattern matching syntax
- Async/await support
- IDE integration (LSP support)
- Debugger integration
- Procedural macros
- Advanced optimization passes
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Repository: github.com/major0/crusty
Crusty builds on the excellent work of:
- The Rust programming language and its ecosystem
- The Rust compiler (rustc) for code generation
- The syn crate for Rust parsing
- The proptest crate for property-based testing