From 22811834abe3603e28128a17ac004b1aeea3d651 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 17 Jan 2026 12:30:34 +0900 Subject: implement identity filter --- src/jq.zig | 16 ++++++++++++++++ src/jq/compile.zig | 22 ++++++++++++++++++++++ src/jq/execute.zig | 22 ++++++++++++++++++++++ src/jq/parse.zig | 33 +++++++++++++++++++++++++++++++++ src/jq/tokenize.zig | 33 +++++++++++++++++++++++++++++++++ src/jv.zig | 3 +++ src/jv/parse.zig | 6 ++++++ src/jv/stringify.zig | 6 ++++++ src/jv/value.zig | 3 +++ src/main.zig | 23 +---------------------- src/root.zig | 48 +++++++++++++++++++++++++++++++++--------------- 11 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 src/jq.zig create mode 100644 src/jq/compile.zig create mode 100644 src/jq/execute.zig create mode 100644 src/jq/parse.zig create mode 100644 src/jq/tokenize.zig create mode 100644 src/jv.zig create mode 100644 src/jv/parse.zig create mode 100644 src/jv/stringify.zig create mode 100644 src/jv/value.zig diff --git a/src/jq.zig b/src/jq.zig new file mode 100644 index 0000000..cf0606b --- /dev/null +++ b/src/jq.zig @@ -0,0 +1,16 @@ +pub const TokenizeError = @import("./jq/tokenize.zig").TokenizeError; +pub const TokenKind = @import("./jq/tokenize.zig").TokenKind; +pub const Token = @import("./jq/tokenize.zig").Token; +pub const tokenize = @import("./jq/tokenize.zig").tokenize; + +pub const ParseError = @import("./jq/parse.zig").ParseError; +pub const AstKind = @import("./jq/parse.zig").AstKind; +pub const Ast = @import("./jq/parse.zig").Ast; +pub const parse = @import("./jq/parse.zig").parse; + +pub const Opcode = @import("./jq/compile.zig").Opcode; +pub const Instr = @import("./jq/compile.zig").Instr; +pub const compile = @import("./jq/compile.zig").compile; + +pub const ExecuteError = @import("./jq/execute.zig").ExecuteError; +pub const execute = @import("./jq/execute.zig").execute; diff --git a/src/jq/compile.zig b/src/jq/compile.zig new file mode 100644 index 0000000..d81f8ac --- /dev/null +++ b/src/jq/compile.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const Ast = @import("./parse.zig").Ast; + +pub const Opcode = enum { + nop, + identity, +}; + +pub const Instr = struct { + op: Opcode, +}; + +pub fn compile(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocator, ast: *const Ast) ![]Instr { + _ = compile_allocator; + var instrs = try std.array_list.Aligned(Instr, null).initCapacity(allocator, 16); + + switch (ast.kind) { + .identity => try instrs.append(allocator, .{ .op = .identity }), + } + + return instrs.toOwnedSlice(allocator); +} diff --git a/src/jq/execute.zig b/src/jq/execute.zig new file mode 100644 index 0000000..9857d46 --- /dev/null +++ b/src/jq/execute.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const jv = @import("../jv.zig"); +const Instr = @import("./compile.zig").Instr; + +pub const ExecuteError = error{ + Unimplemented, +}; + +pub fn execute(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Value) !jv.Value { + _ = allocator; + const len = instrs.len; + var pc: usize = 0; + while (pc < len) { + const cur = instrs[pc]; + _ = switch (cur.op) { + .nop => void, + .identity => return input, + }; + pc += 1; + } + return ExecuteError.Unimplemented; +} diff --git a/src/jq/parse.zig b/src/jq/parse.zig new file mode 100644 index 0000000..0554dca --- /dev/null +++ b/src/jq/parse.zig @@ -0,0 +1,33 @@ +const std = @import("std"); +const Token = @import("./tokenize.zig").Token; + +pub const ParseError = error{ + UnexpectedEnd, + InvalidQuery, +}; + +pub const AstKind = enum { + identity, +}; + +pub const Ast = struct { + kind: AstKind, +}; + +pub fn parse(allocator: std.mem.Allocator, tokens: []const Token) !*Ast { + if (tokens.len != 2) { + return ParseError.InvalidQuery; + } + const t1 = tokens[0]; + const t2 = tokens[1]; + if (t1.kind != .identity) { + return ParseError.InvalidQuery; + } + if (t2.kind != .end) { + return ParseError.UnexpectedEnd; + } + + const root = try allocator.create(Ast); + root.kind = .identity; + return root; +} diff --git a/src/jq/tokenize.zig b/src/jq/tokenize.zig new file mode 100644 index 0000000..0823ea1 --- /dev/null +++ b/src/jq/tokenize.zig @@ -0,0 +1,33 @@ +const std = @import("std"); + +pub const TokenizeError = error{ + UnexpectedEnd, +}; + +pub const TokenKind = enum { + end, + identity, +}; + +pub const Token = struct { + kind: TokenKind, +}; + +pub fn tokenize(allocator: std.mem.Allocator, query: []const u8) ![]Token { + var tokens = try std.array_list.Aligned(Token, null).initCapacity(allocator, 16); + + const len = query.len; + var i: usize = 0; + while (i < len) { + const c = query[i]; + if (c == '.') { + try tokens.append(allocator, .{ .kind = .identity }); + } else { + return TokenizeError.UnexpectedEnd; + } + i += 1; + } + + try tokens.append(allocator, .{ .kind = .end }); + return tokens.toOwnedSlice(allocator); +} diff --git a/src/jv.zig b/src/jv.zig new file mode 100644 index 0000000..73509b6 --- /dev/null +++ b/src/jv.zig @@ -0,0 +1,3 @@ +pub const Value = @import("./jv/value.zig").Value; +pub const parse = @import("./jv/parse.zig").parse; +pub const stringify = @import("./jv/stringify.zig").stringify; diff --git a/src/jv/parse.zig b/src/jv/parse.zig new file mode 100644 index 0000000..c1d35fc --- /dev/null +++ b/src/jv/parse.zig @@ -0,0 +1,6 @@ +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, .{}); +} diff --git a/src/jv/stringify.zig b/src/jv/stringify.zig new file mode 100644 index 0000000..341fed3 --- /dev/null +++ b/src/jv/stringify.zig @@ -0,0 +1,6 @@ +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, .{}); +} diff --git a/src/jv/value.zig b/src/jv/value.zig new file mode 100644 index 0000000..075d5fe --- /dev/null +++ b/src/jv/value.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub const Value = std.json.Value; diff --git a/src/main.zig b/src/main.zig index 03f9f52..1d4f8bf 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,26 +2,5 @@ const std = @import("std"); const zgjq = @import("zgjq"); pub fn main() !void { - // Prints to stderr, ignoring potential errors. - std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); - try zgjq.bufferedPrint(); -} - -test "simple test" { - const gpa = std.testing.allocator; - var list: std.ArrayList(i32) = .empty; - defer list.deinit(gpa); // Try commenting this out and see if zig detects the memory leak! - try list.append(gpa, 42); - try std.testing.expectEqual(@as(i32, 42), list.pop()); -} - -test "fuzz example" { - const Context = struct { - fn testOne(context: @This(), input: []const u8) anyerror!void { - _ = context; - // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case! - try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input)); - } - }; - try std.testing.fuzz(Context{}, Context.testOne, .{}); + // TODO } diff --git a/src/root.zig b/src/root.zig index 94c7cd0..61b61f5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,23 +1,41 @@ -//! By convention, root.zig is the root source file when making a library. const std = @import("std"); +pub const jq = @import("./jq.zig"); +pub const jv = @import("./jv.zig"); -pub fn bufferedPrint() !void { - // Stdout is for the actual output of your application, for example if you - // are implementing gzip, then only the compressed bytes should be sent to - // stdout, not any debugging messages. - var stdout_buffer: [1024]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); - const stdout = &stdout_writer.interface; +pub fn run(allocator: std.mem.Allocator, input: []const u8, query: []const u8) ![]const u8 { + var compile_allocator = std.heap.ArenaAllocator.init(allocator); + defer compile_allocator.deinit(); + const tokens = try jq.tokenize(compile_allocator.allocator(), query); + const ast = try jq.parse(compile_allocator.allocator(), tokens); + const instrs = try jq.compile(allocator, compile_allocator.allocator(), ast); + defer allocator.free(instrs); - try stdout.print("Run `zig build test` to run the tests.\n", .{}); - - try stdout.flush(); // Don't forget to flush! + const parsed = try jv.parse(allocator, input); + defer parsed.deinit(); + const json = parsed.value; + const result = try jq.execute(allocator, instrs, json); + const output = try jv.stringify(allocator, result); + return output; } -pub fn add(a: i32, b: i32) i32 { - return a + b; +fn testRun(expected: []const u8, allocator: std.mem.Allocator, input: []const u8, query: []const u8) !void { + const result = try run(allocator, input, query); + defer allocator.free(result); + try std.testing.expectEqualStrings(expected, result); } -test "basic add functionality" { - try std.testing.expect(add(3, 7) == 10); +test "identity filter" { + var debug_allocator = std.heap.DebugAllocator(.{}).init; + defer std.debug.assert(debug_allocator.deinit() == .ok); + const allocator = debug_allocator.allocator(); + + try testRun("null", allocator, "null", "."); + try testRun("false", allocator, "false", "."); + try testRun("true", allocator, "true", "."); + try testRun("123", allocator, "123", "."); + try testRun("3.1415", allocator, "3.1415", "."); + try testRun("[]", allocator, "[]", "."); + try testRun("{}", allocator, "{}", "."); + try testRun("[1,2,3]", allocator, "[1,2,3]", "."); + try testRun("{\"a\":123}", allocator, "{\"a\":123}", "."); } -- cgit v1.3-1-g0d28