peterd.net

me@peterd.net (720)371-7487 Github Resume

mylang

mylang is an LLVM frontend I wrote that implements a C-like language, but it also adds tuples, destructuring, sum types, and some very basic pattern matching.

The source code is available here. It's written in Rust, which is a language produced by Mozilla as a replacement for C++.

My favorite programming languages are C, C++, and Rust. Rust is especially great because it fixes a lot of the problems that C++ has. In fact, I think it has motivated the C++ committee to fix some of these problems: std::variant, destructuring, std::format, std::span, and stuff in the GSL all share a lot with Rust. I think it will be very hard for Rust to completely supplant C++: more likely, people will just start using Rust libraries to implement components of C++ applications, which is what Mozilla has been doing with Firefox.

Anyways, I'm really interested in the space that C and C++ occupy, and I don't think Rust fills it completely. There's lots of people that don't want or need all the features Rust has, and Rust also trades some amount of precision to enable very high-level programming paradigms. Check out Zig and Odin, they are closer to C than C++. That's what mylang is supposed to be like.

Examples:

// Hello world

fn printf(fmt: *i8, ...) -> i32;
fn main(argc: i32, argv: **i8) -> i32 {
    printf("hello world\n");
    return 0;
}
// Parse compiler args

fn printf(fmt: *i8, ...) -> i32;
fn strcmp(x: *i8, y: *i8) -> i32;

// Returns (filename, -h)
fn parse_args(argc: i32, argv: **i8) -> (*i8, bool) {
    let file: *i8 = null;
    let help = false;
    for let i = 1; i < argc; i += 1 {
        let arg = argv[i];
        if strcmp(arg, "-h") == 0 {
            help = true;
            continue;
        }
        file = arg;
    }
    return (file, help);
}

fn main(argc: i32, argv: **i8) -> i32 {
    let (file, help) = parse_args(argc, argv);
    printf("help = %d, file = %s\n", help, file);
    if help {
        printf("usage: mylangc [-h] <file>\n");
        return 1;
    }
    if file == null {
        printf("missing file argument\n");
        return 1;
    }
    return 0;
}
// 4x4 Matrix multiplication

fn printf(fmt: *i8, ...) -> i32;

type mat4 = [4][4]f32;

fn matmul(A: mat4, B: mat4) -> mat4 {
    let xx = A[0][0] * B[0][0] + A[0][1] * B[1][0] + A[0][2] * B[2][0] + A[0][3] * B[3][0];
    let xy = A[0][0] * B[0][1] + A[0][1] * B[1][1] + A[0][2] * B[2][1] + A[0][3] * B[3][1];
    let xz = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2] * B[2][2] + A[0][3] * B[3][2];
    let xw = A[0][0] * B[0][3] + A[0][1] * B[1][3] + A[0][2] * B[2][3] + A[0][3] * B[3][3];

    let yx = A[1][0] * B[0][0] + A[1][1] * B[1][0] + A[1][2] * B[2][0] + A[1][3] * B[3][0];
    let yy = A[1][0] * B[0][1] + A[1][1] * B[1][1] + A[1][2] * B[2][1] + A[1][3] * B[3][1];
    let yz = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2] * B[2][2] + A[1][3] * B[3][2];
    let yw = A[1][0] * B[0][3] + A[1][1] * B[1][3] + A[1][2] * B[2][3] + A[1][3] * B[3][3];

    let zx = A[2][0] * B[0][0] + A[2][1] * B[1][0] + A[2][2] * B[2][0] + A[2][3] * B[3][0];
    let zy = A[2][0] * B[0][1] + A[2][1] * B[1][1] + A[2][2] * B[2][1] + A[2][3] * B[3][1];
    let zz = A[2][0] * B[0][2] + A[2][1] * B[1][2] + A[2][2] * B[2][2] + A[2][3] * B[3][2];
    let zw = A[2][0] * B[0][3] + A[2][1] * B[1][3] + A[2][2] * B[2][3] + A[2][3] * B[3][3];

    let wx = A[3][0] * B[0][0] + A[3][1] * B[1][0] + A[3][2] * B[2][0] + A[3][3] * B[3][0];
    let wy = A[3][0] * B[0][1] + A[3][1] * B[1][1] + A[3][2] * B[2][1] + A[3][3] * B[3][1];
    let wz = A[3][0] * B[0][2] + A[3][1] * B[1][2] + A[3][2] * B[2][2] + A[3][3] * B[3][2];
    let ww = A[3][0] * B[0][3] + A[3][1] * B[1][3] + A[3][2] * B[2][3] + A[3][3] * B[3][3];

    return [[xx, xy, xz, xw],
            [yx, yy, yz, yw],
            [zx, zy, zz, zw],
            [wx, wy, wz, ww]];
}

fn print(A: mat4) {
    for let i = 0; i < 4; i += 1 {
        for let j = 0; j < 4; j += 1 {
            printf("%10.4f  ", A[i][j] as f64);
        }
        printf("\n");
    }
}

fn main() -> i32 {
    let A = [[ 1.0,  9.0,  3.0,  4.0],
             [ 2.0, -4.0,  5.0,  0.5],
             [-3.0,  1.0,  6.0, -3.0],
             [ 6.0, 10.0, 11.0,  1.0]];
    let B = matmul(A, A);
    print(A);
    print(B);
    return 0;
}
// Structs and auto-deref

fn printf(fmt: *i8, ...) -> i32;

type vec3 struct {
    x: f32,
    y: f32,
    z: f32,
}

fn print(v: vec3) {
    let x = v.x as f64;
    let y = v.y as f64;
    let z = v.z as f64;
    printf("%f %f %f\n", x, y, z);
}

// Field accesses on pointers to structs are auto-deref'd.
fn dot(x: *vec3, y: *vec3) -> f32 {
    return x.x * y.x + x.y * y.y + x.z * y.z;
}

fn main(argc: i32, argv: **i8) -> i32 {
    let a: vec3 = { x: 1.0, y: 2.0, z: 3.0, };
    let b: vec3 = { x: 2.0, y: 3.0, z: 4.0, };
    print(a);
    print(b);

    let ab = dot(&a, &b);
    printf("dot a, b = %f\n", ab as f64);

    return 0;
}
// Pattern matching with `if let`

fn printf(fmt: *i8, ...);

type expr enum {
    int(i32),
    string(*i8),
}

fn print(e: expr) {
    if let int(i) = e {
        printf("%d\n", i);
    }
    if let string(s) = e {
        printf("%s\n", s);
    }
}

fn main() -> i32 {
    let e = expr.int(1);
    print(e);

    e = expr.string("hello world");
    print(e);

    return 0;
}