Compare commits

..

No commits in common. "88ff2a64b66e83e0c4d8400ed23b7e4670e46a68" and "6ea44f552e3a86ade825c8b4d92390a7e86f93c3" have entirely different histories.

8 changed files with 123 additions and 378 deletions

View File

@ -1,12 +1,12 @@
let printer = lambda (pfx): let printer = lambda ():
return lambda (msg): return lambda (msg):
@print(pfx) @print("This is a func call")
@print(msg) @print(msg)
end end
end end
let pr = printer("This is a func call") let pr = printer()
var i = 0 var i = 0
pr("test") pr("test")
@ -25,10 +25,6 @@ else:
pr("i'unno") pr("i'unno")
end end
let pr2 = printer("this is a final closure")
pr2("goodbye")
return { return {
.title = "Game", .title = "Game",
.width = 1280, .width = 1280,

View File

@ -11,9 +11,9 @@ 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(readable_path).get_string(); const readable_path_string = sandboxed_path.joined(readable_path).to_string() orelse return null;
return @ptrCast(ext.SDL_RWFromFile(path_string.ptr, "rb")); return @ptrCast(ext.SDL_RWFromFile(readable_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).get_string(); const path_string = sandboxed_path.joined(path).to_string() orelse return null;
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,8 +96,12 @@ pub const Path = extern struct {
return path; return path;
} }
pub fn get_string(self: Path) [:0]const coral.io.Byte { pub fn to_string(self: Path) ?[:0]const coral.io.Byte {
coral.debug.assert(self.data[self.data.len - 1] == 0); const last_index = self.data.len - 1;
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)));
} }

View File

@ -2,6 +2,10 @@ 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");
@ -11,7 +15,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_stringable: *RuntimeRef, name: []const coral.io.Byte = "",
arg_count: u8, arg_count: u8,
locals_top: usize, locals_top: usize,
@ -84,11 +88,11 @@ 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(callable, args.len); const frame = try self.push_frame(args.len);
defer self.pop_frame(); defer self.pop_frame();
return self.call_frame(frame); return self.call_frame(callable, 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 {
@ -128,12 +132,6 @@ 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)]);
@ -203,7 +201,6 @@ pub const RuntimeEnv = struct {
.string => self.raise(error.TypeMismatch, "string is not get-indexable"), .string => self.raise(error.TypeMismatch, "string is not get-indexable"),
.symbol => self.raise(error.TypeMismatch, "symbol is not get-indexable"), .symbol => self.raise(error.TypeMismatch, "symbol is not get-indexable"),
.syscall => self.raise(error.TypeMismatch, "syscall 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: { .vector2 => |vector2| swizzle: {
const swizzle_symbol = try self.unbox_symbol(index); const swizzle_symbol = try self.unbox_symbol(index);
@ -261,8 +258,6 @@ pub const RuntimeEnv = struct {
} }
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 {
@ -287,17 +282,24 @@ pub const RuntimeEnv = struct {
}; };
} }
break: make_chunk try Chunk.make(self, file_name, &root.environment); break: make_chunk try Chunk.make(self, file_path.to_string() orelse "<script>", &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 name = try self.new_string(file_name); const frame = try self.push_frame(0);
defer self.discard(name);
const frame = try self.push_frame(name, 0);
defer self.pop_frame(); defer self.pop_frame();
@ -355,20 +357,11 @@ 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);
@ -447,7 +440,7 @@ pub const RuntimeEnv = struct {
errdefer table.free(self); errdefer table.free(self);
return try self.new_dynamic(coral.io.bytes_of(&table), Table.typeinfo); return try self.new_dynamic(coral.io.bytes_of(&table).ptr, 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 {
@ -477,23 +470,14 @@ pub const RuntimeEnv = struct {
} }
pub fn pop_frame(self: *RuntimeEnv) void { pub fn pop_frame(self: *RuntimeEnv) void {
const popped_frame = self.frames.pop(); var to_pop = self.locals.values.len - (self.frames.pop() orelse unreachable).locals_top;
coral.debug.assert(popped_frame != null); while (to_pop != 0) {
self.discard(popped_frame.?.name_stringable); if (self.locals.pop() orelse unreachable) |local| {
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);
} }
locals_to_pop -= 1; to_pop -= 1;
} }
} }
@ -501,9 +485,8 @@ pub const RuntimeEnv = struct {
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, name_stringable: *RuntimeRef, arg_count: u8) RuntimeError!Frame { pub fn push_frame(self: *RuntimeEnv, 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,
}; };
@ -524,17 +507,7 @@ pub const RuntimeEnv = struct {
while (remaining_frames != 0) { while (remaining_frames != 0) {
remaining_frames -= 1; remaining_frames -= 1;
const callable_string = try self.to_string(self.frames.values[remaining_frames].name_stringable); self.print_error(self.frames.values[remaining_frames].name);
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.?;
});
} }
} }
@ -600,7 +573,6 @@ pub const RuntimeEnv = struct {
break: convert self.new_string(string[0 .. length.?]); 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)), .symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
.string => value.acquire(), .string => value.acquire(),
@ -689,7 +661,6 @@ 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,
@ -741,23 +712,9 @@ 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| fixed, .fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))),
else => null, else => null,
}; };
} }
@ -770,7 +727,6 @@ 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, else => null,
}; };
} }
@ -801,8 +757,6 @@ pub const RuntimeRef = opaque {
else => false, else => false,
}, },
.boxed => unreachable,
.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,
@ -843,7 +797,6 @@ 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),
}; };
@ -859,7 +812,6 @@ 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,
}; };

View File

@ -1,9 +1,5 @@
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");
@ -16,11 +12,6 @@ name: *kym.RuntimeRef,
arity: u8, arity: u8,
opcodes: OpcodeList, opcodes: OpcodeList,
constants: ConstList, constants: ConstList,
bindings: []?*kym.RuntimeRef,
const Box = struct {
};
const Builtin = enum { const Builtin = enum {
import, import,
@ -41,15 +32,13 @@ const OpcodeList = coral.list.Stack(union (enum) {
push_top, push_top,
push_table: u32, push_table: u32,
push_builtin: Builtin, push_builtin: Builtin,
push_binding: u8, push_closure: 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,
@ -78,8 +67,9 @@ 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});
@ -117,9 +107,10 @@ 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_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding <{binding}>\n", .{ .push_closure => |push_closure| coral.utf8.print_formatted(writer, "push closure <{count}>\n", .{
.binding = push_binding, .count = push_closure,
}), }),
.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", .{
@ -131,16 +122,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
}, },
}), }),
.bind => |bind| coral.utf8.print_formatted(writer, "bind <{count}>\n", .{ .set_local => |local_set| coral.utf8.print_formatted(writer, "set local <{local}>\n", .{.local = local_set}),
.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}),
@ -163,11 +145,11 @@ 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(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { pub fn execute(chunk: 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 < self.opcodes.values.len) : (opcode_cursor += 1) { while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
switch (self.opcodes.values[opcode_cursor]) { switch (chunk.opcodes.values[opcode_cursor]) {
.pop => { .pop => {
if (try env.pop_local()) |ref| { if (try env.pop_local()) |ref| {
env.discard(ref); env.discard(ref);
@ -179,11 +161,11 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
.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 >= self.constants.values.len) { if (push_const >= chunk.constants.values.len) {
return env.raise(error.IllegalState, "invalid constant"); 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| {
@ -237,59 +219,19 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
}, },
.push_boxed => { .push_boxed => {
const value = try env.pop_local(); // TODO: Implement.
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_binding => |push_binding| { .push_self => {
if (push_binding > self.bindings.len) { // TODO: Implement.
return env.raise(error.IllegalState, "binding out of range"); unreachable;
}
try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null);
}, },
.bind => |bind| { .push_closure => |push_closure| {
const lambda = try env.expect(try env.pop_local()); // TODO: Implement.
_ = push_closure;
errdefer env.discard(lambda); unreachable;
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| { .push_builtin => |push_builtin| {
@ -315,34 +257,6 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
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, "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 => { .get_dynamic => {
const index = try env.expect(try env.pop_local()); const index = try env.expect(try env.pop_local());
@ -393,7 +307,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
defer env.discard(callable); defer env.discard(callable);
const call_frame = try env.push_frame(callable, call); const call_frame = try env.push_frame(call);
defer env.pop_frame(); defer env.pop_frame();
@ -580,18 +494,6 @@ 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 coral.io.Byte, environment: *const tree.Environment) kym.RuntimeError!Self {
@ -599,7 +501,6 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
.name = try env.new_symbol(name), .name = try env.new_symbol(name),
.opcodes = OpcodeList.make(env.allocator), .opcodes = OpcodeList.make(env.allocator),
.constants = ConstList.make(env.allocator), .constants = ConstList.make(env.allocator),
.bindings = &.{},
.arity = 0, .arity = 0,
}; };
@ -610,23 +511,6 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
try compiler.compile_environment(environment); try compiler.compile_environment(environment);
if (builtin.mode == .Debug) {
const allocation = try coral.utf8.alloc_formatted(env.allocator, "compiled {name}...", .{.name = name});
defer env.allocator.deallocate(allocation);
app.log_info(allocation);
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; return chunk;
} }
@ -666,7 +550,7 @@ fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.R
pub const typeinfo = &kym.Typeinfo{ pub const typeinfo = &kym.Typeinfo{
.size = @sizeOf(Self), .size = @sizeOf(Self),
.name = "func", .name = "lambda",
.destruct = typeinfo_destruct, .destruct = typeinfo_destruct,
.call = typeinfo_call, .call = typeinfo_call,
}; };

View File

@ -90,18 +90,14 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
errdefer chunk.free(self.env); errdefer chunk.free(self.env);
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)});
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)});
} else { if (lambda_construct.environment.capture_count != 0) {
for (environment.captures[0 .. environment.capture_count]) |capture| { for (lambda_construct.environment.captures[0 .. lambda_construct.environment.capture_count]) |capture| {
try self.chunk.opcodes.push_one(.{.push_local = environment.captures[capture]}); try self.chunk.opcodes.push_one(.{.push_local = environment.captures[capture]});
} }
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)}); try self.chunk.opcodes.push_one(.{.push_closure = lambda_construct.environment.capture_count});
try self.chunk.opcodes.push_one(.{
.bind = lambda_construct.environment.capture_count + environment.capture_count,
});
} }
}, },
@ -150,34 +146,27 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
} }
if (get_capture_index(environment, declaration_get.declaration)) |index| { if (get_capture_index(environment, declaration_get.declaration)) |index| {
try self.chunk.opcodes.push_one(.{.push_binding = index}); try self.chunk.opcodes.push_one(.push_self);
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_fixed(index)});
if (is_declaration_boxed(declaration_get.declaration)) { return self.chunk.opcodes.push_one(.get_dynamic);
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| {
if (get_local_index(environment, declaration_set.declaration)) |index| { try self.compile_expression(environment, declaration_set.assign);
try self.compile_expression(environment, declaration_set.assign);
if (get_local_index(environment, declaration_set.declaration)) |index| {
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 (get_capture_index(environment, declaration_set.declaration)) |index| {
try self.chunk.opcodes.push_one(.{.push_binding = index}); try self.chunk.opcodes.push_one(.push_self);
try self.compile_expression(environment, declaration_set.assign); try self.chunk.opcodes.push_one(.{.push_const = try self.declare_fixed(index)});
if (is_declaration_boxed(declaration_set.declaration)) { return self.chunk.opcodes.push_one(.set_dynamic);
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");
@ -264,7 +253,7 @@ 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);
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); try self.chunk.opcodes.push_one(.push_boxed);
} }
}, },
@ -287,7 +276,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"); 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), Chunk.typeinfo); const constant = try self.env.new_dynamic(coral.io.bytes_of(&chunk).ptr, Chunk.typeinfo);
errdefer self.env.discard(constant); errdefer self.env.discard(constant);
@ -357,11 +346,7 @@ pub fn get_capture_index(self: *const tree.Environment, declaration: *const tree
var capture_index = @as(u8, 0); var capture_index = @as(u8, 0);
while (capture_index < self.capture_count) : (capture_index += 1) { while (capture_index < self.capture_count) : (capture_index += 1) {
const captured_declaration_index = self.captures[capture_index]; if (&enclosing_environment.local_declarations[capture_index] == declaration) {
coral.debug.assert(captured_declaration_index < enclosing_environment.declaration_count);
if (&enclosing_environment.declarations[captured_declaration_index] == declaration) {
return capture_index; return capture_index;
} }
} }
@ -371,19 +356,15 @@ pub fn get_capture_index(self: *const tree.Environment, declaration: *const tree
} }
pub fn get_local_index(self: *const tree.Environment, declaration: *const tree.Declaration) ?u8 { pub fn get_local_index(self: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
var remaining = self.declaration_count; var remaining = self.local_declaration_count;
while (remaining != 0) { while (remaining != 0) {
remaining -= 1; remaining -= 1;
if (&self.declarations[remaining] == declaration) { if (&self.local_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;
}

View File

@ -174,17 +174,11 @@ 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| convert: { .declaration_get => |declaration_get| .{
if (declaration_get.declaration.is.readonly) { .declaration_set = .{
return root.report_error(stream, "readonly declarations cannot be re-assigned", .{}); .assign = try parse(root, stream, environment),
} .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| .{
@ -420,13 +414,15 @@ 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,
}); });
} },
}, },
}, },
}); });
@ -449,9 +445,13 @@ 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", .{}),
}; };
_ = lambda_environment.declare_argument(identifier) catch |declare_error| { if (try lambda_environment.declare(identifier) == null) {
return root.report_declare_error(stream, identifier, declare_error); return root.report_error(stream, "declaration `{identifier}` already exists", .{
}; .identifier = identifier,
});
}
lambda_environment.argument_count += 1;
stream.skip_newlines(); stream.skip_newlines();

View File

@ -93,13 +93,18 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
}, },
.keyword_var, .keyword_let => { .keyword_var, .keyword_let => {
const is_constant = stream.token == .keyword_let; const storage_token = stream.token;
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();
@ -117,30 +122,10 @@ 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 = declare: { .declaration = (try root.environment.declare(identifier)) orelse {
if (is_constant) { return root.report_error(stream, "declaration `{identifier}` already exists", .{
break: declare environment.declare_constant(identifier) catch |declaration_error| { .identifier = identifier,
return switch (declaration_error) { });
error.OutOfMemory => error.OutOfMemory,
error.DeclarationExists =>
root.report_error(stream, "declaration `{identifier}` already exists", .{
.identifier = identifier,
}),
};
};
}
break: declare environment.declare_variable(identifier) catch |declaration_error| {
return switch (declaration_error) {
error.OutOfMemory => error.OutOfMemory,
error.DeclarationExists =>
root.report_error(stream, "declaration `{identifier}` already exists", .{
.identifier = identifier,
}),
};
};
}, },
}, },
}, },

View File

@ -8,26 +8,19 @@ 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: packed struct { is_captured: bool = false,
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]u8 = [_]u8{0} ** capture_max,
capture_count: u8 = 0, capture_count: u8 = 0,
declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max, local_declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
declaration_count: u8 = 0, local_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,
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);
@ -38,57 +31,17 @@ pub const Environment = struct {
}); });
} }
fn declare(self: *Environment, declaration: Declaration) DeclareError!*const Declaration { pub fn declare(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
if (self.declaration_count == self.declarations.len) { if (self.local_declaration_count == self.local_declarations.len) {
return error.OutOfMemory; return error.OutOfMemory;
} }
{ const declaration = &self.local_declarations[self.local_declaration_count];
var environment = self;
while (true) { declaration.* = .{.identifier = identifier};
var remaining_count = environment.declaration_count; self.local_declaration_count += 1;
while (remaining_count != 0) { return declaration;
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 {
@ -96,20 +49,20 @@ pub const Environment = struct {
var ancestry = @as(usize, 0); var ancestry = @as(usize, 0);
while (true) : (ancestry += 1) { while (true) : (ancestry += 1) {
var remaining_count = environment.declaration_count; var remaining_count = self.local_declaration_count;
while (remaining_count != 0) { while (remaining_count != 0) {
remaining_count -= 1; remaining_count -= 1;
const declaration = &environment.declarations[remaining_count]; const declaration = &self.local_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;
while (ancestry != 0) : (ancestry -= 1) { while (ancestry != 0) : (ancestry -= 1) {
if (environment.capture_count == environment.captures.len) { if (environment.capture_count == self.captures.len) {
return error.OutOfMemory; return error.OutOfMemory;
} }
@ -153,16 +106,6 @@ 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);
} }