Skip to content

jakubDoka/hblang

Repository files navigation

hblang

holey byte

Build The Compiler

zig build install
./zig-out/bin/hbc --help

Tour

Try out the examples on depell.

Note: the examples are used to generate unit tests, n = 1 from each group is most interesting, others are more for testing purposes. Note: expectations contain the test case expectations that are asserted when zig build test is run

main fn 1

expectations := .{
    return_value: 42,
}

main := fn(): uint {
    return 42
}

arithmetic 1

main := fn(): uint {
    return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1 - 1
}

arithmetic 2

main := fn(): int {
    a: i8 = 0
    b: u8 = 0
    c: i16 = a - 1
    d: u16 = b + 1
    e: i32 = c - 1
    f: u32 = d + 1
    return f + e
}

arithmetic 4

expectations := .{
    return_value: 1,
}

main := fn(): uint {
    return 3 == 2 * 2 - 1
}

arithmetic 5 (errors)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    v := {}
    return 1 + v * 10
}

arithmetic 6 (missing operators)

main := fn(): uint {
    return 1 << 3 % 2 - 8 >> 3 | 4 & 2 ^ 0
}

arithmetic 7 (unary operators)

main := fn(): uint {
    if !true return 1
    if ~1 != -1 - 1 return 2
    if -1 != ~1 + 1 return 3
    return 0
}

float arithmetic 1

main := fn(): int {
    return @float_to_int(10.0 - 20.0 / 2.0 + 4.0 * (2.0 + 2.0) - 4.0 * 4.0 + 1.0 - 1.0)
}

float arithmetic 2

sin_table := f32.[0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245]

sin := fn(theta: f32): f32 {
    PI := 3.14159265358979323846
    TABLE_SIZE := @as(i32, 256)
    si := @float_to_int(@float_cast(theta) * 0.5 * @int_to_float(TABLE_SIZE)
        / @float_cast(PI))
    d := theta - @int_to_float(si) * 2.0 * PI / @int_to_float(TABLE_SIZE)
    ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1
    si &= TABLE_SIZE - 1
    //return @int_to_float(si)
    return sin_table[@bit_cast(si)] + (sin_table[@bit_cast(ci)] - 0.5
        * sin_table[@bit_cast(si)] * d) * d
}

main := fn(): int {
    //return @float_to_int(sin(0.0))
    return @float_to_int(sin(1000.0) * 1000.0) - 826
}

float arithmetic 3

main := fn(): uint {
    if 1.0 > 2.0 return 1
    if 1.0 >= 2.0 return 2
    if 2.0 <= 1.0 return 3
    if 2.0 < 1.0 return 4
    if 1.0 != 1.0 return 5
    if 2.0 == 1.0 return 6
    return 0
}

literals 1

main := fn(): uint {
    if 10 != 0xa return 16
    if 10 != 0o12 return 8
    if 10 != 0b1010 return 2
    return 0
}

literals 2

expectations := .{
    return_value: 0,
}

hex := fn(): uint {
    return 0x2d
}
dec := fn(): uint {
    return 45
}

main := fn(): uint {
    if hex() != dec() return 1
    return 0
}

literals 3

expectations := .{
    return_value: 69,
}

main := fn(): uint {
    return 'E'
}

functions 1

expectations := .{
    return_value: 33,
}

main := fn(): uint {
    return add_one(10) + add_two(20)
}

add_two := fn(x: uint): uint {
    return x + 2
}

add_one := fn(x: uint): uint {
    return x + 1
}

functions 2

expectations := .{
    return_value: 33,
}

main := @use("main.hb").main

// in: main.hb

one := @use("one.hb")
two := @use("two.hb")

main := fn(): uint {
    return one.add(10) + two.add(20)
}

// in: two.hb

add := fn(x: uint): uint {
    return x + 2
}

// in: one.hb

add := fn(x: uint): uint {
    return x + 1
}

functions 3 (errors)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    imaginary := {}
    imaginary()
    some_fn()
    _ = some_fn(0, 0, 0)
    _ = some_fn(0, 0)
    vl := some_fn(0, {}, 0, 0)
    return some_fn(vl, {}, 0)
}

some_fn := fn(a: uint, b: void, c: u8): uint {
    return
}

functions 4 (returning stack)

expectations := .{
    should_error: true,
}

main := fn(): void {
    rstack := return_direct_stack()
    rstruct := return_indirect_stack()
    v: ?^uint = #null
    ret := return_indirect_stack_but_not(&v)
}

return_direct_stack := fn(): ^uint {
    a := #0
    if true return &a

    return &a
}

return_indirect_stack := fn(): struct{.u: uint; .b: uint; .c: ^uint} {
    v := #0
    return .(0, 0, &v)
}

return_indirect_stack_but_not := fn(arg: ^?^uint): void {
    v := #0

    if arg.* == null {
        arg.* = &v
        arg.* = null
    }
}

functions 5 (inline)

main := fn(): uint {
    return foo()
}

$foo := fn(): uint {
    return 0
}

functions 6 (comptime @Any)

main := fn(): uint {
    return foo(fn(): uint {
        return 0
    })
}

foo := fn($f: @Any()): uint {
    return f()
}

functions 7 (return postion @Any)

main := fn(): uint {
    return @as(u8, quirky_int_cast(@as(uint, 1)))
}

quirky_int_cast := fn(x: @Any()): @Any() {
    v: @ReturnType() = @int_cast(x - 1)
    return v
}

comments 1

// commant is an item
main := fn(): uint {
    // comment is a statement
    foo(
        // comment is an exprression
    )
    return 0
}

foo := fn(comment: void): void {
    return // comment evaluates to void
}

// comments might be formatted in the future

if statements 1

expectations := .{
    return_value: 2,
}

main := fn(): uint {
    return fib(3)
}

fib := fn(x: uint): uint {
    if x <= 2 {
        return 1
    } else {
        return fib(x - 1) + fib(x - 2)
    }
}

if statements 2

expectations := .{
    return_value: 2,
}

main := fn(): uint {
    return fib(3)
}

fib := fn(x: uint): uint {
    if x <= 2 {
        x = 1
    } else {
        x = fib(x - 1) + fib(x - 2)
    }
    return x
}

if statements 3 (comptime)

main := fn(): uint {
    $if true return 0
}

if statements 4 (short circuit)

global := 0

main := fn(): uint {
    if false || true || effectfull(1) {}
    if true && false && effectfull(2) {}

    v := false
    v &&= effectfull(4)
    v = true
    v ||= effectfull(5)

    return global
}

effectfull := fn(vl: uint): bool {
    global = vl
    return true
}

if statements 5 (option unwrapping)

main := fn(): uint {
    val: ?uint = 0

    if v := val && v == 0 {
        return v
    }

    return 1
}

variables 1

main := fn(): uint {
    ඞ := 1
    b := 2+= 1
    return ඞ - b
}

loops 1

expectations := .{
    return_value: 55,
}

main := fn(): uint {
    return fib(10)
}

fib := fn(n: uint): uint {
    b := 1
    a := 0
    loop {
        if n == 0 break
        c := a + b
        a = b
        b = c
        n -= 1
        continue
    }
    return a
}

loops 2

expectations := .{
    return_value: 9,
}

main := fn(): uint {
    return square(3)
}

square := fn(size: uint): uint {
    acc := 0
    y := 0
    loop if y == size break else {
        x := 0
        loop if x == size break else {
            acc += x * y
            x += 1
        }
        y += 1
    }
    return acc
}

loops 3

expectations := .{
    return_value: 4,
}

main := fn(): uint {
    i := 0
    loop if i == 4 break else {
        i += 1
    }
    return i
}

loops 4 (comptime)

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    arr := uint.[1, 2, 3, 4]
    i := 0
    sum := 0
    $loop $if i == arr.len break else {
        sum += arr[i]
        i += 1
    }
    return sum
}

loops 5 (comptime or error)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    arr := uint.[1, 2, 3, 4]
    i := 0
    sum := 0
    $loop if i == arr.len break else {
        sum += arr[i]
        i += 1
    }
    return sum
}

loops 6 (infinite)

expectations := .{
    times_out: true,
}

main := fn(): uint {
    if true loop {}

    return 0
}

loops 7 (loop invariant break)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    arr := uint.[1, 2, 3, 4]
    i := 0
    sum := 0
    loop if i == arr.len break else {
        sum += arr[i]
        //i += 1 // ups forgot to increment
    }
    return sum
}

loops 12 (labels)

main := fn(): uint {
    sum := 0
    x := 0
    loop:outher if x == 10 break:outher else {
        y := 0
        loop if y == 10 break else {
            if x * y == 0 break:outher
            sum += x * y
            y += 1
        }
        x += 1
    }
    return sum
}

loops 13 (while)

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    i := 0
    while i < 10 {
        i += 1
    }
    return i
}

loops 14 (for)

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    sum := 0

    for i := 1..5 {
        sum += i
    }

    return sum
}

loops 15 (for advanced)

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    arr: [4]uint = idk

    for i := 1.., elem := arr[..] {
        elem.* = i
    }

    sum := 0

    for elem := arr[..] {
        sum += elem.*
    }

    return sum
}

loops 16 (for different lengths error)

expectations := .{
    unreaches: true,
}

bl := @Builtins()

main := fn(): uint {
    sum := 0
    for i := 0..2, j := u8.[1, 2, 3][..] {
        sum += i + j.*
    }
    return sum
}

@handler("for_loop_length_mismatch", fn(loc: bl.SrcLoc): never {
    die
})

pointers 1

main := fn(): uint {
    a := #1
    b := &a
    modify(b)
    b.* += 2
    return b.* - 4
}

modify := fn(a: ^uint): void {
    a.* = 2
    return
}

pointers 2

expectations := .{
    return_value: 1,
}

main := fn(): uint {
    a := #1
    b := #2

    c := &a
    d := &b

    swap(c, d)

    return a - b
}

swap := fn(a: ^uint, b: ^uint): void {
    tmp := b.*
    b.* = a.*
    a.* = tmp
}

pointers 3

expectations := .{
    return_value: 1,
}

main := fn(): uint {
    a := #1
    _ = do_stuff(&a)
    return a
}

do_stuff := fn(v: ^uint): uint {
    if v.* == 0 {
        return 0
    } else {
        return 1
    }
}

uninit memory 1 (errors)

expectations := .{
    should_error: true,
}

opaque := false

main := fn(): uint {
    // trivial
    if idk return 0

    // missing init path

    val: uint = idk

    if opaque {
        val = 0
    } else {
        // val = 1 // forgotten
    }

    if val == 1 return 0

    Stru := struct{.a: uint; .b: uint}

    // quite smart
    s := Stru.{a: 1, b: idk}

    if opaque {
        s.b = 1
    } else {
        // s.b = 0 forgotten
    }

    if s.b == 0 return 0
    if s.a == 0 return 10

    if opaque {
        // this is not detectable (conditional)
        // TODO: we should detect at least trivial stuff like this
        if s.b == 0 return 11
    }

    return 1
}

pointers 4 (errors)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    return (~0).*
}

pointers 5 (math)

main := fn(): uint {
    val := uint.[1, 0]
    return (val.ptr + 1 + (val.ptr - val.ptr)).*
}

structs 1

expectations := .{
    return_value: 3,
}

Ty := struct {
    .a: int;
    .b: int = 1;

    sum := fn(t: Ty): int {
        t.a -= 2
        t.b += 1
        return t.a - t.b
    }
}

Ty2 := struct {
    .ty: Ty;
    .c: int;
}

main := fn(): int {
    finst := Ty2.{ty: Ty.{a: 4}, c: 3}
    inst := odher_pass(finst)
    if inst.c != 3 {
        return 0
    }
    if inst.ty.sum() != 0 {
        return 100
    }
    return pass(&inst.ty)
}

pass := fn(t: ^Ty): int {
    t.a -= 1
    t.a += 1
    return t.a - t.b
}

odher_pass := fn(t: Ty2): Ty2 {
    return t
}

structs 2

expectations := .{
    return_value: 3,
}

Ty := struct {
    .a: int;
    .b: int;
    .c: int;
}

main := fn(): int {
    a := Ty.{a: 0, b: 0, c: 0}
    b := Ty.{a: 1, b: 1, c: 1}
    swap(&a, &b)
    return a.a + a.b + a.c
}

swap := fn(a: ^Ty, b: ^Ty): void {
    tmp := a.*
    a.* = b.*
    b.* = tmp
}

structs 5 (comptime)

expectations := .{
    return_value: 6,
}

main := fn(): uint {
    Ty := struct{.a: uint; .b: uint; .c: uint}

    vl := Ty.(1, 2, 3)

    i := 0
    sum := 0
    $loop $if i == @len_of(Ty) break else {
        sum += vl[i]
        i += 1
    }

    return sum
}

structs 6 (packed)

expectations := .{
    return_value: 4,
}

main := fn(): uint {
    Pckd := struct align(1){.a: u8; .b: u16}
    return @size_of(Pckd) + @align_of(Pckd)
}

file structs 1

main := fn(): uint {
    foo := @use("Foo.hb").init(2)
    return foo.a - foo.b
}

// in: Foo.hb
.a: uint;
.b: uint

init := fn(v: uint): @CurrentScope() {
    return .(v, v)
}

generic structs 1

main := fn(): uint {
    $if Vec(uint) != Vec(uint) return 100

    vl: Vec(uint) = nvec(uint, 1)
    return vl.sub()
}

nvec := fn($E: type, v: uint): Vec(E) {
    return .(v, v)
}

Vec := fn($E: type): type return struct {
    .x: E;
    .y: E;

    sub := fn(self: @CurrentScope()): E {
        return self.x - self.y
    }
}

generic structs 2

main := fn(): uint {
    vl: Foo(uint).Bar(u8) = .init()
    return vl.sub()
}

Foo := fn($F: type): type return struct {
    Bar := fn($B: type): type return struct {
        .foo: F;
        .bar: B;

        init := fn(): @CurrentScope() return .(1, 1)

        sub := fn(self: @CurrentScope()): uint {
            return self.foo - self.bar
        }
    }
}

generic structs 3

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    vl: Foo(Foo(uint)) = .(.(10))
    return vl.sub().sub()
}

Foo := fn($F: type): type return struct {
    .foo: F;

    sub := fn(self: @CurrentScope()): F {
        return self.foo
    }
}

generic structs 4

expectations := .{
    return_value: 6,
}

main := fn(): uint {
    val: Array(uint, 3) = .(1, .(2, .(3, .())))
    return val.get(0) + val.get(1) + val.get(2)
}

Array := fn($E: type, $len: u32): type if len == 0 {
    return struct {
        get := fn(self: @CurrentScope(), i: uint): E die
    }
} else {
    return struct {
        .elem: E;
        .next: Array(E, len - 1);

        get := fn(self: @CurrentScope(), i: uint): E {
            if i == 0 return self.elem

            return self.next.get(i - 1)
        }
    }
}

generic structs 5 (iterators)

main := fn(): uint {
    ref := "abcd"

    start := "ab"
    end := "cd"

    Chars := struct {
        .slc: []u8;
        next := fn(self: ^@CurrentScope()): ?u8 {
            if self.slc.len == 0 return null
            defer self.slc = self.slc[1..]
            return self.slc[0]
        }
    }

    Chain := fn($A: type, $B: type): type return struct {
        .state: enum{.a; .b; .done};
        .a: A;
        .b: B;

        Elem := @TypeOf(A.next(idk))

        next := fn(self: ^@CurrentScope()): Elem {
            loop match self.state {
                .a => {
                    nxt := self.a.next()
                    if nxt != null return nxt
                    self.state = .b
                },
                .b => {
                    nxt := self.b.next()
                    if nxt != null return nxt
                    self.state = .done
                },
                .done => return null,
            }
        }
    }

    siter := Chars.(start)
    riter := Chars.(ref)
    citer := Chain(Chars, Chars).(.a, .(start), .(end))

    loop {
        sc := siter.next()
        if sc == null break
        rc := riter.next()
        if rc == null return 1

        if sc.? != rc.? return 2
    }

    riter = .(ref)
    loop {
        rc := riter.next()
        if rc == null break
        sc := citer.next()
        if sc == null return 3

        if sc.? != rc.? return 4
    }

    return 0
}

comptime 1

main := fn(): uint {
    $some_int := uint
    some_fn := fn($ui: type, x: some_int): ui {
        val: some_int = x + 1
        return val * val
    }

    return some_fn(some_int, 9) - (some_fn(some_int, 5) + some_fn(some_int, 7))
}

comptime 2

expectations := .{
    return_value: 33,
}

main := fn(): uint {
    $some_int := uint

    $some_fn := fn(): some_int {
        return 1
    }

    $some_fn2 := fn(): some_int {
        return some_fn() + 1
    }

    $some_fn3 := fn($fnc: fn(): some_int): (fn(): some_int) {
        return fn(): some_int {
            return fnc() + 10
        }
    }

    return some_fn3(some_fn)() + some_fn3(some_fn3(some_fn2))()
}

comptime 4 (@TypeOf)

main := fn(): uint {
    some_int := 0

    Ty := struct{.field: @TypeOf(some_int)}

    return Ty.(some_int).field
}

comptime 5 (integers)

main := fn(): uint {
    $value := 1

    Ty := struct {
        global_value := value
    }

    func := fn(): uint return value

    param := fn($i: uint): type {
        return struct {
            global_value := i - 1
        }
    }

    return Ty.global_value - func() + param(value).global_value
}

comptime 6 (strings)

expectations := .{
    return_value: 69,
}

main := fn(): uint {
    string_to_array := fn($str: []u8): [str.len]u8 {
        vl: [str.len]u8 = #idk
        for i := 0..str.len {
            vl[i] = str[i]
        }
        return vl
    }

    return string_to_array("Edward")[0]
}

comptime 7 (struct)

expectations := .{
    return_value: 2,
}

main := fn(): uint {
    $Dims := struct{.x: uint; .y: uint}
    arr := fn($dims: Dims): [dims.x][dims.y]uint {
        return idk
    }

    return arr(.(2, 1)).len
}

tuples 1

expectations := .{
    return_value: 6,
}

main := fn(): uint {
    tuple := .(1, 2, 3)
    return tuple[0] + tuple[1] + tuple[2]
}

struct operators 1

expectations := .{
    return_value: 10,
}

Point := struct {
    .x: uint;
    .y: uint;
}

Rect := struct {
    .a: Point;
    .b: Point;
}

Color := struct{.b: u8; .g: u8; .r: u8; .a: u8}

main := fn(): uint {
    i := Color.(0, 0, 0, 0)
    i += .(1, 1, 1, 1)
    if i.r + i.g + i.b + i.a != 4 return 1001

    if Point.(1, 1) != Point.(1, 1) return 1002
    if Point.(1, 2) == Point.(1, 1) return 1003

    a := Point.(1, 2)
    b := Point.(3, 4)

    d := Rect.(a + b, b - a)
    zp := Point.(0, 0)
    d2 := Rect.(zp - b, a)
    d2 += d

    c := d2.a + d2.b
    return c.x + c.y
}

unions 1

expectations := .{
    return_value: 1,
}

main := fn(): uint {
    Union := union {
        .single: u16;
        .pair: struct{.l: u8; .r: u8};
    }

    val := Union.{pair: .(1, 1)}

    return val.single - 256
}

tagged union 1

main := fn(): uint {
    Tagged := union(enum) {
        .a: u32;
        .b: f32;
    }

    val := Tagged.{a: 0}

    if val == .b return 1
    if val != .a return 2

    match val {
        .b => return 3,
        .a => return val.a,
    }
}

enums 1

expectations := .{
    return_value: 1,
}

main := fn(): uint {
    Enum := enum{.A; .B; .C}

    n1 := Enum.A
    n2: Enum = .B

    if n1 == n2 return 10

    return @as(u8, n1) + n2
}

enums 3 (customization)

main := fn(): uint {
    Enm := enum(u32){.a := 1; .b; .c := 0}

    if @as(Enm, @bit_cast(@as(u32, 1))) != .a return 1
    if @as(Enm, @bit_cast(@as(u32, 2))) != .b return 2
    if @as(Enm, @bit_cast(@as(u32, 0))) != .c return 3

    return 0
}

enums 4

expectations := .{
    return_value: 69,
}

NameMap := fn($Enum: type): type {
    $info := @type_info(Enum)

    $sum := (fn(): uint {
        sum := 0
        i := 0
        loop if i == info.@enum.fields.len break else {
            sum += info.@enum.fields[i].name.len
            i += 1
        }
        return sum
    })()

    $StrBuf := [sum]u8
    $IndexBuf := [@len_of(Enum) + 1]uint

    return struct {
        .buf: StrBuf;
        .index: IndexBuf;

        new := fn(): @CurrentScope() {
            buf: StrBuf = #idk
            index: IndexBuf = idk
            index[0] = 0

            ii: u8 = 0
            bi := 0
            loop if ii == @len_of(Enum) break else {
                name := info.@enum.fields[ii].name
                ij := 0
                loop if ij == name.len break else {
                    buf[bi + ij] = name[ij]
                    ij += 1
                }

                bi += @int_cast(name.len)
                ii += 1
                index[ii] = bi
            }

            return .(buf, index)
        }

        get := fn(self: ^@CurrentScope(), k: Enum): []u8 {
            return self.buf[self.index[k]..self.index[@as(u8, k) + 1]]
        }
    }
}

Nm := enum{.E; .bcd; .cd}

map := NameMap(Nm).new()

main := fn(): uint {
    return map.get(.E)[0]
}

enums 5

expectations := .{
    return_value: 0,
}

Enum := enum{.A; .B; .C}

main := fn(): uint {
    if Enum.C > .A {
        return 0
    }
    return 1
}

match 1

main := fn(): uint {
    Nm := enum{.a; .b; .c}

    match Nm.a {
        .a => {},
        Nm.b => return 1,
        .c => return 2,
    }

    match Nm.b {
        .a => return 3,
        else => return 0,
    }
}

match 2 (comptime)

main := fn(): uint {
    $match enum{.a}.a {
        .a => {},
    }

    $match enum{.a; .b}.a {
        .a => return 0,
        .b => return 1,
    }
}

match 3 (errors)

expectations := .{
    should_error: true,
}

main := fn(): void {
    match enum{.a; .b}.a {}

    match enum{.a; .b}.a {
        .a => {},
        .a => {},
    }

    $match enum{.a; .b}.a {}

    $match enum{.a; .b}.a {
        .b => {},
    }
}

defer 1

main := fn(): uint {
    i := 0
    {
        defer i += 1
        if i == 1 return 1
    }

    if i != 1 return 2

    loop {
        defer i += 1
        if i == 3 continue
        if i == 4 break
    }

    if i != 5 return 3

    ret_defer := fn(str: ^uint): void {
        defer str.* += 1
    }

    v := #0
    ret_defer(&v)
    if v != 1 return 4

    return 0
}

die 1

expectations := .{
    unreaches: true,
}

main := fn(): uint {
    if false return 1
    die
}

die 2

expectations := .{
    unreaches: true,
}

fallible := fn(): ?^u8 {
    return null
}

main := fn(): void {
    a := fallible()
    if a == null die
    die
}

global variables 1

counter := 0

dec := fn(): void {
    counter -= 1
}

main := fn(): uint {
    counter = 1
    dec()
    return counter
}

global variables 2

expectations := .{
    return_value: 55,
}

some_other := 10

some_fib := fib(some_other)

fib := fn(n: uint): uint {
    if n != 10 return 0
    return 55
}

bigon_era := some_other - 10

main := fn(): uint {
    return some_fib - bigon_era
}

constants 1

expectations := .{
    return_value: 42,
}

$ans := 32
$wer: u8 = 10

main := fn(): uint {
    return ans + wer
}

string errors

expectations := .{
    should_error: true,
}

main := fn(): void {
    _ = "\ඞ"
}

arrays 1

expectations := .{
    return_value: 28,
}

main := fn(): uint {
    arr: [8]uint = idk

    i := 0
    loop if i == arr.len break else {
        arr[i] = i
        i += 1
    }

    i = 0
    sum := 0
    loop if i == arr.len break else {
        sum += arr[i]
        i += 1
    }

    return sum
}

arrays 2

expectations := .{
    return_value: 9,
}

dim: uint = 3

main := fn(): uint {
    narr: [dim][dim]uint = idk

    y := 0
    loop if y == narr.len break else {
        x := 0
        loop if x == narr[y].len break else {
            narr[y][x] = x * y
            x += 1
        }
        y += 1
    }

    linarr: ^[dim * dim]uint = @bit_cast(&narr)

    sum := 0
    i := 0
    loop if i == linarr.len break else {
        sum += linarr.*[i]
        i += 1
    }

    return sum
}

arrays 3 (errors)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    arr := uint.[0, 1, 2]
    return arr[3]
}

slices 1

expectations := .{
    return_value: 50,
}

main := fn(): uint {
    arr := u8.[1, 2, 3, 4]
    slice := arr[..]

    slices := ([]u8).[arr[..], arr[..2], arr[2..], arr[1..3], slice[..], slice[..2], slice[2..], slice[1..3]]

    sum := 0
    i := 0
    loop if i == slices.len break else {
        j := 0
        loop if j == slices[i].len break else {
            sum += slices[i][j]
            j += 1
        }
        i += 1
    }

    return sum
}

opaque := fn(): uint return 0

slices 3

reverse := fn(slice: []u8): []u8 {
    if slice.len == 0 return slice
    j := slice.len - 1
    i := 0
    temp: u8 = 0
    loop if i < j {
        temp = slice[i]
        slice[i] = slice[j]
        slice[j] = temp
        i += 1
        j -= 1
    } else return slice
}

main := fn(): uint {
    arr := u8.[1, 2, 3]
    _ = reverse(&.[])
    _ = reverse(arr[..])
    return arr[0] - arr[1] - arr[2]
}

slices 4

equals := fn(lhs: []u8, rhs: []u8): bool {
    if lhs.len != rhs.len return false
    if lhs.ptr == rhs.ptr return true
    i := 0
    loop if i == lhs.len break else {
        if lhs[i] != rhs[i] return false
        i += 1
    }
    return true
}

main := fn(): uint {
    abc := "abc"
    a_b_c := u8.['a', 'b', 'c'][..]
    if !equals(abc, abc) return 1
    if !equals(a_b_c, abc) return 1

    return 0
}

nullable types 1

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    ten := mkval(uint, 10)

    if ten == null return 1

    if mknull(uint) != null return 2

    return ten.?
}

mknull := fn($T: type): ?T return null
mkval := fn($T: type, val: T): ?T return val

nullable types 2

Stru := struct {
    .a: uint;
    .b: uint;
}

StruF := struct {
    .a: []u8;
}

main := fn(): uint {
    nlbl: ?Stru = .(0, 0)
    other: ?Stru = .{a: 0, b: 0}
    othera: ?[2]uint = .[0, 0]
    otherb: ?StruF = .("")

    if nlbl == null return 1
    if other == null return 2
    if othera == null return 3
    if otherb == null return 4

    nlbl.?.b = 1
    take(&nlbl.?)

    return nlbl.?.a - nlbl.?.b
}

take := fn(s: ^Stru): void {
    s.a += 1
}

nullable types 4 (pointer optimization)

main := fn(): uint {
    if @size_of(?^u8) != @size_of(^u8) return 1
    if @size_of(?struct{.v: ^u8}) != @size_of(^u8) return 2

    v := #3
    ptr := opaque(&v)
    ptr.?.* = 0

    return v
}

opaque := fn(v: @Any()): ?@TypeOf(v) return v

nullable types 5 (or operator)

main := fn(): uint {
    if (@as(?uint, null) || 1) != 1 return 1

    return @as(?uint, 0) || return 1
}

function types 1

expectations := .{
    return_value: 11,
}

main := fn(): uint {
    fun := fn(x: uint): uint return x + 1

    take_fun := fn($f: fn(uint): uint): uint {
        return f(10)
    }

    return take_fun(fun)
}

function pointers 1

call := fn(f: ^fn(u8): u8, v: u8): u8 return f(v)

main := fn(): uint {
    return call(&fn(x: u8): u8 return x - 10, 10)
}

function pointers 6 (vtable)

expectations := .{
    return_value: 98,
}

$vtable_entries := fn($T: type): []type {
    $scratch: [128]type = idk
    $n := 0
    $i := 0
    $while n < @decl_count_of(T) {
        $decl := T[n]
        $if @type_info(@TypeOf(decl)) == .fnty {
            $args := @type_info(@TypeOf(decl)).fnty.args
            $if args.len > 0 $if args[0] == ^T {
                scratch[i] = @TypeOf(&decl)
                i += 1
            }
        }
        n += 1
    }
    return @alloc_global(scratch[..i])
}

Vtable := fn($T: type): type {
    $entries := vtable_entries(T)
    $n := 0

    Field := @ChildOf(@TypeOf(@type_info(T).@struct.fields))
    $decls: [entries.len]Field = idk

    $while n < entries.len {
        decls[n] = .(&.[], entries[n], idk, idk)
        n += 1
    }
    return @eval(@Type(.{@struct: .(@align_of(^void), decls[..], &.[])}))
}

$vtable := fn($V: type, $Dyn: type): Vtable(V) {
    $entries := @eval(vtable_entries(Dyn))

    $if @len_of(Vtable(V)) != entries.len @error(Dyn, " is not an object of kind ", V)

    tmp: Vtable(V) = idk
    $n := 0
    $while n < @len_of(Vtable(V)) {
        tmp[n] = @bit_cast(&Dyn[n])
        n += 1
    }

    return tmp
}

Test := struct {
    .vt: Vtable(Test);
    .obj: ^Test;

    $from := fn(obj: @Any()): Test {
        return .(vtable(Test, @ChildOf(@TypeOf(obj))), @bit_cast(obj))
    }
    $a := fn(this: ^Test): void return this.vt[0](this.obj)
    $b := fn(this: ^Test): i32 return this.vt[1](this.obj)
}

Dyn2 := struct {
    .inner: i32;
    $a := fn(this: ^Dyn2): void {
        this.inner = 98
    }
    $b := fn(this: ^Dyn2): i32 {
        return this.inner
    }
}

main := fn(): int {
    a := Dyn2.(255)
    b := Test.from(&a)
    _ = b.a()
    return b.b()
}

struct patters 1

expectations := .{
    return_value: 3,
}

foo.{bar, bas: .{baz: bax}} := @use("foo.hb")

main := fn(): uint {
    return foo.foo() + bar() + bax()
}

// in: foo.hb
foo := fn(): uint {
    return 0
}
bar := fn(): uint {
    return 1
}
bas := @use("bas.hb")
// in: bas.hb
baz := fn(): uint {
    return 2
}

directives 1 (@size_of, @align_of)

expectations := .{
    return_value: 3,
}

main := fn(): uint {
    return @size_of(struct{.b: u16; .a: u8}) - @align_of(u8)
}

directives 2 (@as)

expectations := .{
    return_value: 3,
}

main := fn(): uint {
    val := @as(struct{.a: uint}, .(3))
    return val.a
}

directives 3 (@ecall)

expectations := .{
    return_value: 3,
}

main := fn(): uint {
    $if @target("hbvm-ableos") return @ecall(
        100,
        &struct{.a: uint; .b: uint}.(1, 2),
        &struct{.a: uint; .b: uint; .c: uint}.(3, 4, 5),
    )
    $if @target("x86_64-linux") return @syscall(60, 3)

    return 3
}

directives 4 (@int_cast)

main := fn(): uint {
    v: uint = 0
    return @as(u8, @int_cast(v))
}

directives 5 (@bit_cast)

main := fn(): uint {
    return @as(u32, @bit_cast(@as(struct{.l: u16; .r: u16}, @bit_cast(@as(u32, 0)))))
}

directives 6 (@len_of)

expectations := .{
    return_value: 4,
}

main := fn(): uint {
    return @len_of(struct{.a: u8; .b: u32}) + @len_of([2]u8)
}

directives 7 (@kind_of)

expectations := .{
    return_value: 7,
}

main := fn(): uint {
    return @kind_of(struct{})
}

directives 8 (@ChildOf)

expectations := .{
    return_value: 1,
}

main := fn(): uint {
    vl := #1
    return deref(^uint, &vl)
}

deref := fn($T: type, arg: T): @ChildOf(T) {
    return arg.*
}

directives 9 (@embed)

expectations := .{
    return_value: 69,
}

main := fn(): uint {
    val: ^[1]u8 = &@embed("mbed.txt")
    return val.*[0]
}

// in: mbed.txt
E

directives 10 (@error)

expectations := .{
    should_error: true,
}

main := fn(): uint {
    $if false @error("never happens")
    @error("no main in sight, here is a type: ", ^u8)
}

directives 11 (@target, @is_comptime)

expectations := .{
    return_value: 1,
}

global: bool = @target("hbvm-ableos") | @target("x86_64-linux") | @target("wasm-freestanding")

main := fn(): uint {
    $if @is_comptime() @error("unecpected")

    return global
}

directives 12 (@inline)

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    return @inline(foo, 10)
}

foo := fn(vl: uint): uint return vl

directives 13 (@Any)

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    _ = func(@as(uint, 1), 2)
    return foo(5) + foo(@as(u8, 5))
}

foo := fn(vl: @Any()): @TypeOf(vl) {
    return vl
}

func := fn(a: @Any(), b: @TypeOf(a)): uint {
    return 0
}

directives 14 (@type_name)

expectations := .{
    return_value: 4,
}

main := fn(): uint {
    return @type_name(uint).len
}

directives 15 (@has_decl)

main := fn(): uint {
    $if @has_decl(scp, "bar") {
        return scp.bar
    }

    $if @has_decl(scp, "foo") {
        return scp.foo
    }
}

scp := enum {
    foo := 0
}

directives 16 (@import())

expectations := .{
    return_value: 10,
}

create := fn(size: uint): ^void @import("malloc")
destroy := fn(ptr: ^void): void @import("free")

smh := fn(): uint @import("smh")
opaque := false

main := fn(): uint {
    $if @target("hbvm-ableos") {
        return 10
    }
    $if @target("wasm-freestanding") {
        //if opaque {
        //    return smh()
        //}
        return 10
    }
    // wont work

    ptr: ^uint = @bit_cast(create(8))
    defer destroy(@bit_cast(ptr))
    ptr.* = 10
    return ptr.*
}

directives 17 (@RootScope())

custom_num := 0

main := fn(): uint {
    return @use("lib.hb").some_num()
}

// in: lib.hb

some_num := fn(): uint {
    $if @has_decl(@RootScope(), "custom_num") return @RootScope().custom_num
    return 1
}

directives 18 (@export())

@export("main", foo)

foo := fn(): uint {
    return 0
}

directives 19 (@frame_pointer())

expectations := .{
    return_value: 8,
}

bar := fn(): uint {
    return foo()
}

foo := fn(): uint {
    return @frame_pointer()
}

main := fn(): uint {
    $if @target("x86_64-linux") {
        return @frame_pointer() - bar() - 16
    } else {
        return 8
        //@frame_pointer() - bar()
    }
}

directives 20 (@handler, @SourceLoc)

expectations := .{
    unreaches: true,
}

bl := @Builtins()

// ignored in this case
@handler("slice_ioob", null)

@handler("slice_ioob", fn(
    loc: bl.SrcLoc,
    slice_len: uint,
    start: uint,
    end: uint,
): never {
    die
})

use_slice := fn(slice: []u8): uint {
    return slice[0]
}

produce_memcpy := fn(vl: [4]u8): void {}

main := fn(): uint {
    vl: u8 = #0
    slc := (&vl)[..1]

    arr := u8.[1, 2, 3, 4]
    produce_memcpy(arr)

    return use_slice(slc[..0])
}

// in: lib.hb

@handler("entry", fn(): uint {
    return @inline(@RootScope().main)
})

@handler("memcpy", fn(dst: ^u8, src: ^u8, len: uint): void {
    while len != 0 {
        dst.* = src.*
        dst += 1
        src += 1
        len -= 1
    }
})

directives 21 (@field_name)

expectations := .{
    return_value: 69,
}

main := fn(): uint {
    return @eval(@type_info(struct{.Edward: void}).@struct.fields[0].name[0])
}

directives 22 (@simd)

dataset := f32.[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

main := fn(): uint {
    // the simd vector type does not accept the length, instead you have
    // to query the length and adjust accordingly
    V := @simd(f32)

    data := dataset
    vect := @as(^V, @bit_cast(data.ptr))[0..data.len / @len_of(V)]

    for v := vect {
        v.* *= v.* + v.* + @splat(1) - v.* / @splat(2)
        v.* = -v.*
    }

    $if @len_of(V) > 1 {
        for s := data[vect.len * @len_of(V)..] {
            s.* *= s.* + s.* + 1 - s.* / 2
            s.* = -s.*
        }
    }

    data2 := dataset
    for v := data2[..] {
        v.* *= v.* + v.* + 1 - v.* / 2
        v.* = -v.*
    }

    for i := 1.., a := data[..], b := data2[..] {
        if a.* != b.* return i
    }

    return 0
}

directives 23

expectations := .{
    return_value: 14,
}

main := fn(): uint {
    return type_id(u8) + type_id(u16) + type_id(u32)
        + type_id(u64) + type_id(i8) + type_id(u8)
}

$type_id := fn($T: type): uint {
    return @eval(inc())
}

type_counter := 0
inc := fn(): uint {
    defer type_counter += 1
    return type_counter
}

comptime arena 1

expectations := .{
    return_value: 10,
}

main := fn(): uint {
    return @eval(something(10))
}

something := fn(len: uint): uint {
    tmp := comptime_arena.checkpoint()
    defer tmp.restore()

    arr := tmp.arena.alloc(u8, len)

    for i := arr {
        i.* = 1
    }

    return arr.len
}

Arena := struct {
    .pos: ^u8;

    $alloc := fn(self: ^Arena, $Elem: type, len: uint): []Elem {
        defer self.pos += @align_of(Elem) - 1 + @bit_cast(self.pos) & @align_of(Elem) - 1
        return self.pos[0..len]
    }

    $checkpoint := fn(self: ^Arena): CheckPoint {
        return CheckPoint.(self, self.pos)
    }
}

CheckPoint := struct {
    .arena: ^Arena;
    .prev_pos: ^u8;

    $restore := fn(self: ^CheckPoint): void {
        self.arena.pos = self.prev_pos
    }
}

comptime_arena_mem: [1024]u8 = idk
comptime_arena := Arena.(&comptime_arena_mem[0])

directives 24 (@type_info)

main := fn(): uint {
    $if @type_info(u8) != .builtin return 1
    $if @type_info(struct{}) != .@struct return 2
    $if @type_info(enum{}) != .@enum return 3

    strct := @type_info(struct{.a: u8; .b: u16})
    $if strct != .@struct return 4
    $if strct.@struct.alignment != 2 return 5
    $if strct.@struct.fields[0].name[0] != 'a' return 6
    $if strct.@struct.fields[1].name[0] != 'b' return 7
    $if strct.@struct.fields[0].ty != u8 return 8
    $if strct.@struct.fields[1].ty != u16 return 9

    return 0
}

directives 25 (@Type)

main := fn(): uint {
    $Ptr := @Type(.{pointer: .{ty: u8}})
    $if Ptr != ^u8 return 2

    $Slice := @Type(.{slice: .{elem: u8}})
    $if Slice != []u8 return 3

    $Nullable := @Type(.{option: .{inner: u8}})
    $if Nullable != ?u8 return 4

    $Array := @Type(.{array: .{elem: u8, len: 1}})
    $if Array != [1]u8 return 5

    $FnTy := @Type(.{fnty: .{args: &.[u8, u8], ret: u8}})
    $if FnTy != (fn(u8, u8): u8) return 6

    $Stru := MakeStruct("foo", u8)
    $StruI := MakeStruct("foo", u8)

    s: StruI = .(0)
    b := Stru.(0)
    s = b

    $Unio := MakeUnion("foo", u8)
    $UnioI := MakeUnion("foo", u8)

    u: UnioI = .{foo: 0}
    c := Unio.{foo: 0}
    u = c

    $Enum := MakeEnum("foo")
    $EnumI := MakeEnum("foo")

    e: EnumI = .foo
    d := Enum.foo
    e = d

    return s.foo
}

MakeStruct := fn($fname: []u8, $ftype: type): type {
    return @eval(@Type(.{@struct: .{
        alignment: @align_of(ftype),
        fields: &.[.{
            name: fname,
            ty: ftype,
            offset: 0,
            default: idk,
        }],
        decls: &.[],
    }}))
}

MakeUnion := fn($fname: []u8, $ftype: type): type {
    return @eval(@Type(.{@union: .{
        tag: void,
        fields: &.[.{
            name: fname,
            ty: ftype,
        }],
        decls: &.[],
    }}))
}

MakeEnum := fn($fname: []u8): type {
    // just triggering stack alignment bug
    fields := @TypeOf(@type_info(idk).@enum.fields[0]).[.{
        name: fname,
        value: 0,
    }, .{
        name: idk,
        value: 1,
    }]
    onm := u8.['b', 'a', 'r']
    fields[1].name = onm[..]

    return @eval(@Type(.{@enum: .{
        backing_int: u8,
        fields: fields[..],
        decls: &.[],
    }}))
}

directives 26 (@thread_local_storage)

local_int: uint = @thread_local_storage()

main := fn(): uint {
    local_int = 1
    return local_int - 1
}

directives 27 (@alloc_global)

mk_arr := fn(len: uint): []u8 {
    buf: [1024]u8 = idk

    for i := 0..len {
        buf[i] = @int_cast(i)
    }

    return @alloc_global(buf[..len])
}

main := fn(): uint {
    arr := @eval(mk_arr(10))
    return arr[0]
}

directives 28 (@decl_count_of)

expectations := .{
    return_value: 10,
}

bag := enum {
    a := 0
    b := 1
    c := 2
    d := 3
    e := 4
}

main := fn(): uint {
    $i := 0
    $sum := 0
    $while i < @decl_count_of(bag) {
        sum += bag[i]
        i += 1
    }
    return sum
}

directives 29 (@parent_ptr)

Stru := struct {
    .a: uint;
    .b: uint;
}

main := fn(): uint {
    value := Stru.(1, 0)
    return access(&value.a)
}

access := fn(vl: ^uint): uint {
    return @as(^Stru, @parent_ptr("a", vl)).b
}

bubble sort

expectations := .{
    return_value: 0,
}

bubble_sort := fn(arr: []uint): void {
    i := 0
    loop if i == arr.len break else {
        j := 0
        loop if j == arr.len - 1 - i break else {
            if arr[j] > arr[j + 1] {
                tmp := arr[j]
                arr[j] = arr[j + 1]
                arr[j + 1] = tmp
            }
            j += 1
        }
        i += 1
    }
}

main := fn(): uint {
    arr := uint.[5, 3, 8, 1, 9, 2, 7, 4, 6]
    bubble_sort(arr[..])
    i := 1
    loop if i == arr.len break else {
        if arr[i - 1] > arr[i] return i
        i += 1
    }
    return 0
}

binary search

expectations := .{
    return_value: 3,
}

binary_search := fn(arr: []uint, target: uint): ?uint {
    lo := 0
    hi := arr.len
    loop if lo >= hi return null else {
        mid := lo + (hi - lo) / 2
        if arr[mid] == target return mid
        if arr[mid] < target {
            lo = mid + 1
        } else {
            hi = mid
        }
    }
}

main := fn(): uint {
    arr := uint.[1, 3, 5, 7, 9, 11]
    idx := binary_search(arr[..], 7)
    if idx == null return 100
    return idx.?
}

hash map

expectations := .{
    return_value: 0,
}

HashMap := fn($K: type, $V: type): type return struct {
    Entry := struct {
        .key: K;
        .val: V;
        .used: bool;
        .dead: bool;
    }

    Self := @CurrentScope();

    .entries: []Entry;
    .count: uint;

    init := fn(mem: []Entry): Self {
        i := 0
        loop if i == mem.len break else {
            mem[i].used = false
            mem[i].dead = false
            i += 1
        }
        return .(mem, 0)
    }

    // FNV-1a inspired byte hash, works on any type via bit_cast to bytes
    hash_key := fn(key: K, cap: uint): uint {
        bytes: ^u8 = @bit_cast(&#key)
        h: uint = 14695981039346656037
        i := 0
        loop if i == @size_of(K) break else {
            h ^= bytes[i]
            h *= 1099511628211
            i += 1
        }
        return h % cap
    }

    insert := fn(self: ^Self, key: K, val: V): bool {
        if self.count * 2 >= self.entries.len return false
        slot := hash_key(key, self.entries.len)
        loop {
            e := &self.entries[slot]
            if !e.used || e.dead {
                e.key = key
                e.val = val
                e.used = true
                e.dead = false
                self.count += 1
                return true
            }
            // already exists, overwrite
            if e.key == key {
                e.val = val
                return true
            }
            slot = (slot + 1) % self.entries.len
        }
    }

    get := fn(self: ^Self, key: K): ?V {
        slot := hash_key(key, self.entries.len)
        loop {
            e := &self.entries[slot]
            if !e.used return null
            if !e.dead && e.key == key return e.val
            slot = (slot + 1) % self.entries.len
        }
    }

    delete := fn(self: ^Self, key: K): bool {
        slot := hash_key(key, self.entries.len)
        loop {
            e := &self.entries[slot]
            if !e.used return false
            if !e.dead && e.key == key {
                e.dead = true
                self.count -= 1
                return true
            }
            slot = (slot + 1) % self.entries.len
        }
    }

    contains := fn(self: ^Self, key: K): bool {
        return self.get(key) != null
    }
}

IntMap := HashMap(uint, uint)

main := fn(): uint {
    buf: [64]IntMap.Entry = idk
    map := IntMap.init(buf[..])

    // basic insert and get
    if !map.insert(1, 100) return 1
    if !map.insert(2, 200) return 2
    if !map.insert(3, 300) return 3

    v := map.get(1) || return 4
    if v != 100 return 5

    v = map.get(2) || return 6
    if v != 200 return 7

    v = map.get(3) || return 8
    if v != 300 return 9

    // overwrite
    if !map.insert(2, 999) return 10
    v = map.get(2) || return 11
    if v != 999 return 12

    // delete
    if !map.delete(2) return 13
    if map.get(2) != null return 14

    // miss
    if map.get(42) != null return 15

    // contains
    if !map.contains(1) return 16
    if map.contains(2) return 17

    // collision stress: insert many keys
    i := 10
    loop if i == 30 break else {
        if !map.insert(i, i * i) return 100 + i
        i += 1
    }

    i = 10
    loop if i == 30 break else {
        v = map.get(i) || return 200 + i
        if v != i * i return 300 + i
        i += 1
    }

    // delete half and re-check
    i = 10
    loop if i == 20 break else {
        if !map.delete(i) return 400 + i
        i += 1
    }

    i = 10
    loop if i == 20 break else {
        if map.get(i) != null return 500 + i
        i += 1
    }

    i = 20
    loop if i == 30 break else {
        v = map.get(i) || return 600 + i
        if v != i * i return 700 + i
        i += 1
    }

    return 0
}

intrusive linked list

// Embed this inside any struct you want to put in the list
ListNode := struct {
    .prev: ?^ListNode;
    .next: ?^ListNode;
}

// The list head just holds sentinel prev/next pointers
List := fn($T: type): type return struct {
    Self := @CurrentScope();

    .head: ?^ListNode;
    .tail: ?^ListNode;
    .len: uint;

    init := fn(): Self {
        return .(null, null, 0)
    }

    // get the ListNode embedded in a T
    node_of := fn(item: ^T): ^ListNode {
        return &item.node
    }

    // recover the parent T from a ListNode pointer
    item_of := fn(node: ^ListNode): ^T {
        return @as(^T, @parent_ptr("node", node))
    }

    push_back := fn(self: ^Self, item: ^T): void {
        node := node_of(item)
        node.prev = self.tail
        node.next = null

        if self.tail != null {
            self.tail.?.next = node
        } else {
            self.head = node
        }
        self.tail = node
        self.len += 1
    }

    push_front := fn(self: ^Self, item: ^T): void {
        node := node_of(item)
        node.next = self.head
        node.prev = null

        if self.head != null {
            self.head.?.prev = node
        } else {
            self.tail = node
        }
        self.head = node
        self.len += 1
    }

    pop_back := fn(self: ^Self): ?^T {
        if self.tail == null return null
        node := self.tail.?
        if node.prev != null {
            node.prev.?.next = null
        } else {
            self.head = null
        }
        self.tail = node.prev
        node.prev = null
        node.next = null
        self.len -= 1
        return item_of(node)
    }

    pop_front := fn(self: ^Self): ?^T {
        if self.head == null return null
        node := self.head.?
        if node.next != null {
            node.next.?.prev = null
        } else {
            self.tail = null
        }
        self.head = node.next
        node.prev = null
        node.next = null
        self.len -= 1
        return item_of(node)
    }

    // unlink an arbitrary node from the middle
    remove := fn(self: ^Self, item: ^T): void {
        node := node_of(item)
        if node.prev != null {
            node.prev.?.next = node.next
        } else {
            self.head = node.next
        }
        if node.next != null {
            node.next.?.prev = node.prev
        } else {
            self.tail = node.prev
        }
        node.prev = null
        node.next = null
        self.len -= 1
    }

    // insert item_new directly after item_before
    insert_after := fn(self: ^Self, item_before: ^T, item_new: ^T): void {
        before := node_of(item_before)
        node := node_of(item_new)

        node.prev = before
        node.next = before.next

        if before.next != null {
            before.next.?.prev = node
        } else {
            self.tail = node
        }
        before.next = node
        self.len += 1
    }

    // sum a uint field across all nodes via a user supplied accessor
    fold_sum := fn(self: ^Self, $acc: fn(^T): uint): uint {
        cur := self.head
        sum := 0
        loop {
            if cur == null break
            sum += acc(item_of(cur.?))
            cur = cur.?.next
        }
        return sum
    }
}

// ---- concrete usage ----

Task := struct {
    .id: uint;
    .cost: uint;
    .node: ListNode;
}

TaskList := List(Task, "node")

main := fn(): uint {
    // stack-allocate a pool of tasks
    pool: [8]Task = idk

    i := 0
    loop if i == 8 break else {
        pool[i].id = i
        // costs 1..8
        pool[i].cost = i + 1
        pool[i].node = .(null, null)
        i += 1
    }

    lst := TaskList.init()

    // push all to back  →  0 1 2 3 4 5 6 7
    i = 0
    loop if i == 8 break else {
        lst.push_back(&pool[i])
        i += 1
    }

    if lst.len != 8 return 1

    // pop front twice  →  2 3 4 5 6 7
    a := lst.pop_front() || return 2
    b := lst.pop_front() || return 3
    if a.id != 0 return 4
    if b.id != 1 return 5
    if lst.len != 6 return 6

    // pop back once  →  2 3 4 5 6
    c := lst.pop_back() || return 7
    if c.id != 7 return 8
    if lst.len != 5 return 9

    // remove middle element (id=4)  →  2 3 5 6
    lst.remove(&pool[4])
    if lst.len != 4 return 10

    // check id=4 is gone by walking forward
    cur := lst.head
    expected := uint.[2, 3, 5, 6]
    i = 0
    loop {
        if cur == null break
        item := TaskList.item_of(cur.?)
        if item.id != expected[i] return 20 + i
        cur = cur.?.next
        i += 1
    }
    if i != 4 return 30

    // walk backward and check symmetry
    cur = lst.tail
    i = 3
    loop {
        if cur == null break
        item := TaskList.item_of(cur.?)
        if item.id != expected[i] return 40 + i
        cur = cur.?.prev
        if i == 0 break
        i -= 1
    }

    // insert pool[4] back after pool[3]  →  2 3 4 5 6
    lst.insert_after(&pool[3], &pool[4])
    if lst.len != 5 return 50

    // fold: sum of costs for ids 2 3 4 5 6  =  3+4+5+6+7 = 25
    total := lst.fold_sum(fn(t: ^Task): uint return t.cost)
    if total != 25 return 51

    // push_front puts id=0 back  →  0 2 3 4 5 6
    lst.push_front(&pool[0])
    if lst.len != 6 return 60
    front := lst.pop_front() || return 61
    if front.id != 0 return 62

    return 0
}

simd byte search

expectations := .{
    return_value: 45,
}

main := fn(): uint {
    // TODO
    $if @target("hbvm-ableos") return 45

    arr: [50]u8 = idk
    for a := arr[..] a.* = 0
    arr[45] = 1

    index_of_scalar := fn($elem: type, slice: []elem, value: elem): ?uint {
        $V := @simd(elem)

        vectorized := @as(^V, @bit_cast(slice.ptr))[0..slice.len / @len_of(V)]
        query: V = @splat(value)
        for i := 0.., v := vectorized {
            mask := @bitmask(v.* == query)
            if mask != 0 return i * @len_of(V) + @count_trailing_zeros(mask)
        }

        $if @size_of(V) > 1 {
            start := vectorized.len * @len_of(V)
            for i := start.., s := slice[start..] {
                if s.* == value return i
            }
        }

        return null
    }

    index := index_of_scalar(u8, arr[..], 1)

    return index || arr.len
}

progress

  • hbvm-ableos target
  • x86_64-linux target
    • folating point math
  • x86_64-windows target
  • wasm-freestanding
    • specific optimizations
  • diagnostics
    • don't crash on cycles
    • colors
  • control flow
    • functions
      • inlining
        • noop compatibility
      • scope inference
      • comptime parameters
    • ifs
      • comptime
      • option unwrap
      • return a value
    • loops
      • infinite
      • while
        • else clause
      • for
        • index
        • multiple slices
        • else clause
      • comptime
      • break
        • break value
      • continue
      • infinite
        • clean up the hacky graph hierarchy
      • labels
    • blocks
      • ? labels
    • match
      • comptime
    • defer
    • die
  • import pattern matching
  • global variables
    • strings
    • comptime evaluation
    • references
    • immutable
      • noop compatibility
  • types
    • idk
    • integers/bool
      • bool literals
      • integer literals
        • binary
        • octal
        • decimal
        • hexadecimal
      • binary operators
        • - + * / % == != <= >= < > << >> | ^ &
      • unary operators
        • - ! ~
      • short circuit (&&, ||)
        • && applied to option patterns
        • || to provide default value
    • floats
      • binary operators
        • - + * / == != <= >= < >
      • unary operators
        • -
    • structs
      • indexing
      • alignment
      • ? field alignment
      • constructors
        • dictionary
        • tuple
      • default values
      • scope
      • file
      • operators
      • comparison
    • enums
      • specific values
      • backing integer
      • scope
    • unions
      • ? alignment
      • ? field alignment
      • tag
        • ? customizable
      • scope
    • pointers
      • slicing
    • slices
      • known size (arrays)
        • comptime interrupt
      • field access
      • indexing
      • slicing
      • empty
      • array slicing
    • tuples
    • nullable types
  • ? directives
    • @use(<string>): <struct>
    • @TypeOf(<expr>): type
    • @as(<ty>, <expr>): <ty>
    • @int_cast(<int>): <infered-int>
    • @size_of(<ty>): uint
      • comptime interrupt
    • @align_of(<ty>): uint
      • comptime interrupt
    • @bit_cast(<expr>): <infered-ty>
    • @ecall(...<expr>): <infered-ty>
    • @embed(<string>): [len]u8
    • ? @inline(<func>, ...<args>): <func>.ret
    • @len_of(<ty>): uint
      • comptime interrupt
    • @kind_of(<ty>): u8
      • comptime interrupt
    • @filed_name(<ty>, i)
      • comptime interrupt
    • @Any(<fn(type): void/type>..): type
      • ? type filters
    • @error(...<expr>): never
    • @compiles(<expr>): bool
    • @ChildOf(<ty>): type
      • comptime interrupt
    • @target("<pat>"): bool
    • @is_comptime(): bool
    • @int_to_float(<int>): <float>
    • @float_to_int(<float>): int
    • @float_cast(<float>): <float>
    • @name_of(<ty>): []u8
      • comptime interrupt
    • @import("<name>")
    • @export("<name>", <fn>)
    • @frame_pointer(): uint
    • @handler("<name>", <handle_fn>)
    • ? @recall(..<args>): never
    • @has_decl(<ty>, "<name>")
      • comptime interrupt
    • @simd(<ty>): type
    • @splat(<value>): @simd(<ty>)
  • optimizations
    • assumptions
    • memory
      • uninitialized global memory
      • constant global loads
      • stack elimination
      • load alias reordering
        • around stores
        • around calls
        • around memcpy
      • store->load forwarding
      • splitting
      • mem2reg
        • scalar locals
        • arbitrary locals
    • compute
      • folding
      • algebra
    • control flow
      • inlining (heuristic)
      • loop unrolling
      • folding
    • clonable insructions
    • allocate abi registers
  • static analisys
    • source locations
    • constraint propagation
      • provenance
    • returning stack reference
      • trivial direct pointer
      • trough memory
      • ? trough memcpy
    • uninitialized reads
    • unreachable loop breaks
    • loop invariant conditions
    • dead code reporting
    • local out of bounds read/write
      • scalar read/write
      • memcpy
    • semantic assertions
      • null checks
      • bound checks

vendored tests

git submodule add https://git.ablecorp.eu/lily-org/lily.git vendored-tests/lily # add a new
git submodule update --init --recursive                                         # fetch
git submodule update --remote vendored-tests/lily                               # update a

Contributing

When contributing make sure to:

  1. Mark what you completed in progress, as the part of your changes. Stuff that is not in the checklist needs an issue. TODO: make an issue template
  2. Add apropriate examples to test the functionality, zig build test will automatically generate a test for the example and run it with rverithing else. It's preferable to add #### <feature> <n> and use n = 1 for demonstration and other examples to cover all ways to use the frature you can think of.

Relevant things to contribute with

  • implementing frontend features mentioned in progress
  • implementing more peephole optimizations (located inside fn idealize*)
  • implementing new target triple (the Machine)

About

Language used in ableOS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages