aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/jq/execute.zig75
-rw-r--r--src/jq/parse.zig10
-rw-r--r--src/jv.zig1
-rw-r--r--src/jv/ops.zig166
-rw-r--r--src/jv/parse.zig22
-rw-r--r--src/jv/stringify.zig2
-rw-r--r--src/jv/value.zig160
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 };
diff --git a/src/jv.zig b/src/jv.zig
index 476d776..4b6fa21 100644
--- a/src/jv.zig
+++ b/src/jv.zig
@@ -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);
+ }
+};