Add Function Literal Syntax to Ona Script #39

Merged
kayomn merged 5 commits from kym-functions into main 2023-09-02 21:15:28 +02:00
3 changed files with 201 additions and 243 deletions
Showing only changes of commit 9eef1074f9 - Show all commits

View File

@ -13,7 +13,7 @@ pub const Manifest = struct {
tick_rate: f32 = 60.0, tick_rate: f32 = 60.0,
pub fn load(self: *Manifest, env: *kym.RuntimeEnv) kym.RuntimeError!void { pub fn load(self: *Manifest, env: *kym.RuntimeEnv) kym.RuntimeError!void {
const manifest = try env.expect(try env.import(file.Path.from(&.{"app.ona"}))); const manifest = try env.import(file.Path.from(&.{"app.ona"})) orelse return;
defer env.discard(manifest); defer env.discard(manifest);

View File

@ -4,6 +4,24 @@ const coral = @import("coral");
const file = @import("./file.zig"); const file = @import("./file.zig");
pub const Frame = struct {
name: []const coral.io.Byte = "",
arg_count: u8,
locals_top: usize,
pub fn args(self: *const Frame, env: *RuntimeEnv) []const ?*const RuntimeRef {
return env.locals.values[self.locals_top .. (self.locals_top + self.arg_count)];
}
pub fn get_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) RuntimeError!*const RuntimeRef {
return self.has_arg(env, arg_index) orelse env.raise(error.BadOperation, "nil reference");
}
pub fn has_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) ?*const RuntimeRef {
return if (arg_index >= self.arg_count) null else env.locals.values[self.locals_top + arg_index];
}
};
pub const Fixed = i32; pub const Fixed = i32;
pub const Float = f64; pub const Float = f64;
@ -44,7 +62,6 @@ pub const RuntimeEnv = struct {
push_false, push_false,
push_const: u16, push_const: u16,
push_local: u8, push_local: u8,
push_arg: u8,
push_table: u32, push_table: u32,
push_builtin: Builtin, push_builtin: Builtin,
local_set: u8, local_set: u8,
@ -73,7 +90,6 @@ pub const RuntimeEnv = struct {
const OpcodeList = coral.list.Stack(Opcode); const OpcodeList = coral.list.Stack(Opcode);
const CompilationUnit = struct { const CompilationUnit = struct {
args: []const []const coral.io.Byte,
locals_buffer: [255]Local = [_]Local{.{}} ** 255, locals_buffer: [255]Local = [_]Local{.{}} ** 255,
locals_count: u8 = 0, locals_count: u8 = 0,
@ -243,10 +259,6 @@ pub const RuntimeEnv = struct {
return chunk.opcodes.push_one(.{.push_local = local.index}); return chunk.opcodes.push_one(.{.push_local = local.index});
} }
if (self.resolve_arg(local_get.identifier)) |arg| {
return chunk.opcodes.push_one(.{.push_arg = arg});
}
return chunk.env.raise(error.OutOfMemory, "undefined local"); return chunk.env.raise(error.OutOfMemory, "undefined local");
}, },
@ -317,16 +329,26 @@ pub const RuntimeEnv = struct {
try self.compile_expression(chunk, declare.assigned_expression); try self.compile_expression(chunk, declare.assigned_expression);
if (self.locals_count == self.locals_buffer.len) { switch (declare.storage) {
return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals"); .@"var" => {
} if (!self.declare_local(.{
self.locals_buffer[self.locals_count] = .{
.identifier = declare.identifier, .identifier = declare.identifier,
.is_readonly = declare.storage != .variant, .is_readonly = false,
}; })) {
return chunk.env.raise(error.BadSyntax, "too many locals");
}
},
self.locals_count += 1; .let => {
// TODO: investigate constant folding.
if (!self.declare_local(.{
.identifier = declare.identifier,
.is_readonly = false,
})) {
return chunk.env.raise(error.BadSyntax, "too many locals");
}
},
}
}, },
.block => |block| { .block => |block| {
@ -378,16 +400,15 @@ pub const RuntimeEnv = struct {
} }
} }
fn resolve_arg(self: *CompilationUnit, arg_identifier: []const coral.io.Byte) ?u8 { fn declare_local(self: *CompilationUnit, local: Local) bool {
var index = @as(u8, 0); if (self.locals_count == self.locals_buffer.len) {
return false;
while (index < self.args.len) {
if (coral.io.are_equal(self.args[index], arg_identifier)) {
return index;
}
} }
return null; self.locals_buffer[self.locals_count] = local;
self.locals_count += 1;
return true;
} }
fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?ResolvedLocal { fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?ResolvedLocal {
@ -415,9 +436,18 @@ pub const RuntimeEnv = struct {
}; };
fn compile(self: *Chunk, statements: []const ast.Statement, args: []const []const coral.io.Byte) RuntimeError!void { fn compile(self: *Chunk, statements: []const ast.Statement, args: []const []const coral.io.Byte) RuntimeError!void {
var unit = CompilationUnit{.args = args}; var unit = CompilationUnit{};
var has_returned = false; var has_returned = false;
for (args) |arg| {
if (!unit.declare_local(.{
.is_readonly = true,
.identifier = arg,
})) {
return self.env.raise(error.BadSyntax, "too many arguments");
}
}
for (statements) |statement| { for (statements) |statement| {
try unit.compile_statement(self, statement); try unit.compile_statement(self, statement);
@ -456,7 +486,7 @@ pub const RuntimeEnv = struct {
return @intCast(self.constants.values.len - 1); return @intCast(self.constants.values.len - 1);
} }
fn execute(self: *Chunk) RuntimeError!?*RuntimeRef { fn execute(self: *Chunk, frame: Frame) RuntimeError!?*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 < self.opcodes.values.len) : (opcode_cursor += 1) {
@ -484,25 +514,13 @@ pub const RuntimeEnv = struct {
return self.env.raise(error.IllegalState, "invalid local"); return self.env.raise(error.IllegalState, "invalid local");
} }
if (self.env.locals.values[push_local]) |local| { if (self.env.locals.values[frame.locals_top + push_local]) |local| {
try self.env.locals.push_one(try self.env.acquire(local)); try self.env.locals.push_one(try self.env.acquire(local));
} else { } else {
try self.env.locals.push_one(null); try self.env.locals.push_one(null);
} }
}, },
.push_arg => |push_arg| {
const arg = try self.env.acquire_arg(push_arg);
errdefer {
if (arg) |ref| {
self.env.discard(ref);
}
}
try self.env.locals.push_one(arg);
},
.push_table => |push_table| { .push_table => |push_table| {
const table = try self.env.new_table(); const table = try self.env.new_table();
@ -511,26 +529,20 @@ pub const RuntimeEnv = struct {
{ {
const dynamic = table.object().payload.dynamic; const dynamic = table.object().payload.dynamic;
const userdata = dynamic.userdata(); const userdata = dynamic.userdata();
const table_set = dynamic.typeinfo().set;
var popped = @as(usize, 0); var popped = @as(usize, 0);
while (popped < push_table) : (popped += 1) { while (popped < push_table) : (popped += 1) {
const index = try self.env.expect(try self.pop_local()); const index = try self.expect(try self.pop_local());
defer self.env.discard(index); defer self.env.discard(index);
const maybe_value = try self.pop_local(); if (try self.pop_local()) |value| {
defer self.env.discard(value);
defer { try table_set(self.env, userdata, index, value);
if (maybe_value) |value| {
self.env.discard(value);
} }
} }
try dynamic.typeinfo().set(.{
.userdata = userdata,
.env = self.env,
}, index, maybe_value);
}
} }
try self.env.locals.push_one(table); try self.env.locals.push_one(table);
@ -550,7 +562,7 @@ pub const RuntimeEnv = struct {
}, },
.local_set => |local_set| { .local_set => |local_set| {
const local = &self.env.locals.values[local_set]; const local = &self.env.locals.values[frame.locals_top + local_set];
if (local.*) |previous_local| { if (local.*) |previous_local| {
self.env.discard(previous_local); self.env.discard(previous_local);
@ -560,11 +572,11 @@ pub const RuntimeEnv = struct {
}, },
.object_get => { .object_get => {
const index = try self.env.expect(try self.pop_local()); const index = try self.expect(try self.pop_local());
defer self.env.discard(index); defer self.env.discard(index);
const indexable = try self.env.expect(try self.pop_local()); const indexable = try self.expect(try self.pop_local());
defer self.env.discard(indexable); defer self.env.discard(indexable);
@ -605,28 +617,17 @@ pub const RuntimeEnv = struct {
.object_call => |object_call| { .object_call => |object_call| {
const result = call: { const result = call: {
const callable = try self.env.expect(try self.pop_local()); const callable = try self.expect(try self.pop_local());
defer self.env.discard(callable); defer self.env.discard(callable);
try self.env.frames.push_one(.{ const call_frame = try self.env.push_frame(object_call);
.name = "<chunk>",
.arg_count = object_call,
.locals_top = self.env.locals.values.len,
});
defer coral.debug.assert(self.env.frames.pop() != null); defer self.env.pop_frame();
const payload = callable.object().payload;
break: call try switch (payload) {
.syscall => |syscall| syscall(self.env),
.dynamic => |dynamic| dynamic.typeinfo().call(.{
.userdata = dynamic.userdata(),
.env = self.env,
}),
break: call try switch (callable.object().payload) {
.syscall => |syscall| syscall(self.env, call_frame),
.dynamic => |dynamic| dynamic.typeinfo().call(self.env, dynamic.userdata(), call_frame),
else => self.env.raise(error.TypeMismatch, "object is not callable"), else => self.env.raise(error.TypeMismatch, "object is not callable"),
}; };
}; };
@ -637,12 +638,6 @@ pub const RuntimeEnv = struct {
} }
} }
for (0 .. object_call) |_| {
if (try self.pop_local()) |popped_arg| {
self.env.discard(popped_arg);
}
}
try self.env.locals.push_one(result); try self.env.locals.push_one(result);
}, },
@ -657,7 +652,7 @@ pub const RuntimeEnv = struct {
}, },
.neg => { .neg => {
const value = try self.env.expect(try self.pop_local()); const value = try self.expect(try self.pop_local());
defer self.env.discard(value); defer self.env.discard(value);
@ -669,11 +664,11 @@ pub const RuntimeEnv = struct {
}, },
.add => { .add => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -709,11 +704,11 @@ pub const RuntimeEnv = struct {
}, },
.sub => { .sub => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -749,11 +744,11 @@ pub const RuntimeEnv = struct {
}, },
.mul => { .mul => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -789,11 +784,11 @@ pub const RuntimeEnv = struct {
}, },
.div => { .div => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -845,11 +840,11 @@ pub const RuntimeEnv = struct {
}, },
.cgt => { .cgt => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -871,11 +866,11 @@ pub const RuntimeEnv = struct {
}, },
.clt => { .clt => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -897,11 +892,11 @@ pub const RuntimeEnv = struct {
}, },
.cge => { .cge => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -923,11 +918,11 @@ pub const RuntimeEnv = struct {
}, },
.cle => { .cle => {
const rhs = try self.env.expect(try self.pop_local()); const rhs = try self.expect(try self.pop_local());
defer self.env.discard(rhs); defer self.env.discard(rhs);
const lhs = try self.env.expect(try self.pop_local()); const lhs = try self.expect(try self.pop_local());
defer self.env.discard(lhs); defer self.env.discard(lhs);
@ -977,6 +972,10 @@ pub const RuntimeEnv = struct {
return self.pop_local(); return self.pop_local();
} }
fn expect(self: *Chunk, value: ?*RuntimeRef) RuntimeError!*RuntimeRef {
return value orelse self.env.raise(error.TypeMismatch, "nil reference");
}
fn free(self: *Chunk) void { fn free(self: *Chunk) void {
while (self.constants.pop()) |constant| { while (self.constants.pop()) |constant| {
self.env.discard(constant); self.env.discard(constant);
@ -1001,28 +1000,26 @@ pub const RuntimeEnv = struct {
return self.env.locals.pop() orelse self.env.raise(error.IllegalState, "stack underflow"); return self.env.locals.pop() orelse self.env.raise(error.IllegalState, "stack underflow");
} }
fn typeinfo_call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef { fn typeinfo_call(env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef {
const chunk = @as(*Chunk, @ptrCast(@alignCast(method.userdata))); const chunk = @as(*Chunk, @ptrCast(@alignCast(userdata)));
if ((method.env.view_args() catch unreachable).len < chunk.arity) { if (frame.arg_count < chunk.arity) {
return method.env.raise(error.BadOperation, "expected more arguments"); return env.raise(error.BadOperation, "expected more arguments");
} }
return chunk.execute(); return chunk.execute(frame);
} }
fn typeinfo_destruct(method: Typeinfo.Method) void { fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void {
@as(*Chunk, @ptrCast(@alignCast(method.userdata))).free(); _ = env;
@as(*Chunk, @ptrCast(@alignCast(userdata))).free();
} }
}; };
const ConstList = coral.list.Stack(*RuntimeRef); const ConstList = coral.list.Stack(*RuntimeRef);
const FrameStack = coral.list.Stack(struct { const FrameStack = coral.list.Stack(Frame);
name: []const coral.io.Byte,
arg_count: u8,
locals_top: usize,
});
pub const Options = struct { pub const Options = struct {
import_access: file.Access = .null, import_access: file.Access = .null,
@ -1046,15 +1043,15 @@ pub const RuntimeEnv = struct {
associative: RefTable, associative: RefTable,
contiguous: RefList, contiguous: RefList,
fn typeinfo_destruct(method: Typeinfo.Method) void { fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void {
const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); const table = @as(*Table, @ptrCast(@alignCast(userdata)));
{ {
var field_iterable = table.associative.as_iterable(); var field_iterable = table.associative.as_iterable();
while (field_iterable.next()) |entry| { while (field_iterable.next()) |entry| {
method.env.discard(entry.key); env.discard(entry.key);
method.env.discard(entry.value); env.discard(entry.value);
} }
} }
@ -1062,18 +1059,18 @@ pub const RuntimeEnv = struct {
while (table.contiguous.pop()) |value| { while (table.contiguous.pop()) |value| {
if (value) |ref| { if (value) |ref| {
method.env.discard(ref); env.discard(ref);
} }
} }
table.contiguous.free(); table.contiguous.free();
} }
fn typeinfo_get(method: Typeinfo.Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef { fn typeinfo_get(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); const table = @as(*Table, @ptrCast(@alignCast(userdata)));
const acquired_index = try method.env.acquire(index); const acquired_index = try env.acquire(index);
defer method.env.discard(acquired_index); defer env.discard(acquired_index);
if (acquired_index.is_fixed()) |fixed| { if (acquired_index.is_fixed()) |fixed| {
if (fixed < 0) { if (fixed < 0) {
@ -1082,22 +1079,22 @@ pub const RuntimeEnv = struct {
} }
if (fixed < table.contiguous.values.len) { if (fixed < table.contiguous.values.len) {
return method.env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null); return env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null);
} }
} }
if (table.associative.lookup(acquired_index)) |value_ref| { if (table.associative.lookup(acquired_index)) |value_ref| {
return method.env.acquire(value_ref); return env.acquire(value_ref);
} }
return null; return null;
} }
fn typeinfo_set(method: Typeinfo.Method, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { fn typeinfo_set(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); const table = @as(*Table, @ptrCast(@alignCast(userdata)));
const acquired_index = try method.env.acquire(index); const acquired_index = try env.acquire(index);
errdefer method.env.discard(acquired_index); errdefer env.discard(acquired_index);
if (acquired_index.is_fixed()) |fixed| { if (acquired_index.is_fixed()) |fixed| {
if (fixed < 0) { if (fixed < 0) {
@ -1109,29 +1106,29 @@ pub const RuntimeEnv = struct {
const maybe_replacing = &table.contiguous.values[@intCast(fixed)]; const maybe_replacing = &table.contiguous.values[@intCast(fixed)];
if (maybe_replacing.*) |replacing| { if (maybe_replacing.*) |replacing| {
method.env.discard(replacing); env.discard(replacing);
} }
maybe_replacing.* = if (value) |ref| try method.env.acquire(ref) else null; maybe_replacing.* = if (value) |ref| try env.acquire(ref) else null;
return; return;
} }
} }
const acquired_value = try method.env.acquire(value orelse { const acquired_value = try env.acquire(value orelse {
if (table.associative.remove(acquired_index)) |removed| { if (table.associative.remove(acquired_index)) |removed| {
method.env.discard(removed.key); env.discard(removed.key);
method.env.discard(removed.value); env.discard(removed.value);
} }
return; return;
}); });
errdefer method.env.discard(acquired_value); errdefer env.discard(acquired_value);
if (try table.associative.replace(acquired_index, acquired_value)) |replaced| { if (try table.associative.replace(acquired_index, acquired_value)) |replaced| {
method.env.discard(replaced.key); env.discard(replaced.key);
method.env.discard(replaced.value); env.discard(replaced.value);
} }
} }
}; };
@ -1146,30 +1143,15 @@ pub const RuntimeEnv = struct {
return @ptrCast(object); return @ptrCast(object);
} }
pub fn acquire_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*RuntimeRef {
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
if (index < frame.arg_count) {
if (self.locals.values[(frame.locals_top - frame.arg_count) + index]) |local| {
return self.acquire(local);
}
}
return null;
}
pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef { pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef {
try self.locals.push_all(args); // TODO: Handle errors.
for (args) |arg| {
try self.locals.push_one(try self.acquire(arg));
}
defer coral.io.assert(self.locals.drop(args.len)); try self.push_frame(args.len);
try self.frames.push_one(.{ defer self.pop_frame();
.name = "<native>",
.arg_count = args.len,
.locals_top = self.locals.values.len,
});
defer coral.io.assert(self.frames.pop() != null);
return switch (callable.object().payload) { return switch (callable.object().payload) {
.syscall => |syscall| syscall(self.env), .syscall => |syscall| syscall(self.env),
@ -1201,10 +1183,7 @@ pub const RuntimeEnv = struct {
.dynamic => |dynamic| { .dynamic => |dynamic| {
if (dynamic.typeinfo().destruct) |destruct| { if (dynamic.typeinfo().destruct) |destruct| {
destruct(.{ destruct(self, dynamic.userdata());
.userdata = dynamic.userdata(),
.env = self,
});
} }
self.allocator.deallocate(dynamic.unpack()); self.allocator.deallocate(dynamic.unpack());
@ -1236,19 +1215,11 @@ pub const RuntimeEnv = struct {
error.OutOfMemory => error.OutOfMemory, error.OutOfMemory => error.OutOfMemory,
}, &.{}); }, &.{});
try self.frames.push_one(.{ const frame = try self.push_frame(0);
.name = file_name,
.arg_count = 0,
.locals_top = self.locals.values.len,
});
defer coral.debug.assert(self.frames.pop() != null); defer self.pop_frame();
return chunk.execute(); return chunk.execute(frame);
}
pub fn expect(self: *RuntimeEnv, value: ?*RuntimeRef) RuntimeError!*RuntimeRef {
return value orelse self.raise(error.TypeMismatch, "nil reference");
} }
pub fn free(self: *RuntimeEnv) void { pub fn free(self: *RuntimeEnv) void {
@ -1273,6 +1244,14 @@ pub const RuntimeEnv = struct {
pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef { pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
return switch (indexable.object().payload) { return switch (indexable.object().payload) {
.false => self.raise(error.TypeMismatch, "false is not get-indexable"),
.true => self.raise(error.TypeMismatch, "true is not get-indexable"),
.fixed => self.raise(error.TypeMismatch, "fixed is not get-indexable"),
.float => self.raise(error.TypeMismatch, "float is not get-indexable"),
.string => self.raise(error.TypeMismatch, "string is not get-indexable"),
.symbol => self.raise(error.TypeMismatch, "symbol is not get-indexable"),
.syscall => self.raise(error.TypeMismatch, "syscall is not get-indexable"),
.vector2 => |vector2| swizzle: { .vector2 => |vector2| swizzle: {
const swizzle_symbol = try self.unbox_symbol(index); const swizzle_symbol = try self.unbox_symbol(index);
var swizzle_buffer = [_]f32{0} ** 3; var swizzle_buffer = [_]f32{0} ** 3;
@ -1324,12 +1303,7 @@ pub const RuntimeEnv = struct {
} }
}, },
.dynamic => |dynamic| dynamic.typeinfo().get(.{ .dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index),
.userdata = dynamic.userdata(),
.env = self,
}, index),
else => self.raise(error.TypeMismatch, "object is not get-indexable"),
}; };
} }
@ -1474,6 +1448,29 @@ pub const RuntimeEnv = struct {
} }
} }
fn pop_frame(self: *RuntimeEnv) void {
var to_pop = self.locals.values.len - (self.frames.pop() orelse unreachable).locals_top;
while (to_pop != 0) {
if (self.locals.pop() orelse unreachable) |local| {
self.discard(local);
}
to_pop -= 1;
}
}
fn push_frame(self: *RuntimeEnv, arg_count: u8) RuntimeError!Frame {
const frame = Frame{
.arg_count = arg_count,
.locals_top = self.locals.values.len - arg_count,
};
try self.frames.push_one(frame);
return frame;
}
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError { pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
self.print_error(message); self.print_error(message);
@ -1494,11 +1491,7 @@ pub const RuntimeEnv = struct {
pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
return switch (indexable.object().payload) { return switch (indexable.object().payload) {
.dynamic => |dynamic| dynamic.typeinfo().set(.{ .dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value),
.userdata = dynamic.userdata(),
.env = self,
}, index, value),
else => self.raise(error.TypeMismatch, "object is not set-indexable"), else => self.raise(error.TypeMismatch, "object is not set-indexable"),
}; };
} }
@ -1540,12 +1533,6 @@ pub const RuntimeEnv = struct {
else => env.raise(error.TypeMismatch, "expected symbol object") else => env.raise(error.TypeMismatch, "expected symbol object")
}; };
} }
pub fn view_args(self: *RuntimeEnv) RuntimeError![]const ?*const RuntimeRef {
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
return self.locals.values[(frame.locals_top - frame.arg_count) ..];
}
}; };
pub const RuntimeError = coral.io.AllocationError || error { pub const RuntimeError = coral.io.AllocationError || error {
@ -1720,31 +1707,26 @@ pub const RuntimeRef = opaque {
} }
}; };
pub const Syscall = fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef; pub const Syscall = fn (env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef;
pub const Typeinfo = struct { pub const Typeinfo = struct {
name: []const coral.io.Byte, name: []const coral.io.Byte,
size: usize, size: usize,
destruct: ?*const fn (method: Method) void = null, destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null,
call: *const fn (method: Method) RuntimeError!?*RuntimeRef = default_call, call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call,
get: *const fn (method: Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get,
set: *const fn (method: Method, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
pub const Method = struct { fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef {
env: *RuntimeEnv, return env.raise(error.BadOperation, "object is not callable");
userdata: []coral.io.Byte,
};
fn default_call(method: Method) RuntimeError!?*RuntimeRef {
return method.env.raise(error.TypeMismatch, "object is not callable");
} }
fn default_get(method: Method, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
return method.env.raise(error.TypeMismatch, "object is not index-gettable"); return env.raise(error.BadOperation, "object is not get-indexable");
} }
fn default_set(method: Method, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
return method.env.raise(error.TypeMismatch, "object is not index-settable"); return env.raise(error.BadOperation, "object is not set-indexable");
} }
}; };
@ -1772,59 +1754,35 @@ pub fn get_key(env: *RuntimeEnv, indexable: *RuntimeRef, key: []const coral.io.B
return env.get(indexable, key_string); return env.get(indexable, key_string);
} }
fn syscall_import(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { fn syscall_import(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
const arg = try env.expect(try env.acquire_arg(0)); return env.import(file.Path.from(&.{try env.unbox_string(try frame.get_arg(env, 0))}));
defer env.discard(arg);
return env.import(file.Path.from(&.{try env.unbox_string(arg)}));
} }
fn syscall_print(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { fn syscall_print(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
const arg = try env.expect(try env.acquire_arg(0)); env.print(try env.unbox_string(try frame.get_arg(env, 0)));
defer env.discard(arg);
env.print(try env.unbox_string(arg));
return null; return null;
} }
fn syscall_vec2(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { fn syscall_vec2(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
const x = decode: { const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
const value = try env.expect(try env.acquire_arg(0));
defer env.discard(value);
break: decode @as(f32, @floatCast(try env.unbox_float(value)));
};
if (try env.acquire_arg(1)) |y| {
defer env.discard(y);
if (frame.has_arg(env, 1)) |y| {
return env.new_vector2(x, @floatCast(try env.unbox_float(y))); return env.new_vector2(x, @floatCast(try env.unbox_float(y)));
} }
return env.new_vector2(x, x); return env.new_vector2(x, x);
} }
fn syscall_vec3(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { fn syscall_vec3(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
const x = decode: { const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
const value = try env.expect(try env.acquire_arg(0));
defer env.discard(value); if (frame.has_arg(env, 1)) |y| {
return env.new_vector3(
break: decode @as(f32, @floatCast(try env.unbox_float(value))); x,
}; @floatCast(try env.unbox_float(y)),
@floatCast(try env.unbox_float(try frame.get_arg(env, 2))),
if (try env.acquire_arg(1)) |y| { );
defer env.discard(y);
const z = try env.expect(try env.acquire_arg(2));
defer env.discard(z);
return env.new_vector3(x, @floatCast(try env.unbox_float(y)), @floatCast(try env.unbox_float(z)));
} }
return env.new_vector3(x, x, x); return env.new_vector3(x, x, x);

View File

@ -134,11 +134,6 @@ pub const Expression = union (enum) {
}); });
}; };
pub const DeclarationStorage = enum {
variant,
readonly,
};
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression; const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
const IdentifierList = coral.list.Stack([]const coral.io.Byte); const IdentifierList = coral.list.Stack([]const coral.io.Byte);
@ -171,6 +166,11 @@ pub const Statement = union (enum) {
block: List, block: List,
expression: Expression, expression: Expression,
pub const DeclarationStorage = enum {
@"var",
let,
};
const List = coral.list.Stack(Statement); const List = coral.list.Stack(Statement);
}; };
@ -740,7 +740,7 @@ pub const Tree = struct {
return .{ return .{
.declare = .{ .declare = .{
.assigned_expression = try self.parse_expression(), .assigned_expression = try self.parse_expression(),
.storage = .variant, .storage = .@"var",
.identifier = identifier, .identifier = identifier,
}, },
}; };
@ -765,7 +765,7 @@ pub const Tree = struct {
return .{ return .{
.declare = .{ .declare = .{
.assigned_expression = try self.parse_expression(), .assigned_expression = try self.parse_expression(),
.storage = .readonly, .storage = .let,
.identifier = identifier, .identifier = identifier,
}, },
}; };