const std = @import("std"); const mem = std.mem; const builtin = @import("builtin"); const bytecode = @import("bytecode.zig"); const Canvas = @import("canvas.zig").Canvas; const render = @import("render.zig"); const Scratch = @import("scratch.zig"); const value = @import("value.zig"); const Vm = @import("vm.zig"); const allocator = if (builtin.cpu.arch == .wasm32) std.heap.wasm_allocator else @import("allocator.zig").hostAllocator; // Scratch export fn haku2_scratch_new(max: usize) ?*Scratch { return Scratch.create(allocator, max) catch return null; } export fn haku2_scratch_destroy(scratch: *Scratch) void { scratch.destroy(allocator); } export fn haku2_scratch_reset(scratch: *Scratch) void { scratch.fixedBuffer = std.heap.FixedBufferAllocator.init(scratch.buffer); } // Limits export fn haku2_limits_new() ?*Vm.Limits { return allocator.create(Vm.Limits) catch null; } export fn haku2_limits_destroy(limits: *Vm.Limits) void { allocator.destroy(limits); } export fn haku2_limits_set_stack_capacity(limits: *Vm.Limits, new: usize) void { limits.stack_capacity = new; } export fn haku2_limits_set_call_stack_capacity(limits: *Vm.Limits, new: usize) void { limits.call_stack_capacity = new; } export fn haku2_limits_set_fuel(limits: *Vm.Limits, new: u32) void { limits.fuel = new; } // Defs export fn haku2_defs_parse( defs_string: [*]const u8, defs_len: usize, tags_string: [*]const u8, tags_len: usize, ) ?*bytecode.Defs { return bytecode.Defs.parse( allocator, defs_string[0..defs_len], tags_string[0..tags_len], ) catch null; } export fn haku2_defs_destroy(defs: *bytecode.Defs) void { defs.destroy(allocator); } // VM export fn haku2_vm_new(s: *Scratch, defs: *const bytecode.Defs, limits: *const Vm.Limits) ?*Vm { const vm = allocator.create(Vm) catch return null; errdefer allocator.destroy(vm); vm.* = Vm.init(s.allocator(), defs, limits) catch return null; return vm; } export fn haku2_vm_destroy(vm: *Vm) void { allocator.destroy(vm); } export fn haku2_vm_run_main( vm: *Vm, scratch: *Scratch, code: [*]const u8, code_len: usize, local_count: u8, ) bool { const chunk = bytecode.Chunk{ .bytecode = code[0..code_len], }; const closure = value.Closure{ .chunk = &chunk, .start = 0, .param_count = 0, .local_count = local_count, .captures = &[_]value.Value{}, }; vm.run(scratch.allocator(), &closure, vm.stack_top) catch return false; return true; } export fn haku2_vm_is_dotter(vm: *const Vm) bool { if (vm.stack.len == 0) return false; const top = vm.stack[vm.stack_top]; return top == .ref and top.ref.* == .reticle and top.ref.reticle == .dotter; } export fn haku2_vm_run_dotter( vm: *Vm, scratch: *Scratch, from_x: f32, from_y: f32, to_x: f32, to_y: f32, num: f32, ) bool { vm.runDotter( scratch.allocator(), .{ from_x, from_y, 0, 0 }, .{ to_x, to_y, 0, 0 }, num, ) catch return false; return true; } export fn haku2_vm_exception_len(vm: *const Vm) usize { if (vm.exception) |exn| { return exn.len; } else { return 0; } } export fn haku2_vm_exception_render(vm: *const Vm, buffer: [*]u8) void { const exn = vm.exception.?; _ = exn.format(buffer[0..exn.len], &exn.args); } // Renderer export fn haku2_render(vm: *Vm, canvas: *Canvas, max_depth: usize) bool { render.render(vm, canvas, max_depth) catch return false; return true; }