From c5e2892def462ccbda765c90375a20e6e1c11eb2 Mon Sep 17 00:00:00 2001 From: liquidev Date: Wed, 11 Jun 2025 16:42:32 +0200 Subject: [PATCH] #broken haku2: progress on making it work --- Justfile | 3 +- crates/haku2/src/lib.rs | 1 - crates/haku2/src/system.zig | 271 ++++++++++++++++++++---------------- crates/haku2/src/vm.zig | 19 ++- crates/rkgk/Cargo.toml | 2 +- crates/rkgk/src/api/wall.rs | 13 ++ crates/rkgk/src/main.rs | 8 +- 7 files changed, 187 insertions(+), 130 deletions(-) diff --git a/Justfile b/Justfile index 8c62691..97006bb 100644 --- a/Justfile +++ b/Justfile @@ -1,9 +1,10 @@ port := "8080" profile := "dev" wasm_profile := "wasm-" + profile +log := "" serve: wasm - RKGK_PORT={{port}} RKGK_WASM_PATH=target/wasm32-unknown-unknown/{{wasm_profile}} cargo run -p rkgk --profile {{profile}} + RKGK_PORT={{port}} RKGK_WASM_PATH=target/wasm32-unknown-unknown/{{wasm_profile}} RUST_LOG={{log}} cargo run -p rkgk --profile {{profile}} wasm: cargo build -p haku-wasm --target wasm32-unknown-unknown --profile {{wasm_profile}} diff --git a/crates/haku2/src/lib.rs b/crates/haku2/src/lib.rs index 7ddf7e3..60f6978 100644 --- a/crates/haku2/src/lib.rs +++ b/crates/haku2/src/lib.rs @@ -3,7 +3,6 @@ use std::{ error::Error, fmt::{self, Display}, marker::{PhantomData, PhantomPinned}, - mem::forget, ptr::{self, NonNull}, slice, }; diff --git a/crates/haku2/src/system.zig b/crates/haku2/src/system.zig index 925afdb..7cb0446 100644 --- a/crates/haku2/src/system.zig +++ b/crates/haku2/src/system.zig @@ -81,42 +81,44 @@ fn typeError(vm: *Vm, val: Value, i: usize, expect: []const u8) Vm.Error { const Vec4 = struct { value: value.Vec4 }; const Rgba = struct { value: value.Rgba }; -fn fromArgument(cx: Context, comptime T: type, i: usize, val: Value) Vm.Error!T { - // NOTE: THIS IS CURRENTLY TERRIBLY BROKEN BECAUSE i IS ITERATED EVEN WHEN IT DOESN'T NEED TO BE. - +fn fromArgument(cx: Context, comptime T: type, i: usize) Vm.Error!T { switch (T) { // Context variables Context => return cx, - *Vm => return cx.vm, - mem.Allocator => return cx.allocator, // No conversion - Value => return val, + Value => return cx.args[i], // Primitives f32 => { + const val = cx.args[i]; if (val != .number) return typeError(cx.vm, val, i, "number"); return val.number; }, Vec4 => { + const val = cx.args[i]; if (val != .vec4) return typeError(cx.vm, val, i, "vec4"); return .{ .value = val.vec4 }; }, Rgba => { + const val = cx.args[i]; if (val != .rgba) return typeError(cx.vm, val, i, "rgba"); return .{ .value = val.rgba }; }, // Refs value.List => { + const val = cx.args[i]; if (val != .ref or val.ref.* != .list) return typeError(cx.vm, val, i, "list"); return val.ref.list; }, *const value.Shape => { + const val = cx.args[i]; if (val != .ref or val.ref.* != .shape) return typeError(cx.vm, val, i, "shape"); return &val.ref.shape; }, *const value.Closure => { + const val = cx.args[i]; if (val != .ref or val.ref.* != .closure) return typeError(cx.vm, val, i, "function"); return &val.ref.closure; }, @@ -149,22 +151,30 @@ fn intoReturn(cx: Context, any: anytype) Vm.Error!Value { /// /// Note that the argument order is important---function arguments go first, then context (such as /// the VM or the allocator.) Otherwise argument indices will not match up. -fn erase(comptime func: anytype) Fn { - return Erased(func).call; +fn erase(comptime name: []const u8, comptime func: anytype) Fn { + return Erased(name, func).call; } -fn Erased(comptime func: anytype) type { +fn countParams(comptime func: anytype) usize { + var count: usize = 0; + inline for (@typeInfo(@TypeOf(func)).@"fn".params) |param| { + if (param.type != Context) count += 1; + } + return count; +} + +fn Erased(comptime name: []const u8, comptime func: anytype) type { return struct { fn call(cx: Context) Vm.Error!Value { - const param_count = @typeInfo(@TypeOf(func)).@"fn".params.len; + const param_count = countParams(func); if (cx.args.len != param_count) { - return cx.vm.throw("function expects {} arguments, but it received {}", .{ param_count, cx.args.len }); + return cx.vm.throw(name ++ " expects {} arguments, but it received {}", .{ param_count, cx.args.len }); } const Args = meta.ArgsTuple(@TypeOf(func)); var args: Args = undefined; inline for (meta.fields(Args), 0..) |field, i| { - @field(args, field.name) = try fromArgument(cx, field.type, i, cx.args[i]); + @field(args, field.name) = try fromArgument(cx, field.type, i); } const result = @call(.auto, func, args); @@ -191,133 +201,133 @@ pub const fns = makeFnTable(&[_]SparseFn{ // Once the rest of the compiler is rewritten in Zig, it would be a good idea to rework this // system _not_ to hardcode the indices here. - .{ 0x00, erase(add) }, // + - .{ 0x01, erase(sub) }, // - - .{ 0x02, erase(mul) }, // * - .{ 0x03, erase(div) }, // / - .{ 0x04, erase(neg) }, // -_ - .{ 0x10, erase(floor) }, // floor - .{ 0x11, erase(ceil) }, // ceil - .{ 0x12, erase(round) }, // round - .{ 0x13, erase(abs) }, // abs - .{ 0x14, erase(mod) }, // mod - .{ 0x15, erase(pow) }, // pow - .{ 0x16, erase(sqrt) }, // sqrt - .{ 0x17, erase(cbrt) }, // cbrt - .{ 0x18, erase(exp) }, // exp - .{ 0x19, erase(exp2) }, // exp2 - .{ 0x1a, erase(ln) }, // ln - .{ 0x1b, erase(log2) }, // log2 - .{ 0x1c, erase(log10) }, // log10 - .{ 0x1d, erase(hypot) }, // hypot - .{ 0x1e, erase(sin) }, // sin - .{ 0x1f, erase(cos) }, // cos - .{ 0x20, erase(tan) }, // tan - .{ 0x21, erase(asin) }, // asin - .{ 0x22, erase(acos) }, // acos - .{ 0x23, erase(atan) }, // atan - .{ 0x24, erase(atan2) }, // atan2 - .{ 0x25, erase(expMinus1) }, // expMinus1 - .{ 0x26, erase(ln1Plus) }, // ln1Plus - .{ 0x27, erase(sinh) }, // sinh - .{ 0x28, erase(cosh) }, // cosh - .{ 0x29, erase(tanh) }, // tanh - .{ 0x2a, erase(asinh) }, // asinh - .{ 0x2b, erase(acosh) }, // acosh - .{ 0x2c, erase(atanh) }, // atanh - .{ 0x2d, erase(min) }, // min - .{ 0x2e, erase(max) }, // max - .{ 0x30, erase(lerp) }, // lerp - .{ 0x40, erase(not) }, // ! - .{ 0x41, erase(Value.eql) }, // == - .{ 0x42, erase(notEql) }, // != - .{ 0x43, erase(less) }, // < - .{ 0x44, erase(lessOrEqual) }, // <= - .{ 0x45, erase(greater) }, // > - .{ 0x46, erase(greaterOrEqual) }, // >= - .{ 0x80, vec }, // vec - .{ 0x81, erase(vecX) }, // vecX - .{ 0x82, erase(vecY) }, // vecY - .{ 0x83, erase(vecZ) }, // vecZ - .{ 0x84, erase(vecW) }, // vecW - .{ 0x85, rgba }, // rgba - .{ 0x86, erase(rgbaR) }, // rgbaR - .{ 0x87, erase(rgbaG) }, // rgbaG - .{ 0x88, erase(rgbaB) }, // rgbaB - .{ 0x89, erase(rgbaA) }, // rgbaA - .{ 0x90, erase(listLen) }, // len - .{ 0x91, erase(listIndex) }, // index - .{ 0x92, erase(range) }, // range - .{ 0x93, erase(map) }, // map - .{ 0x94, erase(filter) }, // filter - .{ 0x95, erase(reduce) }, // reduce - .{ 0x96, erase(flatten) }, // flatten - .{ 0xc0, erase(valueToShape) }, // toShape - .{ 0xc1, erase(line) }, // line - .{ 0xc2, erase(rect) }, // rect - .{ 0xc3, erase(circle) }, // circle - .{ 0xe0, erase(stroke) }, // stroke - .{ 0xe1, erase(fill) }, // fill - .{ 0xf0, erase(withDotter) }, // withDotter + .{ 0x00, erase("+", add) }, + .{ 0x01, erase("-", sub) }, + .{ 0x02, erase("*", mul) }, + .{ 0x03, erase("/", div) }, + .{ 0x04, erase("unary -", neg) }, + .{ 0x10, erase("floor", floor) }, + .{ 0x11, erase("ceil", ceil) }, + .{ 0x12, erase("round", round) }, + .{ 0x13, erase("abs", abs) }, + .{ 0x14, erase("mod", mod) }, + .{ 0x15, erase("pow", pow) }, + .{ 0x16, erase("sqrt", sqrt) }, + .{ 0x17, erase("cbrt", cbrt) }, + .{ 0x18, erase("exp", exp) }, + .{ 0x19, erase("exp2", exp2) }, + .{ 0x1a, erase("ln", ln) }, + .{ 0x1b, erase("log2", log2) }, + .{ 0x1c, erase("log10", log10) }, + .{ 0x1d, erase("hypot", hypot) }, + .{ 0x1e, erase("sin", sin) }, + .{ 0x1f, erase("cos", cos) }, + .{ 0x20, erase("tan", tan) }, + .{ 0x21, erase("asin", asin) }, + .{ 0x22, erase("acos", acos) }, + .{ 0x23, erase("atan", atan) }, + .{ 0x24, erase("atan2", atan2) }, + .{ 0x25, erase("expMinus1", expMinus1) }, + .{ 0x26, erase("ln1Plus", ln1Plus) }, + .{ 0x27, erase("sinh", sinh) }, + .{ 0x28, erase("cosh", cosh) }, + .{ 0x29, erase("tanh", tanh) }, + .{ 0x2a, erase("asinh", asinh) }, + .{ 0x2b, erase("acosh", acosh) }, + .{ 0x2c, erase("atanh", atanh) }, + .{ 0x2d, erase("min", min) }, + .{ 0x2e, erase("max", max) }, + .{ 0x30, erase("lerp", lerp) }, + .{ 0x40, erase("!", not) }, + .{ 0x41, erase("==", Value.eql) }, + .{ 0x42, erase("!=", notEql) }, + .{ 0x43, erase("<", less) }, + .{ 0x44, erase("<=", lessOrEqual) }, + .{ 0x45, erase(">", greater) }, + .{ 0x46, erase(">=", greaterOrEqual) }, + .{ 0x80, vec }, + .{ 0x81, erase("vecX", vecX) }, + .{ 0x82, erase("vecY", vecY) }, + .{ 0x83, erase("vecZ", vecZ) }, + .{ 0x84, erase("vecW", vecW) }, + .{ 0x85, rgba }, + .{ 0x86, erase("rgbaR", rgbaR) }, + .{ 0x87, erase("rgbaG", rgbaG) }, + .{ 0x88, erase("rgbaB", rgbaB) }, + .{ 0x89, erase("rgbaA", rgbaA) }, + .{ 0x90, erase("len", listLen) }, + .{ 0x91, erase("index", listIndex) }, + .{ 0x92, erase("range", range) }, + .{ 0x93, erase("map", map) }, + .{ 0x94, erase("filter", filter) }, + .{ 0x95, erase("reduce", reduce) }, + .{ 0x96, erase("flatten", flatten) }, + .{ 0xc0, erase("toShape", valueToShape) }, + .{ 0xc1, erase("line", line) }, + .{ 0xc2, erase("rect", rect) }, + .{ 0xc3, erase("circle", circle) }, + .{ 0xe0, erase("stroke", stroke) }, + .{ 0xe1, erase("fill", fill) }, + .{ 0xf0, erase("withDotter", withDotter) }, }); -fn add(a: Value, b: Value, vm: *Vm) Vm.Error!Value { +fn add(a: Value, b: Value, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { .number => .{ .number = a.number + b.number }, .vec4 => .{ .vec4 = a.vec4 + b.vec4 }, .rgba => .{ .rgba = a.rgba + b.rgba }, - else => vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), + else => cx.vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), }; } -fn sub(a: Value, b: Value, vm: *Vm) Vm.Error!Value { +fn sub(a: Value, b: Value, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { .number => .{ .number = a.number - b.number }, .vec4 => .{ .vec4 = a.vec4 - b.vec4 }, .rgba => .{ .rgba = a.rgba - b.rgba }, - else => vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), + else => cx.vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), }; } -fn mul(a: Value, b: Value, vm: *Vm) Vm.Error!Value { +fn mul(a: Value, b: Value, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { .number => .{ .number = a.number * b.number }, .vec4 => .{ .vec4 = a.vec4 * b.vec4 }, .rgba => .{ .rgba = a.rgba * b.rgba }, - else => vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), + else => cx.vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), }; } -fn div(a: Value, b: Value, vm: *Vm) Vm.Error!Value { +fn div(a: Value, b: Value, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { .number => .{ .number = a.number * b.number }, .vec4 => .{ .vec4 = a.vec4 * b.vec4 }, .rgba => .{ .rgba = a.rgba * b.rgba }, - else => vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), + else => cx.vm.throw("number, vec4, or rgba arguments expected, but got {s}", .{a.typeName()}), }; } -fn neg(a: Value, vm: *Vm) Vm.Error!Value { +fn neg(a: Value, cx: Context) Vm.Error!Value { return switch (a) { .number => .{ .number = -a.number }, .vec4 => .{ .vec4 = -a.vec4 }, - else => vm.throw("number or vec4 argument expected, but got {s}", .{a.typeName()}), + else => cx.vm.throw("number or vec4 argument expected, but got {s}", .{a.typeName()}), }; } @@ -437,9 +447,9 @@ fn atanh(a: f32) f32 { return math.atanh(a); } -fn min(a: Value, b: Value, vm: *Vm) Vm.Error!Value { +fn min(a: Value, b: Value, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { @@ -450,9 +460,9 @@ fn min(a: Value, b: Value, vm: *Vm) Vm.Error!Value { }; } -fn max(a: Value, b: Value, vm: *Vm) Vm.Error!Value { +fn max(a: Value, b: Value, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { @@ -463,16 +473,16 @@ fn max(a: Value, b: Value, vm: *Vm) Vm.Error!Value { }; } -fn lerp(a: Value, b: Value, t: f32, vm: *Vm) Vm.Error!Value { +fn lerp(a: Value, b: Value, t: f32, cx: Context) Vm.Error!Value { if (meta.activeTag(a) != meta.activeTag(b)) { - return vm.throw("arguments must be of the same type", .{}); + return cx.vm.throw("arguments must be of the same type", .{}); } return switch (a) { .number => .{ .number = math.lerp(a.number, b.number, t) }, .vec4 => .{ .vec4 = math.lerp(a.vec4, b.vec4, @as(value.Vec4, @splat(t))) }, .rgba => .{ .rgba = math.lerp(a.rgba, b.rgba, @as(value.Rgba, @splat(t))) }, - else => vm.throw("number, vec4, or rgba expected, but got {s}", .{a.typeName()}), + else => cx.vm.throw("number, vec4, or rgba expected, but got {s}", .{a.typeName()}), }; } @@ -486,26 +496,26 @@ fn notEql(a: Value, b: Value) bool { return !a.eql(b); } -fn less(a: Value, b: Value, vm: *Vm) Vm.Error!bool { - return a.lt(b) orelse vm.throw("{s} and {s} cannot be compared", .{ a.typeName(), b.typeName() }); +fn less(a: Value, b: Value, cx: Context) Vm.Error!bool { + return a.lt(b) orelse cx.vm.throw("{s} and {s} cannot be compared", .{ a.typeName(), b.typeName() }); } -fn greater(a: Value, b: Value, vm: *Vm) Vm.Error!bool { - return a.gt(b) orelse vm.throw("{s} and {s} cannot be compared", .{ a.typeName(), b.typeName() }); +fn greater(a: Value, b: Value, cx: Context) Vm.Error!bool { + return a.gt(b) orelse cx.vm.throw("{s} and {s} cannot be compared", .{ a.typeName(), b.typeName() }); } -fn lessOrEqual(a: Value, b: Value, vm: *Vm) Vm.Error!bool { - const isGreater = try greater(a, b, vm); +fn lessOrEqual(a: Value, b: Value, cx: Context) Vm.Error!bool { + const isGreater = try greater(a, b, cx); return !isGreater; } -fn greaterOrEqual(a: Value, b: Value, vm: *Vm) Vm.Error!bool { - const isLess = try less(a, b, vm); +fn greaterOrEqual(a: Value, b: Value, cx: Context) Vm.Error!bool { + const isLess = try less(a, b, cx); return !isLess; } fn vec(cx: Context) Vm.Error!Value { - if (cx.args.len > 4) return cx.vm.throw("function expects 1 to 4 arguments, but it received {}", .{cx.args.len}); + if (cx.args.len > 4) return cx.vm.throw("vec expects 1 to 4 arguments, but it received {}", .{cx.args.len}); for (cx.args) |arg| { if (arg != .number) return cx.vm.throw("number expected, but got {s}", .{arg.typeName()}); } @@ -536,7 +546,7 @@ fn vecW(v: Vec4) f32 { } fn rgba(cx: Context) Vm.Error!Value { - if (cx.args.len > 4) return cx.vm.throw("function expects 1 to 4 arguments, but it received {}", .{cx.args.len}); + if (cx.args.len > 4) return cx.vm.throw("rgba expects 1 to 4 arguments, but it received {}", .{cx.args.len}); for (cx.args) |arg| { if (arg != .number) return cx.vm.throw("number expected, but got {s}", .{arg.typeName()}); } @@ -571,13 +581,16 @@ fn listLen(list: value.List) f32 { } /// `index` -fn listIndex(list: value.List, index: f32, vm: *Vm) Vm.Error!Value { +fn listIndex(list: value.List, index: f32, cx: Context) Vm.Error!Value { const i: usize = @intFromFloat(index); - if (i >= list.len) return vm.throw("list index out of bounds. length is {}, index is {}", .{ list.len, i }); + if (i >= list.len) return cx.vm.throw("list index out of bounds. length is {}, index is {}", .{ list.len, i }); return list[i]; } -fn range(fstart: f32, fend: f32, vm: *Vm, a: mem.Allocator) Vm.Error!value.Ref { +fn range(fstart: f32, fend: f32, cx: Context) Vm.Error!value.Ref { + const vm = cx.vm; + const a = cx.allocator; + const start: u32 = @intFromFloat(fstart); const end: u32 = @intFromFloat(fend); @@ -605,11 +618,14 @@ fn range(fstart: f32, fend: f32, vm: *Vm, a: mem.Allocator) Vm.Error!value.Ref { return .{ .list = list }; } -fn map(list: value.List, f: *const value.Closure, vm: *Vm, a: mem.Allocator) Vm.Error!value.Ref { +fn map(list: value.List, f: *const value.Closure, cx: Context) Vm.Error!value.Ref { if (f.param_count != 1) { - return vm.throw("function passed to map must have a single parameter (\\x -> x), but it has {}", .{f.param_count}); + return cx.vm.throw("function passed to map must have a single parameter (\\x -> x), but it has {}", .{f.param_count}); } + const vm = cx.vm; + const a = cx.allocator; + const mapped_list = a.dupe(Value, list) catch return vm.outOfMemory(); for (list, mapped_list) |src, *dst| { const bottom = vm.stack_top; @@ -621,11 +637,14 @@ fn map(list: value.List, f: *const value.Closure, vm: *Vm, a: mem.Allocator) Vm. return .{ .list = mapped_list }; } -fn filter(list: value.List, f: *const value.Closure, vm: *Vm, a: mem.Allocator) Vm.Error!value.Ref { +fn filter(list: value.List, f: *const value.Closure, cx: Context) Vm.Error!value.Ref { if (f.param_count != 1) { - return vm.throw("function passed to filter must have a single parameter (\\x -> True), but it has {}", .{f.param_count}); + return cx.vm.throw("function passed to filter must have a single parameter (\\x -> True), but it has {}", .{f.param_count}); } + const vm = cx.vm; + const a = cx.allocator; + // Implementing filter is a bit tricky to do without resizable arrays. // There are a few paths one could take, but the simplest is to duplicate the list and truncate // its length. This wastes a lot of memory, but it probably isn't going to matter in practice. @@ -651,11 +670,14 @@ fn filter(list: value.List, f: *const value.Closure, vm: *Vm, a: mem.Allocator) return .{ .list = filtered_list }; } -fn reduce(list: value.List, init: Value, f: *const value.Closure, vm: *Vm, a: mem.Allocator) Vm.Error!Value { +fn reduce(list: value.List, init: Value, f: *const value.Closure, cx: Context) Vm.Error!Value { if (f.param_count != 2) { - return vm.throw("function passed to reduce must have two parameters (\\acc, x -> acc), but it has {}", .{f.param_count}); + return cx.vm.throw("function passed to reduce must have two parameters (\\acc, x -> acc), but it has {}", .{f.param_count}); } + const vm = cx.vm; + const a = cx.allocator; + var accumulator = init; for (list) |val| { const bottom = vm.stack_top; @@ -668,7 +690,7 @@ fn reduce(list: value.List, init: Value, f: *const value.Closure, vm: *Vm, a: me return accumulator; } -fn flatten(list: value.List, vm: *Vm, a: mem.Allocator) Vm.Error!value.Ref { +fn flatten(list: value.List, cx: Context) Vm.Error!value.Ref { var len: usize = 0; for (list) |val| { if (val == .ref and val.ref.* == .list) { @@ -678,6 +700,9 @@ fn flatten(list: value.List, vm: *Vm, a: mem.Allocator) Vm.Error!value.Ref { } } + const vm = cx.vm; + const a = cx.allocator; + const flattened_list = a.alloc(Value, len) catch return vm.outOfMemory(); var i: usize = 0; for (list) |val| { @@ -750,9 +775,9 @@ fn fill(color: Rgba, shape: *const value.Shape) value.Ref { } }; } -fn withDotter(cont: *const value.Closure, vm: *Vm) Vm.Error!value.Ref { +fn withDotter(cont: *const value.Closure, cx: Context) Vm.Error!value.Ref { if (cont.param_count != 1) { - return vm.throw("function passed to withDotter must have a single parameter (\\d -> _), but it has {}", .{cont.param_count}); + return cx.vm.throw("function passed to withDotter must have a single parameter (\\d -> _), but it has {}", .{cont.param_count}); } return .{ .reticle = .{ .dotter = .{ .draw = cont, diff --git a/crates/haku2/src/vm.zig b/crates/haku2/src/vm.zig index ee89be7..943e217 100644 --- a/crates/haku2/src/vm.zig +++ b/crates/haku2/src/vm.zig @@ -1,7 +1,7 @@ const std = @import("std"); const mem = std.mem; const debug = std.debug; -const log = std.log; +const log = std.log.scoped(.vm); const testAllocator = std.testing.allocator; const bytecode = @import("bytecode.zig"); @@ -107,6 +107,7 @@ pub fn push(vm: *Vm, val: Value) Error!void { if (vm.stack_top >= vm.stack.len) { return vm.throw("too many live temporary values (local variables and expression operands)", .{}); } + log.debug("PUSH {any} <- {}", .{ vm.stack[0..vm.stack_top], val }); vm.stack[vm.stack_top] = val; vm.stack_top += 1; } @@ -114,6 +115,8 @@ pub fn push(vm: *Vm, val: Value) Error!void { pub fn pop(vm: *Vm) Error!Value { try vm.validateBytecode(vm.stack_top > 0, "value stack underflow", .{}); vm.stack_top -= 1; + const result = vm.stack[vm.stack_top]; + log.debug("POP {any} -> {}", .{ vm.stack[0..vm.stack_top], result }); return vm.stack[vm.stack_top]; } @@ -129,12 +132,14 @@ pub fn pushCall(vm: *Vm, frame: CallFrame) Error!void { if (vm.call_stack_top >= vm.call_stack.len) { return vm.throw("too much recursion", .{}); } + log.debug("PUSH CALL {}", .{frame}); vm.call_stack[vm.call_stack_top] = frame; vm.call_stack_top += 1; } pub fn popCall(vm: *Vm) Error!CallFrame { try vm.validateBytecode(vm.call_stack_top > 0, "call stack underflow", .{}); + log.debug("POP CALL", .{}); vm.call_stack_top -= 1; return vm.call_stack[vm.call_stack_top]; } @@ -172,7 +177,9 @@ inline fn read(comptime T: type, ip: *[*]const u8) T { } inline fn readOpcode(ip: *[*]const u8) bytecode.Opcode { - return @enumFromInt(read(u8, ip)); + const opcode: bytecode.Opcode = @enumFromInt(read(u8, ip)); + log.debug("OP {*} {}", .{ ip.*, opcode }); + return opcode; } /// Before calling this, vm.stack_top should be saved and the appropriate amount of parameters must @@ -186,6 +193,8 @@ pub fn run( init_closure: *const value.Closure, init_bottom: u32, ) Error!void { + log.debug("BEGIN RUN {}", .{init_closure}); + var closure = init_closure; var ip: [*]const u8 = closure.chunk.bytecode[closure.start..].ptr; var bottom = init_bottom; @@ -432,11 +441,13 @@ pub fn run( const arg_count = read(u8, &ip); const system_fn = system.fns[index]; + log.debug("system index={} arg_count={} system_fn={p}", .{ index, arg_count, system_fn }); + vm.storeContext(.{ .fuel = fuel }); const result = try system_fn(.{ .vm = vm, .allocator = allocator, - .args = vm.stack[vm.stack_top - arg_count ..], + .args = vm.stack[vm.stack_top - arg_count .. vm.stack_top], }); const context = vm.restoreContext(); fuel = context.fuel; @@ -487,6 +498,8 @@ pub fn run( return vm.throw("corrupted bytecode: invalid opcode {}", .{invalid_opcode}); }, } + + log.debug("END RUN", .{}); } pub const Dotter = struct { diff --git a/crates/rkgk/Cargo.toml b/crates/rkgk/Cargo.toml index cdff7dc..829cb6d 100644 --- a/crates/rkgk/Cargo.toml +++ b/crates/rkgk/Cargo.toml @@ -30,7 +30,7 @@ tokio = { version = "1.39.2", features = ["full"] } toml = "0.8.19" tower-http = { version = "0.5.2", features = ["fs"] } tracing = "0.1.40" -tracing-subscriber = { version = "0.3.18", features = ["fmt"] } +tracing-subscriber = { version = "0.3.18", features = ["fmt", "env-filter"] } tracy-client = { version = "0.17.1", optional = true} walkdir = "2.5.0" webp = "0.3.0" diff --git a/crates/rkgk/src/api/wall.rs b/crates/rkgk/src/api/wall.rs index 173b15a..94d71b4 100644 --- a/crates/rkgk/src/api/wall.rs +++ b/crates/rkgk/src/api/wall.rs @@ -477,6 +477,19 @@ impl SessionLoop { Interaction::Dotter { from, to, num } => { if brush_ok { jumpstart_trampoline2(&mut haku); + match haku.cont2() { + haku2::Cont::Dotter(dotter) => match dotter.run(&haku2::Dotter { + from: (from.x, from.y), + to: (to.x, to.y), + num, + }) { + Ok(_) => (), + Err(err) => error!("exception while running dotter: {err}"), + }, + other => error!("received Dotter interaction when a {other:?} continuation was next") + } + + // old v1 code if let Some(tramp) = jumpstart_trampoline(&mut haku, &mut trampoline) { let cont = haku.cont(tramp); diff --git a/crates/rkgk/src/main.rs b/crates/rkgk/src/main.rs index 5e1f871..f402982 100644 --- a/crates/rkgk/src/main.rs +++ b/crates/rkgk/src/main.rs @@ -6,6 +6,7 @@ use eyre::Context; use router::router; use tokio::{fs, net::TcpListener}; use tracing::info; +use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _, EnvFilter}; mod api; mod auto_reload; @@ -93,7 +94,12 @@ async fn main() { let _client = tracy_client::Client::start(); color_eyre::install().unwrap(); - tracing_subscriber::fmt().init(); + tracing_subscriber::registry() + .with(tracing_subscriber::fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); + + tracing::debug!("debug logs are enabled"); match fallible_main().await { Ok(_) => (),