aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/jq/codegen.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/jq/codegen.zig')
-rw-r--r--src/jq/codegen.zig246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/jq/codegen.zig b/src/jq/codegen.zig
new file mode 100644
index 0000000..e377a5c
--- /dev/null
+++ b/src/jq/codegen.zig
@@ -0,0 +1,246 @@
+const std = @import("std");
+const jv = @import("../jv.zig");
+const Ast = @import("./parse.zig").Ast;
+const BinaryOp = @import("./parse.zig").BinaryOp;
+
+pub const ConstIndex = enum(u32) { _ };
+
+pub const Opcode = enum {
+ nop,
+ ret,
+ jump,
+ jump_unless,
+ fork,
+ dup,
+ pop,
+ subexp_begin,
+ subexp_end,
+ index,
+ index_opt,
+ add,
+ sub,
+ mul,
+ div,
+ mod,
+ eq,
+ ne,
+ lt,
+ gt,
+ le,
+ ge,
+ @"const",
+ const_true,
+ const_false,
+};
+
+pub const Instr = union(Opcode) {
+ const Self = @This();
+
+ nop,
+ ret,
+ jump: usize,
+ jump_unless: usize,
+ fork: usize,
+ dup,
+ pop,
+ subexp_begin,
+ subexp_end,
+ index,
+ index_opt,
+ add,
+ sub,
+ mul,
+ div,
+ mod,
+ eq,
+ ne,
+ lt,
+ gt,
+ le,
+ ge,
+ @"const": ConstIndex,
+ const_true,
+ const_false,
+
+ pub fn op(self: Self) Opcode {
+ return self;
+ }
+};
+
+const Codegen = struct {
+ instrs: std.ArrayList(Instr),
+ allocator: std.mem.Allocator,
+
+ fn init(allocator: std.mem.Allocator) !Codegen {
+ return .{
+ .instrs = try std.ArrayList(Instr).initCapacity(allocator, 16),
+ .allocator = allocator,
+ };
+ }
+
+ fn generate(self: *Codegen, ast: *const Ast) !void {
+ switch (ast.*) {
+ .identity => try self.emit(.nop),
+ .index => |idx| {
+ try self.generate(idx.base);
+ try self.emit(.subexp_begin);
+ try self.generate(idx.index);
+ try self.emit(.subexp_end);
+ try self.emit(if (idx.is_optional) .index_opt else .index);
+ },
+ .literal => |idx| try self.emit(.{ .@"const" = idx }),
+ .binary_expr => |binary_expr| {
+ try self.emit(.subexp_begin);
+ try self.generate(binary_expr.rhs);
+ try self.emit(.subexp_end);
+ try self.emit(.subexp_begin);
+ try self.generate(binary_expr.lhs);
+ try self.emit(.subexp_end);
+ const op_instr: Instr = switch (binary_expr.op) {
+ .add => .add,
+ .sub => .sub,
+ .mul => .mul,
+ .div => .div,
+ .mod => .mod,
+ .eq => .eq,
+ .ne => .ne,
+ .lt => .lt,
+ .gt => .gt,
+ .le => .le,
+ .ge => .ge,
+ else => return error.Unimplemented,
+ };
+ try self.emit(op_instr);
+ },
+ .and_expr => |and_expr| {
+ // DUP
+ // <lhs>
+ // JUMP_UNLESS l3
+ // POP
+ // <rhs>
+ // JUMP_UNLESS l1
+ // CONST_TRUE
+ // JUMP l2
+ // l1: CONST_FALSE
+ // l2: JUMP l4
+ // l3: POP
+ // CONST_FALSE
+ // l4:
+ try self.emit(.dup);
+ try self.generate(and_expr.lhs);
+ const jump1_idx = self.pos();
+ try self.emit(.{ .jump_unless = 0 });
+ try self.emit(.pop);
+ try self.generate(and_expr.rhs);
+ const jump2_idx = self.pos();
+ try self.emit(.{ .jump_unless = 0 });
+ try self.emit(.const_true);
+ const jump3_idx = self.pos();
+ try self.emit(.{ .jump = 0 });
+ const l1 = self.pos();
+ try self.emit(.const_false);
+ const jump4_idx = self.pos();
+ const l2 = self.pos();
+ try self.emit(.{ .jump = 0 });
+ const l3 = self.pos();
+ try self.emit(.pop);
+ try self.emit(.const_false);
+ const l4 = self.pos();
+
+ self.patchLabel(jump1_idx, l3);
+ self.patchLabel(jump2_idx, l1);
+ self.patchLabel(jump3_idx, l2);
+ self.patchLabel(jump4_idx, l4);
+ },
+ .or_expr => |or_expr| {
+ // DUP
+ // <lhs>
+ // JUMP_UNLESS l1
+ // POP
+ // CONST_TRUE
+ // JUMP l3
+ // l1: POP
+ // <rhs>
+ // JUMP_UNLESS l2
+ // CONST_TRUE
+ // JUMP l3
+ // l2: CONST_FALSE
+ // l3:
+ try self.emit(.dup);
+ try self.generate(or_expr.lhs);
+ const jump1_idx = self.pos();
+ try self.emit(.{ .jump_unless = 0 });
+ try self.emit(.pop);
+ try self.emit(.const_true);
+ const jump2_idx = self.pos();
+ try self.emit(.{ .jump = 0 });
+ const l1 = self.pos();
+ try self.emit(.pop);
+ try self.generate(or_expr.rhs);
+ const jump3_idx = self.pos();
+ try self.emit(.{ .jump_unless = 0 });
+ try self.emit(.const_true);
+ const jump4_idx = self.pos();
+ try self.emit(.{ .jump = 0 });
+ const l2 = self.pos();
+ try self.emit(.const_false);
+ const l3 = self.pos();
+
+ self.patchLabel(jump1_idx, l1);
+ self.patchLabel(jump2_idx, l3);
+ self.patchLabel(jump3_idx, l2);
+ self.patchLabel(jump4_idx, l3);
+ },
+ .pipe => |pipe_expr| {
+ try self.generate(pipe_expr.lhs);
+ try self.generate(pipe_expr.rhs);
+ },
+ .comma => |comma_expr| {
+ // FORK l1
+ // <lhs>
+ // JUMP l2
+ // l1: <rhs>
+ // l2:
+ const fork_index = self.pos();
+ try self.emit(.{ .fork = 0 });
+ try self.generate(comma_expr.lhs);
+ const jump_index = self.pos();
+ try self.emit(.{ .jump = 0 });
+ const l1 = self.pos();
+ try self.generate(comma_expr.rhs);
+ const l2 = self.pos();
+ self.patchLabel(fork_index, l1);
+ self.patchLabel(jump_index, l2);
+ },
+ }
+ }
+
+ fn toOwnedSlice(self: *Codegen) ![]Instr {
+ return self.instrs.toOwnedSlice(self.allocator);
+ }
+
+ fn emit(self: *Codegen, instr: Instr) !void {
+ try self.instrs.append(self.allocator, instr);
+ }
+
+ fn pos(self: *const Codegen) usize {
+ return self.instrs.items.len;
+ }
+
+ fn patchLabel(self: *Codegen, index: usize, target: usize) void {
+ const offset = target - index;
+ self.instrs.items[index] = switch (self.instrs.items[index]) {
+ .jump => .{ .jump = offset },
+ .jump_unless => .{ .jump_unless = offset },
+ .fork => .{ .fork = offset },
+ else => unreachable,
+ };
+ }
+};
+
+pub fn codegen(allocator: std.mem.Allocator, ast: *const Ast) ![]Instr {
+ var gen = try Codegen.init(allocator);
+ try gen.generate(ast);
+ try gen.emit(.ret);
+ return gen.toOwnedSlice();
+}