aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/jq/compile.zig
blob: 40459c0e6ffc95a8ddb5b836b1f66712e352f8e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
const std = @import("std");
const jv = @import("../jv.zig");
const Ast = @import("./parse.zig").Ast;

pub const Opcode = enum {
    nop,
    ret,
    jump,
    fork,
    subexp_begin,
    subexp_end,
    array_index,
    add,
    object_key,
    literal,
};

pub const Instr = union(Opcode) {
    const Self = @This();

    nop,
    ret,
    jump: usize,
    fork: usize,
    subexp_begin,
    subexp_end,
    array_index,
    add,
    object_key: []const u8,
    literal: *jv.Value,

    pub fn op(self: Self) Opcode {
        return self;
    }

    pub fn deinit(self: Self, allocator: std.mem.Allocator) void {
        switch (self) {
            .object_key => |key| allocator.free(key),
            .literal => |value| allocator.destroy(value),
            else => {},
        }
    }
};

fn compileExpr(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocator, ast: *const Ast) ![]Instr {
    var instrs = try std.ArrayList(Instr).initCapacity(allocator, 16);

    switch (ast.*) {
        .identity => try instrs.append(allocator, .nop),
        .array_index => |index| {
            const index_instrs = try compileExpr(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 compileExpr(allocator, compile_allocator, binary_expr.rhs);
            defer allocator.free(rhs_instrs);
            const lhs_instrs = try compileExpr(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);
        },
        .pipe => |pipe_expr| {
            const lhs_instrs = try compileExpr(allocator, compile_allocator, pipe_expr.lhs);
            defer allocator.free(lhs_instrs);
            const rhs_instrs = try compileExpr(allocator, compile_allocator, pipe_expr.rhs);
            defer allocator.free(rhs_instrs);
            try instrs.appendSlice(allocator, lhs_instrs);
            try instrs.appendSlice(allocator, rhs_instrs);
        },
        .comma => |comma_expr| {
            //     FORK l1
            //     <lhs>
            //     JUMP l2
            // l1: <rhs>
            // l2:
            const lhs_instrs = try compileExpr(allocator, compile_allocator, comma_expr.lhs);
            defer allocator.free(lhs_instrs);
            const rhs_instrs = try compileExpr(allocator, compile_allocator, comma_expr.rhs);
            defer allocator.free(rhs_instrs);
            const fork_index = instrs.items.len;
            try instrs.append(allocator, .{ .fork = 0 });
            try instrs.appendSlice(allocator, lhs_instrs);
            const jump_index = instrs.items.len;
            try instrs.append(allocator, .{ .jump = 0 });
            const l1 = instrs.items.len;
            try instrs.appendSlice(allocator, rhs_instrs);
            const l2 = instrs.items.len;
            instrs.items[fork_index] = .{ .fork = l1 - fork_index };
            instrs.items[jump_index] = .{ .jump = l2 - jump_index };
        },
    }

    return instrs.toOwnedSlice(allocator);
}

pub fn compile(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocator, ast: *const Ast) ![]Instr {
    var instrs = try std.ArrayList(Instr).initCapacity(allocator, 16);
    const expr_instrs = try compileExpr(allocator, compile_allocator, ast);
    defer allocator.free(expr_instrs);
    try instrs.appendSlice(allocator, expr_instrs);
    try instrs.append(allocator, .ret);
    return instrs.toOwnedSlice(allocator);
}