commit aec91897f03b022dec6bb6731f1986e193715463
parent 74b66998cf648e3c5b9edc44d8b22a4699ddee91
Author: Ashymad <szymon.mikulicz@posteo.net>
Date: Sat, 16 Mar 2024 14:24:02 +0100
First GC
Diffstat:
5 files changed, 78 insertions(+), 47 deletions(-)
diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig
@@ -2,6 +2,7 @@ const std = @import("std");
const value = @import("value.zig");
const ValueArray = value.ValueArray;
const array = @import("array.zig");
+const Obj = @import("obj.zig").Obj;
pub const OP = enum(u8) {
CONSTANT,
@@ -28,6 +29,8 @@ pub const Chunk = struct {
.code = try array.Array(u8, usize, 8).init(allocator),
.constants = try ValueArray.init(allocator),
.lines = try array.RLEArray(i32, 8).init(allocator),
+ .allocator = allocator,
+ .objects = null,
};
}
@@ -42,15 +45,30 @@ pub const Chunk = struct {
pub fn addConstant(self: *@This(), val: value.Value) Error!u8 {
try self.constants.add(val);
+ self.addObject(val);
return self.constants.len - 1;
}
+ pub fn addObject(self: *@This(), val: value.Value) void {
+ if (val.is(value.Value.obj)) {
+ val.obj.next = self.objects;
+ self.objects = val.obj;
+ }
+ }
+
pub fn deinit(self: *@This()) void {
self.constants.deinit();
self.lines.deinit();
self.code.deinit();
+
+ while (self.objects) |obj| {
+ self.objects = obj.next;
+ obj.free(self.allocator);
+ }
}
+ objects: ?*Obj,
+ allocator: std.mem.Allocator,
code: array.Array(u8, usize, 8),
constants: ValueArray,
lines: array.RLEArray(i32, 8),
diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig
@@ -1,11 +1,12 @@
const std = @import("std");
const scanner = @import("scanner.zig");
-const chunk = @import("chunk.zig");
+const Chunk = @import("chunk.zig").Chunk;
+const OP = @import("chunk.zig").OP;
const Value = @import("value.zig").Value;
const Obj = @import("obj.zig").Obj;
const debug = @import("debug.zig");
-pub const CompilerError = scanner.ScannerError || chunk.Chunk.Error || Value.ParseNumberError || error{ UnexpectedToken, NotAnExpression };
+pub const CompilerError = scanner.ScannerError || Chunk.Error || Value.ParseNumberError || error{ UnexpectedToken, NotAnExpression };
const Precedence = enum {
NONE,
@@ -37,7 +38,7 @@ pub const Compiler = struct {
lastError: CompilerError,
hadError: bool,
panicMode: bool,
- compilingChunk: *chunk.Chunk,
+ compilingChunk: Chunk,
allocator: std.mem.Allocator,
const ParseFn = *const fn (*@This()) void;
@@ -127,8 +128,8 @@ pub const Compiler = struct {
}
}
- fn currentChunk(self: *@This()) *chunk.Chunk {
- return self.compilingChunk;
+ fn currentChunk(self: *@This()) *Chunk {
+ return &self.compilingChunk;
}
fn emitByte(self: *@This(), byte: u8) void {
@@ -138,19 +139,19 @@ pub const Compiler = struct {
};
}
- fn emitOP(self: *@This(), op: chunk.OP) void {
+ fn emitOP(self: *@This(), op: OP) void {
self.currentChunk().writeOP(op, self.previous.line) catch |err| {
self.lastError = err;
self.errorAtCurrent("Out of Memory");
};
}
- fn emit(self: *@This(), op: chunk.OP, byte: u8) void {
+ fn emit(self: *@This(), op: OP, byte: u8) void {
self.emitOP(op);
self.emitByte(byte);
}
- fn emit2OP(self: *@This(), op: chunk.OP, op2: chunk.OP) void {
+ fn emit2OP(self: *@This(), op: OP, op2: OP) void {
self.emitOP(op);
self.emitOP(op2);
}
@@ -165,7 +166,7 @@ pub const Compiler = struct {
}
fn emitReturn(self: *@This()) void {
- self.emitOP(chunk.OP.RETURN);
+ self.emitOP(OP.RETURN);
}
fn errorAtCurrent(self: *@This(), message: []const u8) void {
@@ -239,14 +240,14 @@ pub const Compiler = struct {
fn string(self: *@This()) void {
const ret = Obj.String.init(self.previous.lexeme[1 .. self.previous.lexeme.len - 1], self.allocator) catch |err| {
self.lastError = err;
- self.errorAtPrevious("Out of memory");
+ self.errorAtPrevious("Couldn't allocate object");
return;
};
self.emitConstant(Value{ .obj = ret.cast() });
}
fn emitConstant(self: *@This(), val: Value) void {
- self.emit(chunk.OP.CONSTANT, self.makeConstant(val));
+ self.emit(OP.CONSTANT, self.makeConstant(val));
}
fn makeConstant(self: *@This(), val: Value) u8 {
@@ -268,17 +269,17 @@ pub const Compiler = struct {
self.parsePrecedence(Precedence.UNARY);
switch (operatorType) {
- scanner.TokenType.MINUS => self.emitOP(chunk.OP.NEGATE),
- scanner.TokenType.BANG => self.emitOP(chunk.OP.NOT),
+ scanner.TokenType.MINUS => self.emitOP(OP.NEGATE),
+ scanner.TokenType.BANG => self.emitOP(OP.NOT),
else => unreachable,
}
}
fn literal(self: *@This()) void {
switch (self.previous.type catch unreachable) {
- scanner.TokenType.FALSE => self.emitOP(chunk.OP.FALSE),
- scanner.TokenType.TRUE => self.emitOP(chunk.OP.TRUE),
- scanner.TokenType.NIL => self.emitOP(chunk.OP.NIL),
+ scanner.TokenType.FALSE => self.emitOP(OP.FALSE),
+ scanner.TokenType.TRUE => self.emitOP(OP.TRUE),
+ scanner.TokenType.NIL => self.emitOP(OP.NIL),
else => unreachable,
}
}
@@ -288,16 +289,16 @@ pub const Compiler = struct {
self.parsePrecedence(getRule(operatorType).precedence.inc());
switch (operatorType) {
- scanner.TokenType.PLUS => self.emitOP(chunk.OP.ADD),
- scanner.TokenType.MINUS => self.emitOP(chunk.OP.SUBTRACT),
- scanner.TokenType.STAR => self.emitOP(chunk.OP.MULTIPLY),
- scanner.TokenType.SLASH => self.emitOP(chunk.OP.DIVIDE),
- scanner.TokenType.BANG_EQUAL => self.emit2OP(chunk.OP.EQUAL, chunk.OP.NOT),
- scanner.TokenType.EQUAL_EQUAL => self.emitOP(chunk.OP.EQUAL),
- scanner.TokenType.GREATER => self.emitOP(chunk.OP.GREATER),
- scanner.TokenType.GREATER_EQUAL => self.emit2OP(chunk.OP.LESS, chunk.OP.NOT),
- scanner.TokenType.LESS => self.emitOP(chunk.OP.LESS),
- scanner.TokenType.LESS_EQUAL => self.emit2OP(chunk.OP.GREATER, chunk.OP.NOT),
+ scanner.TokenType.PLUS => self.emitOP(OP.ADD),
+ scanner.TokenType.MINUS => self.emitOP(OP.SUBTRACT),
+ scanner.TokenType.STAR => self.emitOP(OP.MULTIPLY),
+ scanner.TokenType.SLASH => self.emitOP(OP.DIVIDE),
+ scanner.TokenType.BANG_EQUAL => self.emit2OP(OP.EQUAL, OP.NOT),
+ scanner.TokenType.EQUAL_EQUAL => self.emitOP(OP.EQUAL),
+ scanner.TokenType.GREATER => self.emitOP(OP.GREATER),
+ scanner.TokenType.GREATER_EQUAL => self.emit2OP(OP.LESS, OP.NOT),
+ scanner.TokenType.LESS => self.emitOP(OP.LESS),
+ scanner.TokenType.LESS_EQUAL => self.emit2OP(OP.GREATER, OP.NOT),
else => unreachable,
}
}
@@ -315,13 +316,15 @@ pub const Compiler = struct {
// emit bytecode
}
- pub fn compile(source: []const u8, ch: *chunk.Chunk, allocator: std.mem.Allocator) CompilerError!void {
- var self = @This(){ .scanner = try scanner.Scanner.init(source), .current = scanner.Token.Empty, .previous = scanner.Token.Empty, .panicMode = false, .hadError = false, .lastError = scanner.ScannerError.EmptyToken, .compilingChunk = ch, .allocator = allocator };
+ pub fn compile(source: []const u8, allocator: std.mem.Allocator) CompilerError!Chunk {
+ var self = @This(){ .scanner = try scanner.Scanner.init(source), .current = scanner.Token.Empty, .previous = scanner.Token.Empty, .panicMode = false, .hadError = false, .lastError = scanner.ScannerError.EmptyToken, .compilingChunk = try Chunk.init(allocator), .allocator = allocator };
+ errdefer self.compilingChunk.deinit();
+
self.advance();
self.expression();
self.consume(scanner.TokenType.EOF, "Expected end of expression.");
self.endCompiler();
- if (self.hadError) return self.lastError;
+ return if (self.hadError) self.lastError else self.compilingChunk;
}
};
diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig
@@ -2,17 +2,26 @@ const std = @import("std");
pub const Obj = packed struct {
const Self = @This();
+ pub const Error = error{IllegalCastError};
+
+ type: Type,
+ next: ?*Self = null,
pub const String = struct {
obj: Self,
len: usize,
+ fn ptr(self: *const @This()) []align(@alignOf(@This())) const u8 {
+ const p: [*]align(@alignOf(@This())) const u8 = @ptrCast(self);
+ return p[0 .. @sizeOf(@This()) + self.len];
+ }
+
fn data(self: *@This()) [*]u8 {
- const ptr: [*]u8 = @ptrCast(self);
- return ptr + @sizeOf(@This());
+ const p: [*]u8 = @ptrCast(self);
+ return p + @sizeOf(@This());
}
fn cdata(self: *const @This()) [*]const u8 {
- const ptr: [*]const u8 = @ptrCast(self);
- return ptr + @sizeOf(@This());
+ const p: [*]const u8 = @ptrCast(self);
+ return p + @sizeOf(@This());
}
fn new(len: usize, allocator: std.mem.Allocator) !*@This() {
const ret: *@This() = @ptrCast(try allocator.alignedAlloc(u8, @alignOf(@This()), @sizeOf(@This()) + len));
@@ -48,8 +57,11 @@ pub const Obj = packed struct {
}
};
- const Error = error{IllegalCastError};
- type: Type,
+ pub fn free(obj: *Self, allocator: std.mem.Allocator) void {
+ return switch (obj.type) {
+ .String => allocator.free(obj._cast(.String).ptr()),
+ };
+ }
pub fn is(self: *const Self, tp: Type) bool {
return self.type == tp;
diff --git a/zlox/src/value.zig b/zlox/src/value.zig
@@ -20,11 +20,10 @@ pub const Value = union(enum) {
}
fn toTag(comptime from: anytype) Tag {
- if (@TypeOf(from) == Obj.Type) {
- return .obj;
- } else {
- return from;
- }
+ return if (@TypeOf(from) == Obj.Type)
+ .obj
+ else
+ from;
}
pub fn is(self: @This(), comptime tag: anytype) bool {
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -7,11 +7,11 @@ const compiler = @import("compiler.zig");
const Obj = @import("obj.zig").Obj;
const Callback = @import("vm_callbacks.zig");
-pub const InterpreterError = compiler.CompilerError || Callback.Error || Chunk.Error || error{ CompileError, RuntimeError, IndexOutOfBounds, Overflow, DivisionByZero };
+pub const InterpreterError = compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, IndexOutOfBounds, Overflow, DivisionByZero };
pub const VM = struct {
ip: [*]const u8,
- chunk: *const Chunk,
+ chunk: *Chunk,
stack: stackType,
stackTop: [*]Value,
@@ -29,7 +29,7 @@ pub const VM = struct {
return ret;
}
- pub fn interpretChunk(self: *@This(), chunk: *const Chunk, allocator: std.mem.Allocator) InterpreterError!void {
+ pub fn interpretChunk(self: *@This(), chunk: *Chunk, allocator: std.mem.Allocator) InterpreterError!void {
self.resetStack();
self.chunk = chunk;
self.ip = chunk.code.data.ptr;
@@ -37,11 +37,9 @@ pub const VM = struct {
}
pub fn interpret(self: *@This(), source: []const u8, allocator: std.mem.Allocator) InterpreterError!void {
- var chunk = try Chunk.init(allocator);
+ var chunk = try compiler.Compiler.compile(source, allocator);
defer chunk.deinit();
- try compiler.Compiler.compile(source, &chunk, allocator);
-
try self.interpretChunk(&chunk, allocator);
self.resetStack();
}
@@ -70,7 +68,7 @@ pub const VM = struct {
return self.stackTop[0];
}
- pub fn peek(self: *const @This(), distance: usize) Value {
+ pub fn peek(self: *@This(), distance: usize) Value {
return (self.stackTop - (1 + distance))[0];
}
@@ -123,6 +121,7 @@ pub const VM = struct {
@intFromEnum(OP.ADD) => {
if (self.peek(0).is(Obj.Type.String)) {
try self.binary_op(Obj.Type.String, Obj.Type.String, Callback.concatenate(allocator));
+ self.chunk.addObject(self.peek(0));
} else {
try self.binary_op(Value.number, Value.number, Callback.add);
}