DzLox

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

commit aec91897f03b022dec6bb6731f1986e193715463
parent 74b66998cf648e3c5b9edc44d8b22a4699ddee91
Author: Ashymad <szymon.mikulicz@posteo.net>
Date:   Sat, 16 Mar 2024 14:24:02 +0100

First GC

Diffstat:
Mzlox/src/chunk.zig | 18++++++++++++++++++
Mzlox/src/compiler.zig | 61++++++++++++++++++++++++++++++++-----------------------------
Mzlox/src/obj.zig | 24++++++++++++++++++------
Mzlox/src/value.zig | 9++++-----
Mzlox/src/vm.zig | 13++++++-------
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); }