Merge pull request 'Closures, Higher-Order Functions, and Everything in Between' (#44) from kym-captures into main
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Reviewed-on: #44
This commit is contained in:
commit
03a8abc5dd
|
@ -1,29 +1,18 @@
|
||||||
|
|
||||||
let printer = lambda ():
|
let test_param = "monkey wrench"
|
||||||
|
|
||||||
|
let printer = lambda (pfx):
|
||||||
|
@print(test_param)
|
||||||
|
|
||||||
return lambda (msg):
|
return lambda (msg):
|
||||||
@print("This is a func call")
|
@print(pfx)
|
||||||
@print(msg)
|
@print(msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
let pr = printer()
|
let pr = printer("this is a final closure")
|
||||||
var i = 0
|
|
||||||
|
|
||||||
pr("test")
|
pr("goodbye")
|
||||||
|
|
||||||
while i < 5:
|
|
||||||
pr("hello, world")
|
|
||||||
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if i > 6:
|
|
||||||
pr("`i` greater than `6`")
|
|
||||||
elif i == 4:
|
|
||||||
pr("`i` is equal to `4`")
|
|
||||||
else:
|
|
||||||
pr("i'unno")
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.title = "Game",
|
.title = "Game",
|
||||||
|
|
|
@ -196,6 +196,8 @@ pub const HexadecimalFormat = struct {
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = writer;
|
_ = writer;
|
||||||
_ = value;
|
_ = value;
|
||||||
|
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub const Manifest = struct {
|
||||||
if (try kym.get_field(env, manifest, "width")) |ref| {
|
if (try kym.get_field(env, manifest, "width")) |ref| {
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
const fixed = try env.unbox_fixed(ref);
|
const fixed = try env.unwrap_fixed(ref);
|
||||||
|
|
||||||
if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(self.width)).Int)) {
|
if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(self.width)).Int)) {
|
||||||
break: get @intCast(fixed);
|
break: get @intCast(fixed);
|
||||||
|
@ -35,7 +35,7 @@ pub const Manifest = struct {
|
||||||
if (try kym.get_field(env, manifest, "height")) |ref| {
|
if (try kym.get_field(env, manifest, "height")) |ref| {
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
const fixed = try env.unbox_fixed(ref);
|
const fixed = try env.unwrap_fixed(ref);
|
||||||
|
|
||||||
if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(self.height)).Int)) {
|
if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(self.height)).Int)) {
|
||||||
break: get @intCast(fixed);
|
break: get @intCast(fixed);
|
||||||
|
@ -49,7 +49,7 @@ pub const Manifest = struct {
|
||||||
if (try kym.get_field(env, manifest, "tick_rate")) |ref| {
|
if (try kym.get_field(env, manifest, "tick_rate")) |ref| {
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
break: get @floatCast(try env.unbox_float(ref));
|
break: get @floatCast(try env.unwrap_float(ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
break: get self.tick_rate;
|
break: get self.tick_rate;
|
||||||
|
@ -58,7 +58,7 @@ pub const Manifest = struct {
|
||||||
if (try kym.get_field(env, manifest, "title")) |ref| {
|
if (try kym.get_field(env, manifest, "title")) |ref| {
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
const title_string = try env.unbox_string(ref);
|
const title_string = try env.unwrap_string(ref);
|
||||||
const limited_title_len = @min(title_string.len, self.title.len);
|
const limited_title_len = @min(title_string.len, self.title.len);
|
||||||
|
|
||||||
coral.io.copy(&self.title, title_string[0 .. limited_title_len]);
|
coral.io.copy(&self.title, title_string[0 .. limited_title_len]);
|
||||||
|
|
|
@ -11,9 +11,9 @@ pub const Access = union (enum) {
|
||||||
.null => return null,
|
.null => return null,
|
||||||
|
|
||||||
.sandboxed_path => |sandboxed_path| {
|
.sandboxed_path => |sandboxed_path| {
|
||||||
const readable_path_string = sandboxed_path.joined(readable_path).to_string() orelse return null;
|
const path_string = sandboxed_path.joined(readable_path).get_string();
|
||||||
|
|
||||||
return @ptrCast(ext.SDL_RWFromFile(readable_path_string.ptr, "rb"));
|
return @ptrCast(ext.SDL_RWFromFile(path_string.ptr, "rb"));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ pub const Access = union (enum) {
|
||||||
.null => return null,
|
.null => return null,
|
||||||
|
|
||||||
.sandboxed_path => |sandboxed_path| {
|
.sandboxed_path => |sandboxed_path| {
|
||||||
const path_string = sandboxed_path.joined(path).to_string() orelse return null;
|
const path_string = sandboxed_path.joined(path).get_string();
|
||||||
const rw_ops = ext.SDL_RWFromFile(path_string, "rb") orelse return null;
|
const rw_ops = ext.SDL_RWFromFile(path_string, "rb") orelse return null;
|
||||||
const file_size = ext.SDL_RWseek(rw_ops, 0, ext.RW_SEEK_END);
|
const file_size = ext.SDL_RWseek(rw_ops, 0, ext.RW_SEEK_END);
|
||||||
|
|
||||||
|
@ -96,12 +96,8 @@ pub const Path = extern struct {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(self: Path) ?[:0]const coral.io.Byte {
|
pub fn get_string(self: Path) [:0]const coral.io.Byte {
|
||||||
const last_index = self.data.len - 1;
|
coral.debug.assert(self.data[self.data.len - 1] == 0);
|
||||||
|
|
||||||
if (self.data[last_index] != 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return coral.io.slice_sentineled(@as(coral.io.Byte, 0), @as([*:0]const coral.io.Byte, @ptrCast(&self.data)));
|
return coral.io.slice_sentineled(@as(coral.io.Byte, 0), @as([*:0]const coral.io.Byte, @ptrCast(&self.data)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,6 @@ const Chunk = @import("./kym/Chunk.zig");
|
||||||
|
|
||||||
const Table = @import("./kym/Table.zig");
|
const Table = @import("./kym/Table.zig");
|
||||||
|
|
||||||
const app = @import("./app.zig");
|
|
||||||
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const file = @import("./file.zig");
|
const file = @import("./file.zig");
|
||||||
|
@ -15,7 +11,7 @@ const tokens = @import("./kym/tokens.zig");
|
||||||
const tree = @import("./kym/tree.zig");
|
const tree = @import("./kym/tree.zig");
|
||||||
|
|
||||||
pub const Frame = struct {
|
pub const Frame = struct {
|
||||||
name: []const coral.io.Byte = "",
|
name_stringable: *RuntimeRef,
|
||||||
arg_count: u8,
|
arg_count: u8,
|
||||||
locals_top: usize,
|
locals_top: usize,
|
||||||
|
|
||||||
|
@ -23,8 +19,8 @@ pub const Frame = struct {
|
||||||
return env.locals.values[self.locals_top .. (self.locals_top + self.arg_count)];
|
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 {
|
pub fn expect_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");
|
return self.has_arg(env, arg_index) orelse env.raise(error.TypeMismatch, "nil reference", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) ?*const RuntimeRef {
|
pub fn has_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) ?*const RuntimeRef {
|
||||||
|
@ -69,16 +65,24 @@ pub const RuntimeEnv = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) + rhs_float),
|
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) + rhs_float),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not addable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not addable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |lhs_float| switch (rhs.object().payload) {
|
.float => |lhs_float| switch (rhs.object().payload) {
|
||||||
.float => |rhs_float| self.new_float(lhs_float + rhs_float),
|
.float => |rhs_float| self.new_float(lhs_float + rhs_float),
|
||||||
.fixed => |rhs_fixed| self.new_float(lhs_float + @as(Float, @floatFromInt(rhs_fixed))),
|
.fixed => |rhs_fixed| self.new_float(lhs_float + @as(Float, @floatFromInt(rhs_fixed))),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not addable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not addable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => self.raise(error.TypeMismatch, "left-hand object is not addable"),
|
else => self.raise(error.TypeMismatch, "left-hand {typename} is not addable", .{
|
||||||
|
.typename = lhs.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,18 +92,18 @@ pub const RuntimeEnv = struct {
|
||||||
try self.locals.push_one(try self.acquire(arg));
|
try self.locals.push_one(try self.acquire(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
const frame = try self.push_frame(args.len);
|
const frame = try self.push_frame(callable, args.len);
|
||||||
|
|
||||||
defer self.pop_frame();
|
defer self.pop_frame();
|
||||||
|
|
||||||
return self.call_frame(callable, frame);
|
return self.call_frame(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_frame(self: *RuntimeEnv, callable: *const RuntimeRef, frame: Frame) RuntimeError!?*RuntimeRef {
|
pub fn call_frame(self: *RuntimeEnv, callable: *const RuntimeRef, frame: Frame) RuntimeError!?*RuntimeRef {
|
||||||
return switch (callable.object().payload) {
|
return switch (callable.object().payload) {
|
||||||
.syscall => |syscall| syscall(self, frame),
|
.syscall => |syscall| syscall(self, frame),
|
||||||
.dynamic => |dynamic| dynamic.typeinfo().call(self, dynamic.userdata(), frame),
|
.dynamic => |dynamic| dynamic.typeinfo().call(self, dynamic.userdata(), frame),
|
||||||
else => self.raise(error.TypeMismatch, "object is not callable"),
|
else => self.raise(error.TypeMismatch, "{typename} is not callable", .{.typename = callable.typename()}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,16 +112,24 @@ pub const RuntimeEnv = struct {
|
||||||
.fixed => |lhs_fixed| switch (rhs.object().payload) {
|
.fixed => |lhs_fixed| switch (rhs.object().payload) {
|
||||||
.fixed => |rhs_fixed| @as(Float, @floatFromInt(lhs_fixed)) - @as(Float, @floatFromInt(rhs_fixed)),
|
.fixed => |rhs_fixed| @as(Float, @floatFromInt(lhs_fixed)) - @as(Float, @floatFromInt(rhs_fixed)),
|
||||||
.float => |rhs_float| @as(Float, @floatFromInt(lhs_fixed)) - rhs_float,
|
.float => |rhs_float| @as(Float, @floatFromInt(lhs_fixed)) - rhs_float,
|
||||||
else => return self.raise(error.TypeMismatch, "right-hand object is not comparable"),
|
|
||||||
|
else => return self.raise(error.TypeMismatch, "right-hand {typename} is not comparable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |lhs_float| switch (rhs.object().payload) {
|
.float => |lhs_float| switch (rhs.object().payload) {
|
||||||
.float => |rhs_float| lhs_float - rhs_float,
|
.float => |rhs_float| lhs_float - rhs_float,
|
||||||
.fixed => |rhs_fixed| lhs_float - @as(Float, @floatFromInt(rhs_fixed)),
|
.fixed => |rhs_fixed| lhs_float - @as(Float, @floatFromInt(rhs_fixed)),
|
||||||
else => return self.raise(error.TypeMismatch, "right-hand object is not comparable"),
|
|
||||||
|
else => return self.raise(error.TypeMismatch, "right-hand {typename} is not comparable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return self.raise(error.TypeMismatch, "left-hand object is not comparable"),
|
else => return self.raise(error.TypeMismatch, "left-hand {typename} is not comparable", .{
|
||||||
|
.typename = lhs.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +144,12 @@ pub const RuntimeEnv = struct {
|
||||||
switch (object.payload) {
|
switch (object.payload) {
|
||||||
.false, .true, .float, .fixed, .symbol, .vector2, .vector3, .syscall => {},
|
.false, .true, .float, .fixed, .symbol, .vector2, .vector3, .syscall => {},
|
||||||
|
|
||||||
|
.boxed => |*boxed| {
|
||||||
|
if (boxed.*) |boxed_value| {
|
||||||
|
self.discard(boxed_value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
.string => |string| {
|
.string => |string| {
|
||||||
coral.debug.assert(string.len >= 0);
|
coral.debug.assert(string.len >= 0);
|
||||||
self.allocator.deallocate(string.ptr[0 .. @intCast(string.len)]);
|
self.allocator.deallocate(string.ptr[0 .. @intCast(string.len)]);
|
||||||
|
@ -155,21 +173,29 @@ pub const RuntimeEnv = struct {
|
||||||
.fixed => |lhs_fixed| switch (rhs.object().payload) {
|
.fixed => |lhs_fixed| switch (rhs.object().payload) {
|
||||||
.fixed => |rhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) / @as(Float, @floatFromInt(rhs_fixed))),
|
.fixed => |rhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) / @as(Float, @floatFromInt(rhs_fixed))),
|
||||||
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) / rhs_float),
|
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) / rhs_float),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not divisible"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not divisible", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |lhs_float| switch (rhs.object().payload) {
|
.float => |lhs_float| switch (rhs.object().payload) {
|
||||||
.float => |rhs_float| self.new_float(lhs_float / rhs_float),
|
.float => |rhs_float| self.new_float(lhs_float / rhs_float),
|
||||||
.fixed => |rhs_fixed| self.new_float(lhs_float / @as(Float, @floatFromInt(rhs_fixed))),
|
.fixed => |rhs_fixed| self.new_float(lhs_float / @as(Float, @floatFromInt(rhs_fixed))),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not divisible"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not divisible", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => self.raise(error.TypeMismatch, "left-hand object is not divisible"),
|
else => self.raise(error.TypeMismatch, "left-hand {typename} is not divisible", .{
|
||||||
|
.typename = lhs.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect(self: *RuntimeEnv, value: ?*RuntimeRef) RuntimeError!*RuntimeRef {
|
pub fn expect(self: *RuntimeEnv, value: ?*RuntimeRef) RuntimeError!*RuntimeRef {
|
||||||
return value orelse self.raise(error.TypeMismatch, "nil reference");
|
return value orelse self.raise(error.TypeMismatch, "nil reference", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *RuntimeEnv) void {
|
pub fn free(self: *RuntimeEnv) void {
|
||||||
|
@ -194,16 +220,8 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
||||||
return switch (indexable.object().payload) {
|
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: {
|
.vector2 => |vector2| swizzle: {
|
||||||
const swizzle_symbol = try self.unbox_symbol(index);
|
const swizzle_symbol = try self.unwrap_symbol(index);
|
||||||
var swizzle_buffer = [_]f32{0} ** 3;
|
var swizzle_buffer = [_]f32{0} ** 3;
|
||||||
var swizzle_count = @as(usize, 0);
|
var swizzle_count = @as(usize, 0);
|
||||||
|
|
||||||
|
@ -228,7 +246,7 @@ pub const RuntimeEnv = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
.vector3 => |vector3| swizzle: {
|
.vector3 => |vector3| swizzle: {
|
||||||
const swizzle_symbol = try self.unbox_symbol(index);
|
const swizzle_symbol = try self.unwrap_symbol(index);
|
||||||
var swizzle_buffer = [_]f32{0} ** 3;
|
var swizzle_buffer = [_]f32{0} ** 3;
|
||||||
var swizzle_count = @as(usize, 0);
|
var swizzle_count = @as(usize, 0);
|
||||||
|
|
||||||
|
@ -254,14 +272,22 @@ pub const RuntimeEnv = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index),
|
.dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index),
|
||||||
|
|
||||||
|
else => self.raise(error.TypeMismatch, "{typename} is not get-indexable", .{
|
||||||
|
.typename = indexable.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
||||||
|
const file_name = file_path.get_string();
|
||||||
|
|
||||||
var chunk = make_chunk: {
|
var chunk = make_chunk: {
|
||||||
const file_data =
|
const file_data =
|
||||||
(try file.allocate_and_load(self.allocator, self.options.import_access, file_path)) orelse {
|
(try file.allocate_and_load(self.allocator, self.options.import_access, file_path)) orelse {
|
||||||
return self.raise(error.BadOperation, "failed to open or read file specified");
|
return self.raise(error.BadOperation, "failed to open or read `{name}`", .{
|
||||||
|
.name = file_name,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
defer self.allocator.deallocate(file_data);
|
defer self.allocator.deallocate(file_data);
|
||||||
|
@ -282,24 +308,21 @@ pub const RuntimeEnv = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
break: make_chunk try Chunk.make(self, file_path.to_string() orelse "<script>", &root.environment);
|
const chunk_name = try self.new_string(file_name);
|
||||||
|
|
||||||
|
defer self.discard(chunk_name);
|
||||||
|
|
||||||
|
break: make_chunk try Chunk.make(self, chunk_name, &root.environment);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (builtin.mode == .Debug) {
|
|
||||||
const string_ref = try chunk.dump(self);
|
|
||||||
|
|
||||||
defer self.discard(string_ref);
|
|
||||||
|
|
||||||
const string = string_ref.as_string();
|
|
||||||
|
|
||||||
coral.debug.assert(string != null);
|
|
||||||
app.log_info(string.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
defer chunk.free(self);
|
defer chunk.free(self);
|
||||||
|
|
||||||
return execute_chunk: {
|
return execute_chunk: {
|
||||||
const frame = try self.push_frame(0);
|
const name = try self.new_string(file_name);
|
||||||
|
|
||||||
|
defer self.discard(name);
|
||||||
|
|
||||||
|
const frame = try self.push_frame(name, 0);
|
||||||
|
|
||||||
defer self.pop_frame();
|
defer self.pop_frame();
|
||||||
|
|
||||||
|
@ -329,16 +352,24 @@ pub const RuntimeEnv = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) * rhs_float),
|
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) * rhs_float),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not multiplicable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not multiplicable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |lhs_float| switch (rhs.object().payload) {
|
.float => |lhs_float| switch (rhs.object().payload) {
|
||||||
.float => |rhs_float| self.new_float(lhs_float * rhs_float),
|
.float => |rhs_float| self.new_float(lhs_float * rhs_float),
|
||||||
.fixed => |rhs_fixed| self.new_float(lhs_float * @as(Float, @floatFromInt(rhs_fixed))),
|
.fixed => |rhs_fixed| self.new_float(lhs_float * @as(Float, @floatFromInt(rhs_fixed))),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not multiplicable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not multiplicable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => self.raise(error.TypeMismatch, "left-hand object is not multiplicable"),
|
else => self.raise(error.TypeMismatch, "left-hand {typename} is not multiplicable", .{
|
||||||
|
.typename = lhs.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +377,7 @@ pub const RuntimeEnv = struct {
|
||||||
return switch (value.object().payload) {
|
return switch (value.object().payload) {
|
||||||
.fixed => |fixed| self.new_fixed(-fixed),
|
.fixed => |fixed| self.new_fixed(-fixed),
|
||||||
.float => |float| self.new_float(-float),
|
.float => |float| self.new_float(-float),
|
||||||
else => self.raise(error.TypeMismatch, "object is not negatable"),
|
else => self.raise(error.TypeMismatch, "{typename} is not negatable", .{.typename = value.typename()}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,11 +388,20 @@ pub const RuntimeEnv = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_boxed(self: *RuntimeEnv, value: ?*const RuntimeRef) RuntimeError!*RuntimeRef {
|
||||||
|
return RuntimeRef.allocate(self.allocator, .{
|
||||||
|
.ref_count = 1,
|
||||||
|
.payload = .{.boxed = if (value) |ref| ref.acquire() else null},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_dynamic(
|
pub fn new_dynamic(
|
||||||
self: *RuntimeEnv,
|
self: *RuntimeEnv,
|
||||||
userdata: [*]const coral.io.Byte,
|
userdata: []const coral.io.Byte,
|
||||||
typeinfo: *const Typeinfo,
|
typeinfo: *const Typeinfo,
|
||||||
) RuntimeError!*RuntimeRef {
|
) RuntimeError!*RuntimeRef {
|
||||||
|
coral.debug.assert(userdata.len == typeinfo.size);
|
||||||
|
|
||||||
const dynamic = try self.allocator.reallocate(null, @sizeOf(usize) + typeinfo.size);
|
const dynamic = try self.allocator.reallocate(null, @sizeOf(usize) + typeinfo.size);
|
||||||
|
|
||||||
errdefer self.allocator.deallocate(dynamic);
|
errdefer self.allocator.deallocate(dynamic);
|
||||||
|
@ -440,7 +480,7 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
errdefer table.free(self);
|
errdefer table.free(self);
|
||||||
|
|
||||||
return try self.new_dynamic(coral.io.bytes_of(&table).ptr, Table.typeinfo);
|
return try self.new_dynamic(coral.io.bytes_of(&table), Table.typeinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vector2(self: *RuntimeEnv, x: f32, y: f32) RuntimeError!*RuntimeRef {
|
pub fn new_vector2(self: *RuntimeEnv, x: f32, y: f32) RuntimeError!*RuntimeRef {
|
||||||
|
@ -470,23 +510,33 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_frame(self: *RuntimeEnv) void {
|
pub fn pop_frame(self: *RuntimeEnv) void {
|
||||||
var to_pop = self.locals.values.len - (self.frames.pop() orelse unreachable).locals_top;
|
const popped_frame = self.frames.pop();
|
||||||
|
|
||||||
while (to_pop != 0) {
|
coral.debug.assert(popped_frame != null);
|
||||||
if (self.locals.pop() orelse unreachable) |local| {
|
self.discard(popped_frame.?.name_stringable);
|
||||||
|
|
||||||
|
var locals_to_pop = self.locals.values.len - popped_frame.?.locals_top;
|
||||||
|
|
||||||
|
while (locals_to_pop != 0) {
|
||||||
|
const popped_local = self.locals.pop();
|
||||||
|
|
||||||
|
coral.debug.assert(popped_local != null);
|
||||||
|
|
||||||
|
if (popped_local.?) |local| {
|
||||||
self.discard(local);
|
self.discard(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
to_pop -= 1;
|
locals_to_pop -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_local(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
pub fn pop_local(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
return self.locals.pop() orelse self.raise(error.IllegalState, "stack underflow");
|
return self.locals.pop() orelse self.raise(error.IllegalState, "stack underflow", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_frame(self: *RuntimeEnv, arg_count: u8) RuntimeError!Frame {
|
pub fn push_frame(self: *RuntimeEnv, name_stringable: *RuntimeRef, arg_count: u8) RuntimeError!Frame {
|
||||||
const frame = Frame{
|
const frame = Frame{
|
||||||
|
.name_stringable = name_stringable.acquire(),
|
||||||
.arg_count = arg_count,
|
.arg_count = arg_count,
|
||||||
.locals_top = self.locals.values.len - arg_count,
|
.locals_top = self.locals.values.len - arg_count,
|
||||||
};
|
};
|
||||||
|
@ -496,8 +546,14 @@ pub const RuntimeEnv = struct {
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
|
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, comptime format: []const coral.io.Byte, args: anytype) RuntimeError {
|
||||||
self.print_error(message);
|
{
|
||||||
|
const formatted_message = try coral.utf8.alloc_formatted(self.allocator, format, args);
|
||||||
|
|
||||||
|
defer self.allocator.deallocate(formatted_message);
|
||||||
|
|
||||||
|
self.print_error(formatted_message);
|
||||||
|
}
|
||||||
|
|
||||||
if (!self.frames.is_empty()) {
|
if (!self.frames.is_empty()) {
|
||||||
self.print_error("stack trace:");
|
self.print_error("stack trace:");
|
||||||
|
@ -507,7 +563,17 @@ pub const RuntimeEnv = struct {
|
||||||
while (remaining_frames != 0) {
|
while (remaining_frames != 0) {
|
||||||
remaining_frames -= 1;
|
remaining_frames -= 1;
|
||||||
|
|
||||||
self.print_error(self.frames.values[remaining_frames].name);
|
const callable_string = try self.to_string(self.frames.values[remaining_frames].name_stringable);
|
||||||
|
|
||||||
|
defer self.discard(callable_string);
|
||||||
|
|
||||||
|
self.print_error(get_name: {
|
||||||
|
const string = callable_string.as_string();
|
||||||
|
|
||||||
|
coral.debug.assert(string != null);
|
||||||
|
|
||||||
|
break: get_name string.?;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,7 +583,10 @@ pub const RuntimeEnv = struct {
|
||||||
pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
|
pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
|
||||||
return switch (indexable.object().payload) {
|
return switch (indexable.object().payload) {
|
||||||
.dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value),
|
.dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value),
|
||||||
else => self.raise(error.TypeMismatch, "object is not set-indexable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "{typename} is not set-indexable", .{
|
||||||
|
.typename = indexable.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,16 +602,24 @@ pub const RuntimeEnv = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) - rhs_float),
|
.float => |rhs_float| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) - rhs_float),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not subtractable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not subtractable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
.float => |lhs_float| switch (rhs.object().payload) {
|
.float => |lhs_float| switch (rhs.object().payload) {
|
||||||
.float => |rhs_float| self.new_float(lhs_float - rhs_float),
|
.float => |rhs_float| self.new_float(lhs_float - rhs_float),
|
||||||
.fixed => |rhs_fixed| self.new_float(lhs_float - @as(Float, @floatFromInt(rhs_fixed))),
|
.fixed => |rhs_fixed| self.new_float(lhs_float - @as(Float, @floatFromInt(rhs_fixed))),
|
||||||
else => self.raise(error.TypeMismatch, "right-hand object is not subtractable"),
|
|
||||||
|
else => self.raise(error.TypeMismatch, "right-hand {typename} is not subtractable", .{
|
||||||
|
.typename = rhs.typename(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => self.raise(error.TypeMismatch, "left-hand object is not subtractable"),
|
else => self.raise(error.TypeMismatch, "left-hand {typename} is not subtractable", .{
|
||||||
|
.typename = lhs.typename(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,6 +650,7 @@ pub const RuntimeEnv = struct {
|
||||||
break: convert self.new_string(string[0 .. length.?]);
|
break: convert self.new_string(string[0 .. length.?]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.boxed => |boxed| if (boxed) |boxed_value| self.to_string(boxed_value) else self.new_string("nil"),
|
||||||
.symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
|
.symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
|
||||||
.string => value.acquire(),
|
.string => value.acquire(),
|
||||||
|
|
||||||
|
@ -580,7 +658,7 @@ pub const RuntimeEnv = struct {
|
||||||
var string = [_:0]coral.io.Byte{0} ** 64;
|
var string = [_:0]coral.io.Byte{0} ** 64;
|
||||||
var buffer = coral.io.FixedBuffer{.bytes = &string};
|
var buffer = coral.io.FixedBuffer{.bytes = &string};
|
||||||
|
|
||||||
const length = coral.utf8.print_formatted(buffer.as_writer(), "vec3({x}, {y})", .{
|
const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec2({x}, {y})", .{
|
||||||
.x = vector2[0],
|
.x = vector2[0],
|
||||||
.y = vector2[1],
|
.y = vector2[1],
|
||||||
});
|
});
|
||||||
|
@ -594,7 +672,7 @@ pub const RuntimeEnv = struct {
|
||||||
var string = [_:0]coral.io.Byte{0} ** 96;
|
var string = [_:0]coral.io.Byte{0} ** 96;
|
||||||
var buffer = coral.io.FixedBuffer{.bytes = &string};
|
var buffer = coral.io.FixedBuffer{.bytes = &string};
|
||||||
|
|
||||||
const length = coral.utf8.print_formatted(buffer.as_writer(), "vec3({x}, {y}, {z})", .{
|
const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec3({x}, {y}, {z})", .{
|
||||||
.x = vector3[0],
|
.x = vector3[0],
|
||||||
.y = vector3[1],
|
.y = vector3[1],
|
||||||
.z = vector3[2],
|
.z = vector3[2],
|
||||||
|
@ -610,34 +688,36 @@ pub const RuntimeEnv = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_dynamic(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![]coral.io.Byte {
|
pub fn unwrap_dynamic(self: *RuntimeEnv, value: *const RuntimeRef, typeinfo: *const Typeinfo) RuntimeError![]coral.io.Byte {
|
||||||
return switch (value.object().payload) {
|
return value.as_dynamic(typeinfo) orelse self.raise(error.TypeMismatch, "expected dynamic object, not {typename}", .{
|
||||||
.dynamic => |dynamic| dynamic.userdata(),
|
.typename = value.typename(),
|
||||||
else => self.raise(error.TypeMismatch, "expected fixed object")
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_float(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Float {
|
pub fn unwrap_float(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Float {
|
||||||
return switch (value.object().payload) {
|
return switch (value.object().payload) {
|
||||||
.fixed => |fixed| @floatFromInt(fixed),
|
.fixed => |fixed| @floatFromInt(fixed),
|
||||||
.float => |float| float,
|
.float => |float| float,
|
||||||
else => self.raise(error.TypeMismatch, "expected float object")
|
else => self.raise(error.TypeMismatch, "expected float, not {typename}", .{.typename = value.typename()}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_fixed(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Fixed {
|
pub fn unwrap_fixed(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Fixed {
|
||||||
return value.as_fixed() orelse self.raise(error.TypeMismatch, "expected fixed object");
|
return value.as_fixed() orelse self.raise(error.TypeMismatch, "expected fixed, not {typename}", .{
|
||||||
|
.typename = value.typename(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_string(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
pub fn unwrap_string(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
||||||
return value.as_string() orelse self.raise(error.TypeMismatch, "expected string object");
|
return value.as_string() orelse self.raise(error.TypeMismatch, "expected string, not {typename}", .{
|
||||||
|
.typename = value.typename(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_symbol(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![*:0]const coral.io.Byte {
|
pub fn unwrap_symbol(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![*:0]const coral.io.Byte {
|
||||||
return switch (value.object().payload) {
|
return value.as_symbol() orelse self.raise(error.TypeMismatch, "expected symbol, not {typename}", .{
|
||||||
.symbol => |symbol| symbol,
|
.typename = value.typename(),
|
||||||
else => self.raise(error.TypeMismatch, "expected symbol object")
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -661,6 +741,7 @@ pub const RuntimeRef = opaque {
|
||||||
vector2: [2]f32,
|
vector2: [2]f32,
|
||||||
vector3: [3]f32,
|
vector3: [3]f32,
|
||||||
syscall: *const Syscall,
|
syscall: *const Syscall,
|
||||||
|
boxed: ?*RuntimeRef,
|
||||||
|
|
||||||
string: struct {
|
string: struct {
|
||||||
ptr: [*]coral.io.Byte,
|
ptr: [*]coral.io.Byte,
|
||||||
|
@ -712,9 +793,23 @@ pub const RuntimeRef = opaque {
|
||||||
return @ptrCast(try coral.io.allocate_one(allocator, data));
|
return @ptrCast(try coral.io.allocate_one(allocator, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_boxed(self: *const RuntimeRef) ?*?*RuntimeRef {
|
||||||
|
return switch (self.object().payload) {
|
||||||
|
.boxed => |*boxed| boxed,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_dynamic(self: *const RuntimeRef, typeinfo: *const Typeinfo) ?[]u8 {
|
||||||
|
return switch (self.object().payload) {
|
||||||
|
.dynamic => |dynamic| if (dynamic.typeinfo() == typeinfo) dynamic.userdata() else null,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_fixed(self: *const RuntimeRef) ?Fixed {
|
pub fn as_fixed(self: *const RuntimeRef) ?Fixed {
|
||||||
return switch (self.object().payload) {
|
return switch (self.object().payload) {
|
||||||
.fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))),
|
.fixed => |fixed| fixed,
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -727,6 +822,14 @@ pub const RuntimeRef = opaque {
|
||||||
break: get string.ptr[0 .. @intCast(string.len)];
|
break: get string.ptr[0 .. @intCast(string.len)];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.symbol => |symbol| coral.io.slice_sentineled(@as(u8, 0), symbol),
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_symbol(self: *const RuntimeRef) ?[*:0]const coral.io.Byte {
|
||||||
|
return switch (self.object().payload) {
|
||||||
|
.symbol => |symbol| symbol,
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -757,6 +860,18 @@ pub const RuntimeRef = opaque {
|
||||||
else => false,
|
else => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.boxed => |boxed| unbox: {
|
||||||
|
if (boxed) |boxed_value| {
|
||||||
|
break: unbox boxed_value.equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other.as_boxed()) |boxed_value| {
|
||||||
|
break: unbox boxed_value.* == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
break: unbox false;
|
||||||
|
},
|
||||||
|
|
||||||
.vector2 => |self_vector| switch (other.object().payload) {
|
.vector2 => |self_vector| switch (other.object().payload) {
|
||||||
.vector2 => |other_vector| coral.io.are_equal(coral.io.bytes_of(&self_vector), coral.io.bytes_of(&other_vector)),
|
.vector2 => |other_vector| coral.io.are_equal(coral.io.bytes_of(&self_vector), coral.io.bytes_of(&other_vector)),
|
||||||
else => false,
|
else => false,
|
||||||
|
@ -797,6 +912,7 @@ pub const RuntimeRef = opaque {
|
||||||
.vector2 => |vector| @bitCast(vector),
|
.vector2 => |vector| @bitCast(vector),
|
||||||
.vector3 => |vector| coral.io.jenkins_hash(@typeInfo(usize).Int, coral.io.bytes_of(&vector)),
|
.vector3 => |vector| coral.io.jenkins_hash(@typeInfo(usize).Int, coral.io.bytes_of(&vector)),
|
||||||
.syscall => |syscall| @intFromPtr(syscall),
|
.syscall => |syscall| @intFromPtr(syscall),
|
||||||
|
.boxed => |boxed| @intFromPtr(boxed),
|
||||||
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()),
|
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()),
|
||||||
.dynamic => |dynamic| @intFromPtr(dynamic.typeinfo()) ^ @intFromPtr(dynamic.userdata().ptr),
|
.dynamic => |dynamic| @intFromPtr(dynamic.typeinfo()) ^ @intFromPtr(dynamic.userdata().ptr),
|
||||||
};
|
};
|
||||||
|
@ -812,6 +928,7 @@ pub const RuntimeRef = opaque {
|
||||||
.vector2 => |vector| coral.io.all_equals(coral.io.bytes_of(&vector), 0),
|
.vector2 => |vector| coral.io.all_equals(coral.io.bytes_of(&vector), 0),
|
||||||
.vector3 => |vector| coral.io.all_equals(coral.io.bytes_of(&vector), 0),
|
.vector3 => |vector| coral.io.all_equals(coral.io.bytes_of(&vector), 0),
|
||||||
.syscall => true,
|
.syscall => true,
|
||||||
|
.boxed => |boxed| boxed != null,
|
||||||
.string => |string| string.len != 0,
|
.string => |string| string.len != 0,
|
||||||
.dynamic => true,
|
.dynamic => true,
|
||||||
};
|
};
|
||||||
|
@ -827,6 +944,7 @@ pub const RuntimeRef = opaque {
|
||||||
.vector2 => "vector2",
|
.vector2 => "vector2",
|
||||||
.vector3 => "vector3",
|
.vector3 => "vector3",
|
||||||
.syscall => "syscall",
|
.syscall => "syscall",
|
||||||
|
.boxed => "boxed",
|
||||||
.string => "string",
|
.string => "string",
|
||||||
.dynamic => "dynamic",
|
.dynamic => "dynamic",
|
||||||
};
|
};
|
||||||
|
@ -844,18 +962,24 @@ pub const Typeinfo = struct {
|
||||||
set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
||||||
|
|
||||||
fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef {
|
fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef {
|
||||||
return env.raise(error.BadOperation, "object is not callable");
|
return env.raise(error.BadOperation, "this dynamic object is not callable", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
||||||
return env.raise(error.BadOperation, "object is not get-indexable");
|
return env.raise(error.BadOperation, "this dynamic object is not get-indexable", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
|
fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
|
||||||
return env.raise(error.BadOperation, "object is not set-indexable");
|
return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn assert(env: *RuntimeEnv, condition: bool) RuntimeError!void {
|
||||||
|
if (!condition) {
|
||||||
|
return env.raise(error.IllegalState, "assertion", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
||||||
const field_symbol = try env.new_symbol(field);
|
const field_symbol = try env.new_symbol(field);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
const app = @import("../app.zig");
|
||||||
|
|
||||||
const Compiler = @import("./Compiler.zig");
|
const Compiler = @import("./Compiler.zig");
|
||||||
|
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const file = @import("../file.zig");
|
const file = @import("../file.zig");
|
||||||
|
@ -12,6 +16,7 @@ name: *kym.RuntimeRef,
|
||||||
arity: u8,
|
arity: u8,
|
||||||
opcodes: OpcodeList,
|
opcodes: OpcodeList,
|
||||||
constants: ConstList,
|
constants: ConstList,
|
||||||
|
bindings: []?*kym.RuntimeRef,
|
||||||
|
|
||||||
const Builtin = enum {
|
const Builtin = enum {
|
||||||
import,
|
import,
|
||||||
|
@ -23,6 +28,7 @@ const Builtin = enum {
|
||||||
const ConstList = coral.list.Stack(*kym.RuntimeRef);
|
const ConstList = coral.list.Stack(*kym.RuntimeRef);
|
||||||
|
|
||||||
const OpcodeList = coral.list.Stack(union (enum) {
|
const OpcodeList = coral.list.Stack(union (enum) {
|
||||||
|
ret,
|
||||||
pop,
|
pop,
|
||||||
push_nil,
|
push_nil,
|
||||||
push_true,
|
push_true,
|
||||||
|
@ -32,13 +38,15 @@ const OpcodeList = coral.list.Stack(union (enum) {
|
||||||
push_top,
|
push_top,
|
||||||
push_table: u32,
|
push_table: u32,
|
||||||
push_builtin: Builtin,
|
push_builtin: Builtin,
|
||||||
push_closure: u8,
|
push_binding: u8,
|
||||||
push_self,
|
|
||||||
push_boxed,
|
push_boxed,
|
||||||
set_local: u8,
|
set_local: u8,
|
||||||
get_dynamic,
|
get_dynamic,
|
||||||
set_dynamic,
|
set_dynamic,
|
||||||
|
get_box,
|
||||||
|
set_box,
|
||||||
call: u8,
|
call: u8,
|
||||||
|
bind: u8,
|
||||||
|
|
||||||
not,
|
not,
|
||||||
neg,
|
neg,
|
||||||
|
@ -67,23 +75,21 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
||||||
defer buffer.free();
|
defer buffer.free();
|
||||||
|
|
||||||
const writer = coral.list.stack_as_writer(&buffer);
|
const writer = coral.list.stack_as_writer(&buffer);
|
||||||
_ = coral.utf8.print_string(writer, coral.io.slice_sentineled(@as(coral.io.Byte, 0), try env.unbox_symbol(chunk.name)));
|
|
||||||
|
|
||||||
_ = coral.utf8.print_string(writer, ":\n");
|
_ = coral.utf8.print_string(writer, "\n");
|
||||||
|
|
||||||
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
||||||
_ = coral.utf8.print_formatted(writer, "[{instruction}]: ", .{.instruction = opcode_cursor});
|
_ = coral.utf8.print_formatted(writer, "[{instruction}]: ", .{.instruction = opcode_cursor});
|
||||||
|
|
||||||
_ = switch (chunk.opcodes.values[opcode_cursor]) {
|
_ = switch (chunk.opcodes.values[opcode_cursor]) {
|
||||||
|
.ret => coral.utf8.print_string(writer, "ret\n"),
|
||||||
.pop => coral.utf8.print_string(writer, "pop\n"),
|
.pop => coral.utf8.print_string(writer, "pop\n"),
|
||||||
.push_nil => coral.utf8.print_string(writer, "push nil\n"),
|
.push_nil => coral.utf8.print_string(writer, "push nil\n"),
|
||||||
.push_true => coral.utf8.print_string(writer, "push true\n"),
|
.push_true => coral.utf8.print_string(writer, "push true\n"),
|
||||||
.push_false => coral.utf8.print_string(writer, "push false\n"),
|
.push_false => coral.utf8.print_string(writer, "push false\n"),
|
||||||
|
|
||||||
.push_const => |push_const| print: {
|
.push_const => |push_const| print: {
|
||||||
if (push_const >= chunk.constants.values.len) {
|
try kym.assert(env, push_const < chunk.constants.values.len);
|
||||||
return env.raise(error.IllegalState, "invalid constant");
|
|
||||||
}
|
|
||||||
|
|
||||||
const string_ref = try env.to_string(chunk.constants.values[push_const]);
|
const string_ref = try env.to_string(chunk.constants.values[push_const]);
|
||||||
|
|
||||||
|
@ -107,10 +113,9 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.push_boxed => coral.utf8.print_string(writer, "push boxed\n"),
|
.push_boxed => coral.utf8.print_string(writer, "push boxed\n"),
|
||||||
.push_self => coral.utf8.print_string(writer, "push self\n"),
|
|
||||||
|
|
||||||
.push_closure => |push_closure| coral.utf8.print_formatted(writer, "push closure <{count}>\n", .{
|
.push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding <{binding}>\n", .{
|
||||||
.count = push_closure,
|
.binding = push_binding,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.push_builtin => |push_builtin| coral.utf8.print_formatted(writer, "push builtin <{builtin}>\n", .{
|
.push_builtin => |push_builtin| coral.utf8.print_formatted(writer, "push builtin <{builtin}>\n", .{
|
||||||
|
@ -122,7 +127,16 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.set_local => |local_set| coral.utf8.print_formatted(writer, "set local <{local}>\n", .{.local = local_set}),
|
.bind => |bind| coral.utf8.print_formatted(writer, "bind <{count}>\n", .{
|
||||||
|
.count = bind,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.set_local => |local_set| coral.utf8.print_formatted(writer, "set local <{local}>\n", .{
|
||||||
|
.local = local_set,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.get_box => coral.utf8.print_string(writer, "get box\n"),
|
||||||
|
.set_box => coral.utf8.print_string(writer, "set box\n"),
|
||||||
.get_dynamic => coral.utf8.print_string(writer, "get dynamic\n"),
|
.get_dynamic => coral.utf8.print_string(writer, "get dynamic\n"),
|
||||||
.set_dynamic => coral.utf8.print_string(writer, "set dynamic\n"),
|
.set_dynamic => coral.utf8.print_string(writer, "set dynamic\n"),
|
||||||
.call => |call| coral.utf8.print_formatted(writer, "call <{count}>\n", .{.count = call}),
|
.call => |call| coral.utf8.print_formatted(writer, "call <{count}>\n", .{.count = call}),
|
||||||
|
@ -145,11 +159,13 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
||||||
return env.new_string(buffer.values);
|
return env.new_string(buffer.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
var opcode_cursor = @as(u32, 0);
|
var opcode_cursor = @as(u32, 0);
|
||||||
|
|
||||||
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
|
||||||
switch (chunk.opcodes.values[opcode_cursor]) {
|
switch (self.opcodes.values[opcode_cursor]) {
|
||||||
|
.ret => break,
|
||||||
|
|
||||||
.pop => {
|
.pop => {
|
||||||
if (try env.pop_local()) |ref| {
|
if (try env.pop_local()) |ref| {
|
||||||
env.discard(ref);
|
env.discard(ref);
|
||||||
|
@ -161,17 +177,12 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
||||||
.push_false => try env.locals.push_one(try env.new_boolean(false)),
|
.push_false => try env.locals.push_one(try env.new_boolean(false)),
|
||||||
|
|
||||||
.push_const => |push_const| {
|
.push_const => |push_const| {
|
||||||
if (push_const >= chunk.constants.values.len) {
|
try kym.assert(env, push_const < self.constants.values.len);
|
||||||
return env.raise(error.IllegalState, "invalid constant");
|
try env.locals.push_one(self.constants.values[push_const].acquire());
|
||||||
}
|
|
||||||
|
|
||||||
try env.locals.push_one(chunk.constants.values[push_const].acquire());
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_local => |push_local| {
|
.push_local => |push_local| {
|
||||||
if (push_local >= env.locals.values.len) {
|
try kym.assert(env, push_local < (env.locals.values.len - frame.locals_top));
|
||||||
return env.raise(error.IllegalState, "invalid local");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.locals.values[frame.locals_top + push_local]) |local| {
|
if (env.locals.values[frame.locals_top + push_local]) |local| {
|
||||||
try env.locals.push_one(local.acquire());
|
try env.locals.push_one(local.acquire());
|
||||||
|
@ -183,9 +194,7 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
||||||
.push_top => {
|
.push_top => {
|
||||||
const frame_locals = env.locals.values[frame.locals_top ..];
|
const frame_locals = env.locals.values[frame.locals_top ..];
|
||||||
|
|
||||||
if (frame_locals.len == 0) {
|
try kym.assert(env, frame_locals.len != 0);
|
||||||
return env.raise(error.IllegalState, "stack overflow");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame_locals[frame_locals.len - 1]) |local| {
|
if (frame_locals[frame_locals.len - 1]) |local| {
|
||||||
try env.locals.push_one(local.acquire());
|
try env.locals.push_one(local.acquire());
|
||||||
|
@ -219,19 +228,54 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_boxed => {
|
.push_boxed => {
|
||||||
// TODO: Implement.
|
const value = try env.pop_local();
|
||||||
unreachable;
|
|
||||||
|
defer {
|
||||||
|
if (value) |ref| {
|
||||||
|
env.discard(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxable = try env.new_boxed(value);
|
||||||
|
|
||||||
|
errdefer env.discard(boxable);
|
||||||
|
|
||||||
|
try env.locals.push_one(boxable);
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_self => {
|
.push_binding => |push_binding| {
|
||||||
// TODO: Implement.
|
try kym.assert(env, push_binding <= self.bindings.len);
|
||||||
unreachable;
|
try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null);
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_closure => |push_closure| {
|
.bind => |bind| {
|
||||||
// TODO: Implement.
|
const callable = try env.expect(try env.pop_local());
|
||||||
_ = push_closure;
|
|
||||||
unreachable;
|
errdefer env.discard(callable);
|
||||||
|
|
||||||
|
const chunk = @as(*Self, @ptrCast(@alignCast(try env.unwrap_dynamic(callable, typeinfo))));
|
||||||
|
|
||||||
|
chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*kym.RuntimeRef, null));
|
||||||
|
|
||||||
|
for (0 .. bind) |index| {
|
||||||
|
const value = try env.pop_local();
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
if (value) |ref| {
|
||||||
|
env.discard(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const binding = &chunk.bindings[index];
|
||||||
|
|
||||||
|
if (binding.*) |*existing_binding| {
|
||||||
|
env.discard(existing_binding.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.* = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
try env.locals.push_one(callable);
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_builtin => |push_builtin| {
|
.push_builtin => |push_builtin| {
|
||||||
|
@ -257,6 +301,34 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
||||||
local.* = try env.pop_local();
|
local.* = try env.pop_local();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.get_box => {
|
||||||
|
const box = try env.expect(try env.pop_local());
|
||||||
|
|
||||||
|
defer env.discard(box);
|
||||||
|
|
||||||
|
const boxed = box.as_boxed() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "{typename} is not unboxable", .{.typename = box.typename()});
|
||||||
|
};
|
||||||
|
|
||||||
|
try env.locals.push_one(if (boxed.*) |value| value.acquire() else null);
|
||||||
|
},
|
||||||
|
|
||||||
|
.set_box => {
|
||||||
|
const box = try env.expect(try env.pop_local());
|
||||||
|
|
||||||
|
errdefer env.discard(box);
|
||||||
|
|
||||||
|
const boxed = box.as_boxed() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "{typename} is not unboxable", .{.typename = box.typename()});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (boxed.*) |value| {
|
||||||
|
env.discard(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
boxed.* = box;
|
||||||
|
},
|
||||||
|
|
||||||
.get_dynamic => {
|
.get_dynamic => {
|
||||||
const index = try env.expect(try env.pop_local());
|
const index = try env.expect(try env.pop_local());
|
||||||
|
|
||||||
|
@ -286,15 +358,11 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = try env.pop_local() orelse {
|
const index = try env.expect(try env.pop_local());
|
||||||
return env.raise(error.TypeMismatch, "nil is not a valid index");
|
|
||||||
};
|
|
||||||
|
|
||||||
defer env.discard(index);
|
defer env.discard(index);
|
||||||
|
|
||||||
const indexable = try env.pop_local() orelse {
|
const indexable = try env.expect(try env.pop_local());
|
||||||
return env.raise(error.TypeMismatch, "nil is not a valid indexable");
|
|
||||||
};
|
|
||||||
|
|
||||||
defer env.discard(indexable);
|
defer env.discard(indexable);
|
||||||
|
|
||||||
|
@ -307,7 +375,7 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
||||||
|
|
||||||
defer env.discard(callable);
|
defer env.discard(callable);
|
||||||
|
|
||||||
const call_frame = try env.push_frame(call);
|
const call_frame = try env.push_frame(callable, call);
|
||||||
|
|
||||||
defer env.pop_frame();
|
defer env.pop_frame();
|
||||||
|
|
||||||
|
@ -494,14 +562,27 @@ pub fn free(self: *Self, env: *kym.RuntimeEnv) void {
|
||||||
self.constants.free();
|
self.constants.free();
|
||||||
self.opcodes.free();
|
self.opcodes.free();
|
||||||
env.discard(self.name);
|
env.discard(self.name);
|
||||||
|
|
||||||
|
if (self.bindings.len != 0) {
|
||||||
|
for (self.bindings) |binding| {
|
||||||
|
if (binding) |value| {
|
||||||
|
env.discard(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.allocator.deallocate(self.bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bindings = &.{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *const tree.Environment) kym.RuntimeError!Self {
|
pub fn make(env: *kym.RuntimeEnv, name: *const kym.RuntimeRef, environment: *const tree.Environment) kym.RuntimeError!Self {
|
||||||
var chunk = Self{
|
var chunk = Self{
|
||||||
.name = try env.new_symbol(name),
|
.name = name.acquire(),
|
||||||
.opcodes = OpcodeList.make(env.allocator),
|
.opcodes = OpcodeList.make(env.allocator),
|
||||||
.constants = ConstList.make(env.allocator),
|
.constants = ConstList.make(env.allocator),
|
||||||
.arity = 0,
|
.bindings = &.{},
|
||||||
|
.arity = environment.argument_count,
|
||||||
};
|
};
|
||||||
|
|
||||||
var compiler = Compiler{
|
var compiler = Compiler{
|
||||||
|
@ -515,33 +596,33 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syscall_import(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
fn syscall_import(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
return env.import(file.Path.from(&.{try env.unbox_string(try frame.get_arg(env, 0))}));
|
return env.import(file.Path.from(&.{try env.unwrap_string(try frame.expect_arg(env, 0))}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syscall_print(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
fn syscall_print(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
env.print(try env.unbox_string(try frame.get_arg(env, 0)));
|
env.print(try env.unwrap_string(try frame.expect_arg(env, 0)));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syscall_vec2(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
fn syscall_vec2(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
|
const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_arg(env, 0))));
|
||||||
|
|
||||||
if (frame.has_arg(env, 1)) |y| {
|
if (frame.has_arg(env, 1)) |y| {
|
||||||
return env.new_vector2(x, @floatCast(try env.unbox_float(y)));
|
return env.new_vector2(x, @floatCast(try env.unwrap_float(y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return env.new_vector2(x, x);
|
return env.new_vector2(x, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
|
const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_arg(env, 0))));
|
||||||
|
|
||||||
if (frame.has_arg(env, 1)) |y| {
|
if (frame.has_arg(env, 1)) |y| {
|
||||||
return env.new_vector3(
|
return env.new_vector3(
|
||||||
x,
|
x,
|
||||||
@floatCast(try env.unbox_float(y)),
|
@floatCast(try env.unwrap_float(y)),
|
||||||
@floatCast(try env.unbox_float(try frame.get_arg(env, 2))),
|
@floatCast(try env.unwrap_float(try frame.expect_arg(env, 2))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +640,11 @@ fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: kym.Fra
|
||||||
const chunk = @as(*Self, @ptrCast(@alignCast(userdata)));
|
const chunk = @as(*Self, @ptrCast(@alignCast(userdata)));
|
||||||
|
|
||||||
if (frame.arg_count < chunk.arity) {
|
if (frame.arg_count < chunk.arity) {
|
||||||
return env.raise(error.BadOperation, "expected more arguments");
|
return env.raise(error.BadOperation, "expected `{expected_count}` {noun}, {provided_count} provided", .{
|
||||||
|
.expected_count = frame.arg_count,
|
||||||
|
.provided_count = chunk.arity,
|
||||||
|
.noun = if (frame.arg_count == 1) "argument" else "arguments",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunk.execute(env, frame);
|
return chunk.execute(env, frame);
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn compile_argument(self: Self, environment: *const tree.Environment, initial_ar
|
||||||
var argument_count = @as(u8, 0);
|
var argument_count = @as(u8, 0);
|
||||||
|
|
||||||
while (maybe_argument) |argument| {
|
while (maybe_argument) |argument| {
|
||||||
try self.compile_expression(environment, argument);
|
try self.compile_expression(environment, argument, null);
|
||||||
|
|
||||||
maybe_argument = argument.next;
|
maybe_argument = argument.next;
|
||||||
argument_count += 1;
|
argument_count += 1;
|
||||||
|
@ -30,7 +30,7 @@ fn compile_argument(self: Self, environment: *const tree.Environment, initial_ar
|
||||||
return argument_count;
|
return argument_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_expression(self: Self, environment: *const tree.Environment, expression: *const Expr) kym.RuntimeError!void {
|
fn compile_expression(self: Self, environment: *const tree.Environment, expression: *const Expr, name: ?[]const coral.io.Byte) kym.RuntimeError!void {
|
||||||
const number_format = coral.utf8.DecimalFormat{
|
const number_format = coral.utf8.DecimalFormat{
|
||||||
.delimiter = "_",
|
.delimiter = "_",
|
||||||
.positive_prefix = .none,
|
.positive_prefix = .none,
|
||||||
|
@ -68,7 +68,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
var field_count = @as(u32, 0);
|
var field_count = @as(u32, 0);
|
||||||
|
|
||||||
while (table_entry) |entry| : (table_entry = entry.next) {
|
while (table_entry) |entry| : (table_entry = entry.next) {
|
||||||
try self.compile_expression(environment, entry);
|
try self.compile_expression(environment, entry, null);
|
||||||
|
|
||||||
if (entry.kind != .key_value) {
|
if (entry.kind != .key_value) {
|
||||||
try self.chunk.opcodes.push_one(.push_top);
|
try self.chunk.opcodes.push_one(.push_top);
|
||||||
|
@ -81,29 +81,42 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
},
|
},
|
||||||
|
|
||||||
.key_value => |key_value| {
|
.key_value => |key_value| {
|
||||||
try self.compile_expression(environment, key_value.value);
|
try self.compile_expression(environment, key_value.value, null);
|
||||||
try self.compile_expression(environment, key_value.key);
|
try self.compile_expression(environment, key_value.key, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
.lambda_construct => |lambda_construct| {
|
.lambda_construct => |lambda_construct| {
|
||||||
var chunk = try Chunk.make(self.env, "<lambda>", lambda_construct.environment);
|
const anonymous_chunk_name = try self.env.new_symbol(name orelse "<lambda>");
|
||||||
|
|
||||||
|
defer self.env.discard(anonymous_chunk_name);
|
||||||
|
|
||||||
|
var chunk = try Chunk.make(self.env, anonymous_chunk_name, lambda_construct.environment);
|
||||||
|
|
||||||
errdefer chunk.free(self.env);
|
errdefer chunk.free(self.env);
|
||||||
|
|
||||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)});
|
if (lambda_construct.environment.capture_count == 0) {
|
||||||
|
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)});
|
||||||
|
} else {
|
||||||
|
const lambda_captures = lambda_construct.environment.get_captures();
|
||||||
|
var index = lambda_captures.len;
|
||||||
|
|
||||||
if (lambda_construct.environment.capture_count != 0) {
|
while (index != 0) {
|
||||||
for (lambda_construct.environment.captures[0 .. lambda_construct.environment.capture_count]) |capture| {
|
index -= 1;
|
||||||
try self.chunk.opcodes.push_one(.{.push_local = environment.captures[capture]});
|
|
||||||
|
try self.chunk.opcodes.push_one(switch (lambda_captures[index]) {
|
||||||
|
.declaration_index => |declaration_index| .{.push_local = declaration_index},
|
||||||
|
.capture_index => |capture_index| .{.push_binding = capture_index},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.opcodes.push_one(.{.push_closure = lambda_construct.environment.capture_count});
|
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)});
|
||||||
|
try self.chunk.opcodes.push_one(.{.bind = lambda_construct.environment.capture_count});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.binary_op => |binary_op| {
|
.binary_op => |binary_op| {
|
||||||
try self.compile_expression(environment, binary_op.lhs_operand);
|
try self.compile_expression(environment, binary_op.lhs_operand, null);
|
||||||
try self.compile_expression(environment, binary_op.rhs_operand);
|
try self.compile_expression(environment, binary_op.rhs_operand, null);
|
||||||
|
|
||||||
try self.chunk.opcodes.push_one(switch (binary_op.operation) {
|
try self.chunk.opcodes.push_one(switch (binary_op.operation) {
|
||||||
.addition => .add,
|
.addition => .add,
|
||||||
|
@ -119,7 +132,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
},
|
},
|
||||||
|
|
||||||
.unary_op => |unary_op| {
|
.unary_op => |unary_op| {
|
||||||
try self.compile_expression(environment, unary_op.operand);
|
try self.compile_expression(environment, unary_op.operand, null);
|
||||||
|
|
||||||
try self.chunk.opcodes.push_one(switch (unary_op.operation) {
|
try self.chunk.opcodes.push_one(switch (unary_op.operation) {
|
||||||
.boolean_negation => .not,
|
.boolean_negation => .not,
|
||||||
|
@ -130,11 +143,11 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
.invoke => |invoke| {
|
.invoke => |invoke| {
|
||||||
const argument_count = try self.compile_argument(environment, invoke.argument);
|
const argument_count = try self.compile_argument(environment, invoke.argument);
|
||||||
|
|
||||||
try self.compile_expression(environment, invoke.object);
|
try self.compile_expression(environment, invoke.object, null);
|
||||||
try self.chunk.opcodes.push_one(.{.call = argument_count});
|
try self.chunk.opcodes.push_one(.{.call = argument_count});
|
||||||
},
|
},
|
||||||
|
|
||||||
.group => |group| try self.compile_expression(environment, group),
|
.group => |group| try self.compile_expression(environment, group, null),
|
||||||
.import_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .import}),
|
.import_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .import}),
|
||||||
.print_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .print}),
|
.print_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .print}),
|
||||||
.vec2_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .vec2}),
|
.vec2_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .vec2}),
|
||||||
|
@ -145,56 +158,63 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
return self.chunk.opcodes.push_one(.{.push_local = index});
|
return self.chunk.opcodes.push_one(.{.push_local = index});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_capture_index(environment, declaration_get.declaration)) |index| {
|
if (try self.get_binding_index(environment, declaration_get.declaration)) |index| {
|
||||||
try self.chunk.opcodes.push_one(.push_self);
|
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
||||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_fixed(index)});
|
|
||||||
|
|
||||||
return self.chunk.opcodes.push_one(.get_dynamic);
|
if (is_declaration_boxed(declaration_get.declaration)) {
|
||||||
|
try self.chunk.opcodes.push_one(.get_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.env.raise(error.IllegalState, "local out of scope");
|
return self.env.raise(error.IllegalState, "local out of scope", .{});
|
||||||
},
|
},
|
||||||
|
|
||||||
.declaration_set => |declaration_set| {
|
.declaration_set => |declaration_set| {
|
||||||
try self.compile_expression(environment, declaration_set.assign);
|
|
||||||
|
|
||||||
if (get_local_index(environment, declaration_set.declaration)) |index| {
|
if (get_local_index(environment, declaration_set.declaration)) |index| {
|
||||||
|
try self.compile_expression(environment, declaration_set.assign, null);
|
||||||
|
|
||||||
return self.chunk.opcodes.push_one(.{.set_local = index});
|
return self.chunk.opcodes.push_one(.{.set_local = index});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_capture_index(environment, declaration_set.declaration)) |index| {
|
if (try self.get_binding_index(environment, declaration_set.declaration)) |index| {
|
||||||
try self.chunk.opcodes.push_one(.push_self);
|
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
||||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_fixed(index)});
|
try self.compile_expression(environment, declaration_set.assign, null);
|
||||||
|
|
||||||
return self.chunk.opcodes.push_one(.set_dynamic);
|
if (is_declaration_boxed(declaration_set.declaration)) {
|
||||||
|
try self.chunk.opcodes.push_one(.set_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.env.raise(error.IllegalState, "local out of scope");
|
return self.env.raise(error.IllegalState, "local out of scope", .{});
|
||||||
},
|
},
|
||||||
|
|
||||||
.field_get => |field_get| {
|
.field_get => |field_get| {
|
||||||
try self.compile_expression(environment, field_get.object);
|
try self.compile_expression(environment, field_get.object, null);
|
||||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(field_get.identifier)});
|
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(field_get.identifier)});
|
||||||
try self.chunk.opcodes.push_one(.get_dynamic);
|
try self.chunk.opcodes.push_one(.get_dynamic);
|
||||||
},
|
},
|
||||||
|
|
||||||
.field_set => |field_set| {
|
.field_set => |field_set| {
|
||||||
try self.compile_expression(environment, field_set.object);
|
try self.compile_expression(environment, field_set.object, null);
|
||||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(field_set.identifier)});
|
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(field_set.identifier)});
|
||||||
try self.compile_expression(environment, field_set.assign);
|
try self.compile_expression(environment, field_set.assign, null);
|
||||||
try self.chunk.opcodes.push_one(.set_dynamic);
|
try self.chunk.opcodes.push_one(.set_dynamic);
|
||||||
},
|
},
|
||||||
|
|
||||||
.subscript_get => |subscript_get| {
|
.subscript_get => |subscript_get| {
|
||||||
try self.compile_expression(environment, subscript_get.object);
|
try self.compile_expression(environment, subscript_get.object, null);
|
||||||
try self.compile_expression(environment, subscript_get.index);
|
try self.compile_expression(environment, subscript_get.index, null);
|
||||||
try self.chunk.opcodes.push_one(.get_dynamic);
|
try self.chunk.opcodes.push_one(.get_dynamic);
|
||||||
},
|
},
|
||||||
|
|
||||||
.subscript_set => |subscript_set| {
|
.subscript_set => |subscript_set| {
|
||||||
try self.compile_expression(environment, subscript_set.object);
|
try self.compile_expression(environment, subscript_set.object, null);
|
||||||
try self.compile_expression(environment, subscript_set.index);
|
try self.compile_expression(environment, subscript_set.index, null);
|
||||||
try self.compile_expression(environment, subscript_set.assign);
|
try self.compile_expression(environment, subscript_set.assign, null);
|
||||||
try self.chunk.opcodes.push_one(.set_dynamic);
|
try self.chunk.opcodes.push_one(.set_dynamic);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -217,14 +237,17 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
|
||||||
switch (current_statement.kind) {
|
switch (current_statement.kind) {
|
||||||
.@"return" => |@"return"| {
|
.@"return" => |@"return"| {
|
||||||
if (@"return".returned_expression) |expression| {
|
if (@"return".returned_expression) |expression| {
|
||||||
try self.compile_expression(environment, expression);
|
try self.compile_expression(environment, expression, null);
|
||||||
} else {
|
} else {
|
||||||
try self.chunk.opcodes.push_one(.push_nil);
|
try self.chunk.opcodes.push_one(.push_nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Omit ret calls at ends of chunk.
|
||||||
|
try self.chunk.opcodes.push_one(.ret);
|
||||||
},
|
},
|
||||||
|
|
||||||
.@"while" => |@"while"| {
|
.@"while" => |@"while"| {
|
||||||
try self.compile_expression(environment, @"while".loop_expression);
|
try self.compile_expression(environment, @"while".loop_expression, null);
|
||||||
try self.chunk.opcodes.push_one(.{.jf = 0});
|
try self.chunk.opcodes.push_one(.{.jf = 0});
|
||||||
|
|
||||||
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
|
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
|
||||||
|
@ -232,12 +255,12 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
|
||||||
_ = try self.compile_statement(environment, @"while".loop);
|
_ = try self.compile_statement(environment, @"while".loop);
|
||||||
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
|
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
|
||||||
|
|
||||||
try self.compile_expression(environment, @"while".loop_expression);
|
try self.compile_expression(environment, @"while".loop_expression, null);
|
||||||
try self.chunk.opcodes.push_one(.{.jt = origin_index});
|
try self.chunk.opcodes.push_one(.{.jt = origin_index});
|
||||||
},
|
},
|
||||||
|
|
||||||
.@"if" => |@"if"| {
|
.@"if" => |@"if"| {
|
||||||
try self.compile_expression(environment, @"if".then_expression);
|
try self.compile_expression(environment, @"if".then_expression, null);
|
||||||
try self.chunk.opcodes.push_one(.{.jf = 0});
|
try self.chunk.opcodes.push_one(.{.jf = 0});
|
||||||
|
|
||||||
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
|
const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1));
|
||||||
|
@ -251,15 +274,15 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
|
||||||
},
|
},
|
||||||
|
|
||||||
.declare => |declare| {
|
.declare => |declare| {
|
||||||
try self.compile_expression(environment, declare.initial_expression);
|
try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier);
|
||||||
|
|
||||||
if (declare.declaration.is_captured and !declare.declaration.is_readonly) {
|
if (is_declaration_boxed(declare.declaration)) {
|
||||||
try self.chunk.opcodes.push_one(.push_boxed);
|
try self.chunk.opcodes.push_one(.push_boxed);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.top_expression => |top_expression| {
|
.top_expression => |top_expression| {
|
||||||
try self.compile_expression(environment, top_expression);
|
try self.compile_expression(environment, top_expression, null);
|
||||||
|
|
||||||
if (top_expression.kind == .invoke) {
|
if (top_expression.kind == .invoke) {
|
||||||
try self.chunk.opcodes.push_one(.pop);
|
try self.chunk.opcodes.push_one(.pop);
|
||||||
|
@ -271,12 +294,16 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const constants_max = @as(usize, coral.math.max_int(@typeInfo(u16).Int));
|
||||||
|
|
||||||
fn declare_chunk(self: Self, chunk: Chunk) kym.RuntimeError!u16 {
|
fn declare_chunk(self: Self, chunk: Chunk) kym.RuntimeError!u16 {
|
||||||
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
||||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = @as(usize, coral.math.max_int(@typeInfo(u16).Int)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const constant = try self.env.new_dynamic(coral.io.bytes_of(&chunk).ptr, Chunk.typeinfo);
|
const constant = try self.env.new_dynamic(coral.io.bytes_of(&chunk), Chunk.typeinfo);
|
||||||
|
|
||||||
errdefer self.env.discard(constant);
|
errdefer self.env.discard(constant);
|
||||||
|
|
||||||
|
@ -286,8 +313,10 @@ fn declare_chunk(self: Self, chunk: Chunk) kym.RuntimeError!u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_fixed(self: Self, fixed: kym.Fixed) kym.RuntimeError!u16 {
|
fn declare_fixed(self: Self, fixed: kym.Fixed) kym.RuntimeError!u16 {
|
||||||
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const constant = try self.env.new_fixed(fixed);
|
const constant = try self.env.new_fixed(fixed);
|
||||||
|
@ -300,8 +329,10 @@ fn declare_fixed(self: Self, fixed: kym.Fixed) kym.RuntimeError!u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_float(self: Self, float: kym.Float) kym.RuntimeError!u16 {
|
fn declare_float(self: Self, float: kym.Float) kym.RuntimeError!u16 {
|
||||||
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const constant = try self.env.new_float(float);
|
const constant = try self.env.new_float(float);
|
||||||
|
@ -314,8 +345,10 @@ fn declare_float(self: Self, float: kym.Float) kym.RuntimeError!u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_string(self: Self, string: []const coral.io.Byte) kym.RuntimeError!u16 {
|
fn declare_string(self: Self, string: []const coral.io.Byte) kym.RuntimeError!u16 {
|
||||||
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const constant = try self.env.new_string(string);
|
const constant = try self.env.new_string(string);
|
||||||
|
@ -328,8 +361,10 @@ fn declare_string(self: Self, string: []const coral.io.Byte) kym.RuntimeError!u1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_symbol(self: Self, symbol: []const coral.io.Byte) kym.RuntimeError!u16 {
|
fn declare_symbol(self: Self, symbol: []const coral.io.Byte) kym.RuntimeError!u16 {
|
||||||
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const constant = try self.env.new_symbol(symbol);
|
const constant = try self.env.new_symbol(symbol);
|
||||||
|
@ -341,30 +376,42 @@ fn declare_symbol(self: Self, symbol: []const coral.io.Byte) kym.RuntimeError!u1
|
||||||
return @intCast(self.chunk.constants.values.len - 1);
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_capture_index(self: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
|
pub fn get_binding_index(self: *const Self, environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 {
|
||||||
if (self.enclosing) |enclosing_environment| {
|
var binding_index = @as(u8, 0);
|
||||||
var capture_index = @as(u8, 0);
|
|
||||||
|
|
||||||
while (capture_index < self.capture_count) : (capture_index += 1) {
|
while (binding_index < environment.capture_count) : (binding_index += 1) {
|
||||||
if (&enclosing_environment.local_declarations[capture_index] == declaration) {
|
var capture = &environment.captures[binding_index];
|
||||||
return capture_index;
|
var target_environment = environment.enclosing orelse return null;
|
||||||
}
|
|
||||||
|
while (capture.* == .capture_index) {
|
||||||
|
capture = &target_environment.captures[capture.capture_index];
|
||||||
|
target_environment = target_environment.enclosing orelse return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try kym.assert(self.env, capture.* == .declaration_index);
|
||||||
|
|
||||||
|
if (&target_environment.declarations[capture.declaration_index] == declaration) {
|
||||||
|
return binding_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_index(self: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
|
pub fn get_local_index(environment: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
|
||||||
var remaining = self.local_declaration_count;
|
var remaining = environment.declaration_count;
|
||||||
|
|
||||||
while (remaining != 0) {
|
while (remaining != 0) {
|
||||||
remaining -= 1;
|
remaining -= 1;
|
||||||
|
|
||||||
if (&self.local_declarations[remaining] == declaration) {
|
if (&environment.declarations[remaining] == declaration) {
|
||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_declaration_boxed(declaration: *const tree.Declaration) bool {
|
||||||
|
return declaration.is.captured and !declaration.is.readonly;
|
||||||
|
}
|
||||||
|
|
|
@ -174,11 +174,17 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
||||||
|
|
||||||
return root.create_expr(.{
|
return root.create_expr(.{
|
||||||
.kind = switch (expression.kind) {
|
.kind = switch (expression.kind) {
|
||||||
.declaration_get => |declaration_get| .{
|
.declaration_get => |declaration_get| convert: {
|
||||||
.declaration_set = .{
|
if (declaration_get.declaration.is.readonly) {
|
||||||
.assign = try parse(root, stream, environment),
|
return root.report_error(stream, "readonly declarations cannot be re-assigned", .{});
|
||||||
.declaration = declaration_get.declaration,
|
}
|
||||||
},
|
|
||||||
|
break: convert .{
|
||||||
|
.declaration_set = .{
|
||||||
|
.assign = try parse(root, stream, environment),
|
||||||
|
.declaration = declaration_get.declaration,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
.field_get => |field_get| .{
|
.field_get => |field_get| .{
|
||||||
|
@ -414,15 +420,13 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
|
||||||
return root.create_expr(.{
|
return root.create_expr(.{
|
||||||
.next = null,
|
|
||||||
|
|
||||||
.kind = .{
|
.kind = .{
|
||||||
.declaration_get = .{
|
.declaration_get = .{
|
||||||
.declaration = (try environment.resolve_declaration(identifier)) orelse {
|
.declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||||
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -445,13 +449,9 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
||||||
else => return root.report_error(stream, "expected identifier", .{}),
|
else => return root.report_error(stream, "expected identifier", .{}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (try lambda_environment.declare(identifier) == null) {
|
_ = lambda_environment.declare_argument(identifier) catch |declare_error| {
|
||||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
return root.report_declare_error(stream, identifier, declare_error);
|
||||||
.identifier = identifier,
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
lambda_environment.argument_count += 1;
|
|
||||||
|
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
|
|
@ -93,18 +93,13 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
||||||
},
|
},
|
||||||
|
|
||||||
.keyword_var, .keyword_let => {
|
.keyword_var, .keyword_let => {
|
||||||
const storage_token = stream.token;
|
const is_constant = stream.token == .keyword_let;
|
||||||
|
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
|
||||||
const identifier = switch (stream.token) {
|
const identifier = switch (stream.token) {
|
||||||
.identifier => |identifier| identifier,
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream, "expected identifier after declaration", .{}),
|
||||||
else => {
|
|
||||||
return root.report_error(stream, "expected identifier after `{storage}` declaration statement", .{
|
|
||||||
.storage = storage_token.text()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
@ -122,10 +117,16 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
||||||
.declare = .{
|
.declare = .{
|
||||||
.initial_expression = try Expr.parse(root, stream, environment),
|
.initial_expression = try Expr.parse(root, stream, environment),
|
||||||
|
|
||||||
.declaration = (try root.environment.declare(identifier)) orelse {
|
.declaration = declare: {
|
||||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
if (is_constant) {
|
||||||
.identifier = identifier,
|
break: declare environment.declare_constant(identifier) catch |declaration_error| {
|
||||||
});
|
return root.report_declare_error(stream, identifier, declaration_error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break: declare environment.declare_variable(identifier) catch |declaration_error| {
|
||||||
|
return root.report_declare_error(stream, identifier, declaration_error);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,19 +8,31 @@ const tokens = @import("./tokens.zig");
|
||||||
|
|
||||||
pub const Declaration = struct {
|
pub const Declaration = struct {
|
||||||
identifier: []const coral.io.Byte,
|
identifier: []const coral.io.Byte,
|
||||||
is_readonly: bool = false,
|
|
||||||
is_captured: bool = false,
|
is: packed struct {
|
||||||
|
readonly: bool = false,
|
||||||
|
captured: bool = false,
|
||||||
|
} = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Environment = struct {
|
pub const Environment = struct {
|
||||||
captures: [capture_max]u8 = [_]u8{0} ** capture_max,
|
captures: [capture_max]Capture = [_]Capture{.{.declaration_index = 0}} ** capture_max,
|
||||||
capture_count: u8 = 0,
|
capture_count: u8 = 0,
|
||||||
local_declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
||||||
local_declaration_count: u8 = 0,
|
declaration_count: u8 = 0,
|
||||||
argument_count: u8 = 0,
|
argument_count: u8 = 0,
|
||||||
statement: ?*const Stmt = null,
|
statement: ?*const Stmt = null,
|
||||||
enclosing: ?*Environment = null,
|
enclosing: ?*Environment = null,
|
||||||
|
|
||||||
|
pub const Capture = union (enum) {
|
||||||
|
declaration_index: u8,
|
||||||
|
capture_index: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DeclareError = coral.io.AllocationError || error {
|
||||||
|
DeclarationExists,
|
||||||
|
};
|
||||||
|
|
||||||
const capture_max = coral.math.max_int(@typeInfo(u8).Int);
|
const capture_max = coral.math.max_int(@typeInfo(u8).Int);
|
||||||
|
|
||||||
const declaration_max = coral.math.max_int(@typeInfo(u8).Int);
|
const declaration_max = coral.math.max_int(@typeInfo(u8).Int);
|
||||||
|
@ -31,48 +43,96 @@ pub const Environment = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
fn declare(self: *Environment, declaration: Declaration) DeclareError!*const Declaration {
|
||||||
if (self.local_declaration_count == self.local_declarations.len) {
|
if (self.declaration_count == self.declarations.len) {
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
const declaration = &self.local_declarations[self.local_declaration_count];
|
{
|
||||||
|
var environment = self;
|
||||||
|
|
||||||
declaration.* = .{.identifier = identifier};
|
while (true) {
|
||||||
self.local_declaration_count += 1;
|
var remaining_count = environment.declaration_count;
|
||||||
|
|
||||||
return declaration;
|
while (remaining_count != 0) {
|
||||||
|
remaining_count -= 1;
|
||||||
|
|
||||||
|
if (coral.io.are_equal(environment.declarations[remaining_count].identifier, declaration.identifier)) {
|
||||||
|
return error.DeclarationExists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment = environment.enclosing orelse break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const declaration_slot = &self.declarations[self.declaration_count];
|
||||||
|
|
||||||
|
declaration_slot.* = declaration;
|
||||||
|
self.declaration_count += 1;
|
||||||
|
|
||||||
|
return declaration_slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_argument(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
||||||
|
coral.debug.assert(self.declaration_count <= self.argument_count);
|
||||||
|
|
||||||
|
defer self.argument_count += 1;
|
||||||
|
|
||||||
|
return self.declare(.{
|
||||||
|
.identifier = identifier,
|
||||||
|
.is = .{.readonly = true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_constant(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
||||||
|
return self.declare(.{
|
||||||
|
.identifier = identifier,
|
||||||
|
.is = .{.readonly = true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_variable(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
||||||
|
return self.declare(.{.identifier = identifier});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_declaration(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
pub fn resolve_declaration(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
||||||
var environment = self;
|
var environment = self;
|
||||||
var ancestry = @as(usize, 0);
|
var ancestry = @as(u32, 0);
|
||||||
|
|
||||||
while (true) : (ancestry += 1) {
|
while (true) : (ancestry += 1) {
|
||||||
var remaining_count = self.local_declaration_count;
|
var remaining_count = environment.declaration_count;
|
||||||
|
|
||||||
while (remaining_count != 0) {
|
while (remaining_count != 0) {
|
||||||
remaining_count -= 1;
|
remaining_count -= 1;
|
||||||
|
|
||||||
const declaration = &self.local_declarations[remaining_count];
|
const declaration = &environment.declarations[remaining_count];
|
||||||
|
|
||||||
if (coral.io.are_equal(declaration.identifier, identifier)) {
|
if (coral.io.are_equal(declaration.identifier, identifier)) {
|
||||||
if (ancestry != 0) {
|
if (ancestry != 0) {
|
||||||
declaration.is_captured = true;
|
declaration.is.captured = true;
|
||||||
environment = self;
|
environment = self;
|
||||||
|
ancestry -= 1;
|
||||||
|
|
||||||
while (ancestry != 0) : (ancestry -= 1) {
|
while (ancestry != 0) : (ancestry -= 1) {
|
||||||
if (environment.capture_count == self.captures.len) {
|
if (environment.capture_count == environment.captures.len) {
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
environment.captures[environment.capture_count] = remaining_count;
|
|
||||||
environment.capture_count += 1;
|
|
||||||
|
|
||||||
coral.debug.assert(environment.enclosing != null);
|
coral.debug.assert(environment.enclosing != null);
|
||||||
|
|
||||||
environment = environment.enclosing.?;
|
const enclosing_environment = environment.enclosing.?;
|
||||||
|
|
||||||
|
environment.captures[environment.capture_count] = .{
|
||||||
|
.capture_index = enclosing_environment.capture_count
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.capture_count += 1;
|
||||||
|
environment = enclosing_environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
environment.captures[environment.capture_count] = .{.declaration_index = remaining_count};
|
||||||
|
environment.capture_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return declaration;
|
return declaration;
|
||||||
|
@ -82,6 +142,14 @@ pub const Environment = struct {
|
||||||
environment = environment.enclosing orelse return null;
|
environment = environment.enclosing orelse return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_captures(self: *const Environment) []const Capture {
|
||||||
|
return self.captures[0 .. self.capture_count];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_declarations(self: *const Environment) []const Declaration {
|
||||||
|
return self.declarations[0 .. self.declaration_count];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ParseError = coral.io.AllocationError || error {
|
pub const ParseError = coral.io.AllocationError || error {
|
||||||
|
@ -106,6 +174,16 @@ pub const Root = struct {
|
||||||
return error.BadSyntax;
|
return error.BadSyntax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn report_declare_error(self: *Root, stream: *tokens.Stream, identifier: []const coral.io.Byte, @"error": Environment.DeclareError) ParseError {
|
||||||
|
return switch (@"error") {
|
||||||
|
error.OutOfMemory => error.OutOfMemory,
|
||||||
|
|
||||||
|
error.DeclarationExists => self.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||||
|
.identifier = identifier,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_expr(self: *Root, expr: Expr) coral.io.AllocationError!*Expr {
|
pub fn create_expr(self: *Root, expr: Expr) coral.io.AllocationError!*Expr {
|
||||||
return coral.io.allocate_one(self.arena.as_allocator(), expr);
|
return coral.io.allocate_one(self.arena.as_allocator(), expr);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue