Replace System Objects with Builtins and Syscalls #33
|
@ -14,8 +14,8 @@ options = {
|
||||||
options["foo"] = "rab"
|
options["foo"] = "rab"
|
||||||
options[42] = "24"
|
options[42] = "24"
|
||||||
|
|
||||||
@log_info(options.title)
|
@print(options.title)
|
||||||
@log_info(options["foo"])
|
@print(options["foo"])
|
||||||
@log_info(options[42])
|
@print(options[42])
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|
|
@ -12,8 +12,8 @@ pub const Manifest = struct {
|
||||||
height: u16 = 480,
|
height: u16 = 480,
|
||||||
tick_rate: f32 = 60.0,
|
tick_rate: f32 = 60.0,
|
||||||
|
|
||||||
pub fn load(self: *Manifest, env: *kym.RuntimeEnv, file_access: file.Access) kym.RuntimeError!void {
|
pub fn load(self: *Manifest, env: *kym.RuntimeEnv) kym.RuntimeError!void {
|
||||||
const manifest = try env.expect(try env.execute_file(file_access, file.Path.from(&.{"app.ona"})));
|
const manifest = try env.expect(try env.import(file.Path.from(&.{"app.ona"})));
|
||||||
|
|
||||||
defer env.discard(manifest);
|
defer env.discard(manifest);
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,13 @@ const coral = @import("coral");
|
||||||
const ext = @import("./ext.zig");
|
const ext = @import("./ext.zig");
|
||||||
|
|
||||||
pub const Access = union (enum) {
|
pub const Access = union (enum) {
|
||||||
|
null,
|
||||||
sandboxed_path: *const Path,
|
sandboxed_path: *const Path,
|
||||||
|
|
||||||
pub fn open_readable(self: Access, readable_path: Path) ?*Readable {
|
pub fn open_readable(self: Access, readable_path: Path) ?*Readable {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
|
.null => return null,
|
||||||
|
|
||||||
.sandboxed_path => |sandboxed_path| {
|
.sandboxed_path => |sandboxed_path| {
|
||||||
const readable_path_string = sandboxed_path.joined(readable_path).to_string() orelse return null;
|
const readable_path_string = sandboxed_path.joined(readable_path).to_string() orelse return null;
|
||||||
|
|
||||||
|
@ -17,6 +20,8 @@ pub const Access = union (enum) {
|
||||||
|
|
||||||
pub fn query(self: Access, path: Path) ?Info {
|
pub fn query(self: Access, path: Path) ?Info {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
|
.null => return null,
|
||||||
|
|
||||||
.sandboxed_path => |sandboxed_path| {
|
.sandboxed_path => |sandboxed_path| {
|
||||||
const path_string = sandboxed_path.joined(path).to_string() orelse return null;
|
const path_string = sandboxed_path.joined(path).to_string() orelse return null;
|
||||||
const rw_ops = ext.SDL_RWFromFile(path_string, "rb") orelse return null;
|
const rw_ops = ext.SDL_RWFromFile(path_string, "rb") orelse return null;
|
||||||
|
|
|
@ -4,30 +4,14 @@ const coral = @import("coral");
|
||||||
|
|
||||||
const file = @import("./file.zig");
|
const file = @import("./file.zig");
|
||||||
|
|
||||||
pub const Caller = coral.io.Generator(RuntimeError!?*RuntimeRef, *RuntimeEnv);
|
|
||||||
|
|
||||||
pub const ErrorHandler = coral.io.Generator(void, ErrorInfo);
|
|
||||||
|
|
||||||
pub const ErrorInfo = struct {
|
|
||||||
message: []const coral.io.Byte,
|
|
||||||
frames: []const Frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Fixed = i32;
|
pub const Fixed = i32;
|
||||||
|
|
||||||
pub const Float = f64;
|
pub const Float = f64;
|
||||||
|
|
||||||
pub const Frame = struct {
|
|
||||||
name: []const coral.io.Byte,
|
|
||||||
arg_count: u8,
|
|
||||||
locals_top: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RuntimeEnv = struct {
|
pub const RuntimeEnv = struct {
|
||||||
|
options: Options,
|
||||||
interned_symbols: SymbolSet,
|
interned_symbols: SymbolSet,
|
||||||
allocator: coral.io.Allocator,
|
allocator: coral.io.Allocator,
|
||||||
error_handler: ErrorHandler,
|
|
||||||
system_bindings: RefTable,
|
|
||||||
locals: RefList,
|
locals: RefList,
|
||||||
frames: FrameStack,
|
frames: FrameStack,
|
||||||
|
|
||||||
|
@ -37,6 +21,11 @@ pub const RuntimeEnv = struct {
|
||||||
opcodes: OpcodeList,
|
opcodes: OpcodeList,
|
||||||
constants: ConstList,
|
constants: ConstList,
|
||||||
|
|
||||||
|
const Builtin = enum {
|
||||||
|
import,
|
||||||
|
print,
|
||||||
|
};
|
||||||
|
|
||||||
const Constant = union (enum) {
|
const Constant = union (enum) {
|
||||||
fixed: Fixed,
|
fixed: Fixed,
|
||||||
float: Float,
|
float: Float,
|
||||||
|
@ -51,7 +40,7 @@ pub const RuntimeEnv = struct {
|
||||||
push_const: u16,
|
push_const: u16,
|
||||||
push_local: u8,
|
push_local: u8,
|
||||||
push_table: u32,
|
push_table: u32,
|
||||||
push_system: u16,
|
push_builtin: Builtin,
|
||||||
local_set: u8,
|
local_set: u8,
|
||||||
object_get,
|
object_get,
|
||||||
object_set,
|
object_set,
|
||||||
|
@ -175,10 +164,18 @@ pub const RuntimeEnv = struct {
|
||||||
try self.compile_expression(chunk, grouped_expression.*);
|
try self.compile_expression(chunk, grouped_expression.*);
|
||||||
},
|
},
|
||||||
|
|
||||||
.get_system => |get_system| {
|
.builtin => |builtin| {
|
||||||
try chunk.opcodes.push_one(.{
|
coral.debug.assert(builtin.len != 0);
|
||||||
.push_system = try chunk.declare_constant(.{.symbol = get_system}),
|
|
||||||
|
const decoded_builtin = @as(?Builtin, switch (builtin[0]) {
|
||||||
|
'i' => if (coral.io.ends_with(builtin, "mport")) .import else null,
|
||||||
|
'p' => if (coral.io.ends_with(builtin, "rint")) .print else null,
|
||||||
|
else => null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try chunk.opcodes.push_one(.{.push_builtin = decoded_builtin orelse {
|
||||||
|
return chunk.env.raise(error.BadSyntax, "unknown builtin");
|
||||||
|
}});
|
||||||
},
|
},
|
||||||
|
|
||||||
.local_get => |local_get| {
|
.local_get => |local_get| {
|
||||||
|
@ -360,12 +357,15 @@ pub const RuntimeEnv = struct {
|
||||||
try self.env.locals.push_one(table);
|
try self.env.locals.push_one(table);
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_system => |push_system| {
|
.push_builtin => |push_builtin| {
|
||||||
if (self.env.system_bindings.lookup(self.constants.values[push_system])) |syscallable| {
|
const builtin_syscall = try self.env.new_syscall(switch (push_builtin) {
|
||||||
try self.env.locals.push_one(try self.env.acquire(syscallable));
|
.import => syscall_import,
|
||||||
} else {
|
.print => syscall_print,
|
||||||
try self.env.locals.push_one(null);
|
});
|
||||||
}
|
|
||||||
|
errdefer self.env.discard(builtin_syscall);
|
||||||
|
|
||||||
|
try self.env.locals.push_one(builtin_syscall);
|
||||||
},
|
},
|
||||||
|
|
||||||
.local_set => |local_set| {
|
.local_set => |local_set| {
|
||||||
|
@ -441,6 +441,8 @@ pub const RuntimeEnv = struct {
|
||||||
defer coral.debug.assert(self.env.frames.pop() != null);
|
defer coral.debug.assert(self.env.frames.pop() != null);
|
||||||
|
|
||||||
break: call try switch (callable.object().payload) {
|
break: call try switch (callable.object().payload) {
|
||||||
|
.syscall => |syscall| syscall(self.env),
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo().call(.{
|
.dynamic => |dynamic| dynamic.typeinfo().call(.{
|
||||||
.userdata = dynamic.userdata(),
|
.userdata = dynamic.userdata(),
|
||||||
.env = self.env,
|
.env = self.env,
|
||||||
|
@ -799,7 +801,19 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
const ConstList = coral.list.Stack(*RuntimeRef);
|
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,
|
||||||
|
});
|
||||||
kayomn marked this conversation as resolved
|
|||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
import_access: file.Access = .null,
|
||||||
|
print: ?*const Printer = null,
|
||||||
|
print_error: ?*const Printer = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Printer = fn (buffer: []const coral.io.Byte) void;
|
||||||
|
|
||||||
const RefList = coral.list.Stack(?*RuntimeRef);
|
const RefList = coral.list.Stack(?*RuntimeRef);
|
||||||
|
|
||||||
|
@ -916,7 +930,7 @@ pub const RuntimeEnv = struct {
|
||||||
return @ptrCast(object);
|
return @ptrCast(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arg(self: *RuntimeEnv, index: usize) RuntimeError!?*RuntimeRef {
|
pub fn acquire_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*RuntimeRef {
|
||||||
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
||||||
|
|
||||||
if (index < frame.arg_count) {
|
if (index < frame.arg_count) {
|
||||||
|
@ -957,6 +971,8 @@ pub const RuntimeEnv = struct {
|
||||||
defer coral.io.assert(self.frames.pop() != null);
|
defer coral.io.assert(self.frames.pop() != null);
|
||||||
|
|
||||||
return switch (callable.object().payload) {
|
return switch (callable.object().payload) {
|
||||||
|
.syscall => |syscall| syscall(self.env),
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo.call(.{
|
.dynamic => |dynamic| dynamic.typeinfo.call(.{
|
||||||
.userdata = dynamic.userdata(),
|
.userdata = dynamic.userdata(),
|
||||||
.env = self,
|
.env = self,
|
||||||
|
@ -975,7 +991,7 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
if (object.ref_count == 0) {
|
if (object.ref_count == 0) {
|
||||||
switch (object.payload) {
|
switch (object.payload) {
|
||||||
.false, .true, .float, .fixed, .symbol => {},
|
.false, .true, .float, .fixed, .symbol, .syscall => {},
|
||||||
|
|
||||||
.string => |string| {
|
.string => |string| {
|
||||||
coral.debug.assert(string.len >= 0);
|
coral.debug.assert(string.len >= 0);
|
||||||
|
@ -998,8 +1014,8 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_file(self: *RuntimeEnv, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
||||||
const file_data = (try file.allocate_and_load(self.allocator, file_access, file_path)) orelse {
|
const file_data = (try file.allocate_and_load(self.allocator, self.options.import_access, file_path)) orelse {
|
||||||
return self.raise(error.BadOperation, "failed to open or read file specified");
|
return self.raise(error.BadOperation, "failed to open or read file specified");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1043,18 +1059,8 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
var iterable = self.system_bindings.as_iterable();
|
|
||||||
|
|
||||||
while (iterable.next()) |entry| {
|
|
||||||
self.discard(entry.key);
|
|
||||||
self.discard(entry.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.frames.free();
|
self.frames.free();
|
||||||
self.locals.free();
|
self.locals.free();
|
||||||
self.system_bindings.free();
|
|
||||||
self.interned_symbols.free();
|
self.interned_symbols.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1069,13 +1075,12 @@ pub const RuntimeEnv = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make(allocator: coral.io.Allocator, error_handler: ErrorHandler) coral.io.AllocationError!RuntimeEnv {
|
pub fn make(allocator: coral.io.Allocator, options: Options) coral.io.AllocationError!RuntimeEnv {
|
||||||
return RuntimeEnv{
|
return RuntimeEnv{
|
||||||
.locals = RefList.make(allocator),
|
.locals = RefList.make(allocator),
|
||||||
.frames = FrameStack.make(allocator),
|
.frames = FrameStack.make(allocator),
|
||||||
.system_bindings = RefTable.make(allocator, .{}),
|
|
||||||
.interned_symbols = SymbolSet.make(allocator, .{}),
|
.interned_symbols = SymbolSet.make(allocator, .{}),
|
||||||
.error_handler = error_handler,
|
.options = options,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1158,6 +1163,13 @@ pub const RuntimeEnv = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_syscall(self: *RuntimeEnv, value: *const Syscall) RuntimeError!*RuntimeRef {
|
||||||
|
return RuntimeRef.allocate(self.allocator, .{
|
||||||
|
.ref_count = 1,
|
||||||
|
.payload = .{.syscall = value},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_table(self: *RuntimeEnv) RuntimeError!*RuntimeRef {
|
pub fn new_table(self: *RuntimeEnv) RuntimeError!*RuntimeRef {
|
||||||
var table = Table{
|
var table = Table{
|
||||||
.associative = RefTable.make(self.allocator, .{}),
|
.associative = RefTable.make(self.allocator, .{}),
|
||||||
|
@ -1178,11 +1190,32 @@ pub const RuntimeEnv = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print(self: *RuntimeEnv, buffer: []const coral.io.Byte) void {
|
||||||
|
if (self.options.print) |bound_print| {
|
||||||
|
bound_print(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_error(self: *RuntimeEnv, buffer: []const coral.io.Byte) void {
|
||||||
|
if (self.options.print_error) |bound_print_error| {
|
||||||
|
bound_print_error(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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.error_handler.invoke(.{
|
self.print_error(message);
|
||||||
.message = message,
|
|
||||||
.frames = self.frames.values,
|
if (!self.frames.is_empty()) {
|
||||||
});
|
self.print_error("stack trace:");
|
||||||
|
|
||||||
|
var remaining_frames = self.frames.values.len;
|
||||||
|
|
||||||
|
while (remaining_frames != 0) {
|
||||||
|
remaining_frames -= 1;
|
||||||
|
|
||||||
|
self.print_error(self.frames.values[remaining_frames].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return error_value;
|
return error_value;
|
||||||
}
|
}
|
||||||
|
@ -1254,6 +1287,7 @@ pub const RuntimeRef = opaque {
|
||||||
float: Float,
|
float: Float,
|
||||||
fixed: Fixed,
|
fixed: Fixed,
|
||||||
symbol: [*:0]const coral.io.Byte,
|
symbol: [*:0]const coral.io.Byte,
|
||||||
|
syscall: *const Syscall,
|
||||||
|
|
||||||
string: struct {
|
string: struct {
|
||||||
ptr: [*]coral.io.Byte,
|
ptr: [*]coral.io.Byte,
|
||||||
|
@ -1323,6 +1357,11 @@ pub const RuntimeRef = opaque {
|
||||||
else => false,
|
else => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.syscall => |self_syscall| switch (other.object().payload) {
|
||||||
|
.syscall => |other_syscall| self_syscall == other_syscall,
|
||||||
|
else => false,
|
||||||
|
},
|
||||||
|
|
||||||
.string => |self_string| switch (other.object().payload) {
|
.string => |self_string| switch (other.object().payload) {
|
||||||
.string => |other_string| coral.io.equals(self_string.unpack(), other_string.unpack()),
|
.string => |other_string| coral.io.equals(self_string.unpack(), other_string.unpack()),
|
||||||
else => false,
|
else => false,
|
||||||
|
@ -1344,7 +1383,8 @@ pub const RuntimeRef = opaque {
|
||||||
.true => 1231,
|
.true => 1231,
|
||||||
.float => |float| @bitCast(float),
|
.float => |float| @bitCast(float),
|
||||||
.fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))),
|
.fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))),
|
||||||
.symbol => |symbol| @bitCast(@intFromPtr(symbol)),
|
.symbol => |symbol| @intFromPtr(symbol),
|
||||||
|
.syscall => |syscall| @intFromPtr(syscall),
|
||||||
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()),
|
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()),
|
||||||
.dynamic => |dynamic| @intFromPtr(dynamic.typeinfo()) ^ @intFromPtr(dynamic.userdata().ptr),
|
.dynamic => |dynamic| @intFromPtr(dynamic.typeinfo()) ^ @intFromPtr(dynamic.userdata().ptr),
|
||||||
};
|
};
|
||||||
|
@ -1364,12 +1404,15 @@ pub const RuntimeRef = opaque {
|
||||||
.float => |float| float != 0,
|
.float => |float| float != 0,
|
||||||
.fixed => |fixed| fixed != 0,
|
.fixed => |fixed| fixed != 0,
|
||||||
.symbol => true,
|
.symbol => true,
|
||||||
|
.syscall => true,
|
||||||
.string => |string| string.len != 0,
|
.string => |string| string.len != 0,
|
||||||
.dynamic => true,
|
.dynamic => true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Syscall = fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef;
|
||||||
|
|
||||||
pub const Typeinfo = struct {
|
pub const Typeinfo = struct {
|
||||||
name: []const coral.io.Byte,
|
name: []const coral.io.Byte,
|
||||||
size: usize,
|
size: usize,
|
||||||
|
@ -1396,14 +1439,6 @@ pub const Typeinfo = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind_syscaller(env: *RuntimeEnv, name: []const coral.io.Byte, caller: Caller) RuntimeError!void {
|
|
||||||
const callable = try new_caller(env, caller);
|
|
||||||
|
|
||||||
defer env.discard(callable);
|
|
||||||
|
|
||||||
try env.bind_system(name, callable);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
||||||
const field_symbol = try env.new_symbol(field);
|
const field_symbol = try env.new_symbol(field);
|
||||||
|
|
||||||
|
@ -1428,18 +1463,20 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_caller(env: *RuntimeEnv, value: Caller) RuntimeError!*RuntimeRef {
|
fn syscall_import(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
const Callable = struct {
|
const arg = try env.expect(try env.acquire_arg(0));
|
||||||
fn call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef {
|
|
||||||
coral.debug.assert(method.userdata.len == @sizeOf(Caller));
|
|
||||||
|
|
||||||
return @as(*Caller, @ptrCast(@alignCast(method.userdata))).invoke(method.env);
|
defer env.discard(arg);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return env.new_dynamic(coral.io.bytes_of(&value).ptr, &.{
|
return env.import(file.Path.from(&.{try env.unbox_string(arg)}));
|
||||||
.name = "<native>",
|
}
|
||||||
.size = @sizeOf(Caller),
|
|
||||||
.call = Callable.call,
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ pub const Expression = union (enum) {
|
||||||
nil_literal,
|
nil_literal,
|
||||||
true_literal,
|
true_literal,
|
||||||
false_literal,
|
false_literal,
|
||||||
|
builtin: []const coral.io.Byte,
|
||||||
number_literal: []const coral.io.Byte,
|
number_literal: []const coral.io.Byte,
|
||||||
string_literal: []const coral.io.Byte,
|
string_literal: []const coral.io.Byte,
|
||||||
symbol_literal: []const coral.io.Byte,
|
symbol_literal: []const coral.io.Byte,
|
||||||
table_literal: TableLiteral,
|
table_literal: TableLiteral,
|
||||||
grouped_expression: *Expression,
|
grouped_expression: *Expression,
|
||||||
get_system: []const coral.io.Byte,
|
|
||||||
local_get: []const coral.io.Byte,
|
local_get: []const coral.io.Byte,
|
||||||
local_set: []const coral.io.Byte,
|
local_set: []const coral.io.Byte,
|
||||||
|
|
||||||
|
@ -351,10 +351,10 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
break: parse .{.local_get = identifier};
|
break: parse .{.local_get = identifier};
|
||||||
},
|
},
|
||||||
|
|
||||||
.system_identifier => |system_identifier| {
|
.builtin => |builtin| {
|
||||||
self.tokenizer.skip_newlines();
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
break: parse .{.get_system = system_identifier};
|
break: parse .{.builtin = builtin};
|
||||||
},
|
},
|
||||||
|
|
||||||
.symbol_brace_left => {
|
.symbol_brace_left => {
|
||||||
|
|
|
@ -4,9 +4,8 @@ pub const Token = union(enum) {
|
||||||
end,
|
end,
|
||||||
unknown: coral.io.Byte,
|
unknown: coral.io.Byte,
|
||||||
newline,
|
newline,
|
||||||
|
|
||||||
system_identifier: []const coral.io.Byte,
|
|
||||||
identifier: []const coral.io.Byte,
|
identifier: []const coral.io.Byte,
|
||||||
|
builtin: []const coral.io.Byte,
|
||||||
|
|
||||||
symbol_plus,
|
symbol_plus,
|
||||||
symbol_minus,
|
symbol_minus,
|
||||||
|
@ -46,8 +45,8 @@ pub const Token = union(enum) {
|
||||||
.unknown => |unknown| @as([*]const coral.io.Byte, @ptrCast(&unknown))[0 .. 1],
|
.unknown => |unknown| @as([*]const coral.io.Byte, @ptrCast(&unknown))[0 .. 1],
|
||||||
.newline => "newline",
|
.newline => "newline",
|
||||||
|
|
||||||
.system_identifier => |identifier| identifier,
|
|
||||||
.identifier => |identifier| identifier,
|
.identifier => |identifier| identifier,
|
||||||
|
.builtin => |identifier| identifier,
|
||||||
|
|
||||||
.symbol_plus => "+",
|
.symbol_plus => "+",
|
||||||
.symbol_minus => "-",
|
.symbol_minus => "-",
|
||||||
|
@ -166,40 +165,52 @@ pub const Tokenizer = struct {
|
||||||
coral.debug.assert(identifier.len != 0);
|
coral.debug.assert(identifier.len != 0);
|
||||||
|
|
||||||
switch (identifier[0]) {
|
switch (identifier[0]) {
|
||||||
'c' => if (coral.io.ends_with(identifier, "onst")) {
|
'c' => {
|
||||||
self.token = .keyword_const;
|
if (coral.io.ends_with(identifier, "onst")) {
|
||||||
|
self.token = .keyword_const;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'n' => if (coral.io.ends_with(identifier, "il")) {
|
'n' => {
|
||||||
self.token = .keyword_nil;
|
if (coral.io.ends_with(identifier, "il")) {
|
||||||
|
self.token = .keyword_nil;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'f' => if (coral.io.ends_with(identifier, "alse")) {
|
'f' => {
|
||||||
self.token = .keyword_false;
|
if (coral.io.ends_with(identifier, "alse")) {
|
||||||
|
self.token = .keyword_false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
't' => if (coral.io.ends_with(identifier, "rue")) {
|
't' => {
|
||||||
self.token = .keyword_true;
|
if (coral.io.ends_with(identifier, "rue")) {
|
||||||
|
self.token = .keyword_true;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'r' => if (coral.io.ends_with(identifier, "eturn")) {
|
'r' => {
|
||||||
self.token = .keyword_return;
|
if (coral.io.ends_with(identifier, "eturn")) {
|
||||||
|
self.token = .keyword_return;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
's' => if (coral.io.ends_with(identifier, "elf")) {
|
's' => {
|
||||||
self.token = .keyword_self;
|
if (coral.io.ends_with(identifier, "elf")) {
|
||||||
|
self.token = .keyword_self;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {},
|
else => {},
|
||||||
|
@ -213,44 +224,14 @@ pub const Tokenizer = struct {
|
||||||
'@' => {
|
'@' => {
|
||||||
cursor += 1;
|
cursor += 1;
|
||||||
|
|
||||||
if (cursor < self.source.len) switch (self.source[cursor]) {
|
const begin = cursor;
|
||||||
'A'...'Z', 'a'...'z', '_' => {
|
|
||||||
const begin = cursor;
|
|
||||||
|
|
||||||
cursor += 1;
|
while (cursor < self.source.len) switch (self.source[cursor]) {
|
||||||
|
'0'...'9', 'A'...'Z', 'a'...'z', '_' => cursor += 1,
|
||||||
while (cursor < self.source.len) switch (self.source[cursor]) {
|
else => break,
|
||||||
'0'...'9', 'A'...'Z', 'a'...'z', '_' => cursor += 1,
|
|
||||||
else => break,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.token = .{.system_identifier = self.source[begin .. cursor]};
|
|
||||||
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
'"' => {
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
const begin = cursor;
|
|
||||||
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
while (cursor < self.source.len) switch (self.source[cursor]) {
|
|
||||||
'"' => break,
|
|
||||||
else => cursor += 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.token = .{.system_identifier = self.source[begin .. cursor]};
|
|
||||||
cursor += 1;
|
|
||||||
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.token = .symbol_at;
|
self.token = if (begin == cursor) .{.unknown = '@'} else .{.builtin = self.source[begin .. cursor]};
|
||||||
|
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,52 +10,6 @@ const heap = @import("./heap.zig");
|
||||||
|
|
||||||
const kym = @import("./kym.zig");
|
const kym = @import("./kym.zig");
|
||||||
|
|
||||||
fn kym_handle_errors(info: kym.ErrorInfo) void {
|
|
||||||
app.log_fail(info.message);
|
|
||||||
|
|
||||||
var remaining_frames = info.frames.len;
|
|
||||||
|
|
||||||
if (remaining_frames != 0) {
|
|
||||||
app.log_fail("stack trace:");
|
|
||||||
|
|
||||||
while (remaining_frames != 0) {
|
|
||||||
remaining_frames -= 1;
|
|
||||||
|
|
||||||
app.log_fail(info.frames[remaining_frames].name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kym_log_info(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
|
||||||
const argument = try env.expect(try env.arg(0));
|
|
||||||
|
|
||||||
defer env.discard(argument);
|
|
||||||
|
|
||||||
app.log_info(try env.unbox_string(argument));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kym_log_warn(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
|
||||||
const argument = try env.expect(try env.arg(0));
|
|
||||||
|
|
||||||
defer env.discard(argument);
|
|
||||||
|
|
||||||
app.log_warn(try env.unbox_string(argument));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kym_log_fail(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
|
||||||
const argument = try env.expect(try env.arg(0));
|
|
||||||
|
|
||||||
defer env.discard(argument);
|
|
||||||
|
|
||||||
app.log_fail(try env.unbox_string(argument));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last_sdl_error() [:0]const u8 {
|
fn last_sdl_error() [:0]const u8 {
|
||||||
return coral.io.slice_sentineled(@as(u8, 0), @as([*:0]const u8, @ptrCast(ext.SDL_GetError())));
|
return coral.io.slice_sentineled(@as(u8, 0), @as([*:0]const u8, @ptrCast(ext.SDL_GetError())));
|
||||||
}
|
}
|
||||||
|
@ -69,27 +23,19 @@ pub fn run_app(file_access: file.Access) void {
|
||||||
|
|
||||||
defer ext.SDL_Quit();
|
defer ext.SDL_Quit();
|
||||||
|
|
||||||
var script_env = kym.RuntimeEnv.make(heap.allocator, kym.ErrorHandler.from(kym_handle_errors)) catch {
|
var script_env = kym.RuntimeEnv.make(heap.allocator, .{
|
||||||
|
.print = app.log_info,
|
||||||
|
.print_error = app.log_fail,
|
||||||
|
.import_access = file_access,
|
||||||
|
}) catch {
|
||||||
return app.log_fail("failed to initialize script runtime");
|
return app.log_fail("failed to initialize script runtime");
|
||||||
};
|
};
|
||||||
|
|
||||||
defer script_env.free();
|
defer script_env.free();
|
||||||
|
|
||||||
kym.bind_syscaller(&script_env, "log_info", kym.Caller.from(kym_log_info)) catch {
|
|
||||||
return app.log_fail("failed to bind `log_info` syscall");
|
|
||||||
};
|
|
||||||
|
|
||||||
kym.bind_syscaller(&script_env, "log_warn", kym.Caller.from(kym_log_warn)) catch {
|
|
||||||
return app.log_fail("failed to bind `log_warn` syscall");
|
|
||||||
};
|
|
||||||
|
|
||||||
kym.bind_syscaller(&script_env, "log_fail", kym.Caller.from(kym_log_fail)) catch {
|
|
||||||
return app.log_fail("failed to bind `log_fail` syscall");
|
|
||||||
};
|
|
||||||
|
|
||||||
var manifest = app.Manifest{};
|
var manifest = app.Manifest{};
|
||||||
|
|
||||||
manifest.load(&script_env, file_access) catch return;
|
manifest.load(&script_env) catch return;
|
||||||
|
|
||||||
const window = create: {
|
const window = create: {
|
||||||
const pos = ext.SDL_WINDOWPOS_CENTERED;
|
const pos = ext.SDL_WINDOWPOS_CENTERED;
|
||||||
|
|
Loading…
Reference in New Issue
Options should provide reasonable defaults if they're intended to be optional.