commit 05deccbb3b058e462dbb667bda3086e14f934ce2
parent 206ed850745f3073331e937a445aee5582334310
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Mon, 12 Aug 2024 22:12:41 +0200
Native functions
Diffstat:
4 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig
@@ -183,7 +183,10 @@ pub fn Compiler(size: comptime_int) type {
}
fn emitReturn(self: *Self) void {
- self.emit2OP(OP.NIL, OP.RETURN);
+ if (self.currentFunction.type != Obj.Function.Type.Script) {
+ self.emitOP(OP.NIL);
+ }
+ self.emitOP(OP.RETURN);
}
fn errorAtCurrent(self: *Self, message: []const u8) void {
diff --git a/zlox/src/main.zig b/zlox/src/main.zig
@@ -39,7 +39,7 @@ pub fn runFile(allocator: std.mem.Allocator, path: []const u8) anyerror!void {
}
pub fn repl(allocator: std.mem.Allocator, dbg: bool) anyerror!void {
- var VM = vm.VM.init(allocator);
+ var VM = try vm.VM.init(allocator);
defer VM.deinit();
_ = Linenoise.linenoiseHistorySetMaxLen(100);
diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig
@@ -12,11 +12,13 @@ pub const Obj = packed struct {
pub const String = @import("obj/string.zig").String;
pub const Table = @import("obj/table.zig").Table;
pub const Function = @import("obj/function.zig").Function;
+ pub const Native = @import("obj/native.zig").Native;
pub const Type = enum(u8) {
String,
Table,
Function,
+ Native,
pub fn get(comptime self: @This()) type {
return @field(Super, @tagName(self));
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -9,6 +9,7 @@ const Callback = @import("vm/callbacks.zig");
const table = @import("table.zig");
const hash = @import("hash.zig");
const utils = @import("comptime_utils.zig");
+const vm_native = @import("vm/native.zig");
pub const InterpreterError = compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, StackOverflow, IndexOutOfBounds, Overflow, DivisionByZero };
@@ -58,9 +59,17 @@ pub const VM = struct {
}
};
+ fn defineNative(self: *@This(), name: []const u8, arity: u8, fun: Obj.Native.Fn) !void {
+ const nameObj = try self.objects.emplace(.String, &.{name});
+ const funObj = try self.objects.emplace_cast(.Native, Obj.Native.Arg{.fun = fun, .name = name, .arity = arity});
+ _ = try self.globals.set(nameObj, Global.make_con(Value.init(funObj)));
+ }
- pub fn init(allocator: std.mem.Allocator) @This() {
- return @This(){ .globals = Globals.init(allocator), .objects = Obj.List.init(allocator), .allocator = allocator };
+ pub fn init(allocator: std.mem.Allocator) !@This() {
+ var self = @This(){ .globals = Globals.init(allocator), .objects = Obj.List.init(allocator), .allocator = allocator };
+ try self.defineNative("clock", 0, vm_native.clock);
+ try vm_native.set_start();
+ return self;
}
pub fn interpret(self: *@This(), source: []const u8, dbg: bool) InterpreterError!void {
@@ -149,6 +158,16 @@ pub const VM = struct {
fn callValue(self: *@This(), callee: Value, argCount: u8) !void {
if(callee.is(Obj.Type.Function)) {
try self.call(callee.obj.cast(.Function) catch unreachable, argCount);
+ } else if(callee.is(Obj.Type.Native)) {
+ const native = callee.obj.cast(.Native) catch unreachable;
+ if (argCount != native.arity) {
+ self.runtimeError("Expected {d} arguments but got {d}", .{native.arity, argCount});
+ return InterpreterError.RuntimeError;
+ }
+ const result = native.call(argCount, self.stackTop - argCount);
+ self.stackTop -= argCount + 1;
+ self.push(result);
+ return;
} else {
self.runtimeError("Can only call functions and classes", .{});
return InterpreterError.RuntimeError;
@@ -269,7 +288,7 @@ pub const VM = struct {
var pushed = false;
if (obj.is(Value.obj)) {
switch(obj.obj.type) {
- .Function => {},
+ .Function, .Native => {},
inline else => |tp| {
self.push((obj.obj.cast(tp) catch unreachable).get(key) catch Value.init({}));
pushed = true;