C3

A modern systems programming language that evolves C with improved syntax, safety features, and modern conveniences while maintaining simplicity and C compatibility.

languages
c3systemslow-levelc-evolution

Hello World

module hello_world;
import std::io;

fn void main()
{
  io::printn("Hello, world!");
}

Variables

// Simple variable declarations (type inference)
int count = 42;                  // Integer
double price = 19.99;            // Floating point
String name = "Alice";           // String type
bool is_active = true;           // Boolean

// Multiple declarations
int x = 1, y = 2, z = 3;        // Declare multiple variables

Control Flow

// If statements
fn void check_value(int x)
{
    if (x > 10)
    {
        io::printn("Greater than 10");
    }
    else if (x > 5)
    {
        io::printn("Between 6 and 10");
    }
    else
    {
        io::printn("5 or less");
    }
}

// For loop (C-style)
fn void count_to_ten()
{
    for (int i = 0; i < 10; i++)
    {
        io::printfn("%d", i);        // Print current number
    }
}

// While loop
fn void countdown(int n)
{
    while (n > 0)
    {
        io::printfn("%d", n);
        n--;
    }
}

// Foreach with index and value
fn void iterate_array(int[] numbers)
{
    foreach (index, value : numbers)
    {
        io::printfn("Index %d: %d", index, value);
    }
}

// Foreach by reference (modify values)
fn void double_values(int[] numbers)
{
    foreach (&num : numbers)
    {
        *num *= 2;                   // Modify original array
    }
}

Switch Statements

// Switches have implicit break (no fallthrough)
enum Status
{
    PENDING,
    PROCESSING,
    COMPLETE,
    FAILED
}

fn void handle_status(Status s)
{
    switch (s)
    {
        case PENDING:
            io::printn("Waiting to start");
            // Implicit break
        case PROCESSING:
            io::printn("Currently processing");
        case COMPLETE:
            io::printn("Done!");
        case FAILED:
            io::printn("Error occurred");
    }
}

// Using nextcase for explicit fallthrough
fn void categorize(int value)
{
    switch (value)
    {
        case 1:
            io::printn("One");
            nextcase;                // Continue to next case
        case 2:
            io::printn("Small number");
            nextcase;
        case 3:
            io::printn("Less than four");
    }
}

Functions

// Basic function
fn int add(int a, int b)
{
    return a + b;
}

// Function with optional result (error handling)
fn double? divide(int a, int b)
{
    if (b == 0) return DIVISION_BY_ZERO?;
    return (double)a / (double)b;
}

// Function pointer type alias
alias Calculator = fn int(int, int);

fn int multiply(int a, int b)
{
    return a * b;
}

fn void use_callback()
{
    Calculator calc = &multiply;     // Get function pointer
    int result = calc(5, 3);        // Call through pointer
}

Structs

// Struct definition
struct Person
{
    String name;
    int age;
    double height;
    
    // Nested anonymous struct (fields accessed directly)
    struct
    {
        String street;
        String city;
    }
    
    // Named sub-struct
    struct contact
    {
        String email;
        String phone;
    }
}

// Struct initialization
fn void create_person()
{
    Person p = {
        .name = "Bob",
        .age = 30,
        .height = 5.9,
        .street = "123 Main St",    // Anonymous struct field
        .city = "Springfield",
        .contact.email = "bob@example.com"
    };
}

// Struct methods (dot syntax)
fn void Person.greet(Person* this)
{
    io::printfn("Hello, I'm %s", this.name);
}

fn void Person.have_birthday(Person* this)
{
    this.age++;                      // Increment age
}

fn void test_methods()
{
    Person p = { .name = "Alice", .age = 25 };
    p.greet();                       // Call method with dot syntax
    p.have_birthday();
}

Enums

// Basic enum
enum Color
{
    RED,
    GREEN,
    BLUE
}

// Enum with explicit values
enum HttpStatus : int
{
    OK = 200,
    NOT_FOUND = 404,
    SERVER_ERROR = 500
}

// Enum reflection
fn void enum_info()
{
    Color[] all_colors = Color.values;      // Get all enum values
    usz count = Color.elements;             // Number of values (3)
    String[] names = Color.names;           // ["RED", "GREEN", "BLUE"]
    
    Color first = Color.values[0];          // Access specific value
}

Error Handling

// Define custom fault types
faultdef FILE_NOT_FOUND;
faultdef PERMISSION_DENIED;

// Function returning optional result
fn String? read_file(String path)
{
    // Return fault on error
    if (!file_exists(path)) return FILE_NOT_FOUND?;
    if (!has_permission(path)) return PERMISSION_DENIED?;
    
    return load_file_contents(path);
}

// Handling optional results
fn void process_file(String path)
{
    String? content = read_file(path);
    
    // Check for error
    if (catch err = content)
    {
        switch (err)
        {
            case FILE_NOT_FOUND:
                io::printn("File doesn't exist");
            case PERMISSION_DENIED:
                io::printn("Access denied");
            default:
                io::printn("Unknown error");
        }
        return;
    }
    
    // After if-catch, content is unwrapped to String
    io::printfn("File content: %s", content);
}

// Propagate errors with ! suffix
fn void? process_multiple_files()
{
    read_file("config.txt")!;        // Return error if read fails
    read_file("data.txt")!;
}

Defer

// Defer runs on scope exit (cleanup)
fn void example_defer()
{
    defer io::printn("3 - Cleanup");
    defer io::printn("2 - Second defer");
    io::printn("1 - Main code");
    // Prints: "1 - Main code", "2 - Second defer", "3 - Cleanup"
}

// Conditional defer (error handling)
fn void? process_with_cleanup()
{
    defer io::printn("Always runs");
    defer try io::printn("Only on success");
    defer catch io::printn("Only on error");
    defer (catch err) io::printfn("Error was: %s", err);
    
    // If error occurs, only catch defers run
    do_risky_operation()!;
}

Macros

// Simple macro
macro square(x)
{
    return x * x;
}

// Macro with compile-time execution
macro long factorial(long $n)
{
    $if $n <= 1:
        return 1;
    $else
        return $n * factorial($n - 1);
    $endif
}

// Computed at compile time
const long FACT_10 = factorial(10);  // = 3628800 at compile time

// Macro with type checking
<*
  @require types::is_numerical($typeof(x))
*>
macro abs(x)
{
    return x < 0 ? -x : x;
}

Generics

// Generic module (similar to templates)
module list {Type};

struct List
{
    Type* items;
    usz length;
    usz capacity;
}

fn void List.add(List* this, Type item)
{
    if (this.length >= this.capacity)
    {
        this.capacity = this.capacity ? this.capacity * 2 : 8;
        this.items = realloc(this.items, Type.sizeof * this.capacity);
    }
    this.items[this.length++] = item;
}

fn Type List.get(List* this, usz index)
{
    assert(index < this.length);
    return this.items[index];
}

// Using the generic type
alias IntList = List {int};
alias StringList = List {String};

fn void test_generic()
{
    IntList numbers;
    numbers.add(10);
    numbers.add(20);
    io::printfn("First: %d", numbers.get(0));
}

Operator Overloading

// Struct for 2D vector
struct Vec2
{
    float x, y;
}

// Overload + operator
fn Vec2 Vec2.add(self, Vec2 other) @operator(+)
{
    return { self.x + other.x, self.y + other.y };
}

// Overload - operator
fn Vec2 Vec2.subtract(self, Vec2 other) @operator(-)
{
    return { self.x - other.x, self.y - other.y };
}

// Overload * operator (scalar multiplication)
fn Vec2 Vec2.scale(self, float scalar) @operator(*)
{
    return { self.x * scalar, self.y * scalar };
}

fn void test_operators()
{
    Vec2 a = { 2.0, 3.0 };
    Vec2 b = { 1.0, 4.0 };
    
    Vec2 sum = a + b;                // Uses overloaded +
    Vec2 diff = a - b;               // Uses overloaded -
    Vec2 scaled = a * 2.0;           // Uses overloaded *
}

Contracts

// Pre and post conditions
<*
  @param value : "input value"
  @require value >= 0           // Precondition: non-negative
  @return "square root"
  @ensure return >= 0           // Postcondition: result non-negative
*>
fn double sqrt_wrapper(double value)
{
    return math::sqrt(value);
}

// Array bounds checking
<*
  @param arr : "array to search"
  @param index : "index to access"
  @require index >= 0, index < arr.len
*>
fn int get_element(int[] arr, int index)
{
    return arr[index];
}

Interfaces and Dynamic Dispatch

// Define interface
interface Drawable
{
    fn void draw();
    fn String get_name();
}

// Struct implementing interface
struct Circle (Drawable)
{
    float radius;
}

fn void Circle.draw(Circle* this) @dynamic
{
    io::printfn("Drawing circle with radius %f", this.radius);
}

fn String Circle.get_name(Circle*) @dynamic
{
    return "Circle";
}

// Polymorphic function
fn void render(any obj)
{
    Drawable d = (Drawable)obj;
    if (&d.draw)                     // Check if draw method exists
    {
        d.draw();
    }
}

Compile-Time Reflection

// Loop over struct fields at compile time
struct Config
{
    int port;
    String host;
    bool debug;
}

macro print_struct_info($Type)
{
    $foreach $field : $Type.membersof:
        io::printfn("Field: %s, Type: %s, Offset: %s",
            $field.nameof,
            $field.typeid.nameof,
            $field.offsetof);
    $endforeach
}

fn void test_reflection()
{
    print_struct_info(Config);       // Prints info about each field
}