beginning of haku2: a reimplementation of haku in Zig

the goal is to rewrite haku completely, starting with the VM---because it was the most obvious point of improvement
the reason is because Rust is kinda too verbose for low level stuff like this. compare the line numbers between haku1 and haku2's VM and how verbose those lines are, and it's kind of an insane difference
it also feels like Zig's compilation model can work better for small wasm binary sizes

and of course, I also just wanted an excuse to try out Zig :3
This commit is contained in:
りき萌 2025-06-01 23:13:34 +02:00
parent 598c0348f6
commit 01d4514a65
19 changed files with 1946 additions and 11 deletions

View file

@ -207,12 +207,12 @@ pub mod fns {
0x17 Nary "cbrt" => cbrtf,
0x18 Nary "exp" => expf,
0x19 Nary "exp2" => exp2f,
0x1A Nary "ln" => logf,
0x1B Nary "log2" => log2f,
0x1C Nary "log10" => log10f,
0x1D Nary "hypot" => hypotf,
0x1E Nary "sin" => sinf,
0x1F Nary "cos" => cosf,
0x1a Nary "ln" => logf,
0x1b Nary "log2" => log2f,
0x1c Nary "log10" => log10f,
0x1d Nary "hypot" => hypotf,
0x1e Nary "sin" => sinf,
0x1f Nary "cos" => cosf,
0x20 Nary "tan" => tanf,
0x21 Nary "asin" => asinf,
0x22 Nary "acos" => acosf,
@ -223,9 +223,12 @@ pub mod fns {
0x27 Nary "sinh" => sinhf,
0x28 Nary "cosh" => coshf,
0x29 Nary "tanh" => tanhf,
0x2A Nary "asinh" => asinhf,
0x2B Nary "acosh" => acoshf,
0x2C Nary "atanh" => atanhf,
0x2a Nary "asinh" => asinhf,
0x2b Nary "acosh" => acoshf,
0x2c Nary "atanh" => atanhf,
0x2d Nary "min" => min,
0x2e Nary "max" => max,
0x30 Nary "lerp" => lerp_f,
0x40 Unary "!" => not,
0x41 Binary "==" => eq,
@ -390,6 +393,133 @@ pub mod fns {
math1 "atanh" atanhf,
}
pub fn min(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 2 {
return Err(vm.create_exception("`min` expects 2 arguments (min a b)"));
}
let a = args.get(vm, 0);
let b = args.get(vm, 1);
if !a.is_same_type_as(&b) {
return Err(vm.create_exception("arguments to `min` must have the same type"));
}
match a {
Value::Nil | Value::False | Value::True | Value::Tag(_) | Value::Number(_) => {
Ok(if a < b { a } else { b })
}
Value::Vec4(a) => {
let b = b.to_vec4().unwrap();
Ok(Value::Vec4(Vec4 {
x: a.x.min(b.x),
y: a.y.min(b.y),
z: a.z.min(b.z),
w: a.w.min(b.w),
}))
}
Value::Rgba(a) => {
let b = b.to_rgba().unwrap();
Ok(Value::Rgba(Rgba {
r: a.r.min(b.r),
g: a.g.min(b.g),
b: a.b.min(b.b),
a: a.a.min(b.a),
}))
}
Value::Ref(_) => {
Err(vm.create_exception("arguments passed to `min` cannot be compared"))
}
}
}
pub fn max(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 2 {
return Err(vm.create_exception("`max` expects 2 arguments (max a b)"));
}
let a = args.get(vm, 0);
let b = args.get(vm, 1);
if !a.is_same_type_as(&b) {
return Err(vm.create_exception("arguments to `max` must have the same type"));
}
match a {
Value::Nil | Value::False | Value::True | Value::Tag(_) | Value::Number(_) => {
Ok(if a < b { a } else { b })
}
Value::Vec4(a) => {
let b = b.to_vec4().unwrap();
Ok(Value::Vec4(Vec4 {
x: a.x.max(b.x),
y: a.y.max(b.y),
z: a.z.max(b.z),
w: a.w.max(b.w),
}))
}
Value::Rgba(a) => {
let b = b.to_rgba().unwrap();
Ok(Value::Rgba(Rgba {
r: a.r.max(b.r),
g: a.g.max(b.g),
b: a.b.max(b.b),
a: a.a.max(b.a),
}))
}
Value::Ref(_) => {
Err(vm.create_exception("arguments passed to `max` cannot be compared"))
}
}
}
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
pub fn lerp_f(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 3 {
return Err(vm.create_exception("`lerp` expects 3 arguments (max a b)"));
}
let a = args.get(vm, 0);
let b = args.get(vm, 1);
if !a.is_same_type_as(&b) {
return Err(
vm.create_exception("1st and 2nd arguments to `lerp` must have the same type")
);
}
let t = args.get_number(vm, 2, "3rd argument to `lerp` must be a number")?;
match a {
Value::Number(a) => {
let b = b.to_number().unwrap();
Ok(Value::Number(lerp(a, b, t)))
}
Value::Vec4(a) => {
let b = b.to_vec4().unwrap();
Ok(Value::Vec4(Vec4 {
x: lerp(a.x, b.x, t),
y: lerp(a.y, b.y, t),
z: lerp(a.z, b.z, t),
w: lerp(a.w, b.w, t),
}))
}
Value::Rgba(a) => {
let b = b.to_rgba().unwrap();
Ok(Value::Rgba(Rgba {
r: lerp(a.r, b.r, t),
g: lerp(a.g, b.g, t),
b: lerp(a.b, b.b, t),
a: lerp(a.a, b.a, t),
}))
}
Value::Nil | Value::False | Value::True | Value::Tag(_) | Value::Ref(_) => {
Err(vm
.create_exception("arguments passed to `lerp` cannot be linearly interpolated"))
}
}
}
pub fn not(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
let value = args.get(vm, 0);
Ok(Value::from(value.is_falsy()))

View file

@ -1,4 +1,7 @@
use core::ops::{Add, Div, Mul, Neg, Sub};
use core::{
mem::discriminant,
ops::{Add, Div, Mul, Neg, Sub},
};
use alloc::vec::Vec;
@ -54,6 +57,10 @@ impl Value {
_ => None,
}
}
pub fn is_same_type_as(&self, other: &Value) -> bool {
discriminant(self) == discriminant(other)
}
}
impl From<()> for Value {