DzLox

:)
git clone https://git.sr.ht/~ashymad/DzLox
Log | Files | Refs | Submodules | LICENSE

commit 206ed850745f3073331e937a445aee5582334310
parent b2f59867f59d26a2dbdf0a10b0142057053d4849
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Sun, 11 Aug 2024 14:43:30 +0200

Functions done

Diffstat:
Mzlox/src/chunk.zig | 1+
Mzlox/src/compiler.zig | 44+++++++++++++++++++++++++++++++++++++++++---
Mzlox/src/debug.zig | 1+
Mzlox/src/obj/function.zig | 6+++---
Mzlox/src/vm.zig | 47+++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 89 insertions(+), 10 deletions(-)

diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig @@ -33,6 +33,7 @@ pub const OP = enum(u8) { LOOP, SET_INDEX, GET_INDEX, + CALL, }; pub const Chunk = struct { diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -76,11 +76,11 @@ pub fn Compiler(size: comptime_int) type { const tok: Token = @enumFromInt(i); v.* = switch (tok) { // zig fmt: off - T.LEFT_PAREN => R(S.grouping, null, P.NONE ), + T.LEFT_PAREN => R(S.grouping, S.call, P.CALL ), T.RIGHT_PAREN => R(null, null, P.NONE ), T.LEFT_BRACE => R(null, null, P.NONE ), T.RIGHT_BRACE => R(null, null, P.NONE ), - T.LEFT_BRACKET => R(S.table, S.index, P.CALL ), + T.LEFT_BRACKET => R(S.table, S.index, P.CALL ), T.RIGHT_BRACKET => R(null, null, P.NONE ), T.COMMA => R(null, null, P.NONE ), T.DOT => R(null, null, P.NONE ), @@ -183,7 +183,7 @@ pub fn Compiler(size: comptime_int) type { } fn emitReturn(self: *Self) void { - self.emitOP(OP.RETURN); + self.emit2OP(OP.NIL, OP.RETURN); } fn errorAtCurrent(self: *Self, message: []const u8) void { @@ -272,6 +272,28 @@ pub fn Compiler(size: comptime_int) type { self.emitConstant(self.parseLiteralChar()); } + fn call(self: *Self, _: bool) void { + const argCount = self.argumentList(); + self.emit(OP.CALL, argCount); + } + + fn argumentList(self: *Self) u8 { + var argCount: u8 = 0; + if (!self.check(Token.RIGHT_PAREN)) { + while(true) { + self.expression(); + if (argCount == std.math.maxInt(u8)) { + self.errorAtPrevious("Too many arguments"); + return argCount; + } + argCount += 1; + if (!self.match(Token.COMMA)) break; + } + } + self.consume(Token.RIGHT_PAREN, "Expect ')' after arguments"); + return argCount; + } + fn parseLiteralValue(self: *Self) CompilerError!Value { if (self.match(Token.STRING)) { return self.parseLiteralString(); @@ -646,6 +668,8 @@ pub fn Compiler(size: comptime_int) type { self.printStatement(); } else if (self.match(Token.IF)) { self.ifStatement(); + } else if (self.match(Token.RETURN)) { + self.returnStatement(); } else if (self.match(Token.WHILE)) { self.whileStatement(); } else if (self.match(Token.FOR)) { @@ -661,6 +685,20 @@ pub fn Compiler(size: comptime_int) type { } } + fn returnStatement(self: *Self) void { + if (self.currentFunction.type == Obj.Function.Type.Script) { + self.errorAtPrevious("Can't return from top-level code"); + return; + } + if (self.match(Token.SEMICOLON)) { + self.emitReturn(); + } else { + self.expression(); + self.consume(Token.SEMICOLON, "Expect ';' after return value"); + self.emitOP(OP.RETURN); + } + } + fn switchStatement(self: *Self) void { self.consume(Token.LEFT_PAREN, "Expect '(' after 'switch'."); diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig @@ -54,6 +54,7 @@ pub fn disassembleInstruction(ch: chunk.Chunk, offset: usize) !usize { @intFromEnum(OP.LOOP) => try jumpInstruction(name, false, ch, offset), @intFromEnum(OP.SET_INDEX) => simpleInstruction(name, offset), @intFromEnum(OP.GET_INDEX) => simpleInstruction(name, offset), + @intFromEnum(OP.CALL) => simpleInstruction(name, offset), else => blk: { print("Unknown opcode {d} {s}\n", .{op, name}); break :blk offset + 1; diff --git a/zlox/src/obj/function.zig b/zlox/src/obj/function.zig @@ -41,13 +41,13 @@ pub const Function = packed struct { pub fn format(self: *const Self, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { switch(self.type) { - .Function => _ = try writer.write("<function: "), - .Script => _ = try writer.write("<script: "), + .Function => _ = try writer.write("<F: "), + .Script => _ = try writer.write("<S: "), } if (self.name) |name| { _ = try writer.write(name.slice()); } else { - _ = try writer.write("-anonymous-"); + _ = try writer.write("-"); } _ = try writer.writeAll(">"); } diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -10,7 +10,7 @@ const table = @import("table.zig"); const hash = @import("hash.zig"); const utils = @import("comptime_utils.zig"); -pub const InterpreterError = compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, IndexOutOfBounds, Overflow, DivisionByZero }; +pub const InterpreterError = compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, StackOverflow, IndexOutOfBounds, Overflow, DivisionByZero }; pub const VM = struct { objects: Obj.List, @@ -146,6 +146,26 @@ pub const VM = struct { return (self.stackTop - (1 + distance))[0]; } + 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 { + self.runtimeError("Can only call functions and classes", .{}); + return InterpreterError.RuntimeError; + } + } + + fn call(self: *@This(), callee: *Obj.Function, argCount: u8) !void { + if (argCount != callee.arity) { + self.runtimeError("Expected {d} arguments but got {d}", .{callee.arity, argCount}); + return InterpreterError.RuntimeError; + } + if (self.frameCount == callstack_size - 1) + return InterpreterError.StackOverflow; + self.frameCount += 1; + self.frames[self.frameCount - 1] = CallFrame.init(callee, self.stackTop - argCount - 1); + } + fn binary_op(self: *@This(), comptime in_tag: anytype, comptime out_tag: anytype, op: Callback.Type(in_tag, out_tag)) InterpreterError!void { const b = self.pop(); const a = self.pop(); @@ -177,7 +197,16 @@ pub const VM = struct { @intFromEnum(OP.PRINT) => { std.debug.print("{s}\n", .{self.pop()}); }, - @intFromEnum(OP.RETURN) => return, + @intFromEnum(OP.RETURN) => { + const result = self.pop(); + if (self.frameCount == 1) { + _ = self.pop(); + return; + } + self.stackTop = self.frame().slots; + self.frameCount -= 1; + self.push(result); + }, @intFromEnum(OP.POP) => _ = self.pop(), @intFromEnum(OP.CONSTANT) => self.push(self.read_constant()), @intFromEnum(OP.NEGATE) => { @@ -268,6 +297,10 @@ pub const VM = struct { } self.push(val); }, + @intFromEnum(OP.CALL) => { + const argCount = self.read_byte(); + try self.callValue(self.peek(argCount), argCount); + }, @intFromEnum(OP.DEFINE_GLOBAL) => _ = try self.vm.globals.set(self.read_string(), Global.make_var(self.pop())), @intFromEnum(OP.DEFINE_GLOBAL_CONSTANT) => _ = try self.vm.globals.set(self.read_string(), Global.make_con(self.pop())), @intFromEnum(OP.SUBTRACT) => try self.binary_op(Value.number, Value.number, Callback.sub), @@ -286,8 +319,14 @@ pub const VM = struct { } fn runtimeError(self: *@This(), comptime fmt: []const u8, args: anytype) void { - std.debug.print(fmt, args); - std.debug.print("\n[line {d}] in script\n", .{self.frame().function.chunk.lines.get(self.instruction_idx()) catch 0}); + var i = self.frameCount - 1; + while (true) : (i -= 1) { + const fram = self.frames[i]; + const idx = @intFromPtr(fram.ip) - @intFromPtr(fram.function.chunk.code.data.ptr); + std.debug.print("[line {d}] in {s}\n", .{fram.function.chunk.lines.get(idx) catch 0, fram.function}); + if (i == 0) break; + } + std.debug.print(fmt ++ "\n", args); } }; }