From 0b51c7019d55995ba53f361521163007941c844b Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 25 Jan 2026 00:26:55 +0900 Subject: implement comma operator --- src/jq/compile.zig | 25 +++++++++++++++++++++++++ src/jq/execute.zig | 29 +++++++++++++++++++++++++++++ src/jq/parse.zig | 32 +++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 1 deletion(-) (limited to 'src/jq') diff --git a/src/jq/compile.zig b/src/jq/compile.zig index 54563de..40459c0 100644 --- a/src/jq/compile.zig +++ b/src/jq/compile.zig @@ -5,6 +5,8 @@ const Ast = @import("./parse.zig").Ast; pub const Opcode = enum { nop, ret, + jump, + fork, subexp_begin, subexp_end, array_index, @@ -18,6 +20,8 @@ pub const Instr = union(Opcode) { nop, ret, + jump: usize, + fork: usize, subexp_begin, subexp_end, array_index, @@ -74,6 +78,27 @@ fn compileExpr(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocato try instrs.appendSlice(allocator, lhs_instrs); try instrs.appendSlice(allocator, rhs_instrs); }, + .comma => |comma_expr| { + // FORK l1 + // + // JUMP l2 + // l1: + // l2: + const lhs_instrs = try compileExpr(allocator, compile_allocator, comma_expr.lhs); + defer allocator.free(lhs_instrs); + const rhs_instrs = try compileExpr(allocator, compile_allocator, comma_expr.rhs); + defer allocator.free(rhs_instrs); + const fork_index = instrs.items.len; + try instrs.append(allocator, .{ .fork = 0 }); + try instrs.appendSlice(allocator, lhs_instrs); + const jump_index = instrs.items.len; + try instrs.append(allocator, .{ .jump = 0 }); + const l1 = instrs.items.len; + try instrs.appendSlice(allocator, rhs_instrs); + const l2 = instrs.items.len; + instrs.items[fork_index] = .{ .fork = l1 - fork_index }; + instrs.items[jump_index] = .{ .jump = l2 - jump_index }; + }, } return instrs.toOwnedSlice(allocator); diff --git a/src/jq/execute.zig b/src/jq/execute.zig index 82b6557..6d4f194 100644 --- a/src/jq/execute.zig +++ b/src/jq/execute.zig @@ -110,6 +110,7 @@ pub const Runtime = struct { allocator: std.mem.Allocator, values: ValueStack, + forks: std.ArrayList(usize), instrs: []const Instr, pc: usize, @@ -117,6 +118,7 @@ pub const Runtime = struct { return .{ .allocator = allocator, .values = try ValueStack.init(allocator), + .forks = .{}, .instrs = &[_]Instr{}, .pc = 0, }; @@ -129,6 +131,7 @@ pub const Runtime = struct { self.allocator.free(self.instrs); self.values.deinit(); + self.forks.deinit(self.allocator); } pub fn compileFromReader(self: *Self, reader: *std.Io.Reader) !void { @@ -140,6 +143,11 @@ pub const Runtime = struct { const ast = try parse(self.allocator, compile_allocator.allocator(), tokens); const instrs = try compile(self.allocator, compile_allocator.allocator(), ast); self.instrs = instrs; + // std.debug.print("BEGIN\n", .{}); + // for (self.instrs) |instr| { + // std.debug.print("{}\n", .{instr}); + // } + // std.debug.print("END\n", .{}); } pub fn compileFromSlice(self: *Self, query: []const u8) !void { @@ -154,14 +162,23 @@ pub const Runtime = struct { pub fn next(self: *Self) !?jv.Value { std.debug.assert(self.instrs.len > 0); + self.restore_stack(); + while (self.pc < self.instrs.len) : (self.pc += 1) { const cur = self.instrs[self.pc]; + // std.debug.print("{}\n", .{cur}); switch (cur) { .nop => {}, .ret => { self.pc += 1; return self.values.pop(); }, + .jump => |offset| { + self.pc += offset - 1; + }, + .fork => |offset| { + try self.save_stack(self.pc + offset); + }, .subexp_begin => try self.values.dup(), .subexp_end => try self.values.swap(), .array_index => { @@ -199,4 +216,16 @@ pub const Runtime = struct { return null; } + + fn save_stack(self: *Self, target_pc: usize) !void { + try self.forks.append(self.allocator, target_pc); + try self.values.save(); + } + + fn restore_stack(self: *Self) void { + if (self.forks.pop()) |target_pc| { + self.pc = target_pc; + self.values.restore(); + } + } }; diff --git a/src/jq/parse.zig b/src/jq/parse.zig index fa8b28a..60ba89c 100644 --- a/src/jq/parse.zig +++ b/src/jq/parse.zig @@ -15,6 +15,7 @@ pub const AstKind = enum { literal, binary_expr, pipe, + comma, }; pub const BinaryOp = enum { @@ -28,6 +29,7 @@ pub const Ast = union(AstKind) { literal: *jv.Value, binary_expr: struct { op: BinaryOp, lhs: *Ast, rhs: *Ast }, pipe: struct { lhs: *Ast, rhs: *Ast }, + comma: struct { lhs: *Ast, rhs: *Ast }, pub fn kind(self: @This()) AstKind { return self; @@ -101,8 +103,36 @@ fn parseQuery(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, } // GRAMMAR -// expr := term ("+" term)* +// expr := expr1 fn parseExpr(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { + return parseExpr1(allocator, parse_allocator, tokens); +} + +// GRAMMAR +// expr1 := expr2 ("," expr2)* +fn parseExpr1(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { + var lhs = try parseExpr2(allocator, parse_allocator, tokens); + while (true) { + const token = try tokens.peek(); + if (token.kind() == .comma) { + _ = try tokens.next(); + const rhs = try parseExpr2(allocator, parse_allocator, tokens); + const ast = try parse_allocator.create(Ast); + ast.* = .{ .comma = .{ + .lhs = lhs, + .rhs = rhs, + } }; + lhs = ast; + } else { + break; + } + } + return lhs; +} + +// GRAMMAR +// expr2 := term ("+" term)* +fn parseExpr2(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { var lhs = try parseTerm(allocator, parse_allocator, tokens); while (true) { const token = try tokens.peek(); -- cgit v1.3-1-g0d28