zig build install
./zig-out/bin/hbc --helpTry 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
expectations := .{
return_value: 42,
}
main := fn(): uint {
return 42
}main := fn(): uint {
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1 - 1
}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
}expectations := .{
return_value: 1,
}
main := fn(): uint {
return 3 == 2 * 2 - 1
}expectations := .{
should_error: true,
}
main := fn(): uint {
v := {}
return 1 + v * 10
}main := fn(): uint {
return 1 << 3 % 2 - 8 >> 3 | 4 & 2 ^ 0
}main := fn(): uint {
if !true return 1
if ~1 != -1 - 1 return 2
if -1 != ~1 + 1 return 3
return 0
}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)
}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
}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
}main := fn(): uint {
if 10 != 0xa return 16
if 10 != 0o12 return 8
if 10 != 0b1010 return 2
return 0
}expectations := .{
return_value: 0,
}
hex := fn(): uint {
return 0x2d
}
dec := fn(): uint {
return 45
}
main := fn(): uint {
if hex() != dec() return 1
return 0
}expectations := .{
return_value: 69,
}
main := fn(): uint {
return 'E'
}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
}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
}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
}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
}
}main := fn(): uint {
return foo()
}
$foo := fn(): uint {
return 0
}main := fn(): uint {
return foo(fn(): uint {
return 0
})
}
foo := fn($f: @Any()): uint {
return f()
}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
}// 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 futureexpectations := .{
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)
}
}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
}main := fn(): uint {
$if true return 0
}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
}main := fn(): uint {
val: ?uint = 0
if v := val && v == 0 {
return v
}
return 1
}main := fn(): uint {
ඞ := 1
b := 2
ඞ += 1
return ඞ - b
}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
}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
}expectations := .{
return_value: 4,
}
main := fn(): uint {
i := 0
loop if i == 4 break else {
i += 1
}
return i
}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
}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
}expectations := .{
times_out: true,
}
main := fn(): uint {
if true loop {}
return 0
}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
}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
}expectations := .{
return_value: 10,
}
main := fn(): uint {
i := 0
while i < 10 {
i += 1
}
return i
}expectations := .{
return_value: 10,
}
main := fn(): uint {
sum := 0
for i := 1..5 {
sum += i
}
return sum
}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
}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
})
main := fn(): uint {
a := #1
b := &a
modify(b)
b.* += 2
return b.* - 4
}
modify := fn(a: ^uint): void {
a.* = 2
return
}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
}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
}
}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
}expectations := .{
should_error: true,
}
main := fn(): uint {
return (~0).*
}main := fn(): uint {
val := uint.[1, 0]
return (val.ptr + 1 + (val.ptr - val.ptr)).*
}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
}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
}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
}expectations := .{
return_value: 4,
}
main := fn(): uint {
Pckd := struct align(1){.a: u8; .b: u16}
return @size_of(Pckd) + @align_of(Pckd)
}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)
}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
}
}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
}
}
}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
}
}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)
}
}
}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
}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))
}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))()
}main := fn(): uint {
some_int := 0
Ty := struct{.field: @TypeOf(some_int)}
return Ty.(some_int).field
}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
}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]
}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
}expectations := .{
return_value: 6,
}
main := fn(): uint {
tuple := .(1, 2, 3)
return tuple[0] + tuple[1] + tuple[2]
}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
}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
}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,
}
}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
}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
}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]
}expectations := .{
return_value: 0,
}
Enum := enum{.A; .B; .C}
main := fn(): uint {
if Enum.C > .A {
return 0
}
return 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,
}
}main := fn(): uint {
$match enum{.a}.a {
.a => {},
}
$match enum{.a; .b}.a {
.a => return 0,
.b => return 1,
}
}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 => {},
}
}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
}expectations := .{
unreaches: true,
}
main := fn(): uint {
if false return 1
die
}expectations := .{
unreaches: true,
}
fallible := fn(): ?^u8 {
return null
}
main := fn(): void {
a := fallible()
if a == null die
die
}counter := 0
dec := fn(): void {
counter -= 1
}
main := fn(): uint {
counter = 1
dec()
return counter
}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
}expectations := .{
return_value: 42,
}
$ans := 32
$wer: u8 = 10
main := fn(): uint {
return ans + wer
}expectations := .{
should_error: true,
}
main := fn(): void {
_ = "\ඞ"
}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
}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
}expectations := .{
should_error: true,
}
main := fn(): uint {
arr := uint.[0, 1, 2]
return arr[3]
}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 0reverse := 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]
}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
}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 valStru := 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
}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 vmain := fn(): uint {
if (@as(?uint, null) || 1) != 1 return 1
return @as(?uint, 0) || return 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)
}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)
}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()
}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
}expectations := .{
return_value: 3,
}
main := fn(): uint {
return @size_of(struct{.b: u16; .a: u8}) - @align_of(u8)
}expectations := .{
return_value: 3,
}
main := fn(): uint {
val := @as(struct{.a: uint}, .(3))
return val.a
}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
}main := fn(): uint {
v: uint = 0
return @as(u8, @int_cast(v))
}main := fn(): uint {
return @as(u32, @bit_cast(@as(struct{.l: u16; .r: u16}, @bit_cast(@as(u32, 0)))))
}expectations := .{
return_value: 4,
}
main := fn(): uint {
return @len_of(struct{.a: u8; .b: u32}) + @len_of([2]u8)
}expectations := .{
return_value: 7,
}
main := fn(): uint {
return @kind_of(struct{})
}expectations := .{
return_value: 1,
}
main := fn(): uint {
vl := #1
return deref(^uint, &vl)
}
deref := fn($T: type, arg: T): @ChildOf(T) {
return arg.*
}expectations := .{
return_value: 69,
}
main := fn(): uint {
val: ^[1]u8 = &@embed("mbed.txt")
return val.*[0]
}
// in: mbed.txt
Eexpectations := .{
should_error: true,
}
main := fn(): uint {
$if false @error("never happens")
@error("no main in sight, here is a type: ", ^u8)
}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
}expectations := .{
return_value: 10,
}
main := fn(): uint {
return @inline(foo, 10)
}
foo := fn(vl: uint): uint return vlexpectations := .{
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
}expectations := .{
return_value: 4,
}
main := fn(): uint {
return @type_name(uint).len
}main := fn(): uint {
$if @has_decl(scp, "bar") {
return scp.bar
}
$if @has_decl(scp, "foo") {
return scp.foo
}
}
scp := enum {
foo := 0
}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.*
}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
}@export("main", foo)
foo := fn(): uint {
return 0
}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()
}
}
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
}
})expectations := .{
return_value: 69,
}
main := fn(): uint {
return @eval(@type_info(struct{.Edward: void}).@struct.fields[0].name[0])
}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
}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
}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])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
}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: &.[],
}}))
}local_int: uint = @thread_local_storage()
main := fn(): uint {
local_int = 1
return local_int - 1
}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]
}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
}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
}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
}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.?
}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
}// 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
}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
}- 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
- inlining
- 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
- functions
- 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
-
-
-
- binary 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
- known size (arrays)
- 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
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 aWhen contributing make sure to:
- 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
- Add apropriate examples to test the functionality,
zig build testwill automatically generate a test for the example and run it with rverithing else. It's preferable to add#### <feature> <n>and usen = 1for demonstration and other examples to cover all ways to use the frature you can think of.
