aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/jq
diff options
context:
space:
mode:
Diffstat (limited to 'src/jq')
-rw-r--r--src/jq/codegen.zig24
-rw-r--r--src/jq/execute.zig24
-rw-r--r--src/jq/parse.zig40
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;
}
};