diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-25 20:15:24 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-25 20:15:24 +0900 |
| commit | 8b8bc79d647285a170aa928ff31a0989c9ef6e33 (patch) | |
| tree | 14d172520dbd36179ec315010813528cdae40b5e /src | |
| parent | 7fb93cc98fc7738e160bd6bc896cbafe7a1aadcc (diff) | |
| download | zgjq-8b8bc79d647285a170aa928ff31a0989c9ef6e33.tar.gz zgjq-8b8bc79d647285a170aa928ff31a0989c9ef6e33.tar.zst zgjq-8b8bc79d647285a170aa928ff31a0989c9ef6e33.zip | |
refactor: move json value operations to separate file
Diffstat (limited to 'src')
| -rw-r--r-- | src/jq/execute.zig | 109 | ||||
| -rw-r--r-- | src/jv.zig | 2 | ||||
| -rw-r--r-- | src/jv/ops.zig | 188 |
3 files changed, 198 insertions, 101 deletions
diff --git a/src/jq/execute.zig b/src/jq/execute.zig index 2afb7b7..9e3a0d5 100644 --- a/src/jq/execute.zig +++ b/src/jq/execute.zig @@ -9,7 +9,7 @@ pub const ExecuteError = error{ Unimplemented, InvalidType, InternalError, -}; +} || jv.ops.OpsError; const SaveableStack = @import("./saveable_stack.zig").SaveableStack; @@ -193,25 +193,7 @@ pub const Runtime = struct { const base = self.values.pop(); const key = self.values.pop(); - - const result = 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; - }, - .object => |obj| blk: { - const k = switch (key) { - .string => |s| s, - else => return error.InvalidType, - }; - break :blk obj.get(k) orelse .null; - }, - .null => .null, - else => return error.InvalidType, - }; + const result = try jv.ops.index(base, key); try self.values.push(result); }, .add => { @@ -265,7 +247,7 @@ pub const Runtime = struct { _ = self.values.pop(); const lhs = self.values.pop(); const rhs = self.values.pop(); - const result = try compareValues(lhs, rhs, .eq); + const result = try jv.ops.compare(lhs, rhs, .eq); try self.values.push(.{ .bool = result }); }, .ne => { @@ -274,7 +256,7 @@ pub const Runtime = struct { _ = self.values.pop(); const lhs = self.values.pop(); const rhs = self.values.pop(); - const result = try compareValues(lhs, rhs, .ne); + const result = try jv.ops.compare(lhs, rhs, .ne); try self.values.push(.{ .bool = result }); }, .lt => { @@ -283,7 +265,7 @@ pub const Runtime = struct { _ = self.values.pop(); const lhs = self.values.pop(); const rhs = self.values.pop(); - const result = try compareValues(lhs, rhs, .lt); + const result = try jv.ops.compare(lhs, rhs, .lt); try self.values.push(.{ .bool = result }); }, .gt => { @@ -292,7 +274,7 @@ pub const Runtime = struct { _ = self.values.pop(); const lhs = self.values.pop(); const rhs = self.values.pop(); - const result = try compareValues(lhs, rhs, .gt); + const result = try jv.ops.compare(lhs, rhs, .gt); try self.values.push(.{ .bool = result }); }, .le => { @@ -301,7 +283,7 @@ pub const Runtime = struct { _ = self.values.pop(); const lhs = self.values.pop(); const rhs = self.values.pop(); - const result = try compareValues(lhs, rhs, .le); + const result = try jv.ops.compare(lhs, rhs, .le); try self.values.push(.{ .bool = result }); }, .ge => { @@ -310,7 +292,7 @@ pub const Runtime = struct { _ = self.values.pop(); const lhs = self.values.pop(); const rhs = self.values.pop(); - const result = try compareValues(lhs, rhs, .ge); + const result = try jv.ops.compare(lhs, rhs, .ge); try self.values.push(.{ .bool = result }); }, .@"const" => |idx| { @@ -337,78 +319,3 @@ pub const Runtime = struct { } } }; - -const CompareOp = enum { eq, ne, lt, gt, le, ge }; - -fn compareValues(lhs: jv.Value, rhs: jv.Value, op: CompareOp) ExecuteError!bool { - const lhs_tag = std.meta.activeTag(lhs); - const rhs_tag = std.meta.activeTag(rhs); - - if (lhs_tag != rhs_tag) { - const lhs_is_number = lhs_tag == .integer or lhs_tag == .float; - const rhs_is_number = rhs_tag == .integer or rhs_tag == .float; - if (lhs_is_number and rhs_is_number) { - return compareNumbers(lhs, rhs, op); - } - return error.InvalidType; - } - - return switch (lhs) { - .null => switch (op) { - .eq => true, - .ne => false, - .lt, .gt, .le, .ge => error.Unimplemented, - }, - .bool => |lhs_bool| { - const rhs_bool = rhs.bool; - return switch (op) { - .eq => lhs_bool == rhs_bool, - .ne => lhs_bool != rhs_bool, - .lt, .gt, .le, .ge => error.Unimplemented, - }; - }, - .integer, .float => compareNumbers(lhs, rhs, op), - .string => |lhs_str| { - const rhs_str = rhs.string; - const order = std.mem.order(u8, lhs_str, rhs_str); - return switch (op) { - .eq => order == .eq, - .ne => order != .eq, - .lt => order == .lt, - .gt => order == .gt, - .le => order == .lt or order == .eq, - .ge => order == .gt or order == .eq, - }; - }, - .array => switch (op) { - .eq, .ne => error.Unimplemented, - .lt, .gt, .le, .ge => error.Unimplemented, - }, - .object => switch (op) { - .eq, .ne => error.Unimplemented, - .lt, .gt, .le, .ge => error.Unimplemented, - }, - .number_string => error.Unimplemented, - }; -} - -fn compareNumbers(lhs: jv.Value, rhs: jv.Value, op: CompareOp) bool { - const lhs_f: f64 = switch (lhs) { - .integer => |i| @floatFromInt(i), - .float => |f| f, - else => unreachable, - }; - const rhs_f: f64 = switch (rhs) { - .integer => |i| @floatFromInt(i), - .float => |f| f, - else => unreachable, - }; - return switch (op) { - .eq => lhs_f == rhs_f, - .ne => lhs_f != rhs_f, - .lt => lhs_f < rhs_f, - .gt => lhs_f > rhs_f, - .le => lhs_f <= rhs_f, - .ge => lhs_f >= rhs_f, - }; -} @@ -4,3 +4,5 @@ pub const Object = @import("./jv/value.zig").Object; pub const parse = @import("./jv/parse.zig").parse; pub const stringify = @import("./jv/stringify.zig").stringify; + +pub const ops = @import("./jv/ops.zig"); diff --git a/src/jv/ops.zig b/src/jv/ops.zig new file mode 100644 index 0000000..3c702c8 --- /dev/null +++ b/src/jv/ops.zig @@ -0,0 +1,188 @@ +const std = @import("std"); +const Value = @import("./value.zig").Value; +const Array = @import("./value.zig").Array; +const Object = @import("./value.zig").Object; + +pub const OpsError = error{ + InvalidType, + Unimplemented, +}; + +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; + }, + .object => |obj| blk: { + const k = switch (key) { + .string => |s| s, + else => return error.InvalidType, + }; + break :blk obj.get(k) orelse .null; + }, + .null => .null, + else => error.InvalidType, + }; +} + +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); + + if (lhs_tag != rhs_tag) { + const lhs_is_number = lhs_tag == .integer or lhs_tag == .float; + const rhs_is_number = rhs_tag == .integer or rhs_tag == .float; + if (lhs_is_number and rhs_is_number) { + return compareNumbers(lhs, rhs, op); + } + return error.InvalidType; + } + + return switch (lhs) { + .null => switch (op) { + .eq => true, + .ne => false, + .lt, .gt, .le, .ge => error.Unimplemented, + }, + .bool => |lhs_bool| { + const rhs_bool = rhs.bool; + return switch (op) { + .eq => lhs_bool == rhs_bool, + .ne => lhs_bool != rhs_bool, + .lt, .gt, .le, .ge => error.Unimplemented, + }; + }, + .integer, .float => compareNumbers(lhs, rhs, op), + .string => |lhs_str| { + const rhs_str = rhs.string; + const order = std.mem.order(u8, lhs_str, rhs_str); + return switch (op) { + .eq => order == .eq, + .ne => order != .eq, + .lt => order == .lt, + .gt => order == .gt, + .le => order == .lt or order == .eq, + .ge => order == .gt or order == .eq, + }; + }, + .array => switch (op) { + .eq, .ne => error.Unimplemented, + .lt, .gt, .le, .ge => error.Unimplemented, + }, + .object => switch (op) { + .eq, .ne => error.Unimplemented, + .lt, .gt, .le, .ge => error.Unimplemented, + }, + .number_string => error.Unimplemented, + }; +} + +fn compareNumbers(lhs: Value, rhs: Value, op: CompareOp) bool { + const lhs_f: f64 = switch (lhs) { + .integer => |i| @floatFromInt(i), + .float => |f| f, + else => unreachable, + }; + const rhs_f: f64 = switch (rhs) { + .integer => |i| @floatFromInt(i), + .float => |f| f, + else => unreachable, + }; + return switch (op) { + .eq => lhs_f == rhs_f, + .ne => lhs_f != rhs_f, + .lt => lhs_f < rhs_f, + .gt => lhs_f > rhs_f, + .le => lhs_f <= rhs_f, + .ge => lhs_f >= rhs_f, + }; +} + +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 }); + + const base = Value{ .array = 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 })); +} + +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 }); + + const base = Value{ .object = 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" })); +} + +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" })); +} + +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 })); +} + +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)); +} + +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)); +} + +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)); +} + +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)); +} + +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)); +} + +test "compare null" { + try std.testing.expect(try compare(.null, .null, .eq)); + try std.testing.expect(!try compare(.null, .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)); +} |
