implementing native functions (ch24)

This commit is contained in:
Patrick MARIE 2024-08-29 08:28:08 +02:00
parent 3daa675f8d
commit a2200debb4
7 changed files with 94 additions and 4 deletions

View File

@ -4,5 +4,8 @@ build:
run *ARGS:
zig build run -- {{ARGS}}
run-fast *ARGS:
zig build run -Doptimize=ReleaseFast -- {{ARGS}}
test:
zig build test

View File

@ -14,7 +14,7 @@ While reading [Crafting Interpreters](https://craftinginterpreters.com/), after
- [x] 21 - Global Variables
- [x] 22 - Local Variables
- [x] 23 - Jumping Back and Forth
- [ ] 24 - Calls and Functions
- [x] 24 - Calls and Functions
- [ ] 25 - Closures
- [ ] 26 - Garbage Collection
- [ ] 27 - Classes and Instances

9
samples/ch24_fib.lox Normal file
View File

@ -0,0 +1,9 @@
fun fib(n) {
if (n < 2) return n;
return fib(n - 2) + fib(n - 1);
}
var start = clock();
print fib(20);
print clock() - start;

View File

@ -33,9 +33,11 @@ const Precedence = enum {
Primary,
};
const ParserFn = *const fn (*Parser, bool) ParsingError!void;
const ParserRule = struct {
prefix: ?*const fn (*Parser, bool) ParsingError!void,
infix: ?*const fn (*Parser, bool) ParsingError!void,
prefix: ?ParserFn,
infix: ?ParserFn,
precedence: Precedence,
};

View File

@ -63,6 +63,7 @@ pub fn main() !void {
defer std.process.argsFree(allocator, args);
var vm = VM.new(allocator);
vm.init_vm();
defer vm.destroy();
if (args.len == 1) {

View File

@ -3,14 +3,18 @@ const debug = std.debug;
const Allocator = std.mem.Allocator;
const Chunk = @import("./chunk.zig").Chunk;
const Value = @import("./values.zig").Value;
const compute_hash = @import("./utils.zig").compute_hash;
pub const ObjType = enum {
String,
Function,
Native,
};
pub const NativeFn = *const fn (arg_count: usize, args: []Value) Value;
pub const Obj = struct {
kind: ObjType,
allocator: std.mem.Allocator,
@ -68,6 +72,28 @@ pub const Obj = struct {
}
};
pub const Native = struct {
obj: Obj,
native: NativeFn,
pub fn new(allocator: std.mem.Allocator, native: NativeFn) *Native {
const obj = Obj{
.kind = ObjType.Native,
.allocator = allocator,
};
const native_obj = allocator.create(Native) catch unreachable;
native_obj.obj = obj;
native_obj.native = native;
return native_obj;
}
pub fn destroy(self: *Native) void {
self.obj.allocator.destroy(self);
}
};
pub fn is_type(self: *Obj, kind: ObjType) bool {
return self.kind == kind;
}
@ -77,7 +103,11 @@ pub const Obj = struct {
}
pub fn is_function(self: *Obj) bool {
return self.is_function(ObjType.Function);
return self.is_type(ObjType.Function);
}
pub fn is_native(self: *Obj) bool {
return self.is_type(ObjType.Native);
}
pub fn print(self: *Obj) void {
@ -94,6 +124,10 @@ pub const Obj = struct {
debug.print("<fn {s}>", .{obj.name.?.chars});
}
},
ObjType.Native => {
// const obj = self.as_native();
debug.print("<native fn>", .{});
},
}
}
@ -107,6 +141,10 @@ pub const Obj = struct {
const obj: *Function = @fieldParentPtr("obj", self);
obj.destroy();
},
ObjType.Native => {
const obj: *Native = @fieldParentPtr("obj", self);
obj.destroy();
},
}
}
@ -119,4 +157,9 @@ pub const Obj = struct {
std.debug.assert(self.kind == ObjType.Function);
return @fieldParentPtr("obj", self);
}
pub fn as_native(self: *Obj) *Native {
std.debug.assert(self.kind == ObjType.Native);
return @fieldParentPtr("obj", self);
}
};

View File

@ -9,6 +9,7 @@ const OpCode = @import("./opcode.zig").OpCode;
const Value = @import("./values.zig").Value;
const Obj = @import("./object.zig").Obj;
const ObjType = @import("./object.zig").ObjType;
const NativeFn = @import("./object.zig").NativeFn;
const Table = @import("./table.zig").Table;
const compile = @import("./compile.zig").compile;
@ -54,6 +55,10 @@ pub const VM = struct {
};
}
pub fn init_vm(self: *VM) void {
self.define_native("clock", clock_native);
}
pub fn destroy(self: *VM) void {
if (constants.DEBUG_PRINT_INTERNAL_STRINGS) {
self.strings.dump();
@ -378,6 +383,16 @@ pub const VM = struct {
ObjType.Function => {
return self.call(callee.as_obj().as_function(), arg_count);
},
ObjType.Native => {
const native_obj: *Obj.Native = callee.as_obj().as_native();
const value = native_obj.native(
arg_count,
self.stack[self.current_frame().slots_idx - arg_count .. self.current_frame().slots_idx],
);
self.stack_top -= arg_count + 1;
_ = try self.push(value);
return true;
},
else => {},
}
}
@ -406,4 +421,21 @@ pub const VM = struct {
return true;
}
pub fn define_native(self: *VM, name: []const u8, native_fn: NativeFn) void {
_ = try self.push(Value.obj_val(&self.copy_string(name).obj));
_ = try self.push(Value.obj_val(&Obj.Native.new(self.allocator, native_fn).obj));
_ = self.globals.set(self.stack[0].as_string(), self.stack[1]);
_ = self.pop();
_ = self.pop();
}
pub fn clock_native(arg_count: usize, args: []Value) Value {
const ts = std.time.milliTimestamp();
_ = arg_count;
_ = args;
return Value.number_val(@floatFromInt(ts));
}
};