holey-bytes/lang
Jakub Doka 4ebf1c7996
fixing outdated error message
Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
2024-12-25 21:34:58 +01:00
..
src fixing outdated error message 2024-12-25 21:34:58 +01:00
tests properly handling generic method calls 2024-12-25 21:29:30 +01:00
build.rs tests are now autogenerated from the readme, this makes test names DRY 2024-12-25 21:17:03 +01:00
Cargo.toml more forgotten stuff 2024-12-22 21:43:53 +01:00
command-help.txt add new ableos path resolver, separate platform independent code 2024-11-30 18:57:29 +01:00
README.md tests are now autogenerated from the readme, this makes test names DRY 2024-12-25 21:17:03 +01:00

HERE SHALL THE DOCUMENTATION RESIDE

Enforced Political Views

  • worse is better
  • less is more
  • embrace unsafe {}
  • adhere macro_rules!
  • pessimization == death (put in std::pin::Pin and left with hungry crabs)
  • importing external dependencies == death (fn(dependencies) -> ExecutionStrategy)
  • above sell not be disputed, discussed, or questioned

What hblang is

Holey-Bytes-Language (hblang for short) (*.hb) is the only true language targeting hbvm byte code. hblang is low level, manually managed, and procedural. Its rumored to be better then writing hbasm and you should probably use it for complex applications.

What hblang isnt't

hblang knows what it isn't, because it knows what it is, hblang computes this by sub...

Examples

Examples are also used in tests. To add an example that runs during testing add:

#### <name>
```hb
<example>
```

and also:

<name>;

to the run_tests macro at the bottom of the src/son.rs.

Tour Examples

Following examples incrementally introduce language features and syntax.

main_fn

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

arithmetic

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

floating_point_arithmetic

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

functions

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
}

comments

// commant is an item
main := fn(): uint {
	// comment is a statement

	foo(/* comment is an exprression /* if you are crazy */ */)
	return 0
}

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

// comments might be formatted in the future

if_statements

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

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

variables

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

hex_octal_binary_literals

main := fn(): uint {
	hex := 0xFF
	decimal := 255
	octal := 0o377
	binary := 0b11111111
	ascii := '\n'

	if hex == decimal & octal == decimal & binary == decimal {
		return 0
	}

	return 1
}

loops

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

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

pointers

main := fn(): uint {
	a := 1
	b := &a

	boundary := 1000

	b = b + boundary - 2
	b = b - (boundary - 2)

	modify(b)
	drop(a)
	return *b - 2
}

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

drop := fn(a: uint): void {
	return
}

structs

Ty := struct {
	// comment

	a: uint,
}

Ty2 := struct {
	ty: Ty,
	c: uint = 3,
}

useless := struct {}

main := fn(): uint {
	// `packed` structs have no padding (all fields are alighred to 1)
	if @sizeof(packed struct {a: u8, b: u16}) != 3 {
		return 9001
	}

	finst := Ty2.{ty: .{a: 4}}
	inst := odher_pass(finst)
	if inst.c == 3 {
		return pass(&inst.ty)
	}
	return 0
}

pass := fn(t: ^Ty): uint {
	return t.a
}

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

tuples

main := fn(): uint {
	tupl := .(1, 1)
	return tupl[0] - tupl[1]
}

struct_scopes

$zr := 0

Struct := struct {
	a: uint,
	b: uint,

	$zero := Self.(zr, zr)

	$new := fn(a: uint, b: uint): Self return .(a, b)

	$swap := fn(s: ^Self): void {
		_ = diff(*s)
		t := s.a
		s.a = s.b
		s.b = t
	}

	$diff := fn(s: Self): uint return s.a - s.b
}

main := fn(): uint {
	z := Struct.zero
	z += Struct.new(1, 2)
	z.swap()
	return z.diff()
}

enums

Enum := enum {
	A,
	B,
	C,

	$default := Self.A
}

some_enum := fn(): Enum return .A

main := fn(): uint {
	e := some_enum()

	match e {
		Enum.default => return 0,
		_ => return 100,
	}
}

unions

Union := union {
	i: u32,
	f: f32,

	$sconst := 0

	$new := fn(i: u32): Self {
		return .{i}
	}
}

main := fn(): uint {
	v := Union.{f: 0}
	u := Union.new(Union.sconst)
	return v.i + u.i
}

nullable_types

main := fn(): uint {
	a := &1

	b: ?^uint = null
	if decide() b = a

	if b == null return 9001

	c: ?uint = *b
	if decide() c = null

	if c != null return 42

	d: ?u16 = null
	if decide() d = 1

	if d == null return 69

	sf := new_foo()
	if sf == null return 999
	str := "foo\0"
	use_foo(sf, str)

	f := no_foo()

	if decide() f = .(a, 1)

	if f == null return 34

	bar := new_bar(a)

	if decide() bar = null

	if bar != null return 420

	g: ?^uint = null
	g = a

	_rd := *g

	return d - *f.a
}

Foo := struct {a: ^uint, b: uint}
no_foo := fn(): ?Foo return null
new_foo := fn(): ?Foo return .(&0, 0)
use_foo := fn(foo: Foo, str: []u8): void {
}

Bar := struct {a: ?^uint, b: uint}
new_bar := fn(a: ?^uint): ?Bar return .(a, 1)

decide := fn(): bool return !false

inline_return_stack

$fun := fn(): [3]uint {
	res := .[0, 1, 2]
	return res
}

main := fn(): uint {
	vl := fun()
	return vl[0]
}

struct_operators

Point := struct {
	x: uint,
	y: uint,
}

Rect := struct {
	a: Point,
	b: Point,
}

Color := packed 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 1008

	if Point.(1, 1) != Point.(1, 1) return 1009
	if Point.(1, 2) == Point.(1, 1) return 1010
	if Point.(1, 2) < Point.(1, 1) return 1011
	if Point.(1, 1) > Point.(2, 1) return 1012

	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
}

global_variables

global_var := 10

complex_global_var := fib(global_var) - 5

fib := fn(n: uint): uint {
	if 2 > n {
		return n
	}
	return fib(n - 1) + fib(n - 2)
}

main := fn(): uint {
	complex_global_var += 5
	return complex_global_var
}

note: values of global variables are evaluated at compile time

constants

main := fn(): u32 {
	return SOME_CONST + 35
}

$SOME_CONST := 34

directives

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

main := fn(): uint {
	if false {
		@error("unexpected '\0", u8, "', wah now?\0")
	}
	byte := @as(@ChildOf(^u8), 10)
	_ = sum(byte, byte)
	same_type_as_byte := @as(@TypeOf(byte), 30)
	wide_uint := @as(u32, 40)
	_ = sum(wide_uint, wide_uint)
	truncated_uint := @as(u8, @intcast(wide_uint))
	widened_float := @as(f64, @floatcast(1.))
	int_from_float := @as(int, @fti(1.))
	float_from_int := @as(f64, @itf(1))
	size_of_Type_in_bytes := @sizeof(foo.Type)
	align_of_Type_in_bytes := @alignof(foo.Type)
	hardcoded_pointer := @as(^u8, @bitcast(10))
	ecall_that_returns_uint := @as(uint, @eca(1, foo.Type.(10, 20), 5, 6))
	embedded_array := @as([15]u8, @embed("text.txt"))
	two_fields := @lenof(foo.Type)
	the_struct_kind := @kindof(foo.Type)
	return @inline(foo.foo)
}

$sum := fn(a: @Any(), b: @TypeOf(a)): @TypeOf(a) return a + b

// in module: foo.hb

Type := struct {
	brah: uint,
	blah: uint,
}

foo := fn(): uint return 0

// in module: text.txt
arbitrary text
  • @use(<string>): imports a module based on relative path, cycles are allowed when importing
  • @TypeOf(<expr>): results into literal type of whatever the type of <expr> is, <expr> is not included in final binary
  • @as(<ty>, <expr>): hint to the compiler that @TypeOf(<expr>) == <ty>
  • @intcast(<expr>): needs to be used when conversion of @TypeOf(<expr>) would loose precision (widening of integers is implicit)
  • @sizeof(<ty>), @alignof(<ty>): get size and align of a type in bytes
  • @bitcast(<expr>): tell compiler to assume @TypeOf(<expr>) is whatever is inferred, so long as size matches
  • @eca(...<expr>): invoke eca instruction, where return type is inferred and <expr>... are arguments passed to the call in the standard call convention
  • @embed(<string>): include relative file as an array of bytes
  • @inline(<func>, ...<args>): equivalent to <func>(...<args>) but function is guaranteed to inline, compiler will otherwise never inline
  • @lenof(<ty>): reports a length of the type of indexing purposes or length ot a string constant
  • @kindof(<ty>): gives an u8 integer describing the kind of type as an index to array [Builtin, Struct, Tuple, Enum, Union, Ptr, Slice, Opt, Func, Template, Global, Const, Module]
  • @Any(): generic parameter based on inference, TBD: this will ake arguments in the future that restrict what is accepted
  • @error(...<expr>): emit compiler error, if reachable, and use arguments to construct a message, can display strings and types
  • @ChildOf(<ty>): returns the child type of the <ty>, works for pointers and optionals (@ChildOf(?u8) == u8)

c_strings

str_len := fn(str: ^u8): uint {
	len := 0
	loop if *str == 0 break else {
		len += 1
		str += 1
	}
	return len
}

main := fn(): uint {
	// when string ends with '\0' its a C string and thus type is '^u8'
	some_str := "abඞ\n\r\t\{35}\{36373839}\0".ptr
	len := str_len(some_str)
	some_other_str := "fff\0".ptr
	lep := str_len(some_other_str)
	return lep + len
}

struct_patterns

.{fib, fib_iter, Fiber} := @use("fibs.hb")

main := fn(): uint {
	.{a, b} := Fiber.{a: 10, b: 10}
	return fib(a) - fib_iter(b)
}

// in module: fibs.hb

Fiber := struct {a: u8, b: u8}

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

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

arrays

main := fn(): uint {
	addr: u16 = 0x1FF
	msg := u8.[0, 0, @intcast(addr), @intcast(addr >> 8)]
	_force_stack := &msg

	arr := .[1, 2, 4]
	return pass(&arr) + msg[3]
}

pass := fn(arr: ^[3]uint): uint {
	return arr[0] + arr[1] + arr[arr[1]]
}

slices

main := fn(): uint {
	one := &10
	from_ptr: []uint = one[..1]

	arr := .[0, 1, 2, 3]
	start := arr[..2]
	mid := arr[1..3]
	end := arr[2..]
	all := arr[..]

	return start[0] + mid[0] + end[0] + all[3] + all.len - from_ptr[0]
}

inline

main := fn(): uint {
	some_eca()
	return @inline(foo, 1, 2, 3) - bar(3)
}

$some_eca := fn(): void return @eca(8)

// only for functions with no control flow (if, loop)
$bar := fn(a: uint): uint return a * 2

gb := 0

foo := fn(a: uint, b: uint, c: uint): uint {
	if false | gb != 0 return 1
	return a + b + c
}

idk

_edge_case: uint = idk

main := fn(): uint {
	big_array: [128]u8 = idk
	i := 0
	loop if i >= 128 break else {
		big_array[i] = 69
		i += 1
	}
	return big_array[42]
}

generic_functions

add := fn($T: type, a: T, b: T): T {
	if T != void {
		return a + b
	}
}

main := fn(): uint {
	add(void, {
	}, {
	})
	return add(u32, 2, 2) - add(uint, 1, 3)
}

generic_types

malloc_sys_call := 69
free_sys_call := 96

malloc := fn(size: uint, align: uint): ?^void return @eca(malloc_sys_call, size, align)
free := fn(ptr: ^void, size: uint, align: uint): void return @eca(free_sys_call, ptr, size, align)

Vec := fn($Elem: type): type return struct {
	data: ^Elem,
	len: uint,
	cap: uint,

	new := fn(): Self return .{data: @bitcast(@alignof(Elem)), len: 0, cap: 0}

	deinit := fn(vec: ^Self): void {
		free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem));
		*vec = new()
		return
	}

	push := fn(vec: ^Self, value: Elem): ?^Elem {
		if vec.len == vec.cap {
			if vec.cap == 0 {
				vec.cap = 1
			} else {
				vec.cap *= 2
			}

			new_alloc: ?^Elem = @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem)))
			if new_alloc == null return null

			src_cursor := vec.data
			dst_cursor: ^Elem = new_alloc
			end := vec.data + vec.len

			loop if src_cursor == end break else {
				*dst_cursor = *src_cursor
				src_cursor += 1
				dst_cursor += 1
			}

			if vec.len != 0 {
				free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem))
			}
			vec.data = new_alloc
		}

		slot := vec.data + vec.len;
		*slot = value
		vec.len += 1
		return slot
	}
}

main := fn(): uint {
	vec := Vec(uint).new()
	_ = vec.push(35)
	defer vec.deinit()
	vec2 := Vec(u8).new()
	_ = vec2.push(34)
	defer vec2.deinit()
	return *vec.data + *vec2.data
}

die


fun := fn(): never die

main := fn(): never {
	// simply emmits 'un' instruction that immediately terminates the execution
	// the expresion has similar properties to 'return' but does not accept a value
	fun()
}

defer

main := fn(): uint {
	i := 0
	loop {
		defer i += 1

		if i == 10 break

		if i % 3 == 0 {
			continue
		}
	}
	return i - 11
}

unrolled_loops

Nums := struct {a: uint, b: u32, c: u16, d: u8}

main := fn(): uint {
	nums := Nums.(1, 2, 3, 4)
	i := 0
	sum := 0
	$loop if i == @lenof(Nums) break else {
		sum += nums[i]
		i += 1
	}
	return sum - 10
}

Incomplete Examples

fb_driver

arm_fb_ptr := fn(): uint return 100
x86_fb_ptr := fn(): uint return 100

check_platform := fn(): uint {
	return x86_fb_ptr()
}

set_pixel := fn(x: uint, y: uint, width: uint): uint {
	return y * width + x
}

main := fn(): uint {
	fb_ptr := check_platform()
	width := 100
	height := 30
	x := 0
	y := 0
	//t := 0
	i := 0

	loop {
		if x < height {
			//t += set_pixel(x, y, height)
			x += 1
			i += 1
		} else {
			x = 0
			y += 1
			if set_pixel(x, y, height) != i return 0
			if y == width break
		}
	}
	return i
}

Purely Testing Examples

string_array


main := fn(): uint {
	strings := (^u8).["abcdefshijklmnop\0".ptr, "abcdefghijklnnop\0".ptr, "abcdefshijklmnop\0".ptr, "abcdefghijklmnop\0".ptr, "abcdefghijflmnop\0".ptr, "dbcdefghijklmnop\0".ptr, "abcdefghijklmnop\0".ptr]
	return *strings[0]
}

generic_function_in_struct

A := struct {
	a: uint,
	b := fn(self: ^Self, $T: type, c: T): T {
		return c
	}
}

main := fn(): uint {
	a := A.(100)
	return a.b(uint, 100)
}

len_never_goes_down

chars := fn(iter: []u8): struct {
	str: []u8,

	next := fn(self: ^Self): ?u8 {
		if self.str.len == 0 return null
		self.str = self.str[1..]
		return self.str[0]
	}
} {
	return .(iter)
}

main := fn(): void {
	a := chars("Hello, World!")
	loop {
		char := a.next()
		if char == null break
	}
}

slice_to_global_pointer

a := @as(^u8, @bitcast(0))

main := fn(): ^u8 {
	b := a[0..100]
	return b.ptr
}

subsclice_bug

main := fn(): void {
	str := "abcd\0"
	@eca(37, str[1..].ptr)
}

proper_ident_propagation

A := fn($T: type): type return struct {a: T}

$make_a := fn(a: @Any()): A(@TypeOf(a)) {
	return .(a)
}

$make_b := fn(a: @Any()): struct {b: @TypeOf(a)} {
	return .(a)
}

main := fn(): uint {
	a := make_a(100)
	b := make_b(100)
	return a.a - b.b
}

method_receiver_by_value

$log := fn(ptr: ^u8): void return @eca(37, ptr)
Struct := struct {
	ptr: ^u8,
	print := fn(self: Self, rhs: Self): void {
		log(self.ptr)
		log(rhs.ptr)
	}
}

Struct2 := struct {
	ptr: ^u8,
	print2 := fn(self: ^Self, rhs: ^Self): void {
		log(self.ptr)
		log(rhs.ptr)
	}
}

main := fn(): void {
	lhs := Struct.("Hello, World!\0".ptr)
	rhs := Struct.("Goodbye, World!\0".ptr)
	lhs.print(rhs)

	lhs2 := Struct2.("Hello, World!\0".ptr)
	rhs2 := Struct2.("Goodbye, World!\0".ptr)
	lhs2.print2(&rhs2)
}

comparing_floating_points

main := fn(): uint {
	if box(1) < box(0) return 1
	if box(1) <= box(0) return 2
	if box(0) > box(1) return 3
	if box(0) >= box(1) return 4
	return 0
}

box := fn(v: f32): f32 return v
box2 := fn(v: f64): f64 return v

pointer_comparison

main := fn(): int {
	a := 10
	b := 20

	if &a == &a {
		return 10
	} else {
		return 20
	}
}

wrong_dead_code_elimination

Color := struct {b: u8}
main := fn(): void {
	color := Color.(0)
	n: u8 = 1
	loop {
		if color.b == 255 | color.b == 0 {
			n = -n
		}
		color.b += n
	}
}

different_function_destinations

Stru := struct {a: uint, b: uint}
new_stru := fn(): Stru return .(0, 0)

glob_stru := Stru.(1, 1)

main := fn(): uint {
	glob_stru = new_stru()
	if glob_stru.a != 0 return 300
	glob_stru = .(1, 1)
	glob_stru = @inline(new_stru)
	if glob_stru.a != 0 return 200

	glob_stru = .(1, 1)
	strus := Stru.[glob_stru, glob_stru, glob_stru]
	i := 0
	loop if i == 3 break else {
		strus[i] = new_stru()
		i += 1
	}
	if strus[2].a != 0 return 100

	strus = Stru.[glob_stru, glob_stru, glob_stru]
	i = 0
	loop if i == 3 break else {
		strus[i] = @inline(new_stru)
		i += 1
	}
	if strus[2].a != 0 return 10

	return 0
}

triggering_store_in_divergent_branch

opaque := fn(): uint {
	return 1 << 31
}

main := fn(): void {
	a := 0
	loop if a >= opaque() break else {
		valid := true
		b := 0
		loop if b >= opaque() break else {
			if b == 1 << 16 {
				valid = false
				break
			}
			b += 1
		}
		if valid == false continue
		a += 1
	}
}

very_nested_loops


$H := 3
$W := 3

main := fn(): int {
	y: int = 0
	loop if y == H break else {
		x: int = 0
		loop if x == W break else {
			c_r: f64 = @itf(x)
			c_i: f64 = @itf(y)
			if c_i * c_r >= 10.0 return 0
			x += 1
		}
		y += 1
	}

	return 1
}

Color := struct {r: u8, g: u8, b: u8, a: u8}
Vec := struct {x: uint, y: uint}

put_pixel := fn(pos: Vec, color: Color): void {
}

generic_type_mishap

opaque := fn($Expr: type, ptr: ^?Expr): void {
}

process := fn($Expr: type): void {
	optional := @as(?Expr, null)
	timer := 1000
	loop if timer > 0 {
		opaque(Expr, &optional)
		if optional != null return {
		}
		timer -= 1
	}
	return
}

main := fn(): void process(uint)

storing_into_nullable_struct

StructA := struct {b: StructB, c: ^uint, d: uint}

StructB := struct {g: ^uint, c: StructC}

StructC := struct {c: uint}

some_index := fn(): ?uint return 0

heap := u8.[0, 1, 2]

optionala := fn(): ?StructA {
	i := some_index()
	if i == null die
	return .(.(&0, .(1)), &0, heap[i])
}

Struct := struct {inner: uint}

optional := fn(): ?Struct {
	return .(10)
}

do_stuff := fn(arg: uint): uint {
	return arg
}

just_read := fn(s: StructA): void {
}

main := fn(): uint {
	a := optionala()
	if a == null {
		return 10
	}
	a.b.c = .(0)
	just_read(a)
	innera := do_stuff(a.b.c.c)

	val := optional()
	if val == null {
		return 20
	}
	val.inner = 100
	inner := do_stuff(val.inner)
	return innera + inner
}

scheduling_block_did_dirty

Struct := struct {
	pad: uint,
	pad2: uint,
}

file := u8.[255]

opaque := fn(x: uint): uint {
	return file[x]
}

constructor := fn(x: uint): Struct {
	a := opaque(x)
	return .(a, a)
}

main := fn(): void {
	something := constructor(0)
	return
}

null_check_returning_small_global

magic := 127
get := fn(file: ^u8): ?uint {
	if *file == magic {
		return magic
	} else {
		return null
	}
}

some_file := u8.[127, 255, 255, 255, 255, 255]

foo := fn(): ?uint {
	gotten := get(&some_file[0])
	if gotten == null {
		return null
	} else if gotten == 4 {
		return 2
	} else if gotten == magic {
		return 0
	}

	return null
}

main := fn(): uint {
	f := foo()
	if f == null return 100
	return f
}

null_check_in_the_loop

A := struct {
	x_change: u8,
	y_change: u8,
	left: u8,
	middle: u8,
	right: u8,
}

return_fn := fn(): ?A {
	return A.(0, 0, 0, 0, 0)
}

main := fn(): int {
	loop {
		ret := return_fn()
		if ret != null {
			return 1
		}
	}
}

stack_provenance

main := fn(): uint {
	return *dangle()
}
dangle := fn(): ^uint return &0

advanced_floating_point_arithmetic

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 := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI)
	d := theta - @itf(si) * 2.0 * PI / @itf(TABLE_SIZE)
	ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1
	si &= TABLE_SIZE - 1
	return sin_table[@bitcast(si)] + (sin_table[@bitcast(ci)] - 0.5 * sin_table[@bitcast(si)] * d) * d
}

main := fn(): int {
	// expected result: 826
	return @fti(sin(1000.0) * 1000.0)
}

nullable_structure

Structure := struct {}

BigStructure := struct {a: uint, b: uint}

MidStructure := struct {a: u8}

returner_fn := fn(): ?Structure {
	return .()
}
returner_bn := fn(): ?BigStructure {
	return .(0, 0)
}
returner_cn := fn(): ?MidStructure {
	return .(0)
}

main := fn(): int {
	ret := returner_fn()
	bet := returner_bn()
	cet := returner_cn()
	if ret != null & bet != null & cet != null {
		return 1
	}

	return 0
}

needless_unwrap

main := fn(): uint {
	always_nn := @as(?^uint, &0)
	ptr1 := @unwrap(always_nn)
	always_n := @as(?^uint, null)
	ptr2 := @unwrap(always_n)
	return *ptr1 + *ptr2
}

optional_from_eca

main := fn(): uint {
	a := @as(?uint, @eca(0, 0, 0, 0))

	if a == null {
		die
	}
	return a
}

returning_optional_issues

bmp := 0

get_format := fn(): ?uint {
	return bmp
}

main := fn(): uint {
	fmt := get_format()
	if fmt == null {
		return 1
	} else {
		return fmt
	}
}

inlining_issues

main := fn(): void {
	@use("main.hb").put_filled_rect(.(&.(0), 100, 100), .(0, 0), .(0, 0), .(1))
}

// in module: memory.hb

SetMsg := packed struct {a: u8, count: u32, size: u32, src: ^u8, dest: ^u8}
set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void {
	return @eca(8, 2, &SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(SetMsg))
}

// in module: main.hb

Color := struct {r: u8}

Vec2 := fn($Ty: type): type return struct {x: Ty, y: Ty}

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

Surface := struct {
	buf: ^Color,
	width: uint,
	height: uint,
}

indexptr := fn(surface: Surface, x: uint, y: uint): ^Color {
	return surface.buf + y * surface.width + x
}

put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
	top_start_idx := @inline(indexptr, surface, pos.x, pos.y)
	bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1)
	rows_to_fill := tr.y

	loop if rows_to_fill <= 1 break else {
		@inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x))
		@inline(memory.set, Color, &color, bottom_start_idx, @bitcast(tr.x))

		top_start_idx += surface.width
		bottom_start_idx -= surface.width
		rows_to_fill -= 2
	}

	if rows_to_fill == 1 {
		@inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x))
	}

	return
}

only_break_loop

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

bar := fn(): int {
	loop if memory.inb(0x64) != 0 return 1
}

foo := fn(): void {
	loop if (memory.inb(0x64) & 2) == 0 break
	memory.outb(0x60, 0x0)
}

main := fn(): int {
	@inline(foo)
	return @inline(bar)
}

// in module: memory.hb
inb := fn(f: int): int return f
outb := fn(f: int, g: int): void {
}

reading_idk

main := fn(): int {
	a := @as(int, idk)
	return a
}

nonexistent_ident_import

main := @use("foo.hb").main
// in module: foo.hb
foo := fn(): void {
	return
}
foo := fn(): void {
	return
}
main := @use("bar.hb").mian
// in module: bar.hb
main := fn(): void {
	return
}

big_array_crash

sin_table := .[0, 174, 348, 523, 697, 871, 1045, 1218, 1391, 1564, 1736, 1908, 2079, 2249, 2419, 2588, 2756, 2923, 3090, 3255, 3420, 3583, 3746, 3907, 4067, 4226, 4384, 4540, 4695, 4848, 5000, 5150, 5299, 5446, 5591, 5735, 5877, 6018, 6156, 6293, 6427, 6560, 6691, 6819, 6946, 7071, 7193, 7313, 7431, 7547, 7660, 7771, 7880, 7986, 8090, 8191, 8290, 8386, 8480, 8571, 8660, 8746, 8829, 8910, 8987, 9063, 9135, 9205, 9271, 9335, 9396, 9455, 9510, 9563, 9612, 9659, 9702, 9743, 9781, 9816, 9848, 9877, 9902, 9925, 9945, 9961, 9975, 9986, 9993, 9998, 10000]

main := fn(): uint return sin_table[10]

returning_global_struct

Color := struct {r: u8, g: u8, b: u8, a: u8}
white := Color.(255, 255, 255, 255)
random_color := fn(): Color {
	return white
}
main := fn(): uint {
	val := random_color()
	return @as(uint, val.r) + val.g + val.b + val.a
}

small_struct_bitcast

Color := struct {r: u8, g: u8, b: u8, a: u8}
white := Color.(255, 255, 255, 255)
u32_to_color := fn(v: u32): Color return @bitcast(u32_to_u32(@bitcast(v)))
u32_to_u32 := fn(v: u32): u32 return v
main := fn(): uint {
	return u32_to_color(@bitcast(white)).r
}

small_struct_assignment

Color := struct {r: u8, g: u8, b: u8, a: u8}
white := Color.(255, 255, 255, 255)
black := Color.(0, 0, 0, 0)
main := fn(): uint {
	f := black
	f = white
	return f.a
}

intcast_store

SetMsg := packed struct {a: u8, count: u32, size: u32, src: ^u8, dest: ^u8}
set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): u32 {
	l := SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest))
	return l.count
}

main := fn(): uint {
	return set(uint, &0, &0, 1024)
}

string_flip

U := struct {u: uint}
main := fn(): uint {
	arr := @as([2 * 2]U, idk)

	i := 0
	loop if i == 2 * 2 break else {
		arr[i] = .(i)
		i += 1
	}

	i = 0
	loop if i == 2 / 2 break else {
		j := 0
		loop if j == 2 break else {
			a := i * 2 + j
			b := (2 - i - 1) * 2 + j
			tmp := arr[a]
			arr[a] = arr[b]
			arr[b] = tmp
			j += 1
		}
		i += 1
	}

	return arr[2].u
}

memory_swap

U := struct {u: uint, v: uint, g: uint}
main := fn(): uint {
	a := decide(0)
	b := decide(1)

	c := a
	a = b
	b = c

	return b.u + a.u
}

decide := fn(u: uint): U return .(u, 0, 0)

wide_ret

OemIdent := struct {
	dos_version: [8]u8,
	dos_version_name: [8]u8,
}

Stru := struct {
	a: u16,
	b: u16,
}

small_struct := fn(): Stru {
	return .{a: 0, b: 0}
}

maina := fn(major: uint, minor: uint): OemIdent {
	_f := small_struct()
	ver := u8.[0, 0, 0, 3, 1, 0, 0, 0]
	return OemIdent.(ver, ver)
}

main := fn(): uint {
	m := maina(0, 0)
	return m.dos_version[3] - m.dos_version_name[4]
}

comptime_min_reg_leak

a := @use("math.hb").min(100, 50)

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

// in module: math.hb

sizeof_uint := 32
shift := sizeof_uint - 1
min := fn(a: uint, b: uint): uint {
	c := a - b
	return b + (c & c >> shift)
}

different_types

Color := struct {
	r: u8,
	g: u8,
	b: u8,
	a: u8,
}

Point := struct {
	x: u32,
	y: u32,
}

Pixel := struct {
	color: Color,
	pouint: Point,
}

main := fn(): uint {
	pixel := Pixel.{
		color: Color.{
			r: 255,
			g: 0,
			b: 0,
			a: 255,
		},
		pouint: Point.{
			x: 0,
			y: 2,
		},
	}

	soupan := 1
	if *(&pixel.pouint.x + soupan) != 2 {
		return 0
	}

	if *(&pixel.pouint.y - 1) != 0 {
		return 64
	}

	return pixel.pouint.x + pixel.pouint.y + pixel.color.r
		+ pixel.color.g + pixel.color.b + pixel.color.a
}

struct_return_from_module_function

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

main := fn(): uint {
	return 7 - bar.foo().x - bar.foo().y - bar.foo().z
}

// in module: bar.hb


foo := fn(): Foo {
	return .{x: 3, y: 2, z: 2}
}

Foo := struct {x: uint, y: u32, z: u32}

sort_something_viredly

main := fn(): uint {
	return sqrt(100)
}

sqrt := fn(x: uint): uint {
	temp := 0
	g := 0
	b := 32768
	bshift := 15
	loop if b == 0 {
		break
	} else {
		bshift -= 1
		temp = b + (g << 1)
		temp <<= bshift
		if x >= temp {
			g += b
			x -= temp
		}
		b >>= 1
	}
	return g
}

struct_in_register

ColorBGRA := struct {b: u8, g: u8, r: u8, a: u8}
magenta := ColorBGRA.{b: 205, g: 0, r: 205, a: 255}

main := fn(): uint {
	color := magenta
	return color.r
}

comptime_function_from_another_file

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

const_a := 100
const_b := 50
a := stn.math.min(const_a, const_b)

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

// in module: stn.hb
math := @use("math.hb")

// in module: math.hb
sizeof_uint := 32
shift := sizeof_uint - 1
min := fn(a: uint, b: uint): uint {
	c := a - b
	return b + (c & c >> shift)
}

inline_test

fna := fn(a: uint, b: uint, c: uint): uint return a + b + c

scalar_values := fn(): uint {
	return @inline(fna, 1, @inline(fna, 2, 3, 4), -10)
}

A := struct {a: uint}
AB := struct {a: A, b: uint}

mangle := fn(a: A, ab: AB): uint {
	return a.a + ab.a.a - ab.b
}

structs := fn(): uint {
	return @inline(mangle, .(0), .(.(@inline(mangle, .(20), .(.(5), 5))), 20))
}

main := fn(): uint {
	if scalar_values() != 0 return 1
	if structs() != 0 return structs()

	return 0
}

inlined_generic_functions

abs := fn($Expr: type, x: Expr): Expr {
	mask := x >> @bitcast(@sizeof(Expr) - 1)
	return (x ^ mask) - mask
}

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

some_generic_code

some_func := fn($Elem: type): void {
	return
}

main := fn(): void {
	some_func(u8)
	return
}

integer_inference_issues

.{integer_range} := @use("random.hb")
main := fn(): void {
	a := integer_range(0, 1000)
	return
}

// in module: random.hb
integer_range := fn(min: uint, max: uint): uint {
	return @eca(3, 4) % (@bitcast(max) - min + 1) + min
}

signed_to_unsigned_upcast

main := fn(): uint return @as(i32, 1)

writing_into_string

outl := fn(): void {
	msg := "whahaha\0"
	_u := @as(u8, 0)
	return
}

inl := fn(): void {
	msg := "luhahah\0"
	return
}

main := fn(): void {
	outl()
	inl()
	return
}

request_page

request_page := fn(page_count: u8): ^u8 {
	msg := "\{00}\{01}xxxxxxxx\0".ptr
	msg_page_count := msg + 1;
	*msg_page_count = page_count
	return @eca(3, 2, msg, 12)
}

create_back_buffer := fn(total_pages: int): ^u32 {
	if total_pages <= 0xFF {
		return @bitcast(request_page(@intcast(total_pages)))
	}
	ptr := request_page(255)
	remaining := total_pages - 0xFF
	loop if remaining <= 0 break else {
		if remaining < 0xFF {
			_ = request_page(@intcast(remaining))
		} else {
			_ = request_page(0xFF)
		}
		remaining -= 0xFF
	}
	return @bitcast(ptr)
}

main := fn(): void {
	_f := create_back_buffer(400)
	return
}

tests_ptr_to_ptr_copy

main := fn(): uint {
	back_buffer := @as([1024 * 10]u8, idk)

	n := 0
	loop if n >= 1024 break else {
		back_buffer[n] = 64
		n += 1
	}
	n = 1
	loop if n >= 10 break else {
		*(@as(^[1024]u8, @bitcast(&back_buffer)) + n) = *@bitcast(&back_buffer)
		n += 1
	}
	return back_buffer[1024 * 2]
}

global_variable_wiredness

ports := false

inb := fn(): uint return 0

main := fn(): void {
	if ports {
		ports = inb() == 0x0
	}
}

Just Testing Optimizations

elide_stack_offsets_for_parameters_correctly

A := struct {
	f: uint,
	s: B,
}

B := struct {
	a: uint,
	b: uint,
}

main := fn(): uint {
	a := A.(1, .(0, 0))
	return pass(a.s)
}

pass := fn(s: B): uint return s.a + s.b

null_check_test

get_ptr := fn(): ?^uint return null

main := fn(): uint {
	ptr := get_ptr()

	if ptr == null {
		return 0
	}

	loop if *ptr != 10 {
		*ptr += 1
	} else break

	return *ptr
}

const_folding_with_arg

main := fn(arg: uint): uint {
	// reduces to 0
	return arg + 0 - arg * 1 + arg + 1 + arg + 2 + arg + 3 - arg * 3 - 6
}

branch_assignments

main := fn(arg: uint): uint {
	if arg == 1 {
		arg = 1
	} else if arg == 0 {
		arg = 2
	} else {
		arg = 3
	}
	return arg
}

exhaustive_loop_testing

main := fn(): uint {
	loop break

	x := 0
	loop {
		x += 1
		break
	}

	if multiple_breaks(0) != 3 {
		return true
	}

	if multiple_breaks(4) != 10 {
		return 2
	}

	if state_change_in_break(0) != 0 {
		return 3
	}

	if state_change_in_break(4) != 10 {
		return 4
	}

	if continue_and_state_change(10) != 10 {
		return 5
	}

	if continue_and_state_change(3) != 0 {
		return 6
	}

	infinite_loop()
	return 0
}

infinite_loop := fn(): void {
	f := 0
	loop {
		if f == 1 {
			f = 0
		} else {
			f = 1
		}

		f = continue_and_state_change(0)
	}
}

multiple_breaks := fn(arg: uint): uint {
	loop if arg < 10 {
		arg += 1
		if arg == 3 break
	} else break
	return arg
}

state_change_in_break := fn(arg: uint): uint {
	loop if arg < 10 {
		if arg == 3 {
			arg = 0
			break
		}
		arg += 1
	} else break
	return arg
}

continue_and_state_change := fn(arg: uint): uint {
	loop if arg < 10 {
		if arg == 2 {
			arg = 4
			continue
		}
		if arg == 3 {
			arg = 0
			break
		}
		arg += 1
	} else break
	return arg
}

pointer_opts

main := fn(): uint {
	mem := &0;
	*mem = 1;
	*mem = 2

	b := *mem + *mem
	clobber(mem)

	b -= *mem
	return b
}

clobber := fn(cb: ^uint): void {
	*cb = 4
	return
}

conditional_stores

main := fn(): uint {
	cnd := cond()
	mem := &1

	if cnd == 0 {
		*mem = 0
	} else {
		*mem = 2
	}

	return *mem
}

cond := fn(): uint return 0

loop_stores

main := fn(): uint {
	mem := &10

	loop if *mem == 0 break else {
		*mem -= 1
	}

	return *mem
}

dead_code_in_loop

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

	loop if n < 10 {
		if n < 10 break
		n += 1
	} else break

	loop if n == 0 return n

	return 1
}

infinite_loop_after_peephole

main := fn(): uint {
	n := 0
	f := 0
	loop if n != 0 break else {
		f += 1
	}
	return f
}

aliasing_overoptimization

Foo := struct {ptr: ^uint, rnd: uint}

main := fn(): uint {
	mem := &2
	stru := Foo.(mem, 0);
	*stru.ptr = 0
	return *mem
}

global_aliasing_overptimization

var := 0

main := fn(): uint {
	var = 2
	clobber()
	return var
}

clobber := fn(): void {
	var = 0
}

overwrite_aliasing_overoptimization

Foo := struct {a: int, b: int}
Bar := struct {f: Foo, b: int}

main := fn(): int {
	value := Bar.{b: 1, f: .(4, 1)}
	value.f = opaque()
	return value.f.a - value.f.b - value.b
}

opaque := fn(): Foo {
	return .(3, 2)
}

more_if_opts

main := fn(): uint {
	opq1 := opaque()
	opq2 := opaque()
	a := 0

	if opq1 == null {
	} else a = *opq1
	if opq1 != null a = *opq1
	//if opq1 == null | opq2 == null {
	//} else a = *opq1
	//if opq1 != null & opq2 != null a = *opq1

	return a
}

opaque := fn(): ?^uint return null