commit 206ed850745f3073331e937a445aee5582334310
parent b2f59867f59d26a2dbdf0a10b0142057053d4849
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Sun, 11 Aug 2024 14:43:30 +0200
Functions done
Diffstat:
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);
}
};
}