Add line numbers to stack traces
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
kayomn 2023-11-05 22:46:24 +00:00
parent 00631e66a5
commit 75ddf3f2bb
9 changed files with 292 additions and 175 deletions

View File

@ -1,7 +1,7 @@
let test_param = "monkey wrench" let test_param = "monkey wrench"
let printer = lambda (pfx, anotha): let printer = lambda (pfx):
@print(test_param) @print(test_param)
return lambda (msg): return lambda (msg):

View File

@ -374,7 +374,7 @@ pub fn tag_of(comptime value: anytype) Tag(@TypeOf(value)) {
return @as(Tag(@TypeOf(value)), value); return @as(Tag(@TypeOf(value)), value);
} }
pub fn write_null(buffer: []const u8) ?usize { fn write_null(buffer: []const u8) ?usize {
return buffer.len; return buffer.len;
} }

View File

@ -89,21 +89,21 @@ pub const RuntimeEnv = struct {
pub fn call(self: *RuntimeEnv, callable: *const RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef { pub fn call(self: *RuntimeEnv, callable: *const RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef {
// TODO: Handle errors. // TODO: Handle errors.
for (args) |arg| { for (args) |arg| {
try self.locals.push_one(try self.acquire(arg)); try self.locals.push_one(arg.acquire());
} }
const frame = try self.push_frame(callable, args.len); const frame = try self.push_frame(callable, @intCast(args.len));
defer self.pop_frame(); defer self.pop_frame();
return self.call_frame(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, frame: *const Frame) RuntimeError!?*RuntimeRef {
return switch (callable.object().payload) { return switch (frame.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, "{typename} is not callable", .{.typename = callable.typename()}), else => self.raise(error.TypeMismatch, "{typename} is not callable", .{.typename = frame.callable.typename()}),
}; };
} }
@ -271,7 +271,7 @@ pub const RuntimeEnv = struct {
} }
}, },
.dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index), .dynamic => |dynamic| dynamic.typeinfo().get_index(self, dynamic.userdata(), index),
else => self.raise(error.TypeMismatch, "{typename} is not get-indexable", .{ else => self.raise(error.TypeMismatch, "{typename} is not get-indexable", .{
.typename = indexable.typename(), .typename = indexable.typename(),
@ -279,10 +279,17 @@ pub const RuntimeEnv = struct {
}; };
} }
pub fn get_boxed(self: *RuntimeEnv, boxable: *RuntimeRef) RuntimeError!?*RuntimeRef {
return switch (boxable.object().payload) {
.boxed => |boxed| if (boxed) |boxed_value| boxed_value.acquire() else null,
else => self.raise(error.TypeMismatch, "{typename} is not boxable", .{.typename = boxable.typename()}),
};
}
pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef { pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef {
var callable = try self.new_dynamic(coral.io.bytes_of(&make_chunk: {
const file_name = file_path.get_string(); const file_name = file_path.get_string();
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 `{name}`", .{ return self.raise(error.BadOperation, "failed to open or read `{name}`", .{
@ -309,21 +316,11 @@ pub const RuntimeEnv = struct {
} }
break: make_chunk try Chunk.make(self, file_name, &root.environment); break: make_chunk try Chunk.make(self, file_name, &root.environment);
}; }), Chunk.typeinfo);
defer chunk.free(self); defer self.discard(callable);
return execute_chunk: { return self.call(callable, &.{});
const name = try self.new_string(file_name);
defer self.discard(name);
const frame = try self.push_frame(name, 0);
defer self.pop_frame();
break: execute_chunk chunk.execute(self, frame);
};
} }
pub fn make(allocator: coral.io.Allocator, options: Options) coral.io.AllocationError!RuntimeEnv { pub fn make(allocator: coral.io.Allocator, options: Options) coral.io.AllocationError!RuntimeEnv {
@ -531,6 +528,19 @@ pub const RuntimeEnv = struct {
} }
pub fn push_frame(self: *RuntimeEnv, callable: *const RuntimeRef, arg_count: u8) RuntimeError!Frame { pub fn push_frame(self: *RuntimeEnv, callable: *const RuntimeRef, arg_count: u8) RuntimeError!Frame {
const arity = switch (callable.object().payload) {
.dynamic => |dynamic| dynamic.typeinfo().get_arity(dynamic.userdata()),
else => 0,
};
if (arg_count < arity) {
return self.raise(error.BadOperation, "expected `{expected}` {noun}, {provided} provided", .{
.expected = arity,
.provided = arg_count,
.noun = if (arity == 1) "argument" else "arguments",
});
}
const frame = Frame{ const frame = Frame{
.callable = callable.acquire(), .callable = callable.acquire(),
.arg_count = arg_count, .arg_count = arg_count,
@ -543,13 +553,11 @@ pub const RuntimeEnv = struct {
} }
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, comptime format: []const coral.io.Byte, args: anytype) RuntimeError { pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, comptime format: []const coral.io.Byte, args: anytype) RuntimeError {
{
const formatted_message = try coral.utf8.alloc_formatted(self.allocator, format, args); const formatted_message = try coral.utf8.alloc_formatted(self.allocator, format, args);
defer self.allocator.deallocate(formatted_message); defer self.allocator.deallocate(formatted_message);
self.print_error(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:");
@ -559,12 +567,32 @@ 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].callable); const callable = self.frames.values[remaining_frames].callable;
const name = try self.to_string(callable);
defer self.discard(callable_string); defer self.discard(name);
if (callable.as_dynamic(Chunk.typeinfo)) |chunk_userdata| {
const chunk = @as(*Chunk, @ptrCast(@alignCast(chunk_userdata)));
const chunk_name = try coral.utf8.alloc_formatted(self.allocator, "{name}@{line}", .{
.name = get_name: {
const string = name.as_string();
coral.debug.assert(string != null);
break: get_name string.?;
},
.line = chunk.lines.values[chunk.cursor],
});
defer self.allocator.deallocate(chunk_name);
self.print_error(chunk_name);
} else {
self.print_error(get_name: { self.print_error(get_name: {
const string = callable_string.as_string(); const string = name.as_string();
coral.debug.assert(string != null); coral.debug.assert(string != null);
@ -572,13 +600,14 @@ pub const RuntimeEnv = struct {
}); });
} }
} }
}
return error_value; return error_value;
} }
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_index(self, dynamic.userdata(), index, value),
else => self.raise(error.TypeMismatch, "{typename} is not set-indexable", .{ else => self.raise(error.TypeMismatch, "{typename} is not set-indexable", .{
.typename = indexable.typename(), .typename = indexable.typename(),
@ -586,6 +615,22 @@ pub const RuntimeEnv = struct {
}; };
} }
pub fn set_boxed(self: *RuntimeEnv, boxable: *RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
switch (boxable.object().payload) {
.boxed => |*boxed| {
if (boxed.*) |unboxed_value| {
self.discard(unboxed_value);
}
boxed.* = if (value) |ref| ref.acquire() else null;
},
else => return self.raise(error.TypeMismatch, "{typename} is not boxable", .{
.typename = boxable.typename(),
}),
}
}
pub fn sub(self: *RuntimeEnv, lhs: *const RuntimeRef, rhs: *const RuntimeRef) RuntimeError!*RuntimeRef { pub fn sub(self: *RuntimeEnv, lhs: *const RuntimeRef, rhs: *const RuntimeRef) RuntimeError!*RuntimeRef {
return switch (lhs.object().payload) { return switch (lhs.object().payload) {
.fixed => |lhs_fixed| switch (rhs.object().payload) { .fixed => |lhs_fixed| switch (rhs.object().payload) {
@ -789,13 +834,6 @@ 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 { pub fn as_dynamic(self: *const RuntimeRef, typeinfo: *const Typeinfo) ?[]u8 {
return switch (self.object().payload) { return switch (self.object().payload) {
.dynamic => |dynamic| if (dynamic.typeinfo() == typeinfo) dynamic.userdata() else null, .dynamic => |dynamic| if (dynamic.typeinfo() == typeinfo) dynamic.userdata() else null,
@ -861,8 +899,8 @@ pub const RuntimeRef = opaque {
break: unbox boxed_value.equals(other); break: unbox boxed_value.equals(other);
} }
if (other.as_boxed()) |boxed_value| { if (other.object().payload == .boxed) {
break: unbox boxed_value.* == null; break: unbox other.object().payload.boxed == null;
} }
break: unbox false; break: unbox false;
@ -947,26 +985,31 @@ pub const RuntimeRef = opaque {
} }
}; };
pub const Syscall = fn (env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef; pub const Syscall = fn (env: *RuntimeEnv, frame: *const Frame) RuntimeError!?*RuntimeRef;
pub const Typeinfo = struct { pub const Typeinfo = struct {
name: []const coral.io.Byte, name: []const coral.io.Byte,
size: usize, size: usize,
destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null, destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null,
to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*RuntimeRef = default_to_string, to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*RuntimeRef = default_to_string,
call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call, get_arity: *const fn (userdata: []coral.io.Byte) u8 = default_get_arity,
get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: *const Frame) RuntimeError!?*RuntimeRef = default_call,
set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, get_index: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get_index,
set_index: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set_index,
fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef { fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: *const Frame) RuntimeError!?*RuntimeRef {
return env.raise(error.BadOperation, "this dynamic 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_arity(_: []coral.io.Byte) u8 {
return 0;
}
fn default_get_index(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
return env.raise(error.BadOperation, "this dynamic 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_index(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{}); return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{});
} }

View File

@ -2,8 +2,6 @@ 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");
@ -15,6 +13,8 @@ const tree = @import("./tree.zig");
name: *kym.RuntimeRef, name: *kym.RuntimeRef,
arity: u8, arity: u8,
opcodes: OpcodeList, opcodes: OpcodeList,
lines: LineList,
cursor: usize,
constants: ConstList, constants: ConstList,
bindings: []?*kym.RuntimeRef, bindings: []?*kym.RuntimeRef,
@ -27,7 +27,9 @@ 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 LineList = coral.list.Stack(u32);
pub const Opcode = union (enum) {
ret, ret,
pop, pop,
push_nil, push_nil,
@ -64,7 +66,9 @@ const OpcodeList = coral.list.Stack(union (enum) {
jt: u32, jt: u32,
jf: u32, jf: u32,
}); };
const OpcodeList = coral.list.Stack(Opcode);
const Self = @This(); const Self = @This();
@ -99,26 +103,26 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
coral.debug.assert(string != null); coral.debug.assert(string != null);
break: print coral.utf8.print_formatted(writer, "push const <{value}>\n", .{.value = string.?}); break: print coral.utf8.print_formatted(writer, "push const ({value})\n", .{.value = string.?});
}, },
.push_local => |push_local| coral.utf8.print_formatted(writer, "push local <{local}>\n", .{ .push_local => |push_local| coral.utf8.print_formatted(writer, "push local ({local})\n", .{
.local = push_local, .local = push_local,
}), }),
.push_top => coral.utf8.print_string(writer, "push top\n"), .push_top => coral.utf8.print_string(writer, "push top\n"),
.push_table => |push_table| coral.utf8.print_formatted(writer, "push table <{count}>\n", .{ .push_table => |push_table| coral.utf8.print_formatted(writer, "push table ({count})\n", .{
.count = push_table, .count = push_table,
}), }),
.push_boxed => coral.utf8.print_string(writer, "push boxed\n"), .push_boxed => coral.utf8.print_string(writer, "push boxed\n"),
.push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding <{binding}>\n", .{ .push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding ({binding})\n", .{
.binding = push_binding, .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", .{
.builtin = switch (push_builtin) { .builtin = switch (push_builtin) {
.import => "import", .import => "import",
.print => "print", .print => "print",
@ -127,11 +131,11 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
}, },
}), }),
.bind => |bind| coral.utf8.print_formatted(writer, "bind <{count}>\n", .{ .bind => |bind| coral.utf8.print_formatted(writer, "bind ({count})\n", .{
.count = bind, .count = bind,
}), }),
.set_local => |local_set| coral.utf8.print_formatted(writer, "set local <{local}>\n", .{ .set_local => |local_set| coral.utf8.print_formatted(writer, "set local ({local})\n", .{
.local = local_set, .local = local_set,
}), }),
@ -139,7 +143,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
.set_box => coral.utf8.print_string(writer, "set 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}),
.not => coral.utf8.print_string(writer, "not\n"), .not => coral.utf8.print_string(writer, "not\n"),
.neg => coral.utf8.print_string(writer, "neg\n"), .neg => coral.utf8.print_string(writer, "neg\n"),
.add => coral.utf8.print_string(writer, "add\n"), .add => coral.utf8.print_string(writer, "add\n"),
@ -151,19 +155,19 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
.clt => coral.utf8.print_string(writer, "clt\n"), .clt => coral.utf8.print_string(writer, "clt\n"),
.cge => coral.utf8.print_string(writer, "cge\n"), .cge => coral.utf8.print_string(writer, "cge\n"),
.cle => coral.utf8.print_string(writer, "cle\n"), .cle => coral.utf8.print_string(writer, "cle\n"),
.jf => |jf| coral.utf8.print_formatted(writer, "jf <{instruction}>\n", .{.instruction = jf}), .jf => |jf| coral.utf8.print_formatted(writer, "jf ({instruction})\n", .{.instruction = jf}),
.jt => |jt| coral.utf8.print_formatted(writer, "jt <{instruction}>\n", .{.instruction = jt}), .jt => |jt| coral.utf8.print_formatted(writer, "jt ({instruction})\n", .{.instruction = jt}),
}; };
} }
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(self: *Self, env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
var opcode_cursor = @as(u32, 0); self.cursor = 0;
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) { while (self.cursor < self.opcodes.values.len) : (self.cursor += 1) {
switch (self.opcodes.values[opcode_cursor]) { switch (self.opcodes.values[self.cursor]) {
.ret => break, .ret => break,
.pop => { .pop => {
@ -306,27 +310,24 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
defer env.discard(box); defer env.discard(box);
const boxed = box.as_boxed() orelse { if (try env.get_boxed(box)) |unboxed| {
return env.raise(error.TypeMismatch, "{typename} is not unboxable", .{.typename = box.typename()}); errdefer env.discard(unboxed);
};
try env.locals.push_one(if (boxed.*) |value| value.acquire() else null); try env.locals.push_one(unboxed);
} else {
try env.locals.push_one(null);
}
}, },
.set_box => { .set_box => {
const box = try env.expect(try env.pop_local()); const box = try env.expect(try env.pop_local());
errdefer env.discard(box); defer env.discard(box);
const boxed = box.as_boxed() orelse { const value = try env.expect(try env.pop_local());
return env.raise(error.TypeMismatch, "{typename} is not unboxable", .{.typename = box.typename()});
};
if (boxed.*) |value| { defer env.discard(box);
env.discard(value); try env.set_boxed(box, value);
}
boxed.* = box;
}, },
.get_dynamic => { .get_dynamic => {
@ -379,7 +380,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
defer env.pop_frame(); defer env.pop_frame();
break: call try env.call_frame(callable, call_frame); break: call try env.call_frame(&call_frame);
}; };
errdefer { errdefer {
@ -532,10 +533,10 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
defer env.discard(condition); defer env.discard(condition);
if (!condition.is_truthy()) { if (!condition.is_truthy()) {
opcode_cursor = jf; self.cursor = jf;
} }
} else { } else {
opcode_cursor = jf; self.cursor = jf;
} }
}, },
@ -544,7 +545,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
defer env.discard(condition); defer env.discard(condition);
if (condition.is_truthy()) { if (condition.is_truthy()) {
opcode_cursor = jt; self.cursor = jt;
} }
} }
}, },
@ -581,8 +582,10 @@ 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),
.lines = LineList.make(env.allocator),
.bindings = &.{}, .bindings = &.{},
.arity = environment.argument_count, .arity = environment.argument_count,
.cursor = 0,
}; };
var compiler = Compiler{ var compiler = Compiler{
@ -595,17 +598,17 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
return chunk; return chunk;
} }
fn syscall_import(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { fn syscall_import(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
return env.import(file.Path.from(&.{try env.unwrap_string(try frame.expect_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: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
env.print(try env.unwrap_string(try frame.expect_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: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_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| {
@ -615,7 +618,7 @@ fn syscall_vec2(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.R
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: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_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| {
@ -635,26 +638,26 @@ pub const typeinfo = &kym.Typeinfo{
.destruct = typeinfo_destruct, .destruct = typeinfo_destruct,
.call = typeinfo_call, .call = typeinfo_call,
.to_string = typeinfo_to_string, .to_string = typeinfo_to_string,
.get_arity = typeinfo_get_arity,
}; };
fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef {
const chunk = @as(*Self, @ptrCast(@alignCast(userdata))); return @as(*Self, @ptrCast(@alignCast(userdata))).execute(env, frame);
if (frame.arg_count < chunk.arity) {
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);
} }
fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void {
@as(*Self, @ptrCast(@alignCast(userdata))).free(env); @as(*Self, @ptrCast(@alignCast(userdata))).free(env);
} }
fn typeinfo_get_arity(userdata: []coral.io.Byte) u8 {
return @as(*Self, @ptrCast(@alignCast(userdata))).arity;
}
fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*kym.RuntimeRef { fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*kym.RuntimeRef {
return env.to_string(@as(*Self, @ptrCast(@alignCast(userdata))).name); return env.to_string(@as(*Self, @ptrCast(@alignCast(userdata))).name);
} }
pub fn write(self: *Self, line: u32, opcode: Opcode) coral.io.AllocationError!void {
try self.opcodes.push_one(opcode);
try self.lines.push_one(line);
}

View File

@ -37,30 +37,30 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
}; };
switch (expression.kind) { switch (expression.kind) {
.nil_literal => try self.chunk.opcodes.push_one(.push_nil), .nil_literal => try self.chunk.write(expression.line, .push_nil),
.true_literal => try self.chunk.opcodes.push_one(.push_true), .true_literal => try self.chunk.write(expression.line, .push_true),
.false_literal => try self.chunk.opcodes.push_one(.push_false), .false_literal => try self.chunk.write(expression.line, .push_false),
.number_literal => |literal| { .number_literal => |literal| {
for (literal) |codepoint| { for (literal) |codepoint| {
if (codepoint == '.') { if (codepoint == '.') {
return self.chunk.opcodes.push_one(.{ return self.chunk.write(expression.line, .{
.push_const = try self.declare_float(number_format.parse(literal, kym.Float) orelse unreachable), .push_const = try self.declare_float(number_format.parse(literal, kym.Float) orelse unreachable),
}); });
} }
} }
try self.chunk.opcodes.push_one(.{ try self.chunk.write(expression.line, .{
.push_const = try self.declare_fixed(number_format.parse(literal, kym.Fixed) orelse unreachable), .push_const = try self.declare_fixed(number_format.parse(literal, kym.Fixed) orelse unreachable),
}); });
}, },
.string_literal => |literal| { .string_literal => |literal| {
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_string(literal)}); try self.chunk.write(expression.line, .{.push_const = try self.declare_string(literal)});
}, },
.symbol_literal => |literal| { .symbol_literal => |literal| {
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(literal)}); try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(literal)});
}, },
.table_construct => |table_construct| { .table_construct => |table_construct| {
@ -71,13 +71,13 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
try self.compile_expression(environment, entry, null); 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.write(expression.line, .push_top);
} }
field_count += 1; field_count += 1;
} }
try self.chunk.opcodes.push_one(.{.push_table = field_count}); try self.chunk.write(expression.line, .{.push_table = field_count});
}, },
.key_value => |key_value| { .key_value => |key_value| {
@ -91,7 +91,7 @@ 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) { if (lambda_construct.environment.capture_count == 0) {
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)}); try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
} else { } else {
const lambda_captures = lambda_construct.environment.get_captures(); const lambda_captures = lambda_construct.environment.get_captures();
var index = lambda_captures.len; var index = lambda_captures.len;
@ -99,14 +99,14 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
while (index != 0) { while (index != 0) {
index -= 1; index -= 1;
try self.chunk.opcodes.push_one(switch (lambda_captures[index]) { try self.chunk.write(expression.line, switch (lambda_captures[index]) {
.declaration_index => |declaration_index| .{.push_local = declaration_index}, .declaration_index => |declaration_index| .{.push_local = declaration_index},
.capture_index => |capture_index| .{.push_binding = capture_index}, .capture_index => |capture_index| .{.push_binding = capture_index},
}); });
} }
try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)}); try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
try self.chunk.opcodes.push_one(.{.bind = lambda_construct.environment.capture_count}); try self.chunk.write(expression.line, .{.bind = lambda_construct.environment.capture_count});
} }
}, },
@ -114,7 +114,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
try self.compile_expression(environment, binary_op.lhs_operand, null); try self.compile_expression(environment, binary_op.lhs_operand, null);
try self.compile_expression(environment, binary_op.rhs_operand, null); try self.compile_expression(environment, binary_op.rhs_operand, null);
try self.chunk.opcodes.push_one(switch (binary_op.operation) { try self.chunk.write(expression.line, switch (binary_op.operation) {
.addition => .add, .addition => .add,
.subtraction => .sub, .subtraction => .sub,
.multiplication => .mul, .multiplication => .mul,
@ -130,7 +130,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, null); try self.compile_expression(environment, unary_op.operand, null);
try self.chunk.opcodes.push_one(switch (unary_op.operation) { try self.chunk.write(expression.line, switch (unary_op.operation) {
.boolean_negation => .not, .boolean_negation => .not,
.numeric_negation => .neg, .numeric_negation => .neg,
}); });
@ -140,25 +140,25 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
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, null); try self.compile_expression(environment, invoke.object, null);
try self.chunk.opcodes.push_one(.{.call = argument_count}); try self.chunk.write(expression.line, .{.call = argument_count});
}, },
.group => |group| try self.compile_expression(environment, group, null), .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.write(expression.line, .{.push_builtin = .import}),
.print_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .print}), .print_builtin => try self.chunk.write(expression.line, .{.push_builtin = .print}),
.vec2_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .vec2}), .vec2_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec2}),
.vec3_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .vec3}), .vec3_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec3}),
.declaration_get => |declaration_get| { .declaration_get => |declaration_get| {
if (get_local_index(environment, declaration_get.declaration)) |index| { if (get_local_index(environment, declaration_get.declaration)) |index| {
return self.chunk.opcodes.push_one(.{.push_local = index}); return self.chunk.write(expression.line, .{.push_local = index});
} }
if (try self.get_binding_index(environment, declaration_get.declaration)) |index| { if (try self.get_binding_index(environment, declaration_get.declaration)) |index| {
try self.chunk.opcodes.push_one(.{.push_binding = index}); try self.chunk.write(expression.line, .{.push_binding = index});
if (is_declaration_boxed(declaration_get.declaration)) { if (is_declaration_boxed(declaration_get.declaration)) {
try self.chunk.opcodes.push_one(.get_box); try self.chunk.write(expression.line, .get_box);
} }
return; return;
@ -171,15 +171,15 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
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); try self.compile_expression(environment, declaration_set.assign, null);
return self.chunk.opcodes.push_one(.{.set_local = index}); return self.chunk.write(expression.line, .{.set_local = index});
} }
if (try self.get_binding_index(environment, declaration_set.declaration)) |index| { if (try self.get_binding_index(environment, declaration_set.declaration)) |index| {
try self.chunk.opcodes.push_one(.{.push_binding = index}); try self.chunk.write(expression.line, .{.push_binding = index});
try self.compile_expression(environment, declaration_set.assign, null); try self.compile_expression(environment, declaration_set.assign, null);
if (is_declaration_boxed(declaration_set.declaration)) { if (is_declaration_boxed(declaration_set.declaration)) {
try self.chunk.opcodes.push_one(.set_box); try self.chunk.write(expression.line, .set_box);
} }
return; return;
@ -190,28 +190,28 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
.field_get => |field_get| { .field_get => |field_get| {
try self.compile_expression(environment, field_get.object, null); 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.write(expression.line, .{.push_const = try self.declare_symbol(field_get.identifier)});
try self.chunk.opcodes.push_one(.get_dynamic); try self.chunk.write(expression.line, .get_dynamic);
}, },
.field_set => |field_set| { .field_set => |field_set| {
try self.compile_expression(environment, field_set.object, null); 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.write(expression.line, .{.push_const = try self.declare_symbol(field_set.identifier)});
try self.compile_expression(environment, field_set.assign, null); try self.compile_expression(environment, field_set.assign, null);
try self.chunk.opcodes.push_one(.set_dynamic); try self.chunk.write(expression.line, .set_dynamic);
}, },
.subscript_get => |subscript_get| { .subscript_get => |subscript_get| {
try self.compile_expression(environment, subscript_get.object, null); try self.compile_expression(environment, subscript_get.object, null);
try self.compile_expression(environment, subscript_get.index, null); try self.compile_expression(environment, subscript_get.index, null);
try self.chunk.opcodes.push_one(.get_dynamic); try self.chunk.write(expression.line, .get_dynamic);
}, },
.subscript_set => |subscript_set| { .subscript_set => |subscript_set| {
try self.compile_expression(environment, subscript_set.object, null); try self.compile_expression(environment, subscript_set.object, null);
try self.compile_expression(environment, subscript_set.index, null); try self.compile_expression(environment, subscript_set.index, null);
try self.compile_expression(environment, subscript_set.assign, null); try self.compile_expression(environment, subscript_set.assign, null);
try self.chunk.opcodes.push_one(.set_dynamic); try self.chunk.write(expression.line, .set_dynamic);
}, },
} }
} }
@ -221,7 +221,7 @@ pub fn compile_environment(self: Self, environment: *const tree.Environment) kym
const last_statement = try self.compile_statement(environment, statement); const last_statement = try self.compile_statement(environment, statement);
if (last_statement.kind != .@"return") { if (last_statement.kind != .@"return") {
try self.chunk.opcodes.push_one(.push_nil); try self.chunk.write(last_statement.line, .push_nil);
} }
} }
} }
@ -235,16 +235,16 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
if (@"return".returned_expression) |expression| { if (@"return".returned_expression) |expression| {
try self.compile_expression(environment, expression, null); try self.compile_expression(environment, expression, null);
} else { } else {
try self.chunk.opcodes.push_one(.push_nil); try self.chunk.write(current_statement.line, .push_nil);
} }
// TODO: Omit ret calls at ends of chunk. // TODO: Omit ret calls at ends of chunk.
try self.chunk.opcodes.push_one(.ret); try self.chunk.write(current_statement.line, .ret);
}, },
.@"while" => |@"while"| { .@"while" => |@"while"| {
try self.compile_expression(environment, @"while".loop_expression, null); try self.compile_expression(environment, @"while".loop_expression, null);
try self.chunk.opcodes.push_one(.{.jf = 0}); try self.chunk.write(current_statement.line, .{.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));
@ -252,12 +252,12 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
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, null); try self.compile_expression(environment, @"while".loop_expression, null);
try self.chunk.opcodes.push_one(.{.jt = origin_index}); try self.chunk.write(current_statement.line, .{.jt = origin_index});
}, },
.@"if" => |@"if"| { .@"if" => |@"if"| {
try self.compile_expression(environment, @"if".then_expression, null); try self.compile_expression(environment, @"if".then_expression, null);
try self.chunk.opcodes.push_one(.{.jf = 0}); try self.chunk.write(current_statement.line, .{.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));
@ -273,7 +273,7 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier); try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier);
if (is_declaration_boxed(declare.declaration)) { if (is_declaration_boxed(declare.declaration)) {
try self.chunk.opcodes.push_one(.push_boxed); try self.chunk.write(current_statement.line, .push_boxed);
} }
}, },
@ -281,7 +281,7 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s
try self.compile_expression(environment, top_expression, null); 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.write(current_statement.line, .pop);
} }
}, },
} }

View File

@ -7,6 +7,7 @@ const tokens = @import("./tokens.zig");
const tree = @import("./tree.zig"); const tree = @import("./tree.zig");
next: ?*const Self = null, next: ?*const Self = null,
line: u32,
kind: union (enum) { kind: union (enum) {
nil_literal, nil_literal,
@ -80,6 +81,8 @@ pub const BinaryOp = struct {
const unnecessary_temp = expression; const unnecessary_temp = expression;
expression = try root.create_expr(.{ expression = try root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{ .kind = .{
.binary_op = .{ .binary_op = .{
.rhs_operand = try parse_next(root, stream, environment), .rhs_operand = try parse_next(root, stream, environment),
@ -173,6 +176,8 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
} }
return root.create_expr(.{ return root.create_expr(.{
.line = stream.lines_stepped,
.kind = switch (expression.kind) { .kind = switch (expression.kind) {
.declaration_get => |declaration_get| convert: { .declaration_get => |declaration_get| convert: {
if (declaration_get.declaration.is.readonly) { if (declaration_get.declaration.is.readonly) {
@ -239,7 +244,7 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
const unnecessary_temp = expression; const unnecessary_temp = expression;
expression = try root.create_expr(.{ expression = try root.create_expr(.{
.next = null, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.field_get = .{ .field_get = .{
@ -263,7 +268,7 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
const unnecessary_temp = expression; const unnecessary_temp = expression;
expression = try root.create_expr(.{ expression = try root.create_expr(.{
.next = null, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.subscript_get = .{ .subscript_get = .{
@ -281,6 +286,8 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
}, },
.symbol_paren_left => { .symbol_paren_left => {
const lines_stepped = stream.lines_stepped;
stream.skip_newlines(); stream.skip_newlines();
var first_argument = @as(?*Self, null); var first_argument = @as(?*Self, null);
@ -310,7 +317,7 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
const unnecessary_temp = expression; const unnecessary_temp = expression;
expression = try root.create_expr(.{ expression = try root.create_expr(.{
.next = null, .line = lines_stepped,
.kind = .{ .kind = .{
.invoke = .{ .invoke = .{
@ -341,37 +348,55 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .{.group = expression}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.group = expression},
});
}, },
.keyword_nil => { .keyword_nil => {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .nil_literal}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .nil_literal,
});
}, },
.keyword_true => { .keyword_true => {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .true_literal}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .true_literal,
});
}, },
.keyword_false => { .keyword_false => {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .false_literal}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .false_literal,
});
}, },
.number => |value| { .number => |value| {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .{.number_literal = value}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.number_literal = value},
});
}, },
.string => |value| { .string => |value| {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .{.string_literal = value}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.string_literal = value},
});
}, },
.symbol_at => { .symbol_at => {
@ -385,19 +410,31 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
stream.skip_newlines(); stream.skip_newlines();
if (coral.io.are_equal(identifier, "import")) { if (coral.io.are_equal(identifier, "import")) {
return root.create_expr(.{.kind = .import_builtin}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .import_builtin,
});
} }
if (coral.io.are_equal(identifier, "print")) { if (coral.io.are_equal(identifier, "print")) {
return root.create_expr(.{.kind = .print_builtin}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .print_builtin,
});
} }
if (coral.io.are_equal(identifier, "vec2")) { if (coral.io.are_equal(identifier, "vec2")) {
return root.create_expr(.{.kind = .vec2_builtin}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .vec2_builtin,
});
} }
if (coral.io.are_equal(identifier, "vec3")) { if (coral.io.are_equal(identifier, "vec3")) {
return root.create_expr(.{.kind = .vec3_builtin}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .vec3_builtin,
});
} }
return root.report_error(stream, "unexpected identifier after `@`", .{}); return root.report_error(stream, "unexpected identifier after `@`", .{});
@ -413,13 +450,18 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .{.symbol_literal = identifier}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.symbol_literal = identifier},
});
}, },
.identifier => |identifier| { .identifier => |identifier| {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{ return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{ .kind = .{
.declaration_get = .{ .declaration_get = .{
.declaration = (try environment.resolve_declaration(identifier)) orelse { .declaration = (try environment.resolve_declaration(identifier)) orelse {
@ -486,14 +528,20 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .{.lambda_construct = .{.environment = lambda_environment}}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.lambda_construct = .{.environment = lambda_environment}},
});
}, },
.symbol_brace_left => { .symbol_brace_left => {
stream.skip_newlines(); stream.skip_newlines();
if (stream.token == .symbol_brace_right) { if (stream.token == .symbol_brace_right) {
return root.create_expr(.{.kind = .{.table_construct = .{.entry = null}}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.table_construct = .{.entry = null}},
});
} }
const first_entry = try parse_table_entry(root, stream, environment); const first_entry = try parse_table_entry(root, stream, environment);
@ -518,14 +566,17 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{.kind = .{.table_construct = .{.entry = first_entry}}}); return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.table_construct = .{.entry = first_entry}},
});
}, },
.symbol_minus => { .symbol_minus => {
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{ return root.create_expr(.{
.next = null, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.unary_op = .{ .unary_op = .{
@ -540,7 +591,7 @@ 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, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.unary_op = .{ .unary_op = .{
@ -574,10 +625,16 @@ fn parse_table_entry(root: *tree.Root, stream: *tokens.Stream, environment: *tre
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{ return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{ .kind = .{
.key_value = .{ .key_value = .{
.value = try parse(root, stream, environment), .value = try parse(root, stream, environment),
.key = try root.create_expr(.{.kind = .{.symbol_literal = field}}),
.key = try root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{.symbol_literal = field},
}),
}, },
}, },
}); });
@ -601,6 +658,8 @@ fn parse_table_entry(root: *tree.Root, stream: *tokens.Stream, environment: *tre
stream.skip_newlines(); stream.skip_newlines();
return root.create_expr(.{ return root.create_expr(.{
.line = stream.lines_stepped,
.kind = .{ .kind = .{
.key_value = .{ .key_value = .{
.value = try parse(root, stream, environment), .value = try parse(root, stream, environment),

View File

@ -7,6 +7,7 @@ const tokens = @import("./tokens.zig");
const tree = @import("./tree.zig"); const tree = @import("./tree.zig");
next: ?*const Self = null, next: ?*const Self = null,
line: u32,
kind: union (enum) { kind: union (enum) {
top_expression: *const Expr, top_expression: *const Expr,
@ -45,6 +46,7 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
if (stream.token != .end and stream.token != .newline) { if (stream.token != .end and stream.token != .newline) {
return root.create_stmt(.{ return root.create_stmt(.{
.line = stream.lines_stepped,
.kind = .{.@"return" = .{.returned_expression = try Expr.parse(root, stream, environment)}}, .kind = .{.@"return" = .{.returned_expression = try Expr.parse(root, stream, environment)}},
}); });
} }
@ -53,7 +55,10 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
return root.report_error(stream, "expected end or newline after return statement", .{}); return root.report_error(stream, "expected end or newline after return statement", .{});
} }
return root.create_stmt(.{.kind = .{.@"return" = .{.returned_expression = null}}}); return root.create_stmt(.{
.line = stream.lines_stepped,
.kind = .{.@"return" = .{.returned_expression = null}},
});
}, },
.keyword_while => { .keyword_while => {
@ -83,6 +88,8 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
} }
return root.create_stmt(.{ return root.create_stmt(.{
.line = stream.lines_stepped,
.kind = .{ .kind = .{
.@"while" = .{ .@"while" = .{
.loop = first_statement, .loop = first_statement,
@ -113,6 +120,8 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
stream.skip_newlines(); stream.skip_newlines();
return root.create_stmt(.{ return root.create_stmt(.{
.line = stream.lines_stepped,
.kind = .{ .kind = .{
.declare = .{ .declare = .{
.initial_expression = try Expr.parse(root, stream, environment), .initial_expression = try Expr.parse(root, stream, environment),
@ -135,7 +144,10 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
.keyword_if => return parse_branch(root, stream, environment), .keyword_if => return parse_branch(root, stream, environment),
else => return root.create_stmt(.{.kind = .{.top_expression = try Expr.parse(root, stream, environment)}}), else => return root.create_stmt(.{
.line = stream.lines_stepped,
.kind = .{.top_expression = try Expr.parse(root, stream, environment)},
}),
} }
} }
@ -159,7 +171,7 @@ fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
stream.skip_newlines(); stream.skip_newlines();
return root.create_stmt(.{ return root.create_stmt(.{
.next = null, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.@"if" = .{ .@"if" = .{
@ -193,7 +205,7 @@ fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
stream.skip_newlines(); stream.skip_newlines();
return root.create_stmt(.{ return root.create_stmt(.{
.next = null, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.@"if" = .{ .@"if" = .{
@ -207,7 +219,7 @@ fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env
.keyword_elif => { .keyword_elif => {
return root.create_stmt(.{ return root.create_stmt(.{
.next = null, .line = stream.lines_stepped,
.kind = .{ .kind = .{
.@"if" = .{ .@"if" = .{

View File

@ -47,15 +47,15 @@ pub const typeinfo = &kym.Typeinfo{
.size = @sizeOf(Self), .size = @sizeOf(Self),
.name = "table", .name = "table",
.destruct = typeinfo_destruct, .destruct = typeinfo_destruct,
.get = typeinfo_get, .get_index = typeinfo_get_index,
.set = typeinfo_set, .set_index = typeinfo_set_index,
}; };
fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void {
@as(*Self, @ptrCast(@alignCast(userdata))).free(env); @as(*Self, @ptrCast(@alignCast(userdata))).free(env);
} }
fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef { fn typeinfo_get_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef {
const table = @as(*Self, @ptrCast(@alignCast(userdata))); const table = @as(*Self, @ptrCast(@alignCast(userdata)));
const acquired_index = index.acquire(); const acquired_index = index.acquire();
@ -79,7 +79,7 @@ fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const k
return null; return null;
} }
fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void { fn typeinfo_set_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void {
const table = @as(*Self, @ptrCast(@alignCast(userdata))); const table = @as(*Self, @ptrCast(@alignCast(userdata)));
const acquired_index = index.acquire(); const acquired_index = index.acquire();

View File

@ -103,7 +103,7 @@ pub const Token = union(enum) {
pub const Stream = struct { pub const Stream = struct {
source: []const coral.io.Byte, source: []const coral.io.Byte,
lines_stepped: usize = 1, lines_stepped: u32 = 1,
token: Token = .newline, token: Token = .newline,
pub fn skip_newlines(self: *Stream) void { pub fn skip_newlines(self: *Stream) void {