Make tables work and add rawEquals methods

Since hashtables are a hassle with strings and complex datastructures
like the table they are an arraylist for now.

Also implement rawEquals for numerals and values to make table.get work
This commit is contained in:
0x4261756D 2023-11-24 03:22:15 +01:00
parent cfc288e279
commit 5dc1b9d50b
2 changed files with 152 additions and 12 deletions

View File

@ -4,7 +4,7 @@ const types = @import("types.zig");
pub fn interpret(root: parser.ChunkNode, allocator: std.mem.Allocator) !void pub fn interpret(root: parser.ChunkNode, allocator: std.mem.Allocator) !void
{ {
var _ENV = types.Table { .items = std.HashMap(types.Value, types.Value, std.hash_map.AutoContext(types.Value), std.hash_map.default_max_load_percentage).init(allocator) }; var _ENV = types.Table { .entries= std.ArrayList(types.TableEntry).init(allocator) };
try walkChunk(root, &_ENV, allocator); try walkChunk(root, &_ENV, allocator);
} }

View File

@ -1,34 +1,174 @@
const std = @import("std"); const std = @import("std");
pub const Numeral = union(enum) pub const NumeralTag = enum
{
Integer,
Float,
};
pub fn isFloatExactInt(value: f64) bool
{
return value == 0 or (std.math.isNormal(value) and @floor(value) == value);
}
pub const Numeral = union(NumeralTag)
{ {
Integer: i64, Integer: i64,
Float: f64, Float: f64,
pub fn rawEqual(self: Numeral, other: Numeral) bool
{
if(@as(NumeralTag, self) == @as(NumeralTag, other))
{
switch(self)
{
.Float => |value| return value == other.Float,
.Integer => |value| return value == other.Integer,
}
}
// Other is the respective other type
switch(self)
{
.Float => |value|
{
return isFloatExactInt(value) and @as(u64, @intFromFloat(value)) == other.Integer;
},
.Integer => |value|
{
return isFloatExactInt(other.Float) and @as(u64, @intFromFloat(other.Float)) == value;
}
}
}
}; };
test "float int equality"
{
const a = Numeral { .Float = 12.0 };
const b = Numeral { .Integer = 12 };
try std.testing.expect(a.rawEqual(b));
try std.testing.expect(b.rawEqual(a));
try std.testing.expect(a.rawEqual(a));
try std.testing.expect(b.rawEqual(b));
const c = Numeral { .Float = (0.2 + 0.1) * 10.0 };
const d = Numeral { .Integer = 3 };
try std.testing.expect(c.rawEqual(d));
try std.testing.expect(d.rawEqual(c));
try std.testing.expect(c.rawEqual(c));
try std.testing.expect(d.rawEqual(d));
const e = Numeral { .Float = 3.2 };
try std.testing.expect(!a.rawEqual(e));
try std.testing.expect(!b.rawEqual(e));
try std.testing.expect(!c.rawEqual(e));
try std.testing.expect(!d.rawEqual(e));
try std.testing.expect(!e.rawEqual(a));
try std.testing.expect(!e.rawEqual(b));
try std.testing.expect(!e.rawEqual(c));
try std.testing.expect(!e.rawEqual(d));
}
pub const TableEntry = struct
{
key: Value,
value: Value,
};
pub const Table = struct pub const Table = struct
{ {
items: std.HashMap(Value, Value, std.hash_map.AutoContext(Value), std.hash_map.default_max_load_percentage), entries: std.ArrayList(TableEntry),
pub fn insert(self: *const Table, key: Value, value: Value) !bool pub fn get(self: Table, key: Value) Value
{ {
self.items.put(key, value); if(@as(ValueTag, key) == ValueTag.Nil)
} {
return Value.Nil;
pub fn get(self: *const Table, key: Value) !Value }
{ for (self.entries.items) |entry|
const value = self.items.get(key); {
return if(value == null) Value.Nil else value.?; if(entry.key.rawEqual(key))
{
return entry.value;
}
}
return Value.Nil;
} }
}; };
pub const Value = union(enum)
pub const ValueTag = enum
{
Nil,
Bool,
Numeral,
String,
Table,
};
pub const Value = union(ValueTag)
{ {
Nil, Nil,
Bool: bool, Bool: bool,
Numeral: Numeral, Numeral: Numeral,
String: []const u8, String: []const u8,
Table: *Table, Table: *Table,
pub fn rawEqual(self: Value, other: Value) bool
{
if(@as(ValueTag, self) != @as(ValueTag, other))
{
return false;
}
switch(self)
{
.Nil => return true,
.Bool => |value| return value == other.Bool,
.Numeral => |value| return value.rawEqual(other.Numeral),
.String => |value| return std.mem.eql(u8, value, other.String),
.Table => |value| return value == other.Table,
}
}
}; };
test "Value equalities"
{
const a = Value { .Bool = true };
const b = Value { .Numeral = Numeral { .Integer = 1 } };
// true != 1
try std.testing.expect(!a.rawEqual(b));
// 1 != true
try std.testing.expect(!b.rawEqual(a));
const c = Value { .Bool = false };
// true != false
try std.testing.expect(!a.rawEqual(c));
// false!= true
try std.testing.expect(!c.rawEqual(a));
const d = Value { .Bool = true };
// true == true
try std.testing.expect(a.rawEqual(d));
// true == true
try std.testing.expect(d.rawEqual(a));
const e = Value { .String = "foo" };
const f = Value { .String = "bar" };
// foo != bar
try std.testing.expect(!e.rawEqual(f));
// bar != foo
try std.testing.expect(!f.rawEqual(e));
const g = Value { .String = "foo" };
// foo != foo
try std.testing.expect(e.rawEqual(g));
// foo != foo
try std.testing.expect(g.rawEqual(e));
var table = Table { .entries = std.ArrayList(TableEntry).init(std.testing.allocator) };
const h = Value { .Table = &table };
var table2 = Table { .entries = std.ArrayList(TableEntry).init(std.testing.allocator) };
const i = Value { .Table = &table2 };
const j = Value { .Table = &table2 };
try std.testing.expect(h.rawEqual(h));
try std.testing.expect(i.rawEqual(i));
try std.testing.expect(!h.rawEqual(i));
try std.testing.expect(!i.rawEqual(h));
try std.testing.expect(i.rawEqual(j));
try std.testing.expect(!h.rawEqual(j));
}
pub const CodeRegion = struct pub const CodeRegion = struct
{ {
start: ?CodeLocation, start: ?CodeLocation,