diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-17 23:31:40 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-18 00:37:54 +0900 |
| commit | 6739144edaf34d10e0c0901231b196f377007934 (patch) | |
| tree | 015d78a96fa66390fe332226a1f27e7a55bda52e | |
| parent | 0295edea4c2d5e6f2e0a6761483cc9e2c64544f8 (diff) | |
| download | zgjq-6739144edaf34d10e0c0901231b196f377007934.tar.gz zgjq-6739144edaf34d10e0c0901231b196f377007934.tar.zst zgjq-6739144edaf34d10e0c0901231b196f377007934.zip | |
implement addition
| -rw-r--r-- | justfile | 2 | ||||
| -rw-r--r-- | src/jq/compile.zig | 21 | ||||
| -rw-r--r-- | src/jq/execute.zig | 24 | ||||
| -rw-r--r-- | src/jq/parse.zig | 76 | ||||
| -rw-r--r-- | src/root.zig | 10 |
5 files changed, 108 insertions, 25 deletions
@@ -5,7 +5,7 @@ build: fmt @zig build test: fmt - @zig test src/root.zig + @zig build test fmt: @zig fmt . diff --git a/src/jq/compile.zig b/src/jq/compile.zig index 0c30a3d..e3937c6 100644 --- a/src/jq/compile.zig +++ b/src/jq/compile.zig @@ -4,14 +4,20 @@ const Ast = @import("./parse.zig").Ast; pub const Opcode = enum { nop, + subexp_begin, + subexp_end, array_index, + add, object_key, literal, }; pub const Instr = union(Opcode) { nop, + subexp_begin, + subexp_end, array_index, + add, object_key: []const u8, literal: *jv.Value, @@ -28,11 +34,26 @@ pub fn compile(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocato .array_index => |index| { const index_instrs = try compile(allocator, compile_allocator, index); defer allocator.free(index_instrs); + try instrs.append(allocator, .subexp_begin); try instrs.appendSlice(allocator, index_instrs); + try instrs.append(allocator, .subexp_end); try instrs.append(allocator, .array_index); }, .object_key => |key| try instrs.append(allocator, .{ .object_key = key }), .literal => |value| try instrs.append(allocator, .{ .literal = value }), + .binary_expr => |binary_expr| { + const rhs_instrs = try compile(allocator, compile_allocator, binary_expr.rhs); + defer allocator.free(rhs_instrs); + const lhs_instrs = try compile(allocator, compile_allocator, binary_expr.lhs); + defer allocator.free(lhs_instrs); + try instrs.append(allocator, .subexp_begin); + try instrs.appendSlice(allocator, rhs_instrs); + try instrs.append(allocator, .subexp_end); + try instrs.append(allocator, .subexp_begin); + try instrs.appendSlice(allocator, lhs_instrs); + try instrs.append(allocator, .subexp_end); + try instrs.append(allocator, .add); + }, } return instrs.toOwnedSlice(allocator); diff --git a/src/jq/execute.zig b/src/jq/execute.zig index 8e9bbb0..29025b4 100644 --- a/src/jq/execute.zig +++ b/src/jq/execute.zig @@ -74,6 +74,18 @@ const ValueStack = struct { else => error.InvalidType, }; } + + pub fn dup(self: *Self) !void { + const top = self.stack.items[self.stack.items.len - 1]; + try self.push(top); + } + + pub fn swap(self: *Self) void { + const len = self.stack.items.len; + const tmp = self.stack.items[len - 1]; + self.stack.items[len - 1] = self.stack.items[len - 2]; + self.stack.items[len - 2] = tmp; + } }; pub fn execute(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Value) !jv.Value { @@ -89,17 +101,27 @@ pub fn execute(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Va switch (cur) { .nop => {}, .array_index => { - const index: usize = @intCast(try value_stack.popInteger()); const array = try value_stack.popArray(); + const index: usize = @intCast(try value_stack.popInteger()); const result = if (index < array.items.len) array.items[index] else .null; try value_stack.push(result); }, + .add => { + _ = try value_stack.pop(); + const lhs = try value_stack.popInteger(); + const rhs = try value_stack.popInteger(); + 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; try value_stack.push(result); }, .literal => |value| { + _ = try value_stack.pop(); try value_stack.push(value.*); }, } diff --git a/src/jq/parse.zig b/src/jq/parse.zig index 7ceda69..244d2a3 100644 --- a/src/jq/parse.zig +++ b/src/jq/parse.zig @@ -13,6 +13,11 @@ pub const AstKind = enum { array_index, object_key, literal, + binary_expr, +}; + +pub const BinaryOp = enum { + add, }; pub const Ast = union(AstKind) { @@ -20,6 +25,7 @@ pub const Ast = union(AstKind) { array_index: *Ast, object_key: []const u8, literal: *jv.Value, + binary_expr: struct { op: BinaryOp, lhs: *Ast, rhs: *Ast }, pub fn kind(self: @This()) AstKind { return self; @@ -70,42 +76,66 @@ pub fn parse(allocator: std.mem.Allocator, tokens: []const Token) !*Ast { } // GRAMMAR -// query := filter +// query := expr fn parseQuery(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { - const result = try parseFilter(allocator, tokens); + const result = try parseExpr(allocator, tokens); _ = try tokens.expect(.end); return result; } // GRAMMAR -// filter := "." accessor? -fn parseFilter(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { - _ = try tokens.expect(.dot); - - const next_token = try tokens.peek(); - - if (next_token.kind() == .end) { +// expr := term +// | 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.* = .identity; - return ast; + ast.* = .{ .binary_expr = .{ + .op = .add, + .lhs = lhs, + .rhs = rhs, + } }; + lhs = ast; } - - return parseAccessor(allocator, tokens); + return lhs; } // GRAMMAR -// accessor := field_access | index_access -fn parseAccessor(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { - const token = try tokens.peek(); - - if (token.kind() == .identifier) { - return parseFieldAccess(allocator, tokens); - } - if (token.kind() == .bracket_left) { - return parseIndexAccess(allocator, tokens); +// term := "." +// | "." field_access +// | "." index_access +// | NUMBER +fn parseTerm(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { + const first_token = try tokens.peek(); + if (first_token.kind() == .number) { + _ = try tokens.next(); + const number_value = try allocator.create(jv.Value); + number_value.* = .{ .integer = first_token.number }; + const number_node = try allocator.create(Ast); + number_node.* = .{ .literal = number_value }; + return number_node; } - return error.InvalidQuery; + _ = try tokens.expect(.dot); + + const next_token = try tokens.peek(); + switch (next_token.kind()) { + .end => { + const ast = try allocator.create(Ast); + ast.* = .identity; + return ast; + }, + .identifier => { + return parseFieldAccess(allocator, tokens); + }, + .bracket_left => { + return parseIndexAccess(allocator, tokens); + }, + else => return error.InvalidQuery, + } } // GRAMMAR diff --git a/src/root.zig b/src/root.zig index f75f062..e4df465 100644 --- a/src/root.zig +++ b/src/root.zig @@ -70,3 +70,13 @@ test "object key filter" { try testRun("[1,2,3]", allocator, "{\"arr\":[1,2,3]}", ".arr"); try testRun("{\"bar\":true}", allocator, "{\"foo\":{\"bar\":true}}", ".foo"); } + +test "addition" { + var debug_allocator = std.heap.DebugAllocator(.{}).init; + defer std.debug.assert(debug_allocator.deinit() == .ok); + const allocator = debug_allocator.allocator(); + + 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"); +} |
