From 49d8833ba6d09e1976e3cd737e9da71a8d1b8d40 Mon Sep 17 00:00:00 2001 From: bgk- Date: Sun, 31 Mar 2024 20:15:40 -0700 Subject: [PATCH] Refactor state serialization to json --- build.zig.zon | 2 +- src/compiler.zig | 13 +- src/enum.zig | 13 +- src/export.zig | 5 +- src/parser.zig | 1 + src/state.zig | 315 +++++++++++++++++++++++++++++++-------------- src/utils/uuid.zig | 15 ++- src/values.zig | 167 ++++++++++++------------ src/vm.test.zig | 22 +++- src/vm.zig | 46 +++---- 10 files changed, 365 insertions(+), 234 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 48c5075..4c9192e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = "topiary", - .version = "0.10.2", + .version = "0.11.0", .paths = .{""}, .dependencies = .{ }, diff --git a/src/compiler.zig b/src/compiler.zig index ddd0048..b8f7cab 100644 --- a/src/compiler.zig +++ b/src/compiler.zig @@ -447,7 +447,6 @@ pub const Compiler = struct { obj.* = .{ .data = .{ .@"enum" = .{ - .allocator = self.allocator, .name = try self.allocator.dupe(u8, e.name), .values = try names.toOwnedSlice(), }, @@ -507,11 +506,7 @@ pub const Compiler = struct { }, .choice => |c| { for (c.tags) |tag| { - const obj = try self.allocator.create(Value.Obj); - obj.* = .{ .data = .{ .string = try self.allocator.dupe(u8, tag) } }; - const i = try self.addConstant(.{ .obj = obj }); - try self.writeOp(.constant, token); - _ = try self.writeInt(OpCode.Size(.constant), i, token); + try self.getOrSetIdentifierConstant(tag, token); } const name = c.name orelse &c.id; try self.visit_tree.list.append(name); @@ -579,11 +574,7 @@ pub const Compiler = struct { }, .dialogue => |d| { for (d.tags) |tag| { - const obj = try self.allocator.create(Value.Obj); - obj.* = .{ .data = .{ .string = try self.allocator.dupe(u8, tag) } }; - const i = try self.addConstant(.{ .obj = obj }); - try self.writeOp(.constant, token); - _ = try self.writeInt(OpCode.Size(.constant), i, token); + try self.getOrSetIdentifierConstant(tag, token); } try self.compileExpression(d.content); diff --git a/src/enum.zig b/src/enum.zig index 378c475..0474110 100644 --- a/src/enum.zig +++ b/src/enum.zig @@ -1,12 +1,19 @@ const std = @import("std"); +const Value = @import("values.zig").Value; pub const Enum = struct { - allocator: std.mem.Allocator, name: []const u8, values: [][]const u8, - pub const Value = struct { + pub fn init(name: []const u8, values: [][]const u8) Enum { + return .{ + .name = name, + .values = values, + }; + } + + pub const Val = struct { index: u8, - base: *const Enum, + base: *const Value, }; }; diff --git a/src/export.zig b/src/export.zig index 0730dbc..a44e006 100644 --- a/src/export.zig +++ b/src/export.zig @@ -546,7 +546,10 @@ test "Create and Destroy Vm" { const vm_ptr = createVm(buf.ptr, buf.len, @intFromPtr(on_dialogue), @intFromPtr(on_choices)); const vm: *Vm = @ptrFromInt(vm_ptr); + + defer destroyVm(vm_ptr); vm.bytecode.print(std.debug); + defer vm.bytecode.free(alloc); std.debug.print("\n=====\n", .{}); const val_name = "value"; subscribe( @@ -612,6 +615,4 @@ test "Create and Destroy Vm" { } destroyValue(&map_value); } - vm.bytecode.free(alloc); - destroyVm(vm_ptr); } diff --git a/src/parser.zig b/src/parser.zig index 73d346e..490aacb 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -325,6 +325,7 @@ pub const Parser = struct { self.next(); var expr = try self.expression(.lowest); if (expr.type == .function) { + // used for recursive calls expr.type.function.name = name; } return .{ diff --git a/src/state.zig b/src/state.zig index d919dce..19d88ef 100644 --- a/src/state.zig +++ b/src/state.zig @@ -1,116 +1,241 @@ const std = @import("std"); -const Value = @import("./values.zig").Value; +const values = @import("./values.zig"); +const adapter = @import("./values.zig").adapter; const Bytecode = @import("./bytecode.zig").Bytecode; const Vm = @import("./vm.zig").Vm; +const Enum = @import("./enum.zig").Enum; const UUID = @import("./utils/uuid.zig").UUID; const testing = std.testing; +const Nil = values.Nil; +const Void = values.Void; +const Value = values.Value; -/// A StringHashMap of serialized Values -pub const StateMap = struct { - allocator: std.mem.Allocator, - map: Map, - - const Map = std.StringHashMap([]u8); - - pub fn init(allocator: std.mem.Allocator) StateMap { - return .{ - .allocator = allocator, - .map = Map.init(allocator), - }; - } - - pub fn deinit(self: *StateMap) void { - var it = self.map.iterator(); - while (it.next()) |entry| { - self.allocator.free(entry.key_ptr.*); - self.allocator.free(entry.value_ptr.*); +pub const State = struct { + pub fn serialize(vm: *Vm, writer: anytype) !void { + var references = std.ArrayList(Value).init(vm.allocator); + defer references.deinit(); + var seen = std.AutoHashMap(UUID.ID, void).init(vm.allocator); + defer seen.deinit(); + var stream = std.json.writeStream(writer, .{ .whitespace = .indent_2 }); + defer stream.deinit(); + try stream.beginObject(); + for (vm.bytecode.global_symbols) |s| { + if (s.is_extern or vm.globals[s.index] == .void) continue; + const value = vm.globals[s.index]; + if (value == .void) continue; + if (value == .visit and value.visit == 0) continue; + try stream.objectField(s.name); + try serializeValue(value, &stream, &references); + } + while (references.popOrNull()) |value| { + if (seen.contains(value.obj.id)) continue; + try seen.put(value.obj.id, {}); + try stream.objectField(&value.obj.id); + try serializeObj(value, &stream, &references); } - self.map.deinit(); + try stream.endObject(); } - pub fn put(self: *StateMap, name: []const u8, value: Value) !void { - var data = std.ArrayList(u8).init(self.allocator); - errdefer data.deinit(); - try value.serialize(data.writer()); - try self.map.put(try self.allocator.dupe(u8, name), try data.toOwnedSlice()); + fn serializeObj(value: Value, stream: anytype, references: *std.ArrayList(Value)) !void { + try stream.beginObject(); + try stream.objectField(@tagName(value.obj.data)); + switch (value.obj.data) { + .list => |l| { + try stream.beginArray(); + for (l.items) |item| try serializeValue(item, stream, references); + try stream.endArray(); + }, + .set => |s| { + try stream.beginArray(); + for (s.keys()) |item| try serializeValue(item, stream, references); + try stream.endArray(); + }, + .map => |m| { + try stream.beginArray(); + for (m.keys()) |key| { + try stream.beginObject(); + try stream.objectField("key"); + try serializeValue(key, stream, references); + try stream.objectField("value"); + try serializeValue(m.get(key).?, stream, references); + try stream.endObject(); + } + try stream.endArray(); + }, + .@"enum" => |e| { + try stream.beginObject(); + try stream.objectField("name"); + try stream.write(e.name); + try stream.beginArray(); + for (e.values) |v| try stream.write(v); + try stream.endArray(); + try stream.endObject(); + }, + .function => |f| { + try stream.beginObject(); + try stream.objectField("arity"); + try stream.write(f.arity); + try stream.objectField("inst"); + try stream.write(&f.instructions); + try stream.objectField("lines"); + try stream.beginArray(); + for (f.lines) |l| try stream.write(l); + try stream.endArray(); + try stream.objectField("locals_count"); + try stream.write(f.locals_count); + try stream.objectField("is_method"); + try stream.write(f.is_method); + try stream.endObject(); + }, + else => try stream.write("NOT IMPL"), + } + try stream.endObject(); } - pub fn get(self: *StateMap, name: []const u8) !?Value { - const item = self.map.get(name); - if (item) |i| { - var buf = std.io.fixedBufferStream(i); - const value = try Value.deserialize(buf.reader(), self.allocator); - return value; + fn serializeValue(value: Value, stream: anytype, references: *std.ArrayList(Value)) !void { + try stream.beginObject(); + switch (value) { + .obj => |o| switch (o.data) { + .string => try stream.objectField(@tagName(.string)), + else => try stream.objectField(@tagName(.ref)), + }, + else => try stream.objectField(@tagName(value)), + } + switch (value) { + .void => unreachable, + .nil => try stream.write(null), + .bool => |b| try stream.write(b), + .number => |n| try stream.print("{d:.5}", .{n}), + .visit => |v| try stream.write(v), + .enum_value => |e| { + try stream.beginObject(); + try references.append(e.base.*); + try stream.objectField("base"); + try stream.write(e.base.obj.id); + try stream.objectField("index"); + try stream.write(e.index); + try stream.endObject(); + }, + .ref => |r| try stream.write(&r), + .obj => |o| { + switch (o.data) { + .string => |s| try stream.write(&s[0 .. s.len - 1]), + else => { + try references.append(value); + try stream.write(&o.id); + }, + } + }, + else => try stream.write("NOT IMPl"), } - return null; + try stream.endObject(); } - /// Write the given StateMap to a string - pub fn serialize(self: *StateMap, writer: anytype) !void { - try writer.writeInt(u64, @as(u64, @intCast(self.map.count())), .little); - var it = self.map.iterator(); - while (it.next()) |entry| { - try writer.writeInt(u16, @as(u8, @intCast(entry.key_ptr.*.len)), .little); - try writer.writeAll(entry.key_ptr.*); - try writer.writeAll(":"); - try writer.writeInt(u32, @as(u32, @intCast(entry.value_ptr.*.len)), .little); - try writer.writeAll(entry.value_ptr.*); - try writer.writeAll("\n"); + pub fn deserialize(vm: *Vm, json_str: []const u8) !void { + var parsed = try std.json.parseFromSlice(std.json.Value, vm.allocator, json_str, .{}); + defer parsed.deinit(); + var refs = std.AutoHashMap(UUID.ID, Value).init(vm.allocator); + defer refs.deinit(); + const root = parsed.value.object; + for (vm.bytecode.global_symbols) |sym| { + const maybe_entry = root.get(sym.name); + if (maybe_entry) |entry| vm.globals[sym.index] = try deserializeEntry(vm, &root, entry, &refs, null); } } - /// Parse a serialized state back into a StateMap - pub fn deserialize(allocator: std.mem.Allocator, reader: anytype) !StateMap { - const size = try reader.readInt(u64, .little); - var state = StateMap.init(allocator); - var count: usize = 0; - while (count < size) : (count += 1) { - const length = try reader.readInt(u16, .little); - const buf = try allocator.alloc(u8, length); - try reader.readNoEof(buf); - _ = try reader.readByte(); - const value_length = try reader.readInt(u32, .little); - const value = try allocator.alloc(u8, value_length); - try reader.readNoEof(value); - try state.map.put(buf, value); - _ = try reader.readByte(); + fn deserializeEntry(vm: *Vm, root: *const std.json.ObjectMap, entry: std.json.Value, refs: *std.AutoHashMap(UUID.ID, Value), id: ?UUID.ID) !Value { + if (entry.object.get("number")) |v| return .{ .number = @floatCast(v.float) }; + if (entry.object.get("string")) |v| return try vm.gc.create(vm, .{ .string = try vm.allocator.dupe(u8, v.string) }); + if (entry.object.get("nil") != null) return Nil; + if (entry.object.get("boolean")) |v| return if (v.bool) values.True else values.False; + if (entry.object.get("visit")) |v| return .{ .visit = @intCast(v.integer) }; + if (entry.object.get("ref")) |v| { + if (refs.get(UUID.fromString(v.string))) |ref| return ref; + if (root.get(v.string)) |ref| { + const value = try deserializeEntry(vm, root, ref, refs, UUID.fromString(v.string)); + try refs.put(UUID.fromString(v.string), value); + return value; + } + return Void; + } + if (entry.object.get("enum_value")) |v| { + const base_id = UUID.fromString(v.object.get("base").?.string); + const base = if (refs.get(base_id)) |b| b else blk: { + const value = try deserializeEntry(vm, root, v.object.get("base").?, refs, base_id); + try refs.put(base_id, value); + break :blk value; + }; + return .{ + .enum_value = .{ + .base = &base, + .index = @intCast(v.object.get("index").?.integer), + }, + }; + } + if (entry.object.get("list")) |v| { + var list = std.ArrayList(Value).init(vm.allocator); + for (v.array.items) |item| { + try list.append(try deserializeEntry(vm, root, item, refs, null)); + } + var result = try vm.gc.create(vm, .{ .list = list }); + result.obj.id = id.?; + try refs.put(id.?, result); + return result; + } + if (entry.object.get("map")) |v| { + var map = Value.Obj.MapType.init(vm.allocator); + for (v.array.items) |item| { + const key = try deserializeEntry(vm, root, item.object.get("key").?, refs, null); + const value = try deserializeEntry(vm, root, item.object.get("value").?, refs, null); + try map.put(key, value); + } + var result = try vm.gc.create(vm, .{ .map = map }); + result.obj.id = id.?; + try refs.put(id.?, result); + return result; } - return state; + if (entry.object.get("set")) |v| { + var set = Value.Obj.SetType.init(vm.allocator); + for (v.array.items) |item| { + try set.put(try deserializeEntry(vm, root, item, refs, null), {}); + } + var result = try vm.gc.create(vm, .{ .set = set }); + result.obj.id = id.?; + try refs.put(id.?, result); + return result; + } + if (entry.object.get("enum")) |v| { + const values_items = v.object.get("values").?.array.items; + const vals = try vm.allocator.alloc([]const u8, values_items.len); + for (values_items, 0..) |t, i| vals[i] = try vm.allocator.dupe(u8, t.string); + var result = try vm.gc.create(vm, .{ .@"enum" = .{ + .name = v.object.get("name").?.string, + .values = vals, + } }); + result.obj.id = id.?; + try refs.put(id.?, result); + return result; + } + if (entry.object.get("function")) |v| { + const arity = v.object.get("arity").?.integer; + const inst = v.object.get("inst").?.string; + const lines_items = v.object.get("lines").?.array.items; + var lines = try vm.allocator.alloc(u32, lines_items.len); + for (lines_items, 0..) |t, i| lines[i] = @intCast(t.integer); + const locals = v.object.get("locals_count").?.integer; + const is_method = v.object.get("is_method").?.bool; + var result = try vm.gc.create(vm, .{ .function = .{ + .arity = @intCast(arity), + .instructions = try vm.allocator.dupe(u8, inst), + .lines = try vm.allocator.dupe(u32, lines), + .locals_count = @intCast(locals), + .is_method = is_method, + } }); + result.obj.id = id.?; + try refs.put(id.?, result); + return result; + } + return Void; } }; - -test "Stringify" { - var alloc = testing.allocator; - var map = StateMap.init(alloc); - defer map.deinit(); - try map.put("a", .{ .number = 71.005 }); - - const str = try alloc.create(Value.Obj); - defer alloc.destroy(str); - str.* = .{ .data = .{ .string = "some text value" } }; - str.*.id = UUID.new(); - try map.put("b", .{ .obj = str }); - - try map.put("c", .{ .bool = true }); - - const list_obj = try alloc.create(Value.Obj); - defer alloc.destroy(list_obj); - var list = std.ArrayList(Value).init(alloc); - defer list.deinit(); - try list.append(.{ .number = 5 }); - try list.append(.{ .number = 6 }); - list_obj.* = .{ .data = .{ .list = list } }; - try map.put("d", .{ .obj = list_obj }); - - var data = std.ArrayList(u8).init(testing.allocator); - defer data.deinit(); - try map.serialize(data.writer()); - - var buf = std.io.fixedBufferStream(data.items); - var copy = try StateMap.deserialize(alloc, buf.reader()); - defer copy.deinit(); - try testing.expect(copy.map.contains("a")); - try testing.expect((try copy.get("a")).?.eql(.{ .number = 71.005 })); - std.log.warn("\n{s}\n", .{data.items}); -} diff --git a/src/utils/uuid.zig b/src/utils/uuid.zig index fd24a84..b72875d 100644 --- a/src/utils/uuid.zig +++ b/src/utils/uuid.zig @@ -1,15 +1,17 @@ const std = @import("std"); +var rnd: ?std.rand.DefaultPrng = null; pub const UUID = struct { pub const ID = [Size]u8; pub const Empty: ID = [_]u8{ '0', '0', '0', '0', '0', '0', '-', '0', '0', '0', '0', '0', '0' }; const Self = @This(); const Size: usize = 13; - const chars: []const u8 = "23456789ABCDEFGHJKMNPQRSTUVWXYZ"; + const chars: []const u8 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; pub fn new() ID { - return create(@intCast(std.time.milliTimestamp())); + if (rnd == null) rnd = std.rand.DefaultPrng.init(@intCast(std.time.milliTimestamp())); + return create(rnd.?.random().int(u64)); } pub fn create(seed: u64) ID { @@ -25,4 +27,13 @@ pub const UUID = struct { id[6] = '-'; return id; } + + pub fn fromString(str: []const u8) ID { + var id: ID = undefined; + var i: usize = 0; + while (i < Size) : (i += 1) { + id[i] = str[i]; + } + return id; + } }; diff --git a/src/values.zig b/src/values.zig index 2055b52..40ed88b 100644 --- a/src/values.zig +++ b/src/values.zig @@ -27,6 +27,7 @@ pub const Type = enum(u8) { map_pair, visit, enum_value, + ref, }; pub const Iterator = struct { @@ -53,7 +54,8 @@ pub const Value = union(Type) { value: *Value, }, visit: u32, - enum_value: Enum.Value, + enum_value: Enum.Val, + ref: ID, pub const Obj = struct { is_marked: bool = false, @@ -165,6 +167,7 @@ pub const Value = union(Type) { .map_pair => "map_pair", .visit => "visit", .enum_value => "enum_value", + .ref => "reference", .obj => |o| switch (o.data) { .string => "string", .list => "list", @@ -237,13 +240,8 @@ pub const Value = union(Type) { switch (self) { .bool => |b| try writer.writeByte(if (b) '1' else '0'), .number => |n| { - var buf: [128]u8 = undefined; - var buf_stream = std.io.fixedBufferStream(&buf); - try std.fmt.formatFloatDecimal(n, .{ - .precision = 5, - }, buf_stream.writer()); - try writer.writeInt(u16, @as(u16, @intCast(buf_stream.pos)), .little); - try writer.writeAll(buf[0..buf_stream.pos]); + try writer.print("{d:.5}", .{n}); + try writer.writeByte(0); }, .range => |r| { try writer.writeInt(i32, r.start, .little); @@ -258,8 +256,12 @@ pub const Value = union(Type) { }, .enum_value => |e| { try writer.writeByte(e.index); - try writer.writeByte(@intCast(e.base.name.len)); - try writer.writeAll(e.base.name); + const n = e.base.obj.data.@"enum".name; + try writer.writeByte(@intCast(n.len)); + try writer.writeAll(n); + }, + .ref => |r| { + try writer.writeAll(&r); }, .obj => |o| { try writer.writeByte(@intFromEnum(@as(Obj.DataType, o.data))); @@ -295,8 +297,36 @@ pub const Value = union(Type) { try writer.writeInt(u16, @as(u16, @intCast(f.lines.len)), .little); for (f.lines) |l| try writer.writeInt(u32, l, .little); }, + .@"enum" => |e| { + try writer.writeByte(@intCast(e.name.len)); + try writer.writeAll(e.name); + try writer.writeByte(@intCast(e.values.len)); + for (e.values) |value| { + try writer.writeByte(@intCast(value.len)); + try writer.writeAll(value); + } + }, + .class => |c| { + try writer.writeByte(@intCast(c.name.len)); + try writer.writeAll(c.name); + try writer.writeByte(@intCast(c.defaults.len)); + for (c.defaults) |d| { + try writer.writeByte(@intCast(d.name.len)); + try writer.writeAll(d.name); + try serialize(d.value, writer); + } + }, .instance => |i| { - _ = i; + const d = @fieldParentPtr(Obj.Data, "class", i.class); + const c = @fieldParentPtr(Obj, "data", d); + try writer.writeAll(&c.id); + try writer.writeInt(u8, @intCast(i.fields.count()), .little); + var it = i.fields.iterator(); + while (it.next()) |kvp| { + try writer.writeInt(u8, @intCast(kvp.key_ptr.*.len), .little); + try writer.writeAll(kvp.key_ptr.*); + try serialize(kvp.value_ptr.*, writer); + } }, else => {}, } @@ -311,11 +341,9 @@ pub const Value = union(Type) { .nil => Nil, .bool => if (try reader.readByte() == '1') True else False, .number => { - const length = try reader.readInt(u16, .little); - const buf = try allocator.alloc(u8, length); - defer allocator.free(buf); - try reader.readNoEof(buf); - return .{ .number = try std.fmt.parseFloat(f32, buf) }; + const val = try reader.readUntilDelimiterAlloc(allocator, 0, 128); + defer allocator.free(val); + return .{ .number = try std.fmt.parseFloat(f32, val) }; }, .visit => { return .{ .visit = try reader.readInt(u32, .little) }; @@ -342,13 +370,14 @@ pub const Value = union(Type) { var list = try std.ArrayList(Value).initCapacity(allocator, length); var i: usize = 0; while (i < length) : (i += 1) { - list.items[i] = try Value.deserialize(reader, allocator); + try list.append(try Value.deserialize(reader, allocator)); } const obj = try allocator.create(Value.Obj); obj.* = .{ .id = id, .data = .{ .list = list } }; return .{ .obj = obj }; }, .map => { + std.debug.print("\nDESERIALIZE MAP====\n", .{}); const length = try reader.readInt(u16, .little); var map = Value.Obj.MapType.initContext(allocator, adapter); var i: usize = 0; @@ -403,7 +432,7 @@ pub const Value = union(Type) { try reader.readNoEof(name_buf); const values_length = try reader.readByte(); const obj = try allocator.create(Value.Obj); - obj.* = .{ .data = .{ .@"enum" = .{ .allocator = allocator, .name = name_buf, .values = try allocator.alloc([]const u8, values_length) } } }; + obj.* = .{ .data = .{ .@"enum" = .{ .name = name_buf, .values = try allocator.alloc([]const u8, values_length) } } }; for (0..values_length) |i| { const value_name_length = try reader.readByte(); const value_name_buf = try allocator.alloc(u8, value_name_length); @@ -412,6 +441,38 @@ pub const Value = union(Type) { } return .{ .obj = obj }; }, + .class => { + const name_length = try reader.readByte(); + const name_buf = try allocator.alloc(u8, name_length); + try reader.readNoEof(name_buf); + const default_length = try reader.readByte(); + const obj = try allocator.create(Value.Obj); + obj.* = .{ .data = .{ .class = .{ .allocator = allocator, .name = name_buf, .defaults = try allocator.alloc(Class.Field, default_length) } } }; + for (0..default_length) |i| { + const value_name_length = try reader.readByte(); + const value_name_buf = try allocator.alloc(u8, value_name_length); + try reader.readNoEof(value_name_buf); + obj.data.class.defaults[i].name = value_name_buf; + obj.data.class.defaults[i].value = try deserialize(reader, allocator); + } + return .{ .obj = obj }; + }, + .instance => { + var class_id: ID = undefined; + try reader.readNoEof(class_id[0..]); + const obj = try allocator.create(Value.Obj); + const field_length = try reader.readByte(); + var fields = try allocator.alloc(Class.Field, field_length); + for (0..field_length) |i| { + const value_name_length = try reader.readByte(); + const value_name_buf = try allocator.alloc(u8, value_name_length); + try reader.readNoEof(value_name_buf); + fields[i].name = value_name_buf; + fields[i].value = try deserialize(reader, allocator); + } + // obj.* = .{ .data = .{ .instance = try class.createInstance(fields) } }; + return .{ .obj = obj }; + }, else => return error.Unknown, } }, @@ -425,7 +486,7 @@ pub const Value = union(Type) { .bool => |b| writer.print("{}", .{b}), .nil => writer.print("nil", .{}), .visit => |v| writer.print("{d}", .{v}), - .enum_value => |e| writer.print("{s}.{s}", .{ e.base.name, e.base.values[e.index] }), + .enum_value => |e| writer.print("{s}.{s}", .{ e.base.obj.data.@"enum".name, e.base.obj.data.@"enum".values[e.index] }), .obj => |o| { switch (o.data) { .string => |s| writer.print("{s}", .{s}), @@ -502,15 +563,6 @@ pub const Value = union(Type) { } }, .range => |r| writer.print("{}..{}", .{ r.start, r.end }), - // .@"enum" => |e| { - // writer.print("{", .{}); - // for (e, 0..) |item, i| { - // writer.print("{s}", .{item}); - // if (i != e.len - 1) - // writer.print(",\n", .{}); - // } - // writer.print("}\n", .{}); - // }, else => writer.print("{s}", .{@tagName(self)}), } } @@ -527,24 +579,7 @@ pub const Value = union(Type) { .obj => |o| { switch (o.data) { .string => |s| hasher.update(s), - .function => |f| { - hashFn(&hasher, f.locals_count); - hashFn(&hasher, f.instructions.len); - hashFn(&hasher, f.instructions.ptr); - }, - .list => |l| { - hashFn(&hasher, l.items.len); - hashFn(&hasher, l.items.ptr); - }, - .map => |m| { - hashFn(&hasher, m.keys().len); - hashFn(&hasher, m.keys().ptr); - }, - .builtin => |b| { - hashFn(&hasher, b.arity); - hashFn(&hasher, b.backing); - }, - else => return 0, + else => hashFn(&hasher, o.id), } }, .range => |r| { @@ -576,43 +611,7 @@ pub const Value = union(Type) { return false; return switch (o.data) { .string => |s| std.mem.eql(u8, s, b_data.string), - .list => |l| { - const l_b = b_data.list; - if (l.items.len != l_b.items.len) return false; - for (l.items, 0..) |item, i| { - if (!adapter.eql(item, l_b.items[i], 0)) return false; - } - return true; - }, - .map => |m| { - const a_keys = m.keys(); - const b_keys = b_data.map.keys(); - if (a_keys.len != b_keys.len) return false; - for (a_keys) |a_key| { - if (!b_data.map.contains(a_key)) return false; - const a_value = m.get(a_key); - const b_value = b_data.map.get(a_key); - if (a_value == null and b_value != null) return false; - if (a_value != null and b_value == null) return false; - if (!adapter.eql(a_value.?, b_value.?, 0)) return false; - } - return true; - }, - .set => |s| { - const a_keys = s.keys(); - const b_keys = b_data.map.keys(); - if (a_keys.len != b_keys.len) return false; - for (a_keys) |a_key| { - if (!b_data.map.contains(a_key)) return false; - } - return true; - }, - .function => |f| { - const b_f = b_data.function; - const inst = std.mem.eql(u8, f.instructions, b_f.instructions); - return inst and f.locals_count == b_f.locals_count and f.arity == b_f.arity and f.is_method == b_f.is_method; - }, - else => return false, + else => std.mem.eql(u8, &o.id, &b.obj.id), }; }, .range => |r| { @@ -635,7 +634,7 @@ test "Serialize" { try std.testing.expectEqualSlices( u8, - &[_]u8{ 0x03, 0x08, 0x00, 0x31, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30 }, + &[_]u8{ 0x03, 0x31, 0x35, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00 }, data.items, ); } diff --git a/src/vm.test.zig b/src/vm.test.zig index e088865..704deaa 100644 --- a/src/vm.test.zig +++ b/src/vm.test.zig @@ -7,7 +7,7 @@ const compiler = @import("compiler.zig"); const Value = @import("values.zig").Value; const Errors = @import("compiler-error.zig").CompilerErrors; const compileSource = @import("compiler.test.zig").compileSource; -const StateMap = @import("state.zig").StateMap; +const State = @import("state.zig").State; const runners = @import("runner.zig"); const module = @import("module.zig"); const Runner = runners.Runner; @@ -1439,6 +1439,12 @@ test "Save and Load State" { const test_case = \\ var value = 0 \\ value += 1 + \\ var list = List{} + \\ var outer = List{list} + \\ var last = list + \\ var str = "value" + \\ list.add(value) + \\ list[0] = "changed" ; const alloc = testing.allocator; @@ -1450,13 +1456,15 @@ test "Save and Load State" { defer vm.bytecode.free(testing.allocator); try vm.interpret(); - var save = StateMap.init(alloc); - defer save.deinit(); - try vm.saveState(&save); + var data = std.ArrayList(u8).init(alloc); + defer data.deinit(); + try State.serialize(&vm, data.writer()); + std.debug.print("\n{s}\n", .{data.items}); const second_case = \\ var value = 10 \\ value += 5 + \\ var outer = List{} ; var mod2 = Module.create(allocator); @@ -1465,10 +1473,10 @@ test "Save and Load State" { var vm2 = try initTestVm(second_case, &mod2, false); defer vm2.deinit(); defer vm2.bytecode.free(testing.allocator); - try vm2.loadState(&save); + try State.deserialize(&vm2, data.items); + // try vm2.loadState(&save); try testing.expectEqual(vm2.globals[0].number, 1); - + try testing.expectEqualSlices(u8, vm2.globals[1].obj.data.list.items[0].obj.data.list.items[0].obj.data.string, "changed"); try vm2.interpret(); - try testing.expectEqual(vm2.globals[0].number, 6); } diff --git a/src/vm.zig b/src/vm.zig index 24fb282..37b244e 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -152,34 +152,23 @@ pub const Vm = struct { } /// Add the current state to a StateMap - pub fn saveState(self: *Vm, state: *StateMap) !void { - const count: usize = self.bytecode.global_symbols.len; - if (count == 0) return; - - for (self.bytecode.global_symbols) |s| { - if (s.is_extern or self.globals[s.index] == .void) continue; - const value = self.globals[s.index]; - if (switch (value) { - .visit => |v| v == 0, - .obj => |o| switch (o.data) { - .@"enum", .class => true, - else => false, - }, - .range, .map_pair => unreachable, - else => false, - }) continue; - - try state.put(s.name, self.globals[s.index]); - } - } + // pub fn serializeState(self: *Vm) ![]const u8 { + // var map = StateMap.init(self.allocator); + // for (self.bytecode.global_symbols) |s| { + // if (s.is_extern or self.globals[s.index] == .void) continue; + // const value = self.globals[s.index]; + // if (value == .visit and value.visit == 0) continue; + // try map.put(s.name, self.globals[s.index]); + // } + // } /// Load the StateMap into the globals list - pub fn loadState(self: *Vm, state: *StateMap) !void { - for (self.bytecode.global_symbols) |s| { - const value = try state.get(s.name); - if (value) |v| self.globals[s.index] = v; - } - } + // pub fn loadState(self: *Vm, json_string: []const u8) void { + // for (self.bytecode.global_symbols) |s| { + // const value = state.get(s.name); + // if (value) |v| self.globals[s.index] = v; + // } + // } pub fn roots(self: *Vm) []const []Value { return &([_][]Value{ self.globals, self.stack.backing }); @@ -520,7 +509,7 @@ pub const Vm = struct { if (c == '{') { try writer.writeAll(str[s..i]); switch (args.items[a]) { - .number => |n| try std.fmt.formatFloatDecimal(n, std.fmt.FormatOptions{}, list.writer()), + .number => |n| try writer.print("{d:.5}", .{n}), .bool => |b| try writer.writeAll(if (b) "true" else "false"), .obj => |o| try writer.writeAll(o.data.string), .visit => |v| try std.fmt.formatIntValue(v, "", .{}, list.writer()), @@ -745,10 +734,9 @@ pub const Vm = struct { if (index.obj.data != .string) return self.fail("Can only query instance fields by string name, not {s}", .{@tagName(index.obj.data)}); var found = false; - std.debug.print("\n", .{}); for (e.values, 0..) |name, i| { if (std.mem.eql(u8, name, index.obj.data.string)) { - try self.push(.{ .enum_value = .{ .index = @intCast(i), .base = &e } }); + try self.push(.{ .enum_value = .{ .index = @intCast(i), .base = &target } }); found = true; } }