const std = @import("std"); const mem = std.mem; const log = std.log.scoped(.bytecode); /// NOTE: This must be mirrored in bytecode.rs. pub const Opcode = enum(u8) { // Push literal values onto the stack. nil, false, true, tag, number, // (float: f32) rgba, // (r: u8, g: u8, b: u8, a: u8) // Duplicate existing values. local, // (index: u8) push a value relative to the bottom of the current stack window set_local, // (index: u8) set the value of a value relative to the bottom of the current stack window capture, // (index: u8) push a value captured by the current closure def, // (index: u16) get the value of a definition set_def, // (index: u16) set the value of a definition // Create lists. list, // (len: u16) // Create literal functions. function, // (params: u8, then: u16), at `then`: (local_count: u8, capture_count: u8, captures: [capture_count](source: u8, index: u8)) // Control flow. jump, // (offset: u16) jump_if_not, // (offset: u16) field, // (count: u8, tags: [count]u16) look up a closure capture named by a tag. this is used to implement records with fields // Function calls. call, // (argc: u8) system, // (index: u8, argc: u8) fast path for system calls ret, // must be the last opcode; opcodes after .ret are treated as invalid _, // invalid opcodes }; // Constants used by the .function opcode to indicate capture sources. pub const capture_local: u8 = 0; pub const capture_capture: u8 = 1; pub const Chunk = struct { bytecode: []const u8, }; pub const Loc = u16; pub const Defs = struct { num_defs: usize, num_tags: usize, // Unlike the Rust version, we currently do not store strings here, because we still don't // support stack traces. // The VM therefore never needs the names of Defs for any practical purposes. pub fn parse( a: mem.Allocator, // These strings are expected to contain a list of names, where each name is terminated // by a newline (LF). defs_string: []const u8, tags_string: []const u8, ) !*Defs { const d = try a.create(Defs); errdefer a.destroy(d); d.* = .{ .num_defs = mem.count(u8, defs_string, "\n"), .num_tags = mem.count(u8, tags_string, "\n"), }; return d; } pub fn destroy(defs: *Defs, a: mem.Allocator) void { a.destroy(defs); } };