aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jq/compile.zig21
-rw-r--r--src/jq/execute.zig24
-rw-r--r--src/jq/parse.zig76
-rw-r--r--src/root.zig10
4 files changed, 107 insertions, 24 deletions
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");
+}