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
{
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);
}

View File

@ -1,34 +1,174 @@
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,
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
{
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);
}
pub fn get(self: *const Table, key: Value) !Value
{
const value = self.items.get(key);
return if(value == null) Value.Nil else value.?;
if(@as(ValueTag, key) == ValueTag.Nil)
{
return Value.Nil;
}
for (self.entries.items) |entry|
{
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,
Bool: bool,
Numeral: Numeral,
String: []const u8,
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
{
start: ?CodeLocation,