Implement closures
This commit is contained in:
parent
6ea44f552e
commit
880761a722
|
@ -1,12 +1,14 @@
|
|||
|
||||
let printer = lambda ():
|
||||
let printer = lambda (pfx):
|
||||
let prefix = pfx
|
||||
|
||||
return lambda (msg):
|
||||
@print("This is a func call")
|
||||
@print(prefix)
|
||||
@print(msg)
|
||||
end
|
||||
end
|
||||
|
||||
let pr = printer()
|
||||
let pr = printer("This is a func call")
|
||||
var i = 0
|
||||
|
||||
pr("test")
|
||||
|
@ -25,6 +27,10 @@ else:
|
|||
pr("i'unno")
|
||||
end
|
||||
|
||||
let pr2 = printer("this is a final closure")
|
||||
|
||||
pr2("goodbye")
|
||||
|
||||
return {
|
||||
.title = "Game",
|
||||
.width = 1280,
|
||||
|
|
|
@ -11,9 +11,9 @@ pub const Access = union (enum) {
|
|||
.null => return null,
|
||||
|
||||
.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,
|
||||
|
||||
.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 file_size = ext.SDL_RWseek(rw_ops, 0, ext.RW_SEEK_END);
|
||||
|
||||
|
@ -96,12 +96,8 @@ pub const Path = extern struct {
|
|||
return path;
|
||||
}
|
||||
|
||||
pub fn to_string(self: Path) ?[:0]const coral.io.Byte {
|
||||
const last_index = self.data.len - 1;
|
||||
|
||||
if (self.data[last_index] != 0) {
|
||||
return null;
|
||||
}
|
||||
pub fn get_string(self: Path) [:0]const coral.io.Byte {
|
||||
coral.debug.assert(self.data[self.data.len - 1] == 0);
|
||||
|
||||
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 app = @import("./app.zig");
|
||||
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
const file = @import("./file.zig");
|
||||
|
@ -15,7 +11,7 @@ const tokens = @import("./kym/tokens.zig");
|
|||
const tree = @import("./kym/tree.zig");
|
||||
|
||||
pub const Frame = struct {
|
||||
name: []const coral.io.Byte = "",
|
||||
name_stringable: *RuntimeRef,
|
||||
arg_count: u8,
|
||||
locals_top: usize,
|
||||
|
||||
|
@ -88,11 +84,11 @@ pub const RuntimeEnv = struct {
|
|||
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();
|
||||
|
||||
return self.call_frame(callable, frame);
|
||||
return self.call_frame(frame);
|
||||
}
|
||||
|
||||
pub fn call_frame(self: *RuntimeEnv, callable: *const RuntimeRef, frame: Frame) RuntimeError!?*RuntimeRef {
|
||||
|
@ -132,6 +128,12 @@ pub const RuntimeEnv = struct {
|
|||
switch (object.payload) {
|
||||
.false, .true, .float, .fixed, .symbol, .vector2, .vector3, .syscall => {},
|
||||
|
||||
.boxed => |*boxed| {
|
||||
if (boxed.*) |boxed_value| {
|
||||
self.discard(boxed_value);
|
||||
}
|
||||
},
|
||||
|
||||
.string => |string| {
|
||||
coral.debug.assert(string.len >= 0);
|
||||
self.allocator.deallocate(string.ptr[0 .. @intCast(string.len)]);
|
||||
|
@ -201,6 +203,7 @@ pub const RuntimeEnv = struct {
|
|||
.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"),
|
||||
.boxed => self.raise(error.TypeMismatch, "boxed is not get-indexable"),
|
||||
|
||||
.vector2 => |vector2| swizzle: {
|
||||
const swizzle_symbol = try self.unbox_symbol(index);
|
||||
|
@ -258,6 +261,8 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
|
||||
pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
||||
const file_name = file_path.get_string();
|
||||
|
||||
var chunk = make_chunk: {
|
||||
const file_data =
|
||||
(try file.allocate_and_load(self.allocator, self.options.import_access, file_path)) orelse {
|
||||
|
@ -282,24 +287,17 @@ pub const RuntimeEnv = struct {
|
|||
};
|
||||
}
|
||||
|
||||
break: make_chunk try Chunk.make(self, file_path.to_string() orelse "<script>", &root.environment);
|
||||
break: make_chunk try Chunk.make(self, file_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);
|
||||
|
||||
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();
|
||||
|
||||
|
@ -357,11 +355,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(
|
||||
self: *RuntimeEnv,
|
||||
userdata: [*]const coral.io.Byte,
|
||||
userdata: []const coral.io.Byte,
|
||||
typeinfo: *const Typeinfo,
|
||||
) RuntimeError!*RuntimeRef {
|
||||
coral.debug.assert(userdata.len == typeinfo.size);
|
||||
|
||||
const dynamic = try self.allocator.reallocate(null, @sizeOf(usize) + typeinfo.size);
|
||||
|
||||
errdefer self.allocator.deallocate(dynamic);
|
||||
|
@ -440,7 +447,7 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
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 {
|
||||
|
@ -470,14 +477,23 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
|
||||
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) {
|
||||
if (self.locals.pop() orelse unreachable) |local| {
|
||||
coral.debug.assert(popped_frame != null);
|
||||
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);
|
||||
}
|
||||
|
||||
to_pop -= 1;
|
||||
locals_to_pop -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,8 +501,9 @@ pub const RuntimeEnv = struct {
|
|||
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{
|
||||
.name_stringable = name_stringable.acquire(),
|
||||
.arg_count = arg_count,
|
||||
.locals_top = self.locals.values.len - arg_count,
|
||||
};
|
||||
|
@ -507,7 +524,17 @@ pub const RuntimeEnv = struct {
|
|||
while (remaining_frames != 0) {
|
||||
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.?;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,6 +600,7 @@ pub const RuntimeEnv = struct {
|
|||
break: convert self.new_string(string[0 .. length.?]);
|
||||
},
|
||||
|
||||
.boxed => unreachable,
|
||||
.symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
|
||||
.string => value.acquire(),
|
||||
|
||||
|
@ -661,6 +689,7 @@ pub const RuntimeRef = opaque {
|
|||
vector2: [2]f32,
|
||||
vector3: [3]f32,
|
||||
syscall: *const Syscall,
|
||||
boxed: ?*RuntimeRef,
|
||||
|
||||
string: struct {
|
||||
ptr: [*]coral.io.Byte,
|
||||
|
@ -712,9 +741,23 @@ pub const RuntimeRef = opaque {
|
|||
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 {
|
||||
return switch (self.object().payload) {
|
||||
.fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))),
|
||||
.fixed => |fixed| fixed,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
@ -727,6 +770,7 @@ pub const RuntimeRef = opaque {
|
|||
break: get string.ptr[0 .. @intCast(string.len)];
|
||||
},
|
||||
|
||||
.symbol => |symbol| coral.io.slice_sentineled(@as(u8, 0), symbol),
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
@ -757,6 +801,8 @@ pub const RuntimeRef = opaque {
|
|||
else => false,
|
||||
},
|
||||
|
||||
.boxed => unreachable,
|
||||
|
||||
.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)),
|
||||
else => false,
|
||||
|
@ -797,6 +843,7 @@ pub const RuntimeRef = opaque {
|
|||
.vector2 => |vector| @bitCast(vector),
|
||||
.vector3 => |vector| coral.io.jenkins_hash(@typeInfo(usize).Int, coral.io.bytes_of(&vector)),
|
||||
.syscall => |syscall| @intFromPtr(syscall),
|
||||
.boxed => |boxed| @intFromPtr(boxed),
|
||||
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()),
|
||||
.dynamic => |dynamic| @intFromPtr(dynamic.typeinfo()) ^ @intFromPtr(dynamic.userdata().ptr),
|
||||
};
|
||||
|
@ -812,6 +859,7 @@ pub const RuntimeRef = opaque {
|
|||
.vector2 => |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,
|
||||
.boxed => |boxed| boxed != null,
|
||||
.string => |string| string.len != 0,
|
||||
.dynamic => true,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
const app = @import("../app.zig");
|
||||
|
||||
const Compiler = @import("./Compiler.zig");
|
||||
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
const file = @import("../file.zig");
|
||||
|
@ -12,6 +16,11 @@ name: *kym.RuntimeRef,
|
|||
arity: u8,
|
||||
opcodes: OpcodeList,
|
||||
constants: ConstList,
|
||||
bindings: []?*kym.RuntimeRef,
|
||||
|
||||
const Box = struct {
|
||||
|
||||
};
|
||||
|
||||
const Builtin = enum {
|
||||
import,
|
||||
|
@ -32,13 +41,15 @@ const OpcodeList = coral.list.Stack(union (enum) {
|
|||
push_top,
|
||||
push_table: u32,
|
||||
push_builtin: Builtin,
|
||||
push_closure: u8,
|
||||
push_self,
|
||||
push_binding: u8,
|
||||
push_boxed,
|
||||
set_local: u8,
|
||||
get_dynamic,
|
||||
set_dynamic,
|
||||
get_box,
|
||||
set_box,
|
||||
call: u8,
|
||||
bind: u8,
|
||||
|
||||
not,
|
||||
neg,
|
||||
|
@ -67,6 +78,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
|||
defer buffer.free();
|
||||
|
||||
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");
|
||||
|
@ -107,10 +119,9 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
|||
}),
|
||||
|
||||
.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", .{
|
||||
.count = push_closure,
|
||||
.push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding <{binding}>\n", .{
|
||||
.binding = push_binding,
|
||||
}),
|
||||
|
||||
.push_builtin => |push_builtin| coral.utf8.print_formatted(writer, "push builtin <{builtin}>\n", .{
|
||||
|
@ -122,7 +133,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"),
|
||||
.set_dynamic => coral.utf8.print_string(writer, "set dynamic\n"),
|
||||
.call => |call| coral.utf8.print_formatted(writer, "call <{count}>\n", .{.count = call}),
|
||||
|
@ -145,11 +165,11 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
|||
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);
|
||||
|
||||
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
||||
switch (chunk.opcodes.values[opcode_cursor]) {
|
||||
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
|
||||
switch (self.opcodes.values[opcode_cursor]) {
|
||||
.pop => {
|
||||
if (try env.pop_local()) |ref| {
|
||||
env.discard(ref);
|
||||
|
@ -161,11 +181,11 @@ 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_const => |push_const| {
|
||||
if (push_const >= chunk.constants.values.len) {
|
||||
if (push_const >= self.constants.values.len) {
|
||||
return env.raise(error.IllegalState, "invalid constant");
|
||||
}
|
||||
|
||||
try env.locals.push_one(chunk.constants.values[push_const].acquire());
|
||||
try env.locals.push_one(self.constants.values[push_const].acquire());
|
||||
},
|
||||
|
||||
.push_local => |push_local| {
|
||||
|
@ -219,19 +239,59 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
|||
},
|
||||
|
||||
.push_boxed => {
|
||||
// TODO: Implement.
|
||||
unreachable;
|
||||
const value = try env.pop_local();
|
||||
|
||||
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 => {
|
||||
// TODO: Implement.
|
||||
unreachable;
|
||||
.push_binding => |push_binding| {
|
||||
if (push_binding > self.bindings.len) {
|
||||
return env.raise(error.IllegalState, "binding out of range");
|
||||
}
|
||||
|
||||
try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null);
|
||||
},
|
||||
|
||||
.push_closure => |push_closure| {
|
||||
// TODO: Implement.
|
||||
_ = push_closure;
|
||||
unreachable;
|
||||
.bind => |bind| {
|
||||
const lambda = try env.expect(try env.pop_local());
|
||||
|
||||
errdefer env.discard(lambda);
|
||||
|
||||
const chunk = @as(*Self, @ptrCast(@alignCast(lambda.as_dynamic(typeinfo) orelse {
|
||||
return env.raise(error.IllegalState, "cannot bind to non-chunk");
|
||||
})));
|
||||
|
||||
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(lambda);
|
||||
},
|
||||
|
||||
.push_builtin => |push_builtin| {
|
||||
|
@ -257,6 +317,34 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
|||
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, "type is not unboxable");
|
||||
};
|
||||
|
||||
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, "type is not unboxable");
|
||||
};
|
||||
|
||||
if (boxed.*) |value| {
|
||||
env.discard(value);
|
||||
}
|
||||
|
||||
boxed.* = box;
|
||||
},
|
||||
|
||||
.get_dynamic => {
|
||||
const index = try env.expect(try env.pop_local());
|
||||
|
||||
|
@ -307,7 +395,7 @@ pub fn execute(chunk: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeE
|
|||
|
||||
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();
|
||||
|
||||
|
@ -494,6 +582,18 @@ pub fn free(self: *Self, env: *kym.RuntimeEnv) void {
|
|||
self.constants.free();
|
||||
self.opcodes.free();
|
||||
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 {
|
||||
|
@ -501,6 +601,7 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
|
|||
.name = try env.new_symbol(name),
|
||||
.opcodes = OpcodeList.make(env.allocator),
|
||||
.constants = ConstList.make(env.allocator),
|
||||
.bindings = &.{},
|
||||
.arity = 0,
|
||||
};
|
||||
|
||||
|
@ -511,6 +612,17 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
|
|||
|
||||
try compiler.compile_environment(environment);
|
||||
|
||||
if (builtin.mode == .Debug) {
|
||||
const string_ref = try chunk.dump(env);
|
||||
|
||||
defer env.discard(string_ref);
|
||||
|
||||
const string = string_ref.as_string();
|
||||
|
||||
coral.debug.assert(string != null);
|
||||
app.log_info(string.?);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
@ -550,7 +662,7 @@ fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.R
|
|||
|
||||
pub const typeinfo = &kym.Typeinfo{
|
||||
.size = @sizeOf(Self),
|
||||
.name = "lambda",
|
||||
.name = "func",
|
||||
.destruct = typeinfo_destruct,
|
||||
.call = typeinfo_call,
|
||||
};
|
||||
|
|
|
@ -90,14 +90,18 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
|||
|
||||
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) {
|
||||
for (lambda_construct.environment.captures[0 .. lambda_construct.environment.capture_count]) |capture| {
|
||||
if (lambda_construct.environment.capture_count == 0 and environment.capture_count == 0) {
|
||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)});
|
||||
} else {
|
||||
for (environment.captures[0 .. environment.capture_count]) |capture| {
|
||||
try self.chunk.opcodes.push_one(.{.push_local = environment.captures[capture]});
|
||||
}
|
||||
|
||||
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 + environment.capture_count,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -146,27 +150,34 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
|||
}
|
||||
|
||||
if (get_capture_index(environment, declaration_get.declaration)) |index| {
|
||||
try self.chunk.opcodes.push_one(.push_self);
|
||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_fixed(index)});
|
||||
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
||||
|
||||
return self.chunk.opcodes.push_one(.get_dynamic);
|
||||
if (declaration_get.declaration.is.captured) {
|
||||
try self.chunk.opcodes.push_one(.get_box);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return self.env.raise(error.IllegalState, "local out of scope");
|
||||
},
|
||||
|
||||
.declaration_set => |declaration_set| {
|
||||
try self.compile_expression(environment, declaration_set.assign);
|
||||
|
||||
if (get_local_index(environment, declaration_set.declaration)) |index| {
|
||||
try self.compile_expression(environment, declaration_set.assign);
|
||||
|
||||
return self.chunk.opcodes.push_one(.{.set_local = index});
|
||||
}
|
||||
|
||||
if (get_capture_index(environment, declaration_set.declaration)) |index| {
|
||||
try self.chunk.opcodes.push_one(.push_self);
|
||||
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_fixed(index)});
|
||||
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
||||
try self.compile_expression(environment, declaration_set.assign);
|
||||
|
||||
return self.chunk.opcodes.push_one(.set_dynamic);
|
||||
if (declaration_set.declaration.is.captured) {
|
||||
try self.chunk.opcodes.push_one(.set_box);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return self.env.raise(error.IllegalState, "local out of scope");
|
||||
|
@ -253,7 +264,7 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
|
|||
.declare => |declare| {
|
||||
try self.compile_expression(environment, declare.initial_expression);
|
||||
|
||||
if (declare.declaration.is_captured and !declare.declaration.is_readonly) {
|
||||
if (declare.declaration.is.captured and !declare.declaration.is.readonly) {
|
||||
try self.chunk.opcodes.push_one(.push_boxed);
|
||||
}
|
||||
},
|
||||
|
@ -276,7 +287,7 @@ fn declare_chunk(self: Self, chunk: Chunk) kym.RuntimeError!u16 {
|
|||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -346,7 +357,11 @@ pub fn get_capture_index(self: *const tree.Environment, declaration: *const tree
|
|||
var capture_index = @as(u8, 0);
|
||||
|
||||
while (capture_index < self.capture_count) : (capture_index += 1) {
|
||||
if (&enclosing_environment.local_declarations[capture_index] == declaration) {
|
||||
const captured_declaration_index = self.captures[capture_index];
|
||||
|
||||
coral.debug.assert(captured_declaration_index < enclosing_environment.local_declaration_count);
|
||||
|
||||
if (&enclosing_environment.local_declarations[captured_declaration_index] == declaration) {
|
||||
return capture_index;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,11 +174,17 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
|||
|
||||
return root.create_expr(.{
|
||||
.kind = switch (expression.kind) {
|
||||
.declaration_get => |declaration_get| .{
|
||||
.declaration_set = .{
|
||||
.assign = try parse(root, stream, environment),
|
||||
.declaration = declaration_get.declaration,
|
||||
},
|
||||
.declaration_get => |declaration_get| convert: {
|
||||
if (declaration_get.declaration.is.readonly) {
|
||||
return root.report_error(stream, "readonly declarations cannot be re-assigned", .{});
|
||||
}
|
||||
|
||||
break: convert .{
|
||||
.declaration_set = .{
|
||||
.assign = try parse(root, stream, environment),
|
||||
.declaration = declaration_get.declaration,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
.field_get => |field_get| .{
|
||||
|
@ -413,18 +419,19 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
|||
.identifier => |identifier| {
|
||||
stream.skip_newlines();
|
||||
|
||||
const declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
};
|
||||
|
||||
if (declaration.is.argument and declaration.is.captured) {
|
||||
return root.report_error(stream, "arguments cannot be directly captured, create a declaration", .{});
|
||||
}
|
||||
|
||||
return root.create_expr(.{
|
||||
.next = null,
|
||||
|
||||
.kind = .{
|
||||
.declaration_get = .{
|
||||
.declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
.kind = .{.declaration_get = .{.declaration = declaration}},
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -445,7 +452,10 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
|||
else => return root.report_error(stream, "expected identifier", .{}),
|
||||
};
|
||||
|
||||
if (try lambda_environment.declare(identifier) == null) {
|
||||
if (try lambda_environment.declare(.{
|
||||
.identifier = identifier,
|
||||
.is = .{.argument = true},
|
||||
}) == null) {
|
||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
|
|
|
@ -122,7 +122,7 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
|||
.declare = .{
|
||||
.initial_expression = try Expr.parse(root, stream, environment),
|
||||
|
||||
.declaration = (try root.environment.declare(identifier)) orelse {
|
||||
.declaration = (try environment.declare(.{.identifier = identifier})) orelse {
|
||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
|
|
|
@ -8,8 +8,12 @@ const tokens = @import("./tokens.zig");
|
|||
|
||||
pub const Declaration = struct {
|
||||
identifier: []const coral.io.Byte,
|
||||
is_readonly: bool = false,
|
||||
is_captured: bool = false,
|
||||
|
||||
is: packed struct {
|
||||
readonly: bool = false,
|
||||
captured: bool = false,
|
||||
argument: bool = false,
|
||||
} = .{},
|
||||
};
|
||||
|
||||
pub const Environment = struct {
|
||||
|
@ -31,17 +35,17 @@ pub const Environment = struct {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn declare(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
||||
pub fn declare(self: *Environment, declaration: Declaration) coral.io.AllocationError!?*const Declaration {
|
||||
if (self.local_declaration_count == self.local_declarations.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
|
||||
const declaration = &self.local_declarations[self.local_declaration_count];
|
||||
const declaration_slot = &self.local_declarations[self.local_declaration_count];
|
||||
|
||||
declaration.* = .{.identifier = identifier};
|
||||
declaration_slot.* = declaration;
|
||||
self.local_declaration_count += 1;
|
||||
|
||||
return declaration;
|
||||
return declaration_slot;
|
||||
}
|
||||
|
||||
pub fn resolve_declaration(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
||||
|
@ -49,20 +53,20 @@ pub const Environment = struct {
|
|||
var ancestry = @as(usize, 0);
|
||||
|
||||
while (true) : (ancestry += 1) {
|
||||
var remaining_count = self.local_declaration_count;
|
||||
var remaining_count = environment.local_declaration_count;
|
||||
|
||||
while (remaining_count != 0) {
|
||||
remaining_count -= 1;
|
||||
|
||||
const declaration = &self.local_declarations[remaining_count];
|
||||
const declaration = &environment.local_declarations[remaining_count];
|
||||
|
||||
if (coral.io.are_equal(declaration.identifier, identifier)) {
|
||||
if (ancestry != 0) {
|
||||
declaration.is_captured = true;
|
||||
declaration.is.captured = true;
|
||||
environment = self;
|
||||
|
||||
while (ancestry != 0) : (ancestry -= 1) {
|
||||
if (environment.capture_count == self.captures.len) {
|
||||
if (environment.capture_count == environment.captures.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue