A statically-typed language that compiles to C
Features • Quick Look • Building • Status • Docs
An ambitious little programming language I've been tinkering with.
This project has been a long time coming. Back in September last year, I started working on something called dyms (Dynamic Yet Minimal Script) - first in Go, then rewrote it in Rust, then... i gave up. Life happened, motivation faded, School, yk the usual.
Fast forward to now right now and i'm back at it. Ported what I had to Go, cleaned things up, and renamed it Carv. The goal is still the same: build a language that compiles to C, eventually make it self-hosted (write the Carv compiler in Carv itself).
We'll see how far I get this time.
Carv compiles to C and runs natively. It has a tree-walking interpreter too for quick testing.
Features that actually work:
- Static typing with inference
- Pipe operator (
|>) - my favorite part let/mut/constwith proper immutability enforcement- Compound assignment (
+=,-=,*=,/=,%=,&=,|=,^=) - Classes with methods
- Result types (
Ok/Err) with pattern matching - Hash maps
for-inloops over arrays, strings, and maps- Closures - first-class functions with environment capture
- Module system with
require(Rust-inspired, package manager ready) - String interpolation with
f"hello {name}" - Ownership system (move semantics,
clone()for deep copy) - Borrowing (
&T/&mut T) - Interfaces (
interface/implwith vtable-based dynamic dispatch) - Async/await syntax (compiles to state machines)
- Project config via
carv.toml - 40+ built-in functions (strings, files, process, environment, etc.)
// string interpolation
let name = "World";
println(f"Hello, {name}!");
println(f"2 + 2 = {2 + 2}");
// pipes make everything nicer
10 |> double |> add(5) |> print;
// ownership: move semantics
let s = "hello";
let t = s; // s is moved, now invalid
let u = s.clone(); // explicit deep copy
// borrowing: safe references
fn print_len(s: &string) -> int {
return len(s);
}
let msg = "world";
print_len(&msg); // immutable borrow
// error handling without exceptions
fn divide(a: int, b: int) {
if b == 0 {
return Err("nope");
}
return Ok(a / b);
}
let x = divide(10, 2)?;
// hash maps
let scores = {"alice": 100, "bob": 85};
// classes
class Point {
x: int = 0
y: int = 0
}
// closures
let multiplier = 3;
let triple = fn(x: int) -> int {
return x * multiplier;
};
println(f"triple(5) = {triple(5)}");
// async/await (syntax preview)
async fn fetch_data() -> int {
return 42;
}
// math.carv
pub fn add(a: int, b: int) -> int {
return a + b;
}
// main.carv
require { add } from "./math";
println(f"1 + 2 = {add(1, 2)}");
git clone https://github.com/dev-dami/carv
cd carv
make buildThen:
./build/carv run file.carv # interpret
./build/carv build file.carv # compile to binary
./build/carv emit-c file.carv # emit generated C source
./build/carv init # create new project with carv.toml
./build/carv repl # mess around- Lexer, parser, type checker
- Tree-walking interpreter
- C code generation
- Static typing with inference
- Primitives (int, float, string, bool, char)
- Arrays and hash maps
- Result types (
Ok/Err) with pattern matching - Classes with methods
- Ownership system (move semantics)
- Borrowing (
&T/&mut T) - Arena allocator in codegen
- Automatic drop insertion
- First-class functions
- Closures with capture
- Pipe operator (
|>) - Higher-order functions
- Interfaces (
interface/implwith vtables) - Module system (
require) - String interpolation (
f"...") - Async/await syntax (codegen ready, runtime WIP)
- Project config (
carv.toml) - REPL
- Build scripts
- Package manager
- Self-hosting
- LSP / Editor support
MIT
This is a hobby project. I work on it when I have the energy. No promises, no timelines.