DzLox

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

commit aefe9f1ddad710a4d181be6c4dabb37d85cccd7e
parent a6a02ae1e9df9e158839cd29658daca4a7cd50c8
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Sun,  4 Aug 2024 16:31:40 +0200

Chars

Diffstat:
Mzlox/src/compiler.zig | 29+++++++++++++++++++++++++++--
Mzlox/src/hash.zig | 7++++---
Mzlox/src/obj.zig | 10++++++++++
Mzlox/src/scanner.zig | 16+++++++++++++++-
Mzlox/src/value.zig | 3+++
Mzlox/src/vm.zig | 15+++++++++------
6 files changed, 68 insertions(+), 12 deletions(-)

diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -103,6 +103,7 @@ pub fn Compiler(size: comptime_int) type { T.LESS_EQUAL => R(null, S.binary, P.COMPARISON ), T.IDENTIFIER => R(S.variable, null, P.NONE ), T.STRING => R(S.string, null, P.NONE ), + T.CHAR => R(S.char, null, P.NONE ), T.NUMBER => R(S.number, null, P.NONE ), T.AND => R(null, S._and, P.AND ), T.CLASS => R(null, null, P.NONE ), @@ -121,6 +122,7 @@ pub fn Compiler(size: comptime_int) type { T.VAR => R(null, null, P.NONE ), T.CON => R(null, null, P.NONE ), T.WHILE => R(null, null, P.NONE ), + T.SWITCH => R(null, null, P.NONE ), T.EOF => R(null, null, P.NONE ), // zig fmt: on }; @@ -265,11 +267,17 @@ pub fn Compiler(size: comptime_int) type { }); } + fn char(self: *Self, _: bool) void { + self.emitConstant(self.parseLiteralChar()); + } + fn parseLiteralValue(self: *Self) CompilerError!Value { if (self.match(Token.STRING)) { return self.parseLiteralString(); + } else if (self.match(Token.CHAR)) { + return self.parseLiteralChar(); } else if (self.match(Token.NUMBER)) { - return try Value.parseNumber(self.previous.lexeme); + return self.parseLiteralNumber(); } else if (self.match(Token.FALSE)) { return Value.init(false); } else if (self.match(Token.TRUE)) { @@ -279,11 +287,19 @@ pub fn Compiler(size: comptime_int) type { } else if (self.match(Token.LEFT_BRACKET)) { return self.parseLiteralMap(); } else { - self.errorAtCurrent("Map initalizer can only contain literals"); + self.errorAtCurrent("Not a literal value"); return error.UnexpectedToken; } } + fn parseLiteralNumber(self: *Self) !Value { + return Value.parseNumber(self.previous.lexeme); + } + + fn parseLiteralChar(self: *Self) Value { + return Value.init(self.previous.lexeme[1]); + } + fn parseLiteralString(self: *Self) !Value { return Value.init(try self.objects.emplace(.String, &.{self.previous.lexeme[1 .. self.previous.lexeme.len - 1]})); } @@ -574,6 +590,8 @@ pub fn Compiler(size: comptime_int) type { self.whileStatement(); } else if (self.match(Token.FOR)) { self.forStatement(); + } else if (self.match(Token.SWITCH)) { + self.switchStatement(); } else if (self.match(Token.LEFT_BRACE)) { self.beginScope(); self.block(); @@ -583,6 +601,13 @@ pub fn Compiler(size: comptime_int) type { } } + fn switchStatement(self: *Self) void { + self.consume(Token.LEFT_PAREN, "Expect '(' after 'switch'."); + self.expression(); + self.consume(Token.RIGHT_PAREN, "Expect ')' after expression"); + //var array = ValueArray.init(self.allocator); + } + fn whileStatement(self: *Self) void { const loopStart = self.currentChunk().code.len; diff --git a/zlox/src/hash.zig b/zlox/src/hash.zig @@ -52,9 +52,10 @@ pub fn hash_t(T: type) fn (T) u32 { pub fn fun(val: T) u32 { return switch(val) { .number => |v| hash_append_t([]const u8)(hash(v), "\x01"), - .bool => |v| hash_append_t([]const u8)(hash(v), "\x02"), - .nil => hash_t([]const u8)("\x03"), - .obj => |v| hash_append_t([]const u8)(hash(v), "\x04"), + .char => |v| hash_t([]const u8)(&[_]u8{v, 2}), + .bool => |v| hash_append_t([]const u8)(hash(v), "\x03"), + .nil => hash_t([]const u8)("\x04"), + .obj => |v| hash_append_t([]const u8)(hash(v), "\x05"), }; } }.fun, diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -99,6 +99,12 @@ pub const Obj = packed struct { pub fn eql(self: *const Self, other: *const Self) bool { return @intFromPtr(self) == @intFromPtr(other); } + pub fn get(self: *const Self, index: value.Value) !value.Value { + if (!index.is(value.Value.number) or index.number >= @as(value.Value.tagType(value.Value.number), @floatFromInt(self.len)) or index.number < 0) { + return error.KeyError; + } + return value.Value.init(self.data()[@intFromFloat(index.number)]); + } const ArgParams = struct { len: usize, hash: u32 }; @@ -191,6 +197,10 @@ pub const Obj = packed struct { self.hash +%= hash.hash(key) +% hash.hash(val); _ = try self.map.set(key, val); } + + pub fn get(self: *Self, key: value.Value) !value.Value { + return self.map.get(key); + } fn print_element(key: value.Value, val: value.Value) void { key.print(); diff --git a/zlox/src/scanner.zig b/zlox/src/scanner.zig @@ -28,6 +28,7 @@ pub const TokenType = enum { // Literals. IDENTIFIER, STRING, + CHAR, NUMBER, // Keywords. AND, @@ -47,17 +48,19 @@ pub const TokenType = enum { VAR, CON, WHILE, + SWITCH, EOF, }; -pub const ScannerError = error{ UnexpectedCharacter, UnknownCharacter, UnterminatedString, EmptyToken }; +pub const ScannerError = error{ UnexpectedCharacter, UnknownCharacter, UnterminatedString, UnterminatedChar, EmptyToken }; pub fn ScannerErrorString(err: ScannerError) []const u8 { return switch (err) { ScannerError.UnknownCharacter => "Unknown Character", ScannerError.UnexpectedCharacter => "Unexpected Character", ScannerError.UnterminatedString => "Unterminated String", + ScannerError.UnterminatedChar => "Unterminated Char", ScannerError.EmptyToken => "Empty Token", }; } @@ -90,6 +93,7 @@ pub const Scanner = struct { .{ "var", TokenType.VAR }, .{ "con", TokenType.CON }, .{ "while", TokenType.WHILE }, + .{ "switch", TokenType.SWITCH }, }); pub fn init(source: []const u8) !@This() { @@ -124,6 +128,7 @@ pub const Scanner = struct { '<' => return self.makeToken(if (self.match('=')) TokenType.LESS_EQUAL else TokenType.LESS), '>' => return self.makeToken(if (self.match('=')) TokenType.GREATER_EQUAL else TokenType.GREATER), '"' => return self.string(), + '\'' => return self.char(), '0'...'9' => return self.number(), 'a'...'z', 'A'...'Z', '_' => return self.identifier(), else => return self.makeToken(ScannerError.UnknownCharacter), @@ -145,6 +150,15 @@ pub const Scanner = struct { return self.makeToken(TokenType.STRING); } + fn char(self: *@This()) Token { + if (self.isAtEnd()) return self.makeToken(ScannerError.UnterminatedChar); + _ = self.advance(); + if (self.isAtEnd() or self.peek() != '\'') return self.makeToken(ScannerError.UnterminatedChar); + _ = self.advance(); + + return self.makeToken(TokenType.CHAR); + } + fn skipWhitespace(self: *@This()) void { while (true) { switch (self.peek()) { diff --git a/zlox/src/value.zig b/zlox/src/value.zig @@ -4,6 +4,7 @@ const Obj = @import("obj.zig").Obj; pub const Value = union(enum) { number: f64, + char: u8, bool: bool, nil: void, obj: *Obj, @@ -13,6 +14,7 @@ pub const Value = union(enum) { pub fn print(self: @This()) void { switch (self) { .number => |val| std.debug.print("{d}", .{val}), + .char => |val| std.debug.print("'{s}'", .{&[_]u8{val}}), .bool => |val| std.debug.print("{s}", .{if (val) "true" else "false"}), .nil => std.debug.print("nil", .{}), .obj => |o| o.print(), @@ -72,6 +74,7 @@ pub const Value = union(enum) { if (@intFromEnum(self) != @intFromEnum(other)) return false; return switch (self) { .number => |x| x == other.number, + .char => |x| x == other.char, .bool => |x| x == other.bool, .nil => true, .obj => |x| x.eql(other.obj), diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -205,15 +205,18 @@ pub const VM = struct { @intFromEnum(OP.GET_INDEX) => { const idx = self.pop(); const map = self.pop(); - if (!map.is(Obj.Type.Map)) { + if (map.is(Obj.Type.Map) or map.is(Obj.Type.String)) { + switch(map.obj.type) { + inline else => |tp| self.push((map.obj.cast(tp) catch unreachable).get(idx) catch { + idx.print(); + self.runtimeError(" index invalid", .{}); + return InterpreterError.RuntimeError; + }) + } + } else { self.runtimeError("Cannot index a non-map value", .{}); return InterpreterError.RuntimeError; } - self.push((map.obj.cast(.Map) catch unreachable).map.get(idx) catch { - idx.print(); - self.runtimeError(" key does not exist in the map", .{}); - return InterpreterError.RuntimeError; - }); }, @intFromEnum(OP.SET_INDEX) => { const val = self.pop();