#broken haku2: progress on making it work
This commit is contained in:
parent
7658e0d4e8
commit
c5e2892def
7 changed files with 187 additions and 130 deletions
3
Justfile
3
Justfile
|
@ -1,9 +1,10 @@
|
||||||
port := "8080"
|
port := "8080"
|
||||||
profile := "dev"
|
profile := "dev"
|
||||||
wasm_profile := "wasm-" + profile
|
wasm_profile := "wasm-" + profile
|
||||||
|
log := ""
|
||||||
|
|
||||||
serve: wasm
|
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:
|
wasm:
|
||||||
cargo build -p haku-wasm --target wasm32-unknown-unknown --profile {{wasm_profile}}
|
cargo build -p haku-wasm --target wasm32-unknown-unknown --profile {{wasm_profile}}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
marker::{PhantomData, PhantomPinned},
|
marker::{PhantomData, PhantomPinned},
|
||||||
mem::forget,
|
|
||||||
ptr::{self, NonNull},
|
ptr::{self, NonNull},
|
||||||
slice,
|
slice,
|
||||||
};
|
};
|
||||||
|
|
|
@ -81,42 +81,44 @@ fn typeError(vm: *Vm, val: Value, i: usize, expect: []const u8) Vm.Error {
|
||||||
const Vec4 = struct { value: value.Vec4 };
|
const Vec4 = struct { value: value.Vec4 };
|
||||||
const Rgba = struct { value: value.Rgba };
|
const Rgba = struct { value: value.Rgba };
|
||||||
|
|
||||||
fn fromArgument(cx: Context, comptime T: type, i: usize, val: Value) Vm.Error!T {
|
fn fromArgument(cx: Context, comptime T: type, i: usize) Vm.Error!T {
|
||||||
// NOTE: THIS IS CURRENTLY TERRIBLY BROKEN BECAUSE i IS ITERATED EVEN WHEN IT DOESN'T NEED TO BE.
|
|
||||||
|
|
||||||
switch (T) {
|
switch (T) {
|
||||||
// Context variables
|
// Context variables
|
||||||
Context => return cx,
|
Context => return cx,
|
||||||
*Vm => return cx.vm,
|
|
||||||
mem.Allocator => return cx.allocator,
|
|
||||||
|
|
||||||
// No conversion
|
// No conversion
|
||||||
Value => return val,
|
Value => return cx.args[i],
|
||||||
|
|
||||||
// Primitives
|
// Primitives
|
||||||
f32 => {
|
f32 => {
|
||||||
|
const val = cx.args[i];
|
||||||
if (val != .number) return typeError(cx.vm, val, i, "number");
|
if (val != .number) return typeError(cx.vm, val, i, "number");
|
||||||
return val.number;
|
return val.number;
|
||||||
},
|
},
|
||||||
Vec4 => {
|
Vec4 => {
|
||||||
|
const val = cx.args[i];
|
||||||
if (val != .vec4) return typeError(cx.vm, val, i, "vec4");
|
if (val != .vec4) return typeError(cx.vm, val, i, "vec4");
|
||||||
return .{ .value = val.vec4 };
|
return .{ .value = val.vec4 };
|
||||||
},
|
},
|
||||||
Rgba => {
|
Rgba => {
|
||||||
|
const val = cx.args[i];
|
||||||
if (val != .rgba) return typeError(cx.vm, val, i, "rgba");
|
if (val != .rgba) return typeError(cx.vm, val, i, "rgba");
|
||||||
return .{ .value = val.rgba };
|
return .{ .value = val.rgba };
|
||||||
},
|
},
|
||||||
|
|
||||||
// Refs
|
// Refs
|
||||||
value.List => {
|
value.List => {
|
||||||
|
const val = cx.args[i];
|
||||||
if (val != .ref or val.ref.* != .list) return typeError(cx.vm, val, i, "list");
|
if (val != .ref or val.ref.* != .list) return typeError(cx.vm, val, i, "list");
|
||||||
return val.ref.list;
|
return val.ref.list;
|
||||||
},
|
},
|
||||||
*const value.Shape => {
|
*const value.Shape => {
|
||||||
|
const val = cx.args[i];
|
||||||
if (val != .ref or val.ref.* != .shape) return typeError(cx.vm, val, i, "shape");
|
if (val != .ref or val.ref.* != .shape) return typeError(cx.vm, val, i, "shape");
|
||||||
return &val.ref.shape;
|
return &val.ref.shape;
|
||||||
},
|
},
|
||||||
*const value.Closure => {
|
*const value.Closure => {
|
||||||
|
const val = cx.args[i];
|
||||||
if (val != .ref or val.ref.* != .closure) return typeError(cx.vm, val, i, "function");
|
if (val != .ref or val.ref.* != .closure) return typeError(cx.vm, val, i, "function");
|
||||||
return &val.ref.closure;
|
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
|
/// 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.
|
/// the VM or the allocator.) Otherwise argument indices will not match up.
|
||||||
fn erase(comptime func: anytype) Fn {
|
fn erase(comptime name: []const u8, comptime func: anytype) Fn {
|
||||||
return Erased(func).call;
|
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 {
|
return struct {
|
||||||
fn call(cx: Context) Vm.Error!Value {
|
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) {
|
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));
|
const Args = meta.ArgsTuple(@TypeOf(func));
|
||||||
var args: Args = undefined;
|
var args: Args = undefined;
|
||||||
inline for (meta.fields(Args), 0..) |field, i| {
|
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);
|
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
|
// 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.
|
// system _not_ to hardcode the indices here.
|
||||||
|
|
||||||
.{ 0x00, erase(add) }, // +
|
.{ 0x00, erase("+", add) },
|
||||||
.{ 0x01, erase(sub) }, // -
|
.{ 0x01, erase("-", sub) },
|
||||||
.{ 0x02, erase(mul) }, // *
|
.{ 0x02, erase("*", mul) },
|
||||||
.{ 0x03, erase(div) }, // /
|
.{ 0x03, erase("/", div) },
|
||||||
.{ 0x04, erase(neg) }, // -_
|
.{ 0x04, erase("unary -", neg) },
|
||||||
.{ 0x10, erase(floor) }, // floor
|
.{ 0x10, erase("floor", floor) },
|
||||||
.{ 0x11, erase(ceil) }, // ceil
|
.{ 0x11, erase("ceil", ceil) },
|
||||||
.{ 0x12, erase(round) }, // round
|
.{ 0x12, erase("round", round) },
|
||||||
.{ 0x13, erase(abs) }, // abs
|
.{ 0x13, erase("abs", abs) },
|
||||||
.{ 0x14, erase(mod) }, // mod
|
.{ 0x14, erase("mod", mod) },
|
||||||
.{ 0x15, erase(pow) }, // pow
|
.{ 0x15, erase("pow", pow) },
|
||||||
.{ 0x16, erase(sqrt) }, // sqrt
|
.{ 0x16, erase("sqrt", sqrt) },
|
||||||
.{ 0x17, erase(cbrt) }, // cbrt
|
.{ 0x17, erase("cbrt", cbrt) },
|
||||||
.{ 0x18, erase(exp) }, // exp
|
.{ 0x18, erase("exp", exp) },
|
||||||
.{ 0x19, erase(exp2) }, // exp2
|
.{ 0x19, erase("exp2", exp2) },
|
||||||
.{ 0x1a, erase(ln) }, // ln
|
.{ 0x1a, erase("ln", ln) },
|
||||||
.{ 0x1b, erase(log2) }, // log2
|
.{ 0x1b, erase("log2", log2) },
|
||||||
.{ 0x1c, erase(log10) }, // log10
|
.{ 0x1c, erase("log10", log10) },
|
||||||
.{ 0x1d, erase(hypot) }, // hypot
|
.{ 0x1d, erase("hypot", hypot) },
|
||||||
.{ 0x1e, erase(sin) }, // sin
|
.{ 0x1e, erase("sin", sin) },
|
||||||
.{ 0x1f, erase(cos) }, // cos
|
.{ 0x1f, erase("cos", cos) },
|
||||||
.{ 0x20, erase(tan) }, // tan
|
.{ 0x20, erase("tan", tan) },
|
||||||
.{ 0x21, erase(asin) }, // asin
|
.{ 0x21, erase("asin", asin) },
|
||||||
.{ 0x22, erase(acos) }, // acos
|
.{ 0x22, erase("acos", acos) },
|
||||||
.{ 0x23, erase(atan) }, // atan
|
.{ 0x23, erase("atan", atan) },
|
||||||
.{ 0x24, erase(atan2) }, // atan2
|
.{ 0x24, erase("atan2", atan2) },
|
||||||
.{ 0x25, erase(expMinus1) }, // expMinus1
|
.{ 0x25, erase("expMinus1", expMinus1) },
|
||||||
.{ 0x26, erase(ln1Plus) }, // ln1Plus
|
.{ 0x26, erase("ln1Plus", ln1Plus) },
|
||||||
.{ 0x27, erase(sinh) }, // sinh
|
.{ 0x27, erase("sinh", sinh) },
|
||||||
.{ 0x28, erase(cosh) }, // cosh
|
.{ 0x28, erase("cosh", cosh) },
|
||||||
.{ 0x29, erase(tanh) }, // tanh
|
.{ 0x29, erase("tanh", tanh) },
|
||||||
.{ 0x2a, erase(asinh) }, // asinh
|
.{ 0x2a, erase("asinh", asinh) },
|
||||||
.{ 0x2b, erase(acosh) }, // acosh
|
.{ 0x2b, erase("acosh", acosh) },
|
||||||
.{ 0x2c, erase(atanh) }, // atanh
|
.{ 0x2c, erase("atanh", atanh) },
|
||||||
.{ 0x2d, erase(min) }, // min
|
.{ 0x2d, erase("min", min) },
|
||||||
.{ 0x2e, erase(max) }, // max
|
.{ 0x2e, erase("max", max) },
|
||||||
.{ 0x30, erase(lerp) }, // lerp
|
.{ 0x30, erase("lerp", lerp) },
|
||||||
.{ 0x40, erase(not) }, // !
|
.{ 0x40, erase("!", not) },
|
||||||
.{ 0x41, erase(Value.eql) }, // ==
|
.{ 0x41, erase("==", Value.eql) },
|
||||||
.{ 0x42, erase(notEql) }, // !=
|
.{ 0x42, erase("!=", notEql) },
|
||||||
.{ 0x43, erase(less) }, // <
|
.{ 0x43, erase("<", less) },
|
||||||
.{ 0x44, erase(lessOrEqual) }, // <=
|
.{ 0x44, erase("<=", lessOrEqual) },
|
||||||
.{ 0x45, erase(greater) }, // >
|
.{ 0x45, erase(">", greater) },
|
||||||
.{ 0x46, erase(greaterOrEqual) }, // >=
|
.{ 0x46, erase(">=", greaterOrEqual) },
|
||||||
.{ 0x80, vec }, // vec
|
.{ 0x80, vec },
|
||||||
.{ 0x81, erase(vecX) }, // vecX
|
.{ 0x81, erase("vecX", vecX) },
|
||||||
.{ 0x82, erase(vecY) }, // vecY
|
.{ 0x82, erase("vecY", vecY) },
|
||||||
.{ 0x83, erase(vecZ) }, // vecZ
|
.{ 0x83, erase("vecZ", vecZ) },
|
||||||
.{ 0x84, erase(vecW) }, // vecW
|
.{ 0x84, erase("vecW", vecW) },
|
||||||
.{ 0x85, rgba }, // rgba
|
.{ 0x85, rgba },
|
||||||
.{ 0x86, erase(rgbaR) }, // rgbaR
|
.{ 0x86, erase("rgbaR", rgbaR) },
|
||||||
.{ 0x87, erase(rgbaG) }, // rgbaG
|
.{ 0x87, erase("rgbaG", rgbaG) },
|
||||||
.{ 0x88, erase(rgbaB) }, // rgbaB
|
.{ 0x88, erase("rgbaB", rgbaB) },
|
||||||
.{ 0x89, erase(rgbaA) }, // rgbaA
|
.{ 0x89, erase("rgbaA", rgbaA) },
|
||||||
.{ 0x90, erase(listLen) }, // len
|
.{ 0x90, erase("len", listLen) },
|
||||||
.{ 0x91, erase(listIndex) }, // index
|
.{ 0x91, erase("index", listIndex) },
|
||||||
.{ 0x92, erase(range) }, // range
|
.{ 0x92, erase("range", range) },
|
||||||
.{ 0x93, erase(map) }, // map
|
.{ 0x93, erase("map", map) },
|
||||||
.{ 0x94, erase(filter) }, // filter
|
.{ 0x94, erase("filter", filter) },
|
||||||
.{ 0x95, erase(reduce) }, // reduce
|
.{ 0x95, erase("reduce", reduce) },
|
||||||
.{ 0x96, erase(flatten) }, // flatten
|
.{ 0x96, erase("flatten", flatten) },
|
||||||
.{ 0xc0, erase(valueToShape) }, // toShape
|
.{ 0xc0, erase("toShape", valueToShape) },
|
||||||
.{ 0xc1, erase(line) }, // line
|
.{ 0xc1, erase("line", line) },
|
||||||
.{ 0xc2, erase(rect) }, // rect
|
.{ 0xc2, erase("rect", rect) },
|
||||||
.{ 0xc3, erase(circle) }, // circle
|
.{ 0xc3, erase("circle", circle) },
|
||||||
.{ 0xe0, erase(stroke) }, // stroke
|
.{ 0xe0, erase("stroke", stroke) },
|
||||||
.{ 0xe1, erase(fill) }, // fill
|
.{ 0xe1, erase("fill", fill) },
|
||||||
.{ 0xf0, erase(withDotter) }, // withDotter
|
.{ 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)) {
|
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) {
|
return switch (a) {
|
||||||
.number => .{ .number = a.number + b.number },
|
.number => .{ .number = a.number + b.number },
|
||||||
.vec4 => .{ .vec4 = a.vec4 + b.vec4 },
|
.vec4 => .{ .vec4 = a.vec4 + b.vec4 },
|
||||||
.rgba => .{ .rgba = a.rgba + b.rgba },
|
.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)) {
|
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) {
|
return switch (a) {
|
||||||
.number => .{ .number = a.number - b.number },
|
.number => .{ .number = a.number - b.number },
|
||||||
.vec4 => .{ .vec4 = a.vec4 - b.vec4 },
|
.vec4 => .{ .vec4 = a.vec4 - b.vec4 },
|
||||||
.rgba => .{ .rgba = a.rgba - b.rgba },
|
.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)) {
|
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) {
|
return switch (a) {
|
||||||
.number => .{ .number = a.number * b.number },
|
.number => .{ .number = a.number * b.number },
|
||||||
.vec4 => .{ .vec4 = a.vec4 * b.vec4 },
|
.vec4 => .{ .vec4 = a.vec4 * b.vec4 },
|
||||||
.rgba => .{ .rgba = a.rgba * b.rgba },
|
.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)) {
|
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) {
|
return switch (a) {
|
||||||
.number => .{ .number = a.number * b.number },
|
.number => .{ .number = a.number * b.number },
|
||||||
.vec4 => .{ .vec4 = a.vec4 * b.vec4 },
|
.vec4 => .{ .vec4 = a.vec4 * b.vec4 },
|
||||||
.rgba => .{ .rgba = a.rgba * b.rgba },
|
.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) {
|
return switch (a) {
|
||||||
.number => .{ .number = -a.number },
|
.number => .{ .number = -a.number },
|
||||||
.vec4 => .{ .vec4 = -a.vec4 },
|
.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);
|
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)) {
|
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) {
|
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)) {
|
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) {
|
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)) {
|
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) {
|
return switch (a) {
|
||||||
.number => .{ .number = math.lerp(a.number, b.number, t) },
|
.number => .{ .number = math.lerp(a.number, b.number, t) },
|
||||||
.vec4 => .{ .vec4 = math.lerp(a.vec4, b.vec4, @as(value.Vec4, @splat(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))) },
|
.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);
|
return !a.eql(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn less(a: Value, b: Value, vm: *Vm) Vm.Error!bool {
|
fn less(a: Value, b: Value, cx: Context) Vm.Error!bool {
|
||||||
return a.lt(b) orelse vm.throw("{s} and {s} cannot be compared", .{ a.typeName(), b.typeName() });
|
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 {
|
fn greater(a: Value, b: Value, cx: Context) Vm.Error!bool {
|
||||||
return a.gt(b) orelse vm.throw("{s} and {s} cannot be compared", .{ a.typeName(), b.typeName() });
|
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 {
|
fn lessOrEqual(a: Value, b: Value, cx: Context) Vm.Error!bool {
|
||||||
const isGreater = try greater(a, b, vm);
|
const isGreater = try greater(a, b, cx);
|
||||||
return !isGreater;
|
return !isGreater;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn greaterOrEqual(a: Value, b: Value, vm: *Vm) Vm.Error!bool {
|
fn greaterOrEqual(a: Value, b: Value, cx: Context) Vm.Error!bool {
|
||||||
const isLess = try less(a, b, vm);
|
const isLess = try less(a, b, cx);
|
||||||
return !isLess;
|
return !isLess;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vec(cx: Context) Vm.Error!Value {
|
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| {
|
for (cx.args) |arg| {
|
||||||
if (arg != .number) return cx.vm.throw("number expected, but got {s}", .{arg.typeName()});
|
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 {
|
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| {
|
for (cx.args) |arg| {
|
||||||
if (arg != .number) return cx.vm.throw("number expected, but got {s}", .{arg.typeName()});
|
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`
|
/// `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);
|
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];
|
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 start: u32 = @intFromFloat(fstart);
|
||||||
const end: u32 = @intFromFloat(fend);
|
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 };
|
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) {
|
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();
|
const mapped_list = a.dupe(Value, list) catch return vm.outOfMemory();
|
||||||
for (list, mapped_list) |src, *dst| {
|
for (list, mapped_list) |src, *dst| {
|
||||||
const bottom = vm.stack_top;
|
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 };
|
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) {
|
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.
|
// 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
|
// 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.
|
// 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 };
|
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) {
|
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;
|
var accumulator = init;
|
||||||
for (list) |val| {
|
for (list) |val| {
|
||||||
const bottom = vm.stack_top;
|
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;
|
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;
|
var len: usize = 0;
|
||||||
for (list) |val| {
|
for (list) |val| {
|
||||||
if (val == .ref and val.ref.* == .list) {
|
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();
|
const flattened_list = a.alloc(Value, len) catch return vm.outOfMemory();
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
for (list) |val| {
|
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) {
|
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 = .{
|
return .{ .reticle = .{ .dotter = .{
|
||||||
.draw = cont,
|
.draw = cont,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const debug = std.debug;
|
const debug = std.debug;
|
||||||
const log = std.log;
|
const log = std.log.scoped(.vm);
|
||||||
const testAllocator = std.testing.allocator;
|
const testAllocator = std.testing.allocator;
|
||||||
|
|
||||||
const bytecode = @import("bytecode.zig");
|
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) {
|
if (vm.stack_top >= vm.stack.len) {
|
||||||
return vm.throw("too many live temporary values (local variables and expression operands)", .{});
|
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[vm.stack_top] = val;
|
||||||
vm.stack_top += 1;
|
vm.stack_top += 1;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +115,8 @@ pub fn push(vm: *Vm, val: Value) Error!void {
|
||||||
pub fn pop(vm: *Vm) Error!Value {
|
pub fn pop(vm: *Vm) Error!Value {
|
||||||
try vm.validateBytecode(vm.stack_top > 0, "value stack underflow", .{});
|
try vm.validateBytecode(vm.stack_top > 0, "value stack underflow", .{});
|
||||||
vm.stack_top -= 1;
|
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];
|
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) {
|
if (vm.call_stack_top >= vm.call_stack.len) {
|
||||||
return vm.throw("too much recursion", .{});
|
return vm.throw("too much recursion", .{});
|
||||||
}
|
}
|
||||||
|
log.debug("PUSH CALL {}", .{frame});
|
||||||
vm.call_stack[vm.call_stack_top] = frame;
|
vm.call_stack[vm.call_stack_top] = frame;
|
||||||
vm.call_stack_top += 1;
|
vm.call_stack_top += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popCall(vm: *Vm) Error!CallFrame {
|
pub fn popCall(vm: *Vm) Error!CallFrame {
|
||||||
try vm.validateBytecode(vm.call_stack_top > 0, "call stack underflow", .{});
|
try vm.validateBytecode(vm.call_stack_top > 0, "call stack underflow", .{});
|
||||||
|
log.debug("POP CALL", .{});
|
||||||
vm.call_stack_top -= 1;
|
vm.call_stack_top -= 1;
|
||||||
return vm.call_stack[vm.call_stack_top];
|
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 {
|
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
|
/// 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_closure: *const value.Closure,
|
||||||
init_bottom: u32,
|
init_bottom: u32,
|
||||||
) Error!void {
|
) Error!void {
|
||||||
|
log.debug("BEGIN RUN {}", .{init_closure});
|
||||||
|
|
||||||
var closure = init_closure;
|
var closure = init_closure;
|
||||||
var ip: [*]const u8 = closure.chunk.bytecode[closure.start..].ptr;
|
var ip: [*]const u8 = closure.chunk.bytecode[closure.start..].ptr;
|
||||||
var bottom = init_bottom;
|
var bottom = init_bottom;
|
||||||
|
@ -432,11 +441,13 @@ pub fn run(
|
||||||
const arg_count = read(u8, &ip);
|
const arg_count = read(u8, &ip);
|
||||||
const system_fn = system.fns[index];
|
const system_fn = system.fns[index];
|
||||||
|
|
||||||
|
log.debug("system index={} arg_count={} system_fn={p}", .{ index, arg_count, system_fn });
|
||||||
|
|
||||||
vm.storeContext(.{ .fuel = fuel });
|
vm.storeContext(.{ .fuel = fuel });
|
||||||
const result = try system_fn(.{
|
const result = try system_fn(.{
|
||||||
.vm = vm,
|
.vm = vm,
|
||||||
.allocator = allocator,
|
.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();
|
const context = vm.restoreContext();
|
||||||
fuel = context.fuel;
|
fuel = context.fuel;
|
||||||
|
@ -487,6 +498,8 @@ pub fn run(
|
||||||
return vm.throw("corrupted bytecode: invalid opcode {}", .{invalid_opcode});
|
return vm.throw("corrupted bytecode: invalid opcode {}", .{invalid_opcode});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("END RUN", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Dotter = struct {
|
pub const Dotter = struct {
|
||||||
|
|
|
@ -30,7 +30,7 @@ tokio = { version = "1.39.2", features = ["full"] }
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
tower-http = { version = "0.5.2", features = ["fs"] }
|
tower-http = { version = "0.5.2", features = ["fs"] }
|
||||||
tracing = "0.1.40"
|
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}
|
tracy-client = { version = "0.17.1", optional = true}
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
webp = "0.3.0"
|
webp = "0.3.0"
|
||||||
|
|
|
@ -477,6 +477,19 @@ impl SessionLoop {
|
||||||
Interaction::Dotter { from, to, num } => {
|
Interaction::Dotter { from, to, num } => {
|
||||||
if brush_ok {
|
if brush_ok {
|
||||||
jumpstart_trampoline2(&mut haku);
|
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) {
|
if let Some(tramp) = jumpstart_trampoline(&mut haku, &mut trampoline) {
|
||||||
let cont = haku.cont(tramp);
|
let cont = haku.cont(tramp);
|
||||||
|
|
|
@ -6,6 +6,7 @@ use eyre::Context;
|
||||||
use router::router;
|
use router::router;
|
||||||
use tokio::{fs, net::TcpListener};
|
use tokio::{fs, net::TcpListener};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _, EnvFilter};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod auto_reload;
|
mod auto_reload;
|
||||||
|
@ -93,7 +94,12 @@ async fn main() {
|
||||||
let _client = tracy_client::Client::start();
|
let _client = tracy_client::Client::start();
|
||||||
|
|
||||||
color_eyre::install().unwrap();
|
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 {
|
match fallible_main().await {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue