Compare commits

..

No commits in common. "a0554d0d2e670675d80214c6b3545c348306cdec" and "0e26b8acde04c040233cef58b2257a72873b17ba" have entirely different histories.

5 changed files with 243 additions and 352 deletions

View File

@ -1,25 +1,18 @@
var i = 0
let pr = lambda (str):
@print("This is a func call")
@print(str)
end
pr("")
while i < 5:
pr("hello, world")
@print("hello, world")
i = i + 1
end
if i > 6:
pr("`i` greater than `6`")
@print("`i` greater than `6`")
elif i == 4:
pr("`i` is equal to `4`")
@print("`i` is equal to `4`")
else:
pr("i'unno")
@print("i'unno")
end
return {

View File

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

View File

@ -4,24 +4,6 @@ const coral = @import("coral");
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 Float = f64;
@ -36,7 +18,6 @@ pub const RuntimeEnv = struct {
const Chunk = struct {
env: *RuntimeEnv,
name: []coral.io.Byte,
arity: u8,
opcodes: OpcodeList,
constants: ConstList,
@ -52,7 +33,6 @@ pub const RuntimeEnv = struct {
float: Float,
string: []const coral.io.Byte,
symbol: []const coral.io.Byte,
chunk: Chunk,
};
const Opcode = union (enum) {
@ -144,39 +124,17 @@ pub const RuntimeEnv = struct {
});
},
.table_literal => |literal| {
if (literal.values.len > coral.math.max_int(@typeInfo(u32).Int)) {
.table_literal => |fields| {
if (fields.values.len > coral.math.max_int(@typeInfo(u32).Int)) {
return error.OutOfMemory;
}
for (literal.values) |field| {
for (fields.values) |field| {
try self.compile_expression(chunk, field.value_expression);
try self.compile_expression(chunk, field.key_expression);
}
try chunk.opcodes.push_one(.{.push_table = @intCast(literal.values.len)});
},
.lambda_literal => |literal| {
if (literal.argument_identifiers.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
return error.OutOfMemory;
}
var lambda_chunk = try Chunk.make(
chunk.env,
"<lambda>",
@intCast(literal.argument_identifiers.values.len),
);
errdefer lambda_chunk.free();
try lambda_chunk.compile(literal.block_statements.values, literal.argument_identifiers.values);
try chunk.opcodes.push_one(.{
.push_const = try chunk.declare_constant(.{
.chunk = lambda_chunk,
}),
});
try chunk.opcodes.push_one(.{.push_table = @intCast(fields.values.len)});
},
.binary_operation => |operation| {
@ -255,11 +213,11 @@ pub const RuntimeEnv = struct {
},
.local_get => |local_get| {
if (self.resolve_local(local_get.identifier)) |local| {
return chunk.opcodes.push_one(.{.push_local = local.index});
}
return chunk.env.raise(error.OutOfMemory, "undefined local");
try chunk.opcodes.push_one(.{
.push_local = (self.resolve_local(local_get.identifier) orelse {
return chunk.env.raise(error.OutOfMemory, "undefined local");
}).index,
});
},
.local_set => |local_set| {
@ -329,26 +287,16 @@ pub const RuntimeEnv = struct {
try self.compile_expression(chunk, declare.assigned_expression);
switch (declare.storage) {
.@"var" => {
if (!self.declare_local(.{
.identifier = declare.identifier,
.is_readonly = false,
})) {
return chunk.env.raise(error.BadSyntax, "too many locals");
}
},
.let => {
// TODO: investigate constant folding.
if (!self.declare_local(.{
.identifier = declare.identifier,
.is_readonly = false,
})) {
return chunk.env.raise(error.BadSyntax, "too many locals");
}
},
if (self.locals_count == self.locals_buffer.len) {
return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals");
}
self.locals_buffer[self.locals_count] = .{
.identifier = declare.identifier,
.is_readonly = declare.storage != .variant,
};
self.locals_count += 1;
},
.block => |block| {
@ -400,17 +348,6 @@ pub const RuntimeEnv = struct {
}
}
fn declare_local(self: *CompilationUnit, local: Local) bool {
if (self.locals_count == self.locals_buffer.len) {
return false;
}
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 {
if (self.locals_count == 0) {
return null;
@ -435,29 +372,11 @@ 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) RuntimeError!void {
var unit = CompilationUnit{};
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| {
try unit.compile_statement(self, statement);
if (statement == .@"return") {
has_returned = true;
}
}
if (!has_returned) {
try self.opcodes.push_one(.push_nil);
}
}
@ -466,27 +385,27 @@ pub const RuntimeEnv = struct {
return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants");
}
const constant_object = try switch (constant) {
const constant_index = self.constants.values.len;
try self.constants.push_one(try switch (constant) {
.fixed => |fixed| self.env.new_fixed(fixed),
.float => |float| self.env.new_float(float),
.string => |string| self.env.new_string(string),
.symbol => |symbol| self.env.new_symbol(symbol),
});
.chunk => |chunk| self.env.new_dynamic(coral.io.bytes_of(&chunk).ptr, &.{
.size = @sizeOf(Chunk),
.name = "lambda",
.destruct = typeinfo_destruct,
.call = typeinfo_call,
}),
};
errdefer self.env.discard(constant_object);
try self.constants.push_one(constant_object);
return @intCast(self.constants.values.len - 1);
return @intCast(constant_index);
}
fn execute(self: *Chunk, frame: Frame) RuntimeError!?*RuntimeRef {
fn execute(self: *Chunk) RuntimeError!?*RuntimeRef {
try self.env.frames.push_one(.{
.arg_count = 0,
.locals_top = self.env.locals.values.len,
.name = self.name,
});
defer coral.debug.assert(self.env.frames.pop() != null);
var opcode_cursor = @as(u32, 0);
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
@ -514,7 +433,7 @@ pub const RuntimeEnv = struct {
return self.env.raise(error.IllegalState, "invalid local");
}
if (self.env.locals.values[frame.locals_top + push_local]) |local| {
if (self.env.locals.values[push_local]) |local| {
try self.env.locals.push_one(try self.env.acquire(local));
} else {
try self.env.locals.push_one(null);
@ -529,19 +448,25 @@ pub const RuntimeEnv = struct {
{
const dynamic = table.object().payload.dynamic;
const userdata = dynamic.userdata();
const table_set = dynamic.typeinfo().set;
var popped = @as(usize, 0);
while (popped < push_table) : (popped += 1) {
const index = try self.expect(try self.pop_local());
const index = try self.env.expect(try self.pop_local());
defer self.env.discard(index);
if (try self.pop_local()) |value| {
defer self.env.discard(value);
const maybe_value = try self.pop_local();
try table_set(self.env, userdata, index, value);
defer {
if (maybe_value) |value| {
self.env.discard(value);
}
}
try dynamic.typeinfo().set(.{
.userdata = userdata,
.env = self.env,
}, index, maybe_value);
}
}
@ -562,7 +487,7 @@ pub const RuntimeEnv = struct {
},
.local_set => |local_set| {
const local = &self.env.locals.values[frame.locals_top + local_set];
const local = &self.env.locals.values[local_set];
if (local.*) |previous_local| {
self.env.discard(previous_local);
@ -572,11 +497,11 @@ pub const RuntimeEnv = struct {
},
.object_get => {
const index = try self.expect(try self.pop_local());
const index = try self.env.expect(try self.pop_local());
defer self.env.discard(index);
const indexable = try self.expect(try self.pop_local());
const indexable = try self.env.expect(try self.pop_local());
defer self.env.discard(indexable);
@ -617,21 +542,36 @@ pub const RuntimeEnv = struct {
.object_call => |object_call| {
const result = call: {
const callable = try self.expect(try self.pop_local());
const callable = try self.env.expect(try self.pop_local());
defer self.env.discard(callable);
const call_frame = try self.env.push_frame(object_call);
try self.env.frames.push_one(.{
.name = "<lambda>",
.arg_count = object_call,
.locals_top = self.env.locals.values.len,
});
defer self.env.pop_frame();
defer coral.debug.assert(self.env.frames.pop() != null);
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),
.syscall => |syscall| syscall(self.env),
.dynamic => |dynamic| dynamic.typeinfo().call(.{
.userdata = dynamic.userdata(),
.env = self.env,
}),
else => self.env.raise(error.TypeMismatch, "object is not callable"),
};
};
for (0 .. object_call) |_| {
if (try self.pop_local()) |popped_arg| {
self.env.discard(popped_arg);
}
}
errdefer {
if (result) |ref| {
self.env.discard(ref);
@ -652,7 +592,7 @@ pub const RuntimeEnv = struct {
},
.neg => {
const value = try self.expect(try self.pop_local());
const value = try self.env.expect(try self.pop_local());
defer self.env.discard(value);
@ -664,11 +604,11 @@ pub const RuntimeEnv = struct {
},
.add => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -704,11 +644,11 @@ pub const RuntimeEnv = struct {
},
.sub => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -744,11 +684,11 @@ pub const RuntimeEnv = struct {
},
.mul => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -784,11 +724,11 @@ pub const RuntimeEnv = struct {
},
.div => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -840,11 +780,11 @@ pub const RuntimeEnv = struct {
},
.cgt => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -866,11 +806,11 @@ pub const RuntimeEnv = struct {
},
.clt => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -892,11 +832,11 @@ pub const RuntimeEnv = struct {
},
.cge => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -918,11 +858,11 @@ pub const RuntimeEnv = struct {
},
.cle => {
const rhs = try self.expect(try self.pop_local());
const rhs = try self.env.expect(try self.pop_local());
defer self.env.discard(rhs);
const lhs = try self.expect(try self.pop_local());
const lhs = try self.env.expect(try self.pop_local());
defer self.env.discard(lhs);
@ -972,10 +912,6 @@ pub const RuntimeEnv = struct {
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 {
while (self.constants.pop()) |constant| {
self.env.discard(constant);
@ -986,12 +922,11 @@ pub const RuntimeEnv = struct {
self.env.allocator.deallocate(self.name);
}
fn make(env: *RuntimeEnv, name: []const coral.io.Byte, arity: u8) coral.io.AllocationError!Chunk {
fn make(env: *RuntimeEnv, name: []const coral.io.Byte) coral.io.AllocationError!Chunk {
return .{
.name = try coral.io.allocate_copy(env.allocator, name),
.opcodes = OpcodeList.make(env.allocator),
.constants = ConstList.make(env.allocator),
.arity = arity,
.env = env,
};
}
@ -999,27 +934,15 @@ pub const RuntimeEnv = struct {
fn pop_local(self: *Chunk) RuntimeError!?*RuntimeRef {
return self.env.locals.pop() orelse self.env.raise(error.IllegalState, "stack underflow");
}
fn typeinfo_call(env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef {
const chunk = @as(*Chunk, @ptrCast(@alignCast(userdata)));
if (frame.arg_count < chunk.arity) {
return env.raise(error.BadOperation, "expected more arguments");
}
return chunk.execute(frame);
}
fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void {
_ = env;
@as(*Chunk, @ptrCast(@alignCast(userdata))).free();
}
};
const ConstList = coral.list.Stack(*RuntimeRef);
const FrameStack = coral.list.Stack(Frame);
const FrameStack = coral.list.Stack(struct {
name: []const coral.io.Byte,
arg_count: u8,
locals_top: usize,
});
pub const Options = struct {
import_access: file.Access = .null,
@ -1043,15 +966,15 @@ pub const RuntimeEnv = struct {
associative: RefTable,
contiguous: RefList,
fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void {
const table = @as(*Table, @ptrCast(@alignCast(userdata)));
fn typeinfo_destruct(method: Typeinfo.Method) void {
const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr)));
{
var field_iterable = table.associative.as_iterable();
while (field_iterable.next()) |entry| {
env.discard(entry.key);
env.discard(entry.value);
method.env.discard(entry.key);
method.env.discard(entry.value);
}
}
@ -1059,18 +982,18 @@ pub const RuntimeEnv = struct {
while (table.contiguous.pop()) |value| {
if (value) |ref| {
env.discard(ref);
method.env.discard(ref);
}
}
table.contiguous.free();
}
fn typeinfo_get(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
const table = @as(*Table, @ptrCast(@alignCast(userdata)));
const acquired_index = try env.acquire(index);
fn typeinfo_get(method: Typeinfo.Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr)));
const acquired_index = try method.env.acquire(index);
defer env.discard(acquired_index);
defer method.env.discard(acquired_index);
if (acquired_index.is_fixed()) |fixed| {
if (fixed < 0) {
@ -1079,22 +1002,22 @@ pub const RuntimeEnv = struct {
}
if (fixed < table.contiguous.values.len) {
return env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null);
return method.env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null);
}
}
if (table.associative.lookup(acquired_index)) |value_ref| {
return env.acquire(value_ref);
return method.env.acquire(value_ref);
}
return null;
}
fn typeinfo_set(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
const table = @as(*Table, @ptrCast(@alignCast(userdata)));
const acquired_index = try env.acquire(index);
fn typeinfo_set(method: Typeinfo.Method, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr)));
const acquired_index = try method.env.acquire(index);
errdefer env.discard(acquired_index);
errdefer method.env.discard(acquired_index);
if (acquired_index.is_fixed()) |fixed| {
if (fixed < 0) {
@ -1106,31 +1029,32 @@ pub const RuntimeEnv = struct {
const maybe_replacing = &table.contiguous.values[@intCast(fixed)];
if (maybe_replacing.*) |replacing| {
env.discard(replacing);
method.env.discard(replacing);
}
maybe_replacing.* = if (value) |ref| try env.acquire(ref) else null;
maybe_replacing.* = if (value) |ref| try method.env.acquire(ref) else null;
return;
}
}
const acquired_value = try env.acquire(value orelse {
const acquired_value = try method.env.acquire(value orelse {
if (table.associative.remove(acquired_index)) |removed| {
env.discard(removed.key);
env.discard(removed.value);
method.env.discard(removed.key);
method.env.discard(removed.value);
}
return;
});
errdefer env.discard(acquired_value);
errdefer method.env.discard(acquired_value);
if (try table.associative.replace(acquired_index, acquired_value)) |replaced| {
env.discard(replaced.key);
env.discard(replaced.value);
method.env.discard(replaced.key);
method.env.discard(replaced.value);
}
}
};
pub fn acquire(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!*RuntimeRef {
@ -1143,15 +1067,45 @@ pub const RuntimeEnv = struct {
return @ptrCast(object);
}
pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef {
// TODO: Handle errors.
for (args) |arg| {
try self.locals.push_one(try self.acquire(arg));
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);
}
}
try self.push_frame(args.len);
return null;
}
defer self.pop_frame();
pub fn bind_system(self: *RuntimeEnv, name: []const coral.io.Byte, value: *const RuntimeRef) RuntimeError!void {
const name_symbol = try self.new_symbol(name);
errdefer self.discard(name_symbol);
const acquired_value = try self.acquire(value);
errdefer self.discard(acquired_value);
if (try self.system_bindings.replace(name_symbol, acquired_value)) |replaced| {
self.discard(replaced.key);
self.discard(replaced.value);
}
}
pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef {
try self.locals.push_all(args);
defer coral.io.assert(self.locals.drop(args.len));
try self.frames.push_one(.{
.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) {
.syscall => |syscall| syscall(self.env),
@ -1183,7 +1137,10 @@ pub const RuntimeEnv = struct {
.dynamic => |dynamic| {
if (dynamic.typeinfo().destruct) |destruct| {
destruct(self, dynamic.userdata());
destruct(.{
.userdata = dynamic.userdata(),
.env = self,
});
}
self.allocator.deallocate(dynamic.unpack());
@ -1202,7 +1159,7 @@ pub const RuntimeEnv = struct {
defer self.allocator.deallocate(file_data);
const file_name = file_path.to_string() orelse "<script>";
var chunk = try Chunk.make(self, file_name, 0);
var chunk = try Chunk.make(self, file_name);
defer chunk.free();
@ -1213,13 +1170,13 @@ pub const RuntimeEnv = struct {
try chunk.compile(ast_tree.parse(file_data) catch |parse_error| return switch (parse_error) {
error.BadSyntax => self.raise(error.BadSyntax, ast_tree.error_message()),
error.OutOfMemory => error.OutOfMemory,
}, &.{});
});
const frame = try self.push_frame(0);
return chunk.execute();
}
defer self.pop_frame();
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 {
@ -1244,14 +1201,6 @@ pub const RuntimeEnv = struct {
pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef {
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: {
const swizzle_symbol = try self.unbox_symbol(index);
var swizzle_buffer = [_]f32{0} ** 3;
@ -1303,7 +1252,12 @@ pub const RuntimeEnv = struct {
}
},
.dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index),
.dynamic => |dynamic| dynamic.typeinfo().get(.{
.userdata = dynamic.userdata(),
.env = self,
}, index),
else => self.raise(error.TypeMismatch, "object is not get-indexable"),
};
}
@ -1448,29 +1402,6 @@ 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 {
self.print_error(message);
@ -1491,7 +1422,11 @@ pub const RuntimeEnv = struct {
pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void {
return switch (indexable.object().payload) {
.dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value),
.dynamic => |dynamic| dynamic.typeinfo().set(.{
.userdata = dynamic.userdata(),
.env = self,
}, index, value),
else => self.raise(error.TypeMismatch, "object is not set-indexable"),
};
}
@ -1707,26 +1642,31 @@ pub const RuntimeRef = opaque {
}
};
pub const Syscall = fn (env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef;
pub const Syscall = fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef;
pub const Typeinfo = struct {
name: []const coral.io.Byte,
size: usize,
destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null,
call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call,
get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get,
set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
destruct: ?*const fn (method: Method) void = null,
call: *const fn (method: Method) RuntimeError!?*RuntimeRef = default_call,
get: *const fn (method: Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get,
set: *const fn (method: Method, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef {
return env.raise(error.BadOperation, "object is not callable");
pub const Method = struct {
env: *RuntimeEnv,
userdata: []coral.io.Byte,
};
fn default_call(method: Method) RuntimeError!?*RuntimeRef {
return method.env.raise(error.TypeMismatch, "object is not callable");
}
fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
return env.raise(error.BadOperation, "object is not get-indexable");
fn default_get(method: Method, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
return method.env.raise(error.TypeMismatch, "object is not index-gettable");
}
fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
return env.raise(error.BadOperation, "object is not set-indexable");
fn default_set(method: Method, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
return method.env.raise(error.TypeMismatch, "object is not index-settable");
}
};
@ -1754,35 +1694,59 @@ pub fn get_key(env: *RuntimeEnv, indexable: *RuntimeRef, key: []const coral.io.B
return env.get(indexable, key_string);
}
fn syscall_import(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
return env.import(file.Path.from(&.{try env.unbox_string(try frame.get_arg(env, 0))}));
fn syscall_import(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
const arg = try env.expect(try env.acquire_arg(0));
defer env.discard(arg);
return env.import(file.Path.from(&.{try env.unbox_string(arg)}));
}
fn syscall_print(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
env.print(try env.unbox_string(try frame.get_arg(env, 0)));
fn syscall_print(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
const arg = try env.expect(try env.acquire_arg(0));
defer env.discard(arg);
env.print(try env.unbox_string(arg));
return null;
}
fn syscall_vec2(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
fn syscall_vec2(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
const x = decode: {
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, x);
}
fn syscall_vec3(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef {
const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0))));
fn syscall_vec3(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
const x = decode: {
const value = try env.expect(try env.acquire_arg(0));
if (frame.has_arg(env, 1)) |y| {
return env.new_vector3(
x,
@floatCast(try env.unbox_float(y)),
@floatCast(try env.unbox_float(try frame.get_arg(env, 2))),
);
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);
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);

View File

@ -74,11 +74,6 @@ pub const Expression = union (enum) {
table_literal: TableLiteral,
grouped_expression: *Expression,
lambda_literal: struct {
argument_identifiers: IdentifierList,
block_statements: Statement.List,
},
local_get: struct {
identifier: []const coral.io.Byte,
},
@ -134,9 +129,12 @@ pub const Expression = union (enum) {
});
};
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
pub const DeclarationStorage = enum {
variant,
readonly,
};
const IdentifierList = coral.list.Stack([]const coral.io.Byte);
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
pub const ParseError = error {
OutOfMemory,
@ -166,11 +164,6 @@ pub const Statement = union (enum) {
block: List,
expression: Expression,
pub const DeclarationStorage = enum {
@"var",
let,
};
const List = coral.list.Stack(Statement);
};
@ -228,6 +221,8 @@ pub const Tree = struct {
fn parse_branch(self: *Tree) ParseError!Statement {
const allocator = self.arena.as_allocator();
defer self.tokenizer.skip_newlines();
self.tokenizer.step();
const condition_expression = try self.parse_expression();
@ -243,8 +238,6 @@ pub const Tree = struct {
while (true) {
switch (self.tokenizer.token) {
.keyword_end => {
self.tokenizer.skip_newlines();
return .{
.@"if" = .{
.condition_expression = condition_expression,
@ -269,8 +262,6 @@ pub const Tree = struct {
try else_statements.push_one(try self.parse_statement());
}
self.tokenizer.skip_newlines();
return .{
.@"if" = .{
.else_statement = try coral.io.allocate_one(allocator, Statement{.block = else_statements}),
@ -413,57 +404,6 @@ pub const Tree = struct {
break: parse .{.builtin = builtin};
},
.keyword_lambda => {
self.tokenizer.skip_newlines();
if (self.tokenizer.token != .symbol_paren_left) {
return self.report("expected `(` after opening lambda block");
}
var argument_identifiers = IdentifierList.make(allocator);
while (true) {
self.tokenizer.skip_newlines();
switch (self.tokenizer.token) {
.identifier => |identifier| try argument_identifiers.push_one(identifier),
.symbol_paren_right => break,
else => return self.report("expected identifier or closing `)` in argument list"),
}
self.tokenizer.skip_newlines();
switch (self.tokenizer.token) {
.symbol_comma => continue,
.symbol_paren_right => break,
else => return self.report("expected `,` or closing `)` after identifier in argument list"),
}
}
self.tokenizer.skip_newlines();
if (self.tokenizer.token != .symbol_colon) {
return self.report("expected `:` after closing `)` of lambda block argument list");
}
self.tokenizer.skip_newlines();
var block_statements = Statement.List.make(allocator);
while (self.tokenizer.token != .keyword_end) {
try block_statements.push_one(try self.parse_statement());
}
self.tokenizer.skip_newlines();
break: parse .{
.lambda_literal = .{
.argument_identifiers = argument_identifiers,
.block_statements = block_statements,
},
};
},
.symbol_brace_left => {
var table_literal = Expression.TableLiteral.make(allocator);
@ -740,7 +680,7 @@ pub const Tree = struct {
return .{
.declare = .{
.assigned_expression = try self.parse_expression(),
.storage = .@"var",
.storage = .variant,
.identifier = identifier,
},
};
@ -765,7 +705,7 @@ pub const Tree = struct {
return .{
.declare = .{
.assigned_expression = try self.parse_expression(),
.storage = .let,
.storage = .readonly,
.identifier = identifier,
},
};

View File

@ -46,7 +46,6 @@ pub const Token = union(enum) {
keyword_elif,
keyword_var,
keyword_let,
keyword_lambda,
pub fn text(self: Token) []const coral.io.Byte {
return switch (self) {
@ -96,7 +95,6 @@ pub const Token = union(enum) {
.keyword_else => "else",
.keyword_var => "var",
.keyword_let => "let",
.keyword_lambda => "lambda",
};
}
};
@ -236,12 +234,6 @@ pub const Tokenizer = struct {
},
'l' => {
if (coral.io.ends_with(identifier, "ambda")) {
self.token = .keyword_lambda;
return;
}
if (coral.io.ends_with(identifier, "et")) {
self.token = .keyword_let;
@ -325,6 +317,8 @@ pub const Tokenizer = struct {
const begin = cursor;
cursor += 1;
while (cursor < self.source.len) switch (self.source[cursor]) {
'"' => break,
else => cursor += 1,