diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/jq/execute.zig | 75 | ||||
| -rw-r--r-- | src/jq/parse.zig | 10 | ||||
| -rw-r--r-- | src/jv.zig | 1 | ||||
| -rw-r--r-- | src/jv/ops.zig | 166 | ||||
| -rw-r--r-- | src/jv/parse.zig | 22 | ||||
| -rw-r--r-- | src/jv/stringify.zig | 2 | ||||
| -rw-r--r-- | src/jv/value.zig | 160 |
7 files changed, 296 insertions, 140 deletions
diff --git a/src/jq/execute.zig b/src/jq/execute.zig index 3775952..5749b0b 100644 --- a/src/jq/execute.zig +++ b/src/jq/execute.zig @@ -39,41 +39,41 @@ const ValueStack = struct { pub fn popInteger(self: *Self) ExecuteError!i64 { const value = self.pop(); - return switch (value) { - .integer => |i| i, + return switch (value.kind()) { + .integer => value.integer(), else => error.InvalidType, }; } pub fn popNumber(self: *Self) ExecuteError!f64 { const value = self.pop(); - return switch (value) { - .integer => |i| @floatFromInt(i), - .float => |f| f, + return switch (value.kind()) { + .integer => @floatFromInt(value.integer()), + .float => value.float(), else => error.InvalidType, }; } pub fn popString(self: *Self) ExecuteError![]const u8 { const value = self.pop(); - return switch (value) { - .string => |s| s, + return switch (value.kind()) { + .string => value.string(), else => error.InvalidType, }; } pub fn popArray(self: *Self) ExecuteError!jv.Array { const value = self.pop(); - return switch (value) { - .array => |a| a, + return switch (value.kind()) { + .array => value.array(), else => error.InvalidType, }; } pub fn popObject(self: *Self) ExecuteError!jv.Object { const value = self.pop(); - return switch (value) { - .object => |o| o, + return switch (value.kind()) { + .object => value.object(), else => error.InvalidType, }; } @@ -119,10 +119,10 @@ pub const Runtime = struct { pub fn init(allocator: std.mem.Allocator) !Self { // The order of this table must match with ConstIndex's order. var constants = try std.ArrayList(jv.Value).initCapacity(allocator, 4); - try constants.append(allocator, .null); - try constants.append(allocator, .{ .bool = false }); - try constants.append(allocator, .{ .bool = true }); - try constants.append(allocator, .{ .array = jv.Array.init(allocator) }); + try constants.append(allocator, jv.Value.null); + try constants.append(allocator, jv.Value.false); + try constants.append(allocator, jv.Value.true); + try constants.append(allocator, jv.Value.initArray(jv.Array.init(allocator))); return .{ .allocator = allocator, @@ -137,21 +137,11 @@ pub const Runtime = struct { pub fn deinit(self: *Self) void { for (self.variables.items) |*value| { - switch (value.*) { - .string => |s| self.allocator.free(s), - .array => |*a| a.deinit(), - .object => |*o| o.deinit(), - else => {}, - } + value.deinit(self.allocator); } self.variables.deinit(self.allocator); for (self.constants.items) |*value| { - switch (value.*) { - .string => |s| self.allocator.free(s), - .array => |*a| a.deinit(), - .object => |*o| o.deinit(), - else => {}, - } + value.deinit(self.allocator); } self.constants.deinit(self.allocator); self.allocator.free(self.instrs); @@ -244,7 +234,7 @@ pub const Runtime = struct { const base = self.values.pop(); const key = self.values.pop(); - const result = jv.ops.index(base, key) catch .null; + const result = jv.ops.index(base, key) catch jv.Value.null; try self.values.push(result); }, .add => { @@ -254,7 +244,7 @@ pub const Runtime = struct { const lhs = try self.values.popInteger(); const rhs = try self.values.popInteger(); const result = lhs + rhs; - try self.values.push(.{ .integer = result }); + try self.values.push(jv.Value.initInteger(result)); }, .sub => { std.debug.assert(self.values.ensureSize(3)); @@ -263,7 +253,7 @@ pub const Runtime = struct { const lhs = try self.values.popInteger(); const rhs = try self.values.popInteger(); const result = lhs - rhs; - try self.values.push(.{ .integer = result }); + try self.values.push(jv.Value.initInteger(result)); }, .mul => { std.debug.assert(self.values.ensureSize(3)); @@ -272,7 +262,7 @@ pub const Runtime = struct { const lhs = try self.values.popInteger(); const rhs = try self.values.popInteger(); const result = lhs * rhs; - try self.values.push(.{ .integer = result }); + try self.values.push(jv.Value.initInteger(result)); }, .div => { std.debug.assert(self.values.ensureSize(3)); @@ -281,7 +271,7 @@ pub const Runtime = struct { const lhs = try self.values.popInteger(); const rhs = try self.values.popInteger(); const result = @divTrunc(lhs, rhs); - try self.values.push(.{ .integer = result }); + try self.values.push(jv.Value.initInteger(result)); }, .mod => { std.debug.assert(self.values.ensureSize(3)); @@ -290,7 +280,7 @@ pub const Runtime = struct { const lhs = try self.values.popInteger(); const rhs = try self.values.popInteger(); const result = @mod(lhs, rhs); - try self.values.push(.{ .integer = result }); + try self.values.push(jv.Value.initInteger(result)); }, .eq => { std.debug.assert(self.values.ensureSize(3)); @@ -299,7 +289,7 @@ pub const Runtime = struct { const lhs = self.values.pop(); const rhs = self.values.pop(); const result = try jv.ops.compare(lhs, rhs, .eq); - try self.values.push(.{ .bool = result }); + try self.values.push(jv.Value.initBool(result)); }, .ne => { std.debug.assert(self.values.ensureSize(3)); @@ -308,7 +298,7 @@ pub const Runtime = struct { const lhs = self.values.pop(); const rhs = self.values.pop(); const result = try jv.ops.compare(lhs, rhs, .ne); - try self.values.push(.{ .bool = result }); + try self.values.push(jv.Value.initBool(result)); }, .lt => { std.debug.assert(self.values.ensureSize(3)); @@ -317,7 +307,7 @@ pub const Runtime = struct { const lhs = self.values.pop(); const rhs = self.values.pop(); const result = try jv.ops.compare(lhs, rhs, .lt); - try self.values.push(.{ .bool = result }); + try self.values.push(jv.Value.initBool(result)); }, .gt => { std.debug.assert(self.values.ensureSize(3)); @@ -326,7 +316,7 @@ pub const Runtime = struct { const lhs = self.values.pop(); const rhs = self.values.pop(); const result = try jv.ops.compare(lhs, rhs, .gt); - try self.values.push(.{ .bool = result }); + try self.values.push(jv.Value.initBool(result)); }, .le => { std.debug.assert(self.values.ensureSize(3)); @@ -335,7 +325,7 @@ pub const Runtime = struct { const lhs = self.values.pop(); const rhs = self.values.pop(); const result = try jv.ops.compare(lhs, rhs, .le); - try self.values.push(.{ .bool = result }); + try self.values.push(jv.Value.initBool(result)); }, .ge => { std.debug.assert(self.values.ensureSize(3)); @@ -344,7 +334,7 @@ pub const Runtime = struct { const lhs = self.values.pop(); const rhs = self.values.pop(); const result = try jv.ops.compare(lhs, rhs, .ge); - try self.values.push(.{ .bool = result }); + try self.values.push(jv.Value.initBool(result)); }, .alt => { std.debug.assert(self.values.ensureSize(3)); @@ -368,17 +358,14 @@ pub const Runtime = struct { // TODO: Allocate all local variables at startup. while (self.variables.items.len <= @intFromEnum(idx)) { - try self.variables.append(self.allocator, .null); + try self.variables.append(self.allocator, jv.Value.null); } self.variables.items[@intFromEnum(idx)] = self.values.pop(); }, .append => |idx| { std.debug.assert(self.values.ensureSize(1)); - switch (self.variables.items[@intFromEnum(idx)]) { - .array => |*a| try a.append(self.values.pop()), - else => unreachable, - } + try self.variables.items[@intFromEnum(idx)].arrayAppend(self.values.pop()); }, } } diff --git a/src/jq/parse.zig b/src/jq/parse.zig index 82ff7bd..a56b7eb 100644 --- a/src/jq/parse.zig +++ b/src/jq/parse.zig @@ -332,9 +332,9 @@ const Parser = struct { _ = try self.tokens.next(); const i: i64 = @intFromFloat(f); if (@as(f64, @floatFromInt(i)) == f) { - try self.constants.append(self.allocator, .{ .integer = i }); + try self.constants.append(self.allocator, jv.Value.initInteger(i)); } else { - try self.constants.append(self.allocator, .{ .float = f }); + try self.constants.append(self.allocator, jv.Value.initFloat(f)); } const idx: ConstIndex = @enumFromInt(self.constants.items.len - 1); const number_node = try self.compile_allocator.create(Ast); @@ -343,7 +343,7 @@ const Parser = struct { }, .string => |s| { _ = try self.tokens.next(); - try self.constants.append(self.allocator, .{ .string = try self.allocator.dupe(u8, s) }); + try self.constants.append(self.allocator, jv.Value.initString(try self.allocator.dupe(u8, s))); const idx: ConstIndex = @enumFromInt(self.constants.items.len - 1); const string_node = try self.compile_allocator.create(Ast); string_node.* = .{ .literal = idx }; @@ -372,7 +372,7 @@ const Parser = struct { .brace_left => { _ = try self.tokens.next(); _ = try self.tokens.expect(.brace_right); - try self.constants.append(self.allocator, .{ .object = jv.Object.init(self.allocator) }); + try self.constants.append(self.allocator, jv.Value.initObject(jv.Object.init(self.allocator))); const idx: ConstIndex = @enumFromInt(self.constants.items.len - 1); const object_node = try self.compile_allocator.create(Ast); object_node.* = .{ .literal = idx }; @@ -383,7 +383,7 @@ const Parser = struct { const is_optional = self.tokens.consumeIf(.question); const base_ast = try self.compile_allocator.create(Ast); base_ast.* = .identity; - try self.constants.append(self.allocator, .{ .string = try self.allocator.dupe(u8, name) }); + try self.constants.append(self.allocator, jv.Value.initString(try self.allocator.dupe(u8, name))); const idx: ConstIndex = @enumFromInt(self.constants.items.len - 1); const key_ast = try self.compile_allocator.create(Ast); key_ast.* = .{ .literal = idx }; @@ -2,6 +2,7 @@ pub const Value = @import("./jv/value.zig").Value; pub const Array = @import("./jv/value.zig").Array; pub const Object = @import("./jv/value.zig").Object; +pub const Parsed = @import("./jv/parse.zig").Parsed; pub const parse = @import("./jv/parse.zig").parse; pub const stringify = @import("./jv/stringify.zig").stringify; diff --git a/src/jv/ops.zig b/src/jv/ops.zig index 21f9efb..d29c9cc 100644 --- a/src/jv/ops.zig +++ b/src/jv/ops.zig @@ -9,22 +9,16 @@ pub const OpsError = error{ }; pub fn index(base: Value, key: Value) OpsError!Value { - return switch (base) { - .array => |arr| blk: { - const idx: usize = @intCast(switch (key) { - .integer => |i| i, - else => return error.InvalidType, - }); - break :blk if (idx < arr.items.len) arr.items[idx] else .null; + return switch (base.kind()) { + .array => blk: { + if (key.kind() != .integer) return error.InvalidType; + break :blk base.arrayGet(@intCast(key.integer())); }, - .object => |obj| blk: { - const k = switch (key) { - .string => |s| s, - else => return error.InvalidType, - }; - break :blk obj.get(k) orelse .null; + .object => blk: { + if (key.kind() != .string) return error.InvalidType; + break :blk base.objectGet(key.string()) orelse Value.null; }, - .null => .null, + .null => Value.null, else => error.InvalidType, }; } @@ -32,8 +26,8 @@ pub fn index(base: Value, key: Value) OpsError!Value { pub const CompareOp = enum { eq, ne, lt, gt, le, ge }; pub fn compare(lhs: Value, rhs: Value, op: CompareOp) OpsError!bool { - const lhs_tag = std.meta.activeTag(lhs); - const rhs_tag = std.meta.activeTag(rhs); + const lhs_tag = lhs.kind(); + const rhs_tag = rhs.kind(); if (lhs_tag != rhs_tag) { const lhs_is_number = lhs_tag == .integer or lhs_tag == .float; @@ -44,14 +38,15 @@ pub fn compare(lhs: Value, rhs: Value, op: CompareOp) OpsError!bool { return error.InvalidType; } - return switch (lhs) { + return switch (lhs_tag) { .null => switch (op) { .eq => true, .ne => false, .lt, .gt, .le, .ge => error.Unimplemented, }, - .bool => |lhs_bool| { - const rhs_bool = rhs.bool; + .bool => { + const lhs_bool = lhs.boolean(); + const rhs_bool = rhs.boolean(); return switch (op) { .eq => lhs_bool == rhs_bool, .ne => lhs_bool != rhs_bool, @@ -59,8 +54,9 @@ pub fn compare(lhs: Value, rhs: Value, op: CompareOp) OpsError!bool { }; }, .integer, .float => compareNumbers(lhs, rhs, op), - .string => |lhs_str| { - const rhs_str = rhs.string; + .string => { + const lhs_str = lhs.string(); + const rhs_str = rhs.string(); const order = std.mem.order(u8, lhs_str, rhs_str); return switch (op) { .eq => order == .eq, @@ -84,14 +80,14 @@ pub fn compare(lhs: Value, rhs: Value, op: CompareOp) OpsError!bool { } fn compareNumbers(lhs: Value, rhs: Value, op: CompareOp) bool { - const lhs_f: f64 = switch (lhs) { - .integer => |i| @floatFromInt(i), - .float => |f| f, + const lhs_f: f64 = switch (lhs.kind()) { + .integer => @floatFromInt(lhs.integer()), + .float => lhs.float(), else => unreachable, }; - const rhs_f: f64 = switch (rhs) { - .integer => |i| @floatFromInt(i), - .float => |f| f, + const rhs_f: f64 = switch (rhs.kind()) { + .integer => @floatFromInt(rhs.integer()), + .float => rhs.float(), else => unreachable, }; return switch (op) { @@ -107,90 +103,90 @@ fn compareNumbers(lhs: Value, rhs: Value, op: CompareOp) bool { test "index array" { var arr = Array.init(std.testing.allocator); defer arr.deinit(); - try arr.append(.{ .integer = 10 }); - try arr.append(.{ .integer = 20 }); - try arr.append(.{ .integer = 30 }); + try arr.append(Value.initInteger(10)); + try arr.append(Value.initInteger(20)); + try arr.append(Value.initInteger(30)); - const base = Value{ .array = arr }; + const base = Value.initArray(arr); - try std.testing.expectEqual(Value{ .integer = 10 }, try index(base, .{ .integer = 0 })); - try std.testing.expectEqual(Value{ .integer = 20 }, try index(base, .{ .integer = 1 })); - try std.testing.expectEqual(Value{ .integer = 30 }, try index(base, .{ .integer = 2 })); - try std.testing.expectEqual(Value.null, try index(base, .{ .integer = 3 })); + try std.testing.expectEqual(Value.initInteger(10), try index(base, Value.initInteger(0))); + try std.testing.expectEqual(Value.initInteger(20), try index(base, Value.initInteger(1))); + try std.testing.expectEqual(Value.initInteger(30), try index(base, Value.initInteger(2))); + try std.testing.expectEqual(Value.null, try index(base, Value.initInteger(3))); } test "index object" { var obj = Object.init(std.testing.allocator); defer obj.deinit(); - try obj.put("foo", .{ .integer = 1 }); - try obj.put("bar", .{ .integer = 2 }); + try obj.set("foo", Value.initInteger(1)); + try obj.set("bar", Value.initInteger(2)); - const base = Value{ .object = obj }; + const base = Value.initObject(obj); - try std.testing.expectEqual(Value{ .integer = 1 }, try index(base, .{ .string = "foo" })); - try std.testing.expectEqual(Value{ .integer = 2 }, try index(base, .{ .string = "bar" })); - try std.testing.expectEqual(Value.null, try index(base, .{ .string = "baz" })); + try std.testing.expectEqual(Value.initInteger(1), try index(base, Value.initString("foo"))); + try std.testing.expectEqual(Value.initInteger(2), try index(base, Value.initString("bar"))); + try std.testing.expectEqual(Value.null, try index(base, Value.initString("baz"))); } test "index null" { - try std.testing.expectEqual(Value.null, try index(.null, .{ .integer = 0 })); - try std.testing.expectEqual(Value.null, try index(.null, .{ .string = "foo" })); + try std.testing.expectEqual(Value.null, try index(Value.null, Value.initInteger(0))); + try std.testing.expectEqual(Value.null, try index(Value.null, Value.initString("foo"))); } test "index invalid type" { - try std.testing.expectError(error.InvalidType, index(.{ .integer = 42 }, .{ .integer = 0 })); - try std.testing.expectError(error.InvalidType, index(.{ .string = "foo" }, .{ .integer = 0 })); + try std.testing.expectError(error.InvalidType, index(Value.initInteger(42), Value.initInteger(0))); + try std.testing.expectError(error.InvalidType, index(Value.initString("foo"), Value.initInteger(0))); } test "compare integers" { - try std.testing.expect(try compare(.{ .integer = 1 }, .{ .integer = 1 }, .eq)); - try std.testing.expect(!try compare(.{ .integer = 1 }, .{ .integer = 2 }, .eq)); - try std.testing.expect(try compare(.{ .integer = 1 }, .{ .integer = 2 }, .ne)); - try std.testing.expect(try compare(.{ .integer = 1 }, .{ .integer = 2 }, .lt)); - try std.testing.expect(try compare(.{ .integer = 2 }, .{ .integer = 1 }, .gt)); - try std.testing.expect(try compare(.{ .integer = 1 }, .{ .integer = 1 }, .le)); - try std.testing.expect(try compare(.{ .integer = 1 }, .{ .integer = 1 }, .ge)); + try std.testing.expect(try compare(Value.initInteger(1), Value.initInteger(1), .eq)); + try std.testing.expect(!try compare(Value.initInteger(1), Value.initInteger(2), .eq)); + try std.testing.expect(try compare(Value.initInteger(1), Value.initInteger(2), .ne)); + try std.testing.expect(try compare(Value.initInteger(1), Value.initInteger(2), .lt)); + try std.testing.expect(try compare(Value.initInteger(2), Value.initInteger(1), .gt)); + try std.testing.expect(try compare(Value.initInteger(1), Value.initInteger(1), .le)); + try std.testing.expect(try compare(Value.initInteger(1), Value.initInteger(1), .ge)); } test "compare floats" { - try std.testing.expect(try compare(.{ .float = 1.5 }, .{ .float = 1.5 }, .eq)); - try std.testing.expect(try compare(.{ .float = 1.5 }, .{ .float = 2.5 }, .lt)); - try std.testing.expect(try compare(.{ .float = 2.5 }, .{ .float = 1.5 }, .gt)); + try std.testing.expect(try compare(Value.initFloat(1.5), Value.initFloat(1.5), .eq)); + try std.testing.expect(try compare(Value.initFloat(1.5), Value.initFloat(2.5), .lt)); + try std.testing.expect(try compare(Value.initFloat(2.5), Value.initFloat(1.5), .gt)); } test "compare mixed numbers" { - try std.testing.expect(try compare(.{ .integer = 2 }, .{ .float = 2.0 }, .eq)); - try std.testing.expect(try compare(.{ .float = 1.5 }, .{ .integer = 2 }, .lt)); - try std.testing.expect(try compare(.{ .integer = 3 }, .{ .float = 2.5 }, .gt)); + try std.testing.expect(try compare(Value.initInteger(2), Value.initFloat(2.0), .eq)); + try std.testing.expect(try compare(Value.initFloat(1.5), Value.initInteger(2), .lt)); + try std.testing.expect(try compare(Value.initInteger(3), Value.initFloat(2.5), .gt)); } test "compare strings" { - try std.testing.expect(try compare(.{ .string = "abc" }, .{ .string = "abc" }, .eq)); - try std.testing.expect(try compare(.{ .string = "abc" }, .{ .string = "abd" }, .ne)); - try std.testing.expect(try compare(.{ .string = "abc" }, .{ .string = "abd" }, .lt)); - try std.testing.expect(try compare(.{ .string = "abd" }, .{ .string = "abc" }, .gt)); + try std.testing.expect(try compare(Value.initString("abc"), Value.initString("abc"), .eq)); + try std.testing.expect(try compare(Value.initString("abc"), Value.initString("abd"), .ne)); + try std.testing.expect(try compare(Value.initString("abc"), Value.initString("abd"), .lt)); + try std.testing.expect(try compare(Value.initString("abd"), Value.initString("abc"), .gt)); } test "compare booleans" { - try std.testing.expect(try compare(.{ .bool = true }, .{ .bool = true }, .eq)); - try std.testing.expect(try compare(.{ .bool = false }, .{ .bool = false }, .eq)); - try std.testing.expect(try compare(.{ .bool = true }, .{ .bool = false }, .ne)); + try std.testing.expect(try compare(Value.initBool(true), Value.initBool(true), .eq)); + try std.testing.expect(try compare(Value.initBool(false), Value.initBool(false), .eq)); + try std.testing.expect(try compare(Value.initBool(true), Value.initBool(false), .ne)); } test "compare null" { - try std.testing.expect(try compare(.null, .null, .eq)); - try std.testing.expect(!try compare(.null, .null, .ne)); + try std.testing.expect(try compare(Value.null, Value.null, .eq)); + try std.testing.expect(!try compare(Value.null, Value.null, .ne)); } test "compare different types" { - try std.testing.expectError(error.InvalidType, compare(.{ .integer = 1 }, .{ .string = "1" }, .eq)); - try std.testing.expectError(error.InvalidType, compare(.null, .{ .integer = 0 }, .eq)); + try std.testing.expectError(error.InvalidType, compare(Value.initInteger(1), Value.initString("1"), .eq)); + try std.testing.expectError(error.InvalidType, compare(Value.null, Value.initInteger(0), .eq)); } pub fn isFalsy(value: Value) bool { - return switch (value) { + return switch (value.kind()) { .null => true, - .bool => |b| !b, + .bool => !value.boolean(), else => false, }; } @@ -200,21 +196,21 @@ pub fn isTruthy(value: Value) bool { } test "isFalsy" { - try std.testing.expect(isFalsy(.null)); - try std.testing.expect(isFalsy(.{ .bool = false })); - try std.testing.expect(!isFalsy(.{ .bool = true })); - try std.testing.expect(!isFalsy(.{ .integer = 0 })); - try std.testing.expect(!isFalsy(.{ .integer = 1 })); - try std.testing.expect(!isFalsy(.{ .string = "" })); - try std.testing.expect(!isFalsy(.{ .string = "hello" })); + try std.testing.expect(isFalsy(Value.null)); + try std.testing.expect(isFalsy(Value.initBool(false))); + try std.testing.expect(!isFalsy(Value.initBool(true))); + try std.testing.expect(!isFalsy(Value.initInteger(0))); + try std.testing.expect(!isFalsy(Value.initInteger(1))); + try std.testing.expect(!isFalsy(Value.initString(""))); + try std.testing.expect(!isFalsy(Value.initString("hello"))); } test "isTruthy" { - try std.testing.expect(!isTruthy(.null)); - try std.testing.expect(!isTruthy(.{ .bool = false })); - try std.testing.expect(isTruthy(.{ .bool = true })); - try std.testing.expect(isTruthy(.{ .integer = 0 })); - try std.testing.expect(isTruthy(.{ .integer = 1 })); - try std.testing.expect(isTruthy(.{ .string = "" })); - try std.testing.expect(isTruthy(.{ .string = "hello" })); + try std.testing.expect(!isTruthy(Value.null)); + try std.testing.expect(!isTruthy(Value.initBool(false))); + try std.testing.expect(isTruthy(Value.initBool(true))); + try std.testing.expect(isTruthy(Value.initInteger(0))); + try std.testing.expect(isTruthy(Value.initInteger(1))); + try std.testing.expect(isTruthy(Value.initString(""))); + try std.testing.expect(isTruthy(Value.initString("hello"))); } diff --git a/src/jv/parse.zig b/src/jv/parse.zig index c1d35fc..dc3fcef 100644 --- a/src/jv/parse.zig +++ b/src/jv/parse.zig @@ -1,6 +1,24 @@ const std = @import("std"); const Value = @import("./value.zig").Value; -pub fn parse(allocator: std.mem.Allocator, input: []const u8) !std.json.Parsed(Value) { - return try std.json.parseFromSlice(Value, allocator, input, .{}); +pub const Parsed = struct { + value: Value, + arena: *std.heap.ArenaAllocator, + allocator: std.mem.Allocator, + + pub fn deinit(self: *const Parsed) void { + const arena = self.arena; + const allocator = self.allocator; + arena.deinit(); + allocator.destroy(arena); + } +}; + +pub fn parse(allocator: std.mem.Allocator, input: []const u8) !Parsed { + const internal = try std.json.parseFromSlice(std.json.Value, allocator, input, .{}); + return .{ + .value = .{ ._internal = internal.value }, + .arena = internal.arena, + .allocator = allocator, + }; } diff --git a/src/jv/stringify.zig b/src/jv/stringify.zig index b553c6b..0fc40a7 100644 --- a/src/jv/stringify.zig +++ b/src/jv/stringify.zig @@ -2,5 +2,5 @@ const std = @import("std"); const Value = @import("./value.zig").Value; pub fn stringify(allocator: std.mem.Allocator, value: Value) ![]u8 { - return try std.json.Stringify.valueAlloc(allocator, value, .{ .whitespace = .indent_2 }); + return try std.json.Stringify.valueAlloc(allocator, value._internal, .{ .whitespace = .indent_2 }); } diff --git a/src/jv/value.zig b/src/jv/value.zig index 6a4c4b3..ffdec44 100644 --- a/src/jv/value.zig +++ b/src/jv/value.zig @@ -1,5 +1,159 @@ const std = @import("std"); +const Allocator = std.mem.Allocator; -pub const Value = std.json.Value; -pub const Array = std.json.Array; -pub const Object = std.json.ObjectMap; +pub const Value = struct { + const _Internal = std.json.Value; + _internal: _Internal, + + pub const Kind = enum { null, bool, integer, float, string, array, object, number_string }; + + pub fn kind(self: Value) Kind { + return switch (self._internal) { + .null => .null, + .bool => .bool, + .integer => .integer, + .float => .float, + .string => .string, + .array => .array, + .object => .object, + .number_string => .number_string, + }; + } + + pub const @"null": Value = .{ ._internal = .null }; + pub const @"true": Value = .{ ._internal = .{ .bool = true } }; + pub const @"false": Value = .{ ._internal = .{ .bool = false } }; + + pub fn initBool(b: bool) Value { + return .{ ._internal = .{ .bool = b } }; + } + + pub fn initInteger(i: i64) Value { + return .{ ._internal = .{ .integer = i } }; + } + + pub fn initFloat(f: f64) Value { + return .{ ._internal = .{ .float = f } }; + } + + pub fn initString(s: []const u8) Value { + return .{ ._internal = .{ .string = s } }; + } + + pub fn initArray(arr: Array) Value { + return .{ ._internal = .{ .array = arr._internal } }; + } + + pub fn initObject(obj: Object) Value { + return .{ ._internal = .{ .object = obj._internal } }; + } + + pub fn deinit(self: *Value, allocator: Allocator) void { + switch (self._internal) { + .string => |s| allocator.free(s), + .array => |*a| a.deinit(), + .object => |*o| o.deinit(), + else => {}, + } + } + + pub fn boolean(self: Value) bool { + return self._internal.bool; + } + + pub fn integer(self: Value) i64 { + return self._internal.integer; + } + + pub fn float(self: Value) f64 { + return self._internal.float; + } + + pub fn string(self: Value) []const u8 { + return self._internal.string; + } + + pub fn array(self: Value) Array { + return .{ ._internal = self._internal.array }; + } + + pub fn object(self: Value) Object { + return .{ ._internal = self._internal.object }; + } + + pub fn arrayGet(self: Value, idx: usize) Value { + const items = self._internal.array.items; + if (idx < items.len) { + return .{ ._internal = items[idx] }; + } + return Value.null; + } + + pub fn arrayLen(self: Value) usize { + return self._internal.array.items.len; + } + + pub fn arrayAppend(self: *Value, item: Value) !void { + try self._internal.array.append(item._internal); + } + + pub fn objectGet(self: Value, key: []const u8) ?Value { + if (self._internal.object.get(key)) |v| { + return .{ ._internal = v }; + } + return null; + } + + pub fn objectSet(self: *Value, key: []const u8, val: Value) !void { + try self._internal.object.put(key, val._internal); + } +}; + +pub const Array = struct { + const _Internal = std.json.Array; + _internal: _Internal, + + pub fn init(allocator: Allocator) Array { + return .{ ._internal = _Internal.init(allocator) }; + } + + pub fn deinit(self: *Array) void { + self._internal.deinit(); + } + + pub fn get(self: Array, idx: usize) Value { + return .{ ._internal = self._internal.items[idx] }; + } + + pub fn len(self: Array) usize { + return self._internal.items.len; + } + + pub fn append(self: *Array, value: Value) !void { + try self._internal.append(value._internal); + } +}; + +pub const Object = struct { + const _Internal = std.json.ObjectMap; + _internal: _Internal, + + pub fn init(allocator: Allocator) Object { + return .{ ._internal = _Internal.init(allocator) }; + } + + pub fn deinit(self: *Object) void { + self._internal.deinit(); + } + + pub fn get(self: Object, key: []const u8) ?Value { + if (self._internal.get(key)) |v| { + return .{ ._internal = v }; + } + return null; + } + + pub fn set(self: *Object, key: []const u8, value: Value) !void { + try self._internal.put(key, value._internal); + } +}; |
