commit aefe9f1ddad710a4d181be6c4dabb37d85cccd7e
parent a6a02ae1e9df9e158839cd29658daca4a7cd50c8
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Sun, 4 Aug 2024 16:31:40 +0200
Chars
Diffstat:
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();