diff --git a/.justfile b/.justfile index 69346f9..ba4bb2a 100644 --- a/.justfile +++ b/.justfile @@ -4,5 +4,8 @@ build: run *ARGS: zig build run -- {{ARGS}} +run-fast *ARGS: + zig build run -Doptimize=ReleaseFast -- {{ARGS}} + test: zig build test diff --git a/README.md b/README.md index 5c56873..b07000c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/samples/ch24_fib.lox b/samples/ch24_fib.lox new file mode 100644 index 0000000..d2742e7 --- /dev/null +++ b/samples/ch24_fib.lox @@ -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; diff --git a/src/compile.zig b/src/compile.zig index ae0c0b1..98e604f 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -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, }; diff --git a/src/main.zig b/src/main.zig index 6b28e2f..5912a66 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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) { diff --git a/src/object.zig b/src/object.zig index 69de867..09d8704 100644 --- a/src/object.zig +++ b/src/object.zig @@ -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("", .{obj.name.?.chars}); } }, + ObjType.Native => { + // const obj = self.as_native(); + debug.print("", .{}); + }, } } @@ -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); + } }; diff --git a/src/vm.zig b/src/vm.zig index abf50f5..08bad7f 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -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)); + } };