diff options
Diffstat (limited to 'src/jq')
| -rw-r--r-- | src/jq/codegen.zig | 24 | ||||
| -rw-r--r-- | src/jq/execute.zig | 24 | ||||
| -rw-r--r-- | src/jq/parse.zig | 40 |
3 files changed, 84 insertions, 4 deletions
diff --git a/src/jq/codegen.zig b/src/jq/codegen.zig index 6ca5382..62535c1 100644 --- a/src/jq/codegen.zig +++ b/src/jq/codegen.zig @@ -19,6 +19,8 @@ pub const Opcode = enum { subexp_end, index, index_opt, + slice, + slice_opt, add, sub, mul, @@ -52,6 +54,8 @@ pub const Instr = union(Opcode) { subexp_end, index, index_opt, + slice, + slice_opt, add, sub, mul, @@ -97,6 +101,26 @@ const Codegen = struct { try self.emit(.subexp_end); try self.emit(if (idx.is_optional) .index_opt else .index); }, + .slice => |slice| { + try self.generate(slice.base); + // from + try self.emit(.subexp_begin); + if (slice.from) |from| { + try self.generate(from); + } else { + try self.emit(.{ .@"const" = .null }); + } + try self.emit(.subexp_end); + // to + try self.emit(.subexp_begin); + if (slice.to) |to| { + try self.generate(to); + } else { + try self.emit(.{ .@"const" = .null }); + } + try self.emit(.subexp_end); + try self.emit(if (slice.is_optional) .slice_opt else .slice); + }, .literal => |idx| try self.emit(.{ .@"const" = idx }), .binary_expr => |binary_expr| { try self.emit(.subexp_begin); diff --git a/src/jq/execute.zig b/src/jq/execute.zig index 3096be2..9fa410f 100644 --- a/src/jq/execute.zig +++ b/src/jq/execute.zig @@ -273,6 +273,30 @@ pub const Runtime = struct { key.deinit(self.allocator); try self.values.push(result); }, + .slice => { + std.debug.assert(self.values.ensureSize(3)); + + const base = self.values.pop(); + const to = self.values.pop(); + const from = self.values.pop(); + const result = try jv.ops.slice(self.allocator, base, from, to); + base.deinit(self.allocator); + to.deinit(self.allocator); + from.deinit(self.allocator); + try self.values.push(result); + }, + .slice_opt => { + std.debug.assert(self.values.ensureSize(3)); + + const base = self.values.pop(); + const to = self.values.pop(); + const from = self.values.pop(); + const result = jv.ops.slice(self.allocator, base, from, to) catch jv.Value.null; + base.deinit(self.allocator); + to.deinit(self.allocator); + from.deinit(self.allocator); + try self.values.push(result); + }, .add => { std.debug.assert(self.values.ensureSize(3)); diff --git a/src/jq/parse.zig b/src/jq/parse.zig index 6abd7c2..ad64824 100644 --- a/src/jq/parse.zig +++ b/src/jq/parse.zig @@ -12,6 +12,7 @@ pub const ParseError = error{ pub const AstKind = enum { identity, index, + slice, literal, binary_expr, or_expr, @@ -47,6 +48,7 @@ pub const BinaryOp = enum { pub const Ast = union(AstKind) { identity, index: struct { base: *Ast, index: *Ast, is_optional: bool }, + slice: struct { base: *Ast, from: ?*Ast, to: ?*Ast, is_optional: bool }, literal: ConstIndex, binary_expr: struct { op: BinaryOp, lhs: *Ast, rhs: *Ast }, or_expr: struct { lhs: *Ast, rhs: *Ast }, @@ -397,13 +399,43 @@ const Parser = struct { fn parseSuffix(self: *Self, base: *Ast) Error!*Ast { _ = try self.tokens.expect(.bracket_left); - const index_expr = try self.parseExpr(); - _ = try self.tokens.expect(.bracket_right); - const is_optional = self.tokens.consumeIf(.question); + // Handle [:to] form. + if (self.tokens.consumeIf(.colon)) { + const to_expr = try self.parseQuery(); + _ = try self.tokens.expect(.bracket_right); + const is_optional = self.tokens.consumeIf(.question); + const ast = try self.compile_allocator.create(Ast); + ast.* = .{ .slice = .{ .base = base, .from = null, .to = to_expr, .is_optional = is_optional } }; + return ast; + } + + const first_query = try self.parseQuery(); + // Handle [from:to] or [from:] form. + if (self.tokens.consumeIf(.colon)) { + if (self.tokens.consumeIf(.bracket_right)) { + // [from:] + const is_optional = self.tokens.consumeIf(.question); + const ast = try self.compile_allocator.create(Ast); + ast.* = .{ .slice = .{ .base = base, .from = first_query, .to = null, .is_optional = is_optional } }; + return ast; + } else { + // [from:to] + const to_expr = try self.parseQuery(); + _ = try self.tokens.expect(.bracket_right); + const is_optional = self.tokens.consumeIf(.question); + const ast = try self.compile_allocator.create(Ast); + ast.* = .{ .slice = .{ .base = base, .from = first_query, .to = to_expr, .is_optional = is_optional } }; + return ast; + } + } + + // Handle [index] form. + _ = try self.tokens.expect(.bracket_right); + const is_optional = self.tokens.consumeIf(.question); const ast = try self.compile_allocator.create(Ast); - ast.* = .{ .index = .{ .base = base, .index = index_expr, .is_optional = is_optional } }; + ast.* = .{ .index = .{ .base = base, .index = first_query, .is_optional = is_optional } }; return ast; } }; |
