|
|
|
@ -4,6 +4,24 @@ const coral = @import("coral");
|
|
|
|
|
|
|
|
|
|
const file = @import("./file.zig");
|
|
|
|
|
|
|
|
|
|
pub const Frame = struct {
|
|
|
|
|
name: []const coral.io.Byte = "",
|
|
|
|
|
arg_count: u8,
|
|
|
|
|
locals_top: usize,
|
|
|
|
|
|
|
|
|
|
pub fn args(self: *const Frame, env: *RuntimeEnv) []const ?*const RuntimeRef {
|
|
|
|
|
return env.locals.values[self.locals_top .. (self.locals_top + self.arg_count)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) RuntimeError!*const RuntimeRef {
|
|
|
|
|
return self.has_arg(env, arg_index) orelse env.raise(error.BadOperation, "nil reference");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn has_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) ?*const RuntimeRef {
|
|
|
|
|
return if (arg_index >= self.arg_count) null else env.locals.values[self.locals_top + arg_index];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub const Fixed = i32;
|
|
|
|
|
|
|
|
|
|
pub const Float = f64;
|
|
|
|
@ -18,6 +36,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
const Chunk = struct {
|
|
|
|
|
env: *RuntimeEnv,
|
|
|
|
|
name: []coral.io.Byte,
|
|
|
|
|
arity: u8,
|
|
|
|
|
opcodes: OpcodeList,
|
|
|
|
|
constants: ConstList,
|
|
|
|
|
|
|
|
|
@ -33,6 +52,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
float: Float,
|
|
|
|
|
string: []const coral.io.Byte,
|
|
|
|
|
symbol: []const coral.io.Byte,
|
|
|
|
|
chunk: Chunk,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Opcode = union (enum) {
|
|
|
|
@ -124,17 +144,39 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.table_literal => |fields| {
|
|
|
|
|
if (fields.values.len > coral.math.max_int(@typeInfo(u32).Int)) {
|
|
|
|
|
.table_literal => |literal| {
|
|
|
|
|
if (literal.values.len > coral.math.max_int(@typeInfo(u32).Int)) {
|
|
|
|
|
return error.OutOfMemory;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (fields.values) |field| {
|
|
|
|
|
for (literal.values) |field| {
|
|
|
|
|
try self.compile_expression(chunk, field.value_expression);
|
|
|
|
|
try self.compile_expression(chunk, field.key_expression);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try chunk.opcodes.push_one(.{.push_table = @intCast(fields.values.len)});
|
|
|
|
|
try chunk.opcodes.push_one(.{.push_table = @intCast(literal.values.len)});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.lambda_literal => |literal| {
|
|
|
|
|
if (literal.argument_identifiers.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
|
|
|
|
|
return error.OutOfMemory;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lambda_chunk = try Chunk.make(
|
|
|
|
|
chunk.env,
|
|
|
|
|
"<lambda>",
|
|
|
|
|
@intCast(literal.argument_identifiers.values.len),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
errdefer lambda_chunk.free();
|
|
|
|
|
|
|
|
|
|
try lambda_chunk.compile(literal.block_statements.values, literal.argument_identifiers.values);
|
|
|
|
|
|
|
|
|
|
try chunk.opcodes.push_one(.{
|
|
|
|
|
.push_const = try chunk.declare_constant(.{
|
|
|
|
|
.chunk = lambda_chunk,
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.binary_operation => |operation| {
|
|
|
|
@ -213,11 +255,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.local_get => |local_get| {
|
|
|
|
|
try chunk.opcodes.push_one(.{
|
|
|
|
|
.push_local = (self.resolve_local(local_get.identifier) orelse {
|
|
|
|
|
return chunk.env.raise(error.OutOfMemory, "undefined local");
|
|
|
|
|
}).index,
|
|
|
|
|
});
|
|
|
|
|
if (self.resolve_local(local_get.identifier)) |local| {
|
|
|
|
|
return chunk.opcodes.push_one(.{.push_local = local.index});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chunk.env.raise(error.OutOfMemory, "undefined local");
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.local_set => |local_set| {
|
|
|
|
@ -287,16 +329,26 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
|
|
|
|
|
try self.compile_expression(chunk, declare.assigned_expression);
|
|
|
|
|
|
|
|
|
|
if (self.locals_count == self.locals_buffer.len) {
|
|
|
|
|
return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals");
|
|
|
|
|
switch (declare.storage) {
|
|
|
|
|
.@"var" => {
|
|
|
|
|
if (!self.declare_local(.{
|
|
|
|
|
.identifier = declare.identifier,
|
|
|
|
|
.is_readonly = false,
|
|
|
|
|
})) {
|
|
|
|
|
return chunk.env.raise(error.BadSyntax, "too many locals");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.let => {
|
|
|
|
|
// TODO: investigate constant folding.
|
|
|
|
|
if (!self.declare_local(.{
|
|
|
|
|
.identifier = declare.identifier,
|
|
|
|
|
.is_readonly = false,
|
|
|
|
|
})) {
|
|
|
|
|
return chunk.env.raise(error.BadSyntax, "too many locals");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.locals_buffer[self.locals_count] = .{
|
|
|
|
|
.identifier = declare.identifier,
|
|
|
|
|
.is_readonly = declare.storage != .variant,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.locals_count += 1;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.block => |block| {
|
|
|
|
@ -348,6 +400,17 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn declare_local(self: *CompilationUnit, local: Local) bool {
|
|
|
|
|
if (self.locals_count == self.locals_buffer.len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.locals_buffer[self.locals_count] = local;
|
|
|
|
|
self.locals_count += 1;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?ResolvedLocal {
|
|
|
|
|
if (self.locals_count == 0) {
|
|
|
|
|
return null;
|
|
|
|
@ -372,11 +435,29 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fn compile(self: *Chunk, statements: []const ast.Statement) RuntimeError!void {
|
|
|
|
|
fn compile(self: *Chunk, statements: []const ast.Statement, args: []const []const coral.io.Byte) RuntimeError!void {
|
|
|
|
|
var unit = CompilationUnit{};
|
|
|
|
|
var has_returned = false;
|
|
|
|
|
|
|
|
|
|
for (args) |arg| {
|
|
|
|
|
if (!unit.declare_local(.{
|
|
|
|
|
.is_readonly = true,
|
|
|
|
|
.identifier = arg,
|
|
|
|
|
})) {
|
|
|
|
|
return self.env.raise(error.BadSyntax, "too many arguments");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (statements) |statement| {
|
|
|
|
|
try unit.compile_statement(self, statement);
|
|
|
|
|
|
|
|
|
|
if (statement == .@"return") {
|
|
|
|
|
has_returned = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!has_returned) {
|
|
|
|
|
try self.opcodes.push_one(.push_nil);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -385,27 +466,27 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const constant_index = self.constants.values.len;
|
|
|
|
|
|
|
|
|
|
try self.constants.push_one(try switch (constant) {
|
|
|
|
|
const constant_object = try switch (constant) {
|
|
|
|
|
.fixed => |fixed| self.env.new_fixed(fixed),
|
|
|
|
|
.float => |float| self.env.new_float(float),
|
|
|
|
|
.string => |string| self.env.new_string(string),
|
|
|
|
|
.symbol => |symbol| self.env.new_symbol(symbol),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return @intCast(constant_index);
|
|
|
|
|
.chunk => |chunk| self.env.new_dynamic(coral.io.bytes_of(&chunk).ptr, &.{
|
|
|
|
|
.size = @sizeOf(Chunk),
|
|
|
|
|
.name = "lambda",
|
|
|
|
|
.destruct = typeinfo_destruct,
|
|
|
|
|
.call = typeinfo_call,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
errdefer self.env.discard(constant_object);
|
|
|
|
|
try self.constants.push_one(constant_object);
|
|
|
|
|
|
|
|
|
|
return @intCast(self.constants.values.len - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn execute(self: *Chunk) RuntimeError!?*RuntimeRef {
|
|
|
|
|
try self.env.frames.push_one(.{
|
|
|
|
|
.arg_count = 0,
|
|
|
|
|
.locals_top = self.env.locals.values.len,
|
|
|
|
|
.name = self.name,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
defer coral.debug.assert(self.env.frames.pop() != null);
|
|
|
|
|
|
|
|
|
|
fn execute(self: *Chunk, frame: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
var opcode_cursor = @as(u32, 0);
|
|
|
|
|
|
|
|
|
|
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
|
|
|
|
@ -433,7 +514,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
return self.env.raise(error.IllegalState, "invalid local");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self.env.locals.values[push_local]) |local| {
|
|
|
|
|
if (self.env.locals.values[frame.locals_top + push_local]) |local| {
|
|
|
|
|
try self.env.locals.push_one(try self.env.acquire(local));
|
|
|
|
|
} else {
|
|
|
|
|
try self.env.locals.push_one(null);
|
|
|
|
@ -448,25 +529,19 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
{
|
|
|
|
|
const dynamic = table.object().payload.dynamic;
|
|
|
|
|
const userdata = dynamic.userdata();
|
|
|
|
|
const table_set = dynamic.typeinfo().set;
|
|
|
|
|
var popped = @as(usize, 0);
|
|
|
|
|
|
|
|
|
|
while (popped < push_table) : (popped += 1) {
|
|
|
|
|
const index = try self.env.expect(try self.pop_local());
|
|
|
|
|
const index = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(index);
|
|
|
|
|
|
|
|
|
|
const maybe_value = try self.pop_local();
|
|
|
|
|
if (try self.pop_local()) |value| {
|
|
|
|
|
defer self.env.discard(value);
|
|
|
|
|
|
|
|
|
|
defer {
|
|
|
|
|
if (maybe_value) |value| {
|
|
|
|
|
self.env.discard(value);
|
|
|
|
|
}
|
|
|
|
|
try table_set(self.env, userdata, index, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try dynamic.typeinfo().set(.{
|
|
|
|
|
.userdata = userdata,
|
|
|
|
|
.env = self.env,
|
|
|
|
|
}, index, maybe_value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -487,7 +562,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.local_set => |local_set| {
|
|
|
|
|
const local = &self.env.locals.values[local_set];
|
|
|
|
|
const local = &self.env.locals.values[frame.locals_top + local_set];
|
|
|
|
|
|
|
|
|
|
if (local.*) |previous_local| {
|
|
|
|
|
self.env.discard(previous_local);
|
|
|
|
@ -497,11 +572,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.object_get => {
|
|
|
|
|
const index = try self.env.expect(try self.pop_local());
|
|
|
|
|
const index = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(index);
|
|
|
|
|
|
|
|
|
|
const indexable = try self.env.expect(try self.pop_local());
|
|
|
|
|
const indexable = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(indexable);
|
|
|
|
|
|
|
|
|
@ -542,36 +617,21 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
|
|
|
|
|
.object_call => |object_call| {
|
|
|
|
|
const result = call: {
|
|
|
|
|
const callable = try self.env.expect(try self.pop_local());
|
|
|
|
|
const callable = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(callable);
|
|
|
|
|
|
|
|
|
|
try self.env.frames.push_one(.{
|
|
|
|
|
.name = "<lambda>",
|
|
|
|
|
.arg_count = object_call,
|
|
|
|
|
.locals_top = self.env.locals.values.len,
|
|
|
|
|
});
|
|
|
|
|
const call_frame = try self.env.push_frame(object_call);
|
|
|
|
|
|
|
|
|
|
defer coral.debug.assert(self.env.frames.pop() != null);
|
|
|
|
|
defer self.env.pop_frame();
|
|
|
|
|
|
|
|
|
|
break: call try switch (callable.object().payload) {
|
|
|
|
|
.syscall => |syscall| syscall(self.env),
|
|
|
|
|
|
|
|
|
|
.dynamic => |dynamic| dynamic.typeinfo().call(.{
|
|
|
|
|
.userdata = dynamic.userdata(),
|
|
|
|
|
.env = self.env,
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
.syscall => |syscall| syscall(self.env, call_frame),
|
|
|
|
|
.dynamic => |dynamic| dynamic.typeinfo().call(self.env, dynamic.userdata(), call_frame),
|
|
|
|
|
else => self.env.raise(error.TypeMismatch, "object is not callable"),
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (0 .. object_call) |_| {
|
|
|
|
|
if (try self.pop_local()) |popped_arg| {
|
|
|
|
|
self.env.discard(popped_arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errdefer {
|
|
|
|
|
if (result) |ref| {
|
|
|
|
|
self.env.discard(ref);
|
|
|
|
@ -592,7 +652,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.neg => {
|
|
|
|
|
const value = try self.env.expect(try self.pop_local());
|
|
|
|
|
const value = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(value);
|
|
|
|
|
|
|
|
|
@ -604,11 +664,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.add => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -644,11 +704,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.sub => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -684,11 +744,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.mul => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -724,11 +784,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.div => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -780,11 +840,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.cgt => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -806,11 +866,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.clt => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -832,11 +892,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.cge => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -858,11 +918,11 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.cle => {
|
|
|
|
|
const rhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const rhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(rhs);
|
|
|
|
|
|
|
|
|
|
const lhs = try self.env.expect(try self.pop_local());
|
|
|
|
|
const lhs = try self.expect(try self.pop_local());
|
|
|
|
|
|
|
|
|
|
defer self.env.discard(lhs);
|
|
|
|
|
|
|
|
|
@ -912,6 +972,10 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
return self.pop_local();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn expect(self: *Chunk, value: ?*RuntimeRef) RuntimeError!*RuntimeRef {
|
|
|
|
|
return value orelse self.env.raise(error.TypeMismatch, "nil reference");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn free(self: *Chunk) void {
|
|
|
|
|
while (self.constants.pop()) |constant| {
|
|
|
|
|
self.env.discard(constant);
|
|
|
|
@ -922,11 +986,12 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
self.env.allocator.deallocate(self.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make(env: *RuntimeEnv, name: []const coral.io.Byte) coral.io.AllocationError!Chunk {
|
|
|
|
|
fn make(env: *RuntimeEnv, name: []const coral.io.Byte, arity: u8) coral.io.AllocationError!Chunk {
|
|
|
|
|
return .{
|
|
|
|
|
.name = try coral.io.allocate_copy(env.allocator, name),
|
|
|
|
|
.opcodes = OpcodeList.make(env.allocator),
|
|
|
|
|
.constants = ConstList.make(env.allocator),
|
|
|
|
|
.arity = arity,
|
|
|
|
|
.env = env,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@ -934,15 +999,27 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
fn pop_local(self: *Chunk) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return self.env.locals.pop() orelse self.env.raise(error.IllegalState, "stack underflow");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn typeinfo_call(env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const chunk = @as(*Chunk, @ptrCast(@alignCast(userdata)));
|
|
|
|
|
|
|
|
|
|
if (frame.arg_count < chunk.arity) {
|
|
|
|
|
return env.raise(error.BadOperation, "expected more arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chunk.execute(frame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void {
|
|
|
|
|
_ = env;
|
|
|
|
|
|
|
|
|
|
@as(*Chunk, @ptrCast(@alignCast(userdata))).free();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const ConstList = coral.list.Stack(*RuntimeRef);
|
|
|
|
|
|
|
|
|
|
const FrameStack = coral.list.Stack(struct {
|
|
|
|
|
name: []const coral.io.Byte,
|
|
|
|
|
arg_count: u8,
|
|
|
|
|
locals_top: usize,
|
|
|
|
|
});
|
|
|
|
|
const FrameStack = coral.list.Stack(Frame);
|
|
|
|
|
|
|
|
|
|
pub const Options = struct {
|
|
|
|
|
import_access: file.Access = .null,
|
|
|
|
@ -966,15 +1043,15 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
associative: RefTable,
|
|
|
|
|
contiguous: RefList,
|
|
|
|
|
|
|
|
|
|
fn typeinfo_destruct(method: Typeinfo.Method) void {
|
|
|
|
|
const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr)));
|
|
|
|
|
fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void {
|
|
|
|
|
const table = @as(*Table, @ptrCast(@alignCast(userdata)));
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var field_iterable = table.associative.as_iterable();
|
|
|
|
|
|
|
|
|
|
while (field_iterable.next()) |entry| {
|
|
|
|
|
method.env.discard(entry.key);
|
|
|
|
|
method.env.discard(entry.value);
|
|
|
|
|
env.discard(entry.key);
|
|
|
|
|
env.discard(entry.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -982,18 +1059,18 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
|
|
|
|
|
while (table.contiguous.pop()) |value| {
|
|
|
|
|
if (value) |ref| {
|
|
|
|
|
method.env.discard(ref);
|
|
|
|
|
env.discard(ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.contiguous.free();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn typeinfo_get(method: Typeinfo.Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr)));
|
|
|
|
|
const acquired_index = try method.env.acquire(index);
|
|
|
|
|
fn typeinfo_get(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const table = @as(*Table, @ptrCast(@alignCast(userdata)));
|
|
|
|
|
const acquired_index = try env.acquire(index);
|
|
|
|
|
|
|
|
|
|
defer method.env.discard(acquired_index);
|
|
|
|
|
defer env.discard(acquired_index);
|
|
|
|
|
|
|
|
|
|
if (acquired_index.is_fixed()) |fixed| {
|
|
|
|
|
if (fixed < 0) {
|
|
|
|
@ -1002,22 +1079,22 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fixed < table.contiguous.values.len) {
|
|
|
|
|
return method.env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null);
|
|
|
|
|
return env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (table.associative.lookup(acquired_index)) |value_ref| {
|
|
|
|
|
return method.env.acquire(value_ref);
|
|
|
|
|
return env.acquire(value_ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn typeinfo_set(method: Typeinfo.Method, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
|
|
|
|
|
const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr)));
|
|
|
|
|
const acquired_index = try method.env.acquire(index);
|
|
|
|
|
fn typeinfo_set(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
|
|
|
|
|
const table = @as(*Table, @ptrCast(@alignCast(userdata)));
|
|
|
|
|
const acquired_index = try env.acquire(index);
|
|
|
|
|
|
|
|
|
|
errdefer method.env.discard(acquired_index);
|
|
|
|
|
errdefer env.discard(acquired_index);
|
|
|
|
|
|
|
|
|
|
if (acquired_index.is_fixed()) |fixed| {
|
|
|
|
|
if (fixed < 0) {
|
|
|
|
@ -1029,32 +1106,31 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
const maybe_replacing = &table.contiguous.values[@intCast(fixed)];
|
|
|
|
|
|
|
|
|
|
if (maybe_replacing.*) |replacing| {
|
|
|
|
|
method.env.discard(replacing);
|
|
|
|
|
env.discard(replacing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_replacing.* = if (value) |ref| try method.env.acquire(ref) else null;
|
|
|
|
|
maybe_replacing.* = if (value) |ref| try env.acquire(ref) else null;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const acquired_value = try method.env.acquire(value orelse {
|
|
|
|
|
const acquired_value = try env.acquire(value orelse {
|
|
|
|
|
if (table.associative.remove(acquired_index)) |removed| {
|
|
|
|
|
method.env.discard(removed.key);
|
|
|
|
|
method.env.discard(removed.value);
|
|
|
|
|
env.discard(removed.key);
|
|
|
|
|
env.discard(removed.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
errdefer method.env.discard(acquired_value);
|
|
|
|
|
errdefer env.discard(acquired_value);
|
|
|
|
|
|
|
|
|
|
if (try table.associative.replace(acquired_index, acquired_value)) |replaced| {
|
|
|
|
|
method.env.discard(replaced.key);
|
|
|
|
|
method.env.discard(replaced.value);
|
|
|
|
|
env.discard(replaced.key);
|
|
|
|
|
env.discard(replaced.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub fn acquire(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!*RuntimeRef {
|
|
|
|
@ -1067,45 +1143,15 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
return @ptrCast(object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn acquire_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
|
|
|
|
|
|
|
|
|
if (index < frame.arg_count) {
|
|
|
|
|
if (self.locals.values[(frame.locals_top - frame.arg_count) + index]) |local| {
|
|
|
|
|
return self.acquire(local);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn bind_system(self: *RuntimeEnv, name: []const coral.io.Byte, value: *const RuntimeRef) RuntimeError!void {
|
|
|
|
|
const name_symbol = try self.new_symbol(name);
|
|
|
|
|
|
|
|
|
|
errdefer self.discard(name_symbol);
|
|
|
|
|
|
|
|
|
|
const acquired_value = try self.acquire(value);
|
|
|
|
|
|
|
|
|
|
errdefer self.discard(acquired_value);
|
|
|
|
|
|
|
|
|
|
if (try self.system_bindings.replace(name_symbol, acquired_value)) |replaced| {
|
|
|
|
|
self.discard(replaced.key);
|
|
|
|
|
self.discard(replaced.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef {
|
|
|
|
|
try self.locals.push_all(args);
|
|
|
|
|
// TODO: Handle errors.
|
|
|
|
|
for (args) |arg| {
|
|
|
|
|
try self.locals.push_one(try self.acquire(arg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer coral.io.assert(self.locals.drop(args.len));
|
|
|
|
|
try self.push_frame(args.len);
|
|
|
|
|
|
|
|
|
|
try self.frames.push_one(.{
|
|
|
|
|
.name = "<native>",
|
|
|
|
|
.arg_count = args.len,
|
|
|
|
|
.locals_top = self.locals.values.len,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
defer coral.io.assert(self.frames.pop() != null);
|
|
|
|
|
defer self.pop_frame();
|
|
|
|
|
|
|
|
|
|
return switch (callable.object().payload) {
|
|
|
|
|
.syscall => |syscall| syscall(self.env),
|
|
|
|
@ -1137,10 +1183,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
|
|
|
|
|
.dynamic => |dynamic| {
|
|
|
|
|
if (dynamic.typeinfo().destruct) |destruct| {
|
|
|
|
|
destruct(.{
|
|
|
|
|
.userdata = dynamic.userdata(),
|
|
|
|
|
.env = self,
|
|
|
|
|
});
|
|
|
|
|
destruct(self, dynamic.userdata());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.allocator.deallocate(dynamic.unpack());
|
|
|
|
@ -1159,7 +1202,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
defer self.allocator.deallocate(file_data);
|
|
|
|
|
|
|
|
|
|
const file_name = file_path.to_string() orelse "<script>";
|
|
|
|
|
var chunk = try Chunk.make(self, file_name);
|
|
|
|
|
var chunk = try Chunk.make(self, file_name, 0);
|
|
|
|
|
|
|
|
|
|
defer chunk.free();
|
|
|
|
|
|
|
|
|
@ -1170,13 +1213,13 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
try chunk.compile(ast_tree.parse(file_data) catch |parse_error| return switch (parse_error) {
|
|
|
|
|
error.BadSyntax => self.raise(error.BadSyntax, ast_tree.error_message()),
|
|
|
|
|
error.OutOfMemory => error.OutOfMemory,
|
|
|
|
|
});
|
|
|
|
|
}, &.{});
|
|
|
|
|
|
|
|
|
|
return chunk.execute();
|
|
|
|
|
}
|
|
|
|
|
const frame = try self.push_frame(0);
|
|
|
|
|
|
|
|
|
|
pub fn expect(self: *RuntimeEnv, value: ?*RuntimeRef) RuntimeError!*RuntimeRef {
|
|
|
|
|
return value orelse self.raise(error.TypeMismatch, "nil reference");
|
|
|
|
|
defer self.pop_frame();
|
|
|
|
|
|
|
|
|
|
return chunk.execute(frame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn free(self: *RuntimeEnv) void {
|
|
|
|
@ -1201,6 +1244,14 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
|
|
|
|
|
pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return switch (indexable.object().payload) {
|
|
|
|
|
.false => self.raise(error.TypeMismatch, "false is not get-indexable"),
|
|
|
|
|
.true => self.raise(error.TypeMismatch, "true is not get-indexable"),
|
|
|
|
|
.fixed => self.raise(error.TypeMismatch, "fixed is not get-indexable"),
|
|
|
|
|
.float => self.raise(error.TypeMismatch, "float is not get-indexable"),
|
|
|
|
|
.string => self.raise(error.TypeMismatch, "string is not get-indexable"),
|
|
|
|
|
.symbol => self.raise(error.TypeMismatch, "symbol is not get-indexable"),
|
|
|
|
|
.syscall => self.raise(error.TypeMismatch, "syscall is not get-indexable"),
|
|
|
|
|
|
|
|
|
|
.vector2 => |vector2| swizzle: {
|
|
|
|
|
const swizzle_symbol = try self.unbox_symbol(index);
|
|
|
|
|
var swizzle_buffer = [_]f32{0} ** 3;
|
|
|
|
@ -1252,12 +1303,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.dynamic => |dynamic| dynamic.typeinfo().get(.{
|
|
|
|
|
.userdata = dynamic.userdata(),
|
|
|
|
|
.env = self,
|
|
|
|
|
}, index),
|
|
|
|
|
|
|
|
|
|
else => self.raise(error.TypeMismatch, "object is not get-indexable"),
|
|
|
|
|
.dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1402,6 +1448,29 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pop_frame(self: *RuntimeEnv) void {
|
|
|
|
|
var to_pop = self.locals.values.len - (self.frames.pop() orelse unreachable).locals_top;
|
|
|
|
|
|
|
|
|
|
while (to_pop != 0) {
|
|
|
|
|
if (self.locals.pop() orelse unreachable) |local| {
|
|
|
|
|
self.discard(local);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to_pop -= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn push_frame(self: *RuntimeEnv, arg_count: u8) RuntimeError!Frame {
|
|
|
|
|
const frame = Frame{
|
|
|
|
|
.arg_count = arg_count,
|
|
|
|
|
.locals_top = self.locals.values.len - arg_count,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try self.frames.push_one(frame);
|
|
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
|
|
|
|
|
self.print_error(message);
|
|
|
|
|
|
|
|
|
@ -1422,11 +1491,7 @@ pub const RuntimeEnv = struct {
|
|
|
|
|
|
|
|
|
|
pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
|
|
|
|
|
return switch (indexable.object().payload) {
|
|
|
|
|
.dynamic => |dynamic| dynamic.typeinfo().set(.{
|
|
|
|
|
.userdata = dynamic.userdata(),
|
|
|
|
|
.env = self,
|
|
|
|
|
}, index, value),
|
|
|
|
|
|
|
|
|
|
.dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value),
|
|
|
|
|
else => self.raise(error.TypeMismatch, "object is not set-indexable"),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@ -1642,31 +1707,26 @@ pub const RuntimeRef = opaque {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub const Syscall = fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef;
|
|
|
|
|
pub const Syscall = fn (env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef;
|
|
|
|
|
|
|
|
|
|
pub const Typeinfo = struct {
|
|
|
|
|
name: []const coral.io.Byte,
|
|
|
|
|
size: usize,
|
|
|
|
|
destruct: ?*const fn (method: Method) void = null,
|
|
|
|
|
call: *const fn (method: Method) RuntimeError!?*RuntimeRef = default_call,
|
|
|
|
|
get: *const fn (method: Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get,
|
|
|
|
|
set: *const fn (method: Method, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
|
|
|
|
destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null,
|
|
|
|
|
call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call,
|
|
|
|
|
get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get,
|
|
|
|
|
set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
|
|
|
|
|
|
|
|
|
pub const Method = struct {
|
|
|
|
|
env: *RuntimeEnv,
|
|
|
|
|
userdata: []coral.io.Byte,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fn default_call(method: Method) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return method.env.raise(error.TypeMismatch, "object is not callable");
|
|
|
|
|
fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return env.raise(error.BadOperation, "object is not callable");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn default_get(method: Method, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return method.env.raise(error.TypeMismatch, "object is not index-gettable");
|
|
|
|
|
fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return env.raise(error.BadOperation, "object is not get-indexable");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn default_set(method: Method, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
|
|
|
|
|
return method.env.raise(error.TypeMismatch, "object is not index-settable");
|
|
|
|
|
fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
|
|
|
|
|
return env.raise(error.BadOperation, "object is not set-indexable");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -1694,59 +1754,35 @@ pub fn get_key(env: *RuntimeEnv, indexable: *RuntimeRef, key: []const coral.io.B
|
|
|
|
|
return env.get(indexable, key_string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn syscall_import(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const arg = try env.expect(try env.acquire_arg(0));
|
|
|
|
|
|
|
|
|
|
defer env.discard(arg);
|
|
|
|
|
|
|
|
|
|
return env.import(file.Path.from(&.{try env.unbox_string(arg)}));
|
|
|
|
|
fn syscall_import(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
return env.import(file.Path.from(&.{try env.unbox_string(try frame.get_arg(env, 0))}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn syscall_print(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const arg = try env.expect(try env.acquire_arg(0));
|
|
|
|
|
|
|
|
|
|
defer env.discard(arg);
|
|
|
|
|
|
|
|
|
|
env.print(try env.unbox_string(arg));
|
|
|
|
|
fn syscall_print(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
env.print(try env.unbox_string(try frame.get_arg(env, 0)));
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn syscall_vec2(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const x = decode: {
|
|
|
|
|
const value = try env.expect(try env.acquire_arg(0));
|
|
|
|
|
|
|
|
|
|
defer env.discard(value);
|
|
|
|
|
|
|
|
|
|
break: decode @as(f32, @floatCast(try env.unbox_float(value)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (try env.acquire_arg(1)) |y| {
|
|
|
|
|
defer env.discard(y);
|
|
|
|
|
fn syscall_vec2(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
|
|
|
|
|
|
|
|
|
|
if (frame.has_arg(env, 1)) |y| {
|
|
|
|
|
return env.new_vector2(x, @floatCast(try env.unbox_float(y)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return env.new_vector2(x, x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn syscall_vec3(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const x = decode: {
|
|
|
|
|
const value = try env.expect(try env.acquire_arg(0));
|
|
|
|
|
fn syscall_vec3(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
|
|
|
|
|
const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
|
|
|
|
|
|
|
|
|
|
defer env.discard(value);
|
|
|
|
|
|
|
|
|
|
break: decode @as(f32, @floatCast(try env.unbox_float(value)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (try env.acquire_arg(1)) |y| {
|
|
|
|
|
defer env.discard(y);
|
|
|
|
|
|
|
|
|
|
const z = try env.expect(try env.acquire_arg(2));
|
|
|
|
|
|
|
|
|
|
defer env.discard(z);
|
|
|
|
|
|
|
|
|
|
return env.new_vector3(x, @floatCast(try env.unbox_float(y)), @floatCast(try env.unbox_float(z)));
|
|
|
|
|
if (frame.has_arg(env, 1)) |y| {
|
|
|
|
|
return env.new_vector3(
|
|
|
|
|
x,
|
|
|
|
|
@floatCast(try env.unbox_float(y)),
|
|
|
|
|
@floatCast(try env.unbox_float(try frame.get_arg(env, 2))),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return env.new_vector3(x, x, x);
|
|
|
|
|