80 lines
2.4 KiB
Zig
80 lines
2.4 KiB
Zig
|
const std = @import("std");
|
||
|
const mem = std.mem;
|
||
|
|
||
|
/// 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);
|
||
|
}
|
||
|
};
|