aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-18 00:49:33 +0900
committernsfisis <nsfisis@gmail.com>2026-01-18 00:53:35 +0900
commit8e8fcf1dd73c785f6901cf53ce17380099d15bd1 (patch)
treee0bab2aaf525ad874ce013a3ace046ff224fc86a /src
parent6739144edaf34d10e0c0901231b196f377007934 (diff)
downloadzgjq-8e8fcf1dd73c785f6901cf53ce17380099d15bd1.tar.gz
zgjq-8e8fcf1dd73c785f6901cf53ce17380099d15bd1.tar.zst
zgjq-8e8fcf1dd73c785f6901cf53ce17380099d15bd1.zip
implement pipe operator
Diffstat (limited to 'src')
-rw-r--r--src/jq/compile.zig8
-rw-r--r--src/jq/execute.zig4
-rw-r--r--src/jq/parse.zig54
-rw-r--r--src/root.zig11
4 files changed, 58 insertions, 19 deletions
diff --git a/src/jq/compile.zig b/src/jq/compile.zig
index e3937c6..f4044c9 100644
--- a/src/jq/compile.zig
+++ b/src/jq/compile.zig
@@ -54,6 +54,14 @@ pub fn compile(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocato
try instrs.append(allocator, .subexp_end);
try instrs.append(allocator, .add);
},
+ .pipe => |pipe_expr| {
+ const lhs_instrs = try compile(allocator, compile_allocator, pipe_expr.lhs);
+ defer allocator.free(lhs_instrs);
+ const rhs_instrs = try compile(allocator, compile_allocator, pipe_expr.rhs);
+ defer allocator.free(rhs_instrs);
+ try instrs.appendSlice(allocator, lhs_instrs);
+ try instrs.appendSlice(allocator, rhs_instrs);
+ },
}
return instrs.toOwnedSlice(allocator);
diff --git a/src/jq/execute.zig b/src/jq/execute.zig
index 29025b4..8982458 100644
--- a/src/jq/execute.zig
+++ b/src/jq/execute.zig
@@ -100,6 +100,8 @@ pub fn execute(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Va
const cur = instrs[pc];
switch (cur) {
.nop => {},
+ .subexp_begin => try value_stack.dup(),
+ .subexp_end => value_stack.swap(),
.array_index => {
const array = try value_stack.popArray();
const index: usize = @intCast(try value_stack.popInteger());
@@ -113,8 +115,6 @@ pub fn execute(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Va
const result = lhs + rhs;
try value_stack.push(.{ .integer = result });
},
- .subexp_begin => try value_stack.dup(),
- .subexp_end => value_stack.swap(),
.object_key => |key| {
const obj = try value_stack.popObject();
const result = obj.get(key) orelse .null;
diff --git a/src/jq/parse.zig b/src/jq/parse.zig
index 244d2a3..26458f9 100644
--- a/src/jq/parse.zig
+++ b/src/jq/parse.zig
@@ -14,6 +14,7 @@ pub const AstKind = enum {
object_key,
literal,
binary_expr,
+ pipe,
};
pub const BinaryOp = enum {
@@ -26,6 +27,7 @@ pub const Ast = union(AstKind) {
object_key: []const u8,
literal: *jv.Value,
binary_expr: struct { op: BinaryOp, lhs: *Ast, rhs: *Ast },
+ pipe: struct { lhs: *Ast, rhs: *Ast },
pub fn kind(self: @This()) AstKind {
return self;
@@ -76,29 +78,47 @@ pub fn parse(allocator: std.mem.Allocator, tokens: []const Token) !*Ast {
}
// GRAMMAR
-// query := expr
+// query := expr ("|" expr)*
fn parseQuery(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast {
- const result = try parseExpr(allocator, tokens);
+ var lhs = try parseExpr(allocator, tokens);
+ while (true) {
+ const token = try tokens.peek();
+ if (token.kind() == .pipe) {
+ _ = try tokens.next();
+ const rhs = try parseExpr(allocator, tokens);
+ const ast = try allocator.create(Ast);
+ ast.* = .{ .pipe = .{
+ .lhs = lhs,
+ .rhs = rhs,
+ } };
+ lhs = ast;
+ } else {
+ break;
+ }
+ }
_ = try tokens.expect(.end);
- return result;
+ return lhs;
}
// GRAMMAR
-// expr := term
-// | term + term
+// expr := term ("+" term)*
fn parseExpr(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast {
var lhs = try parseTerm(allocator, tokens);
- const token = try tokens.peek();
- if (token.kind() == .plus) {
- _ = try tokens.next();
- const rhs = try parseTerm(allocator, tokens);
- const ast = try allocator.create(Ast);
- ast.* = .{ .binary_expr = .{
- .op = .add,
- .lhs = lhs,
- .rhs = rhs,
- } };
- lhs = ast;
+ while (true) {
+ const token = try tokens.peek();
+ if (token.kind() == .plus) {
+ _ = try tokens.next();
+ const rhs = try parseTerm(allocator, tokens);
+ const ast = try allocator.create(Ast);
+ ast.* = .{ .binary_expr = .{
+ .op = .add,
+ .lhs = lhs,
+ .rhs = rhs,
+ } };
+ lhs = ast;
+ } else {
+ break;
+ }
}
return lhs;
}
@@ -123,7 +143,7 @@ fn parseTerm(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast {
const next_token = try tokens.peek();
switch (next_token.kind()) {
- .end => {
+ .end, .pipe, .plus => {
const ast = try allocator.create(Ast);
ast.* = .identity;
return ast;
diff --git a/src/root.zig b/src/root.zig
index e4df465..b89b54f 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -79,4 +79,15 @@ test "addition" {
try testRun("579", allocator, "null", "123 + 456");
try testRun("35", allocator, "{\"a\":12,\"b\":23}", ".a + .b");
try testRun("12", allocator, "[1,2,3]", ".[1] + 10");
+ try testRun("6", allocator, "null", "1 + 2 + 3");
+}
+
+test "pipe operator" {
+ var debug_allocator = std.heap.DebugAllocator(.{}).init;
+ defer std.debug.assert(debug_allocator.deinit() == .ok);
+ const allocator = debug_allocator.allocator();
+
+ try testRun("123", allocator, "{\"a\":{\"b\":123}}", ".a | .b");
+ try testRun("584", allocator, "null", "123 + 456 | . + 5");
+ try testRun("10", allocator, "null", "1 | . + 2 | . + 3 | . | 4 + .");
}