diff --git a/src/treewalker.zig b/src/treewalker.zig index 08d97a9..bc3aa7f 100644 --- a/src/treewalker.zig +++ b/src/treewalker.zig @@ -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); } diff --git a/src/types.zig b/src/types.zig index 8ee2a85..eb5b913 100644 --- a/src/types.zig +++ b/src/types.zig @@ -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,