Cinder by Example

Cinder is small programming language for low-level development.

The Standard Library

Modules under std/ ship with the compiler and are always on the include

path. Load them with use.

I/O

std/io.cnd has print helpers and readers.

use "std/io.cnd";

fn main() -> i32 {
    println("hello");

    let n = 42;
    print(&n, .I32);

    let sp: []u8 = " ";
    print(&sp, .S);

    let big: u64 = 7;
    print(&big, .U64);
    putchar(10);

    return 0;
}
$ ./io
hello
42 7
FunctionWrites
print(&x, .Tag)one value, selected by tag
println(s)string plus newline
putchar(c)one byte
printerr(s) / printlnerr(s)stderr
read_byte() -> ?u8one byte from stdin, none on EOF

The print tags are I32, I64, I128, U8, U64, U128, F64,

Bool, S (a []u8 string), and Ptr.

Vec

std/vec.cnd is a growable byte buffer. Functions take a pointer to the

struct; methods would be pointless without mutation.

use "std/io.cnd";
use "std/vec.cnd";

fn main() -> i32 {
    let mut v = Vec { data: null, len: 0, cap: 0 };
    vec_init(&v);
    let ok = vec_push(&v, 10) && vec_push(&v, 20) && vec_push(&v, 30);
    if !ok { return 1; }
    let mut total: u32 = 0;
    for i in 0..vec_len(&v) {
        let b = vec_get(&v, i) else { return 2; };
        total += b;
    }

    let out: i32 = total as i32;
    print(&out, .I32);
    putchar(10);

    vec_deinit(&v);
    return 0;
}
$ ./vec
60

vecfromslice(s) builds a vector from a string.

String

std/string.cnd is an owning, growable string built on Vec.

use "std/io.cnd";
use "std/string.cnd";

fn main() -> i32 {
    let mut s = string_from("cin");
    if !string_push_byte(&s, 'd' as u8) { return 1; }
    if !string_append(&s, "er") { return 2; }
    println(string_as_slice(&s));
    string_deinit(&s);
    return 0;
}
$ ./string
cinder

Str helpers

std/core/str.cnd is pure and allocation-free.

use "std/io.cnd";
use "std/core/str.cnd";

fn main() -> i32 {
    let s = "  cinder  ";
    let t = str_trim(s);
    println(t);
    if str_starts_with(t, "cin") {
        println("starts with cin");
    }
    return 0;
}
$ ./str
cinder
starts with cin

Math, memory, ascii

std/core/math.cnd (mathmax, mathgcd, mathispow2, ...),

std/core/mem.cnd (memcopy, memzero, memalignup, ...), and

std/core/ascii.cnd (asciiisdigit, asciitoupper, ...) are tiny,

portable helpers.

Allocator

std/alloc.cnd exposes alloc(n), alloc_realloc(p, n), and dealloc(p)

backed by libc on hosted targets.

use "std/io.cnd";
use "std/alloc.cnd";

fn main() -> i32 {
    let p = alloc(16);
    if p == null { return 1; }
    unsafe {
        p[0] = 65;
        p[1] = 66;
        p[2] = 67;
        p[3] = 0;
        let slice = p[..3];
        print(&slice, .S);
    }
    putchar(10);
    dealloc(p);
    return 0;
}
$ ./alloc
ABC

Command line arguments

main may declare the C-style signature main(argc: i32, argv: **u8)

to receive the argument count and a pointer to the argument strings.

argv[0] is the program path; real arguments start at argv[1].

use "std/io.cnd";
use "std/core/str.cnd";

fn cstr_slice(p: *u8) -> []u8 {
    unsafe {
        let len = strlen(p);
        return p[..len];
    }
}

fn main(argc: i32, argv: **u8) -> i32 {
    let n: i32 = argc;
    print(&n, .I32);
    putchar(10);
    for i in 1 .. argc {
        unsafe {
            println(cstr_slice(argv[i as usize]));
        }
    }
    return 0;
}
$ ./args one two
3
one
two

File I/O

std/file.cnd reads and writes whole files through the C library. Paths are

[]u8 slices. filereadall returns a heap buffer (NUL-terminated) that you

must release with dealloc.

use "std/file.cnd";
use "std/io.cnd";

fn main() -> i32 {
    let path = "/tmp/cinder_doc_file.txt";
    if !file_write_all(path, "hello file") { return 1; }
    let data = file_read_all(path) else { return 2; };
    println(data);
    unsafe { dealloc(data.ptr); }
    return 0;
}
$ ./file
hello file
FunctionBehavior
filereadall(path) -> ?[]u8whole file into a heap buffer
filewriteall(path, data) -> boolwrite, truncating existing file
file_exists(path) -> boolcan the file be opened
file_size(path) -> ?usizesize in bytes
file_remove(path) -> booldelete the file

Port I/O and panic

std/x86.cnd declares outb/inb/etc. for bare-metal; std/panic.cnd

defines panic(msg) which aborts the program.