diff options
| -rw-r--r-- | src/jq.zig | 15 | ||||
| -rw-r--r-- | src/jq/compile.zig | 12 | ||||
| -rw-r--r-- | src/jq/execute.zig | 42 | ||||
| -rw-r--r-- | src/jq/parse.zig | 42 | ||||
| -rw-r--r-- | src/root.zig | 24 |
5 files changed, 73 insertions, 62 deletions
@@ -1,16 +1 @@ -pub const TokenizeError = @import("./jq/tokenize.zig").TokenizeError; -pub const TokenKind = @import("./jq/tokenize.zig").TokenKind; -pub const Token = @import("./jq/tokenize.zig").Token; -pub const tokenize = @import("./jq/tokenize.zig").tokenize; - -pub const ParseError = @import("./jq/parse.zig").ParseError; -pub const AstKind = @import("./jq/parse.zig").AstKind; -pub const Ast = @import("./jq/parse.zig").Ast; -pub const parse = @import("./jq/parse.zig").parse; - -pub const Opcode = @import("./jq/compile.zig").Opcode; -pub const Instr = @import("./jq/compile.zig").Instr; -pub const compile = @import("./jq/compile.zig").compile; - -pub const ExecuteError = @import("./jq/execute.zig").ExecuteError; pub const Runtime = @import("./jq/execute.zig").Runtime; diff --git a/src/jq/compile.zig b/src/jq/compile.zig index 5719f9c..54563de 100644 --- a/src/jq/compile.zig +++ b/src/jq/compile.zig @@ -14,6 +14,8 @@ pub const Opcode = enum { }; pub const Instr = union(Opcode) { + const Self = @This(); + nop, ret, subexp_begin, @@ -23,9 +25,17 @@ pub const Instr = union(Opcode) { object_key: []const u8, literal: *jv.Value, - pub fn op(self: @This()) Opcode { + 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 { diff --git a/src/jq/execute.zig b/src/jq/execute.zig index 3916d5b..82b6557 100644 --- a/src/jq/execute.zig +++ b/src/jq/execute.zig @@ -1,6 +1,9 @@ const std = @import("std"); const jv = @import("../jv.zig"); +const tokenize = @import("./tokenize.zig").tokenize; +const parse = @import("./parse.zig").parse; const Instr = @import("./compile.zig").Instr; +const compile = @import("./compile.zig").compile; pub const ExecuteError = error{ Unimplemented, @@ -105,27 +108,52 @@ const ValueStack = struct { pub const Runtime = struct { const Self = @This(); + allocator: std.mem.Allocator, values: ValueStack, instrs: []const Instr, - input: jv.Value, pc: usize, - pub fn init(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Value) !Self { - var self = Self{ + pub fn init(allocator: std.mem.Allocator) !Self { + return .{ + .allocator = allocator, .values = try ValueStack.init(allocator), - .instrs = instrs, - .input = input, + .instrs = &[_]Instr{}, .pc = 0, }; - try self.values.push(input); - return self; } pub fn deinit(self: *Self) void { + for (self.instrs) |instr| { + instr.deinit(self.allocator); + } + self.allocator.free(self.instrs); + self.values.deinit(); } + pub fn compileFromReader(self: *Self, reader: *std.Io.Reader) !void { + std.debug.assert(self.instrs.len == 0); + + var compile_allocator = std.heap.ArenaAllocator.init(self.allocator); + defer compile_allocator.deinit(); + const tokens = try tokenize(compile_allocator.allocator(), reader); + const ast = try parse(self.allocator, compile_allocator.allocator(), tokens); + const instrs = try compile(self.allocator, compile_allocator.allocator(), ast); + self.instrs = instrs; + } + + pub fn compileFromSlice(self: *Self, query: []const u8) !void { + var reader = std.Io.Reader.fixed(query); + return self.compileFromReader(&reader); + } + + pub fn start(self: *Self, input: jv.Value) !void { + try self.values.push(input); + } + pub fn next(self: *Self) !?jv.Value { + std.debug.assert(self.instrs.len > 0); + while (self.pc < self.instrs.len) : (self.pc += 1) { const cur = self.instrs[self.pc]; switch (cur) { diff --git a/src/jq/parse.zig b/src/jq/parse.zig index 26458f9..fa8b28a 100644 --- a/src/jq/parse.zig +++ b/src/jq/parse.zig @@ -72,21 +72,21 @@ pub const TokenStream = struct { } }; -pub fn parse(allocator: std.mem.Allocator, tokens: []const Token) !*Ast { +pub fn parse(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: []const Token) !*Ast { var token_stream = TokenStream.init(tokens); - return parseQuery(allocator, &token_stream); + return parseQuery(allocator, parse_allocator, &token_stream); } // GRAMMAR // query := expr ("|" expr)* -fn parseQuery(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { - var lhs = try parseExpr(allocator, tokens); +fn parseQuery(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { + var lhs = try parseExpr(allocator, parse_allocator, tokens); while (true) { const token = try tokens.peek(); if (token.kind() == .pipe) { _ = try tokens.next(); - const rhs = try parseExpr(allocator, tokens); - const ast = try allocator.create(Ast); + const rhs = try parseExpr(allocator, parse_allocator, tokens); + const ast = try parse_allocator.create(Ast); ast.* = .{ .pipe = .{ .lhs = lhs, .rhs = rhs, @@ -102,14 +102,14 @@ fn parseQuery(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { // GRAMMAR // expr := term ("+" term)* -fn parseExpr(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { - var lhs = try parseTerm(allocator, tokens); +fn parseExpr(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { + var lhs = try parseTerm(allocator, parse_allocator, tokens); while (true) { const token = try tokens.peek(); if (token.kind() == .plus) { _ = try tokens.next(); - const rhs = try parseTerm(allocator, tokens); - const ast = try allocator.create(Ast); + const rhs = try parseTerm(allocator, parse_allocator, tokens); + const ast = try parse_allocator.create(Ast); ast.* = .{ .binary_expr = .{ .op = .add, .lhs = lhs, @@ -128,13 +128,13 @@ fn parseExpr(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { // | "." field_access // | "." index_access // | NUMBER -fn parseTerm(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { +fn parseTerm(allocator: std.mem.Allocator, parse_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); + const number_node = try parse_allocator.create(Ast); number_node.* = .{ .literal = number_value }; return number_node; } @@ -144,15 +144,15 @@ fn parseTerm(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { const next_token = try tokens.peek(); switch (next_token.kind()) { .end, .pipe, .plus => { - const ast = try allocator.create(Ast); + const ast = try parse_allocator.create(Ast); ast.* = .identity; return ast; }, .identifier => { - return parseFieldAccess(allocator, tokens); + return parseFieldAccess(allocator, parse_allocator, tokens); }, .bracket_left => { - return parseIndexAccess(allocator, tokens); + return parseIndexAccess(allocator, parse_allocator, tokens); }, else => return error.InvalidQuery, } @@ -160,25 +160,25 @@ fn parseTerm(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { // GRAMMAR // field_access := IDENTIFIER -fn parseFieldAccess(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { +fn parseFieldAccess(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { const token = try tokens.expect(.identifier); - const ast = try allocator.create(Ast); - ast.* = .{ .object_key = token.identifier }; + const ast = try parse_allocator.create(Ast); + ast.* = .{ .object_key = try allocator.dupe(u8, token.identifier) }; return ast; } // GRAMMAR // index_access := "[" NUMBER "]" -fn parseIndexAccess(allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { +fn parseIndexAccess(allocator: std.mem.Allocator, parse_allocator: std.mem.Allocator, tokens: *TokenStream) !*Ast { _ = try tokens.expect(.bracket_left); const index_token = try tokens.expect(.number); _ = try tokens.expect(.bracket_right); const index_value = try allocator.create(jv.Value); index_value.* = .{ .integer = index_token.number }; - const index_node = try allocator.create(Ast); + const index_node = try parse_allocator.create(Ast); index_node.* = .{ .literal = index_value }; - const ast = try allocator.create(Ast); + const ast = try parse_allocator.create(Ast); ast.* = .{ .array_index = index_node }; return ast; } diff --git a/src/root.zig b/src/root.zig index 9c22deb..ccfd36d 100644 --- a/src/root.zig +++ b/src/root.zig @@ -3,40 +3,28 @@ pub const jq = @import("./jq.zig"); pub const jv = @import("./jv.zig"); pub fn run(allocator: std.mem.Allocator, input: []const u8, query: []const u8) ![]const u8 { - var compile_allocator = std.heap.ArenaAllocator.init(allocator); - defer compile_allocator.deinit(); - var reader = std.Io.Reader.fixed(query); - const tokens = try jq.tokenize(compile_allocator.allocator(), &reader); - const ast = try jq.parse(compile_allocator.allocator(), tokens); - const instrs = try jq.compile(allocator, compile_allocator.allocator(), ast); - defer allocator.free(instrs); - const parsed = try jv.parse(allocator, input); defer parsed.deinit(); const json = parsed.value; - var runtime = try jq.Runtime.init(allocator, instrs, json); + var runtime = try jq.Runtime.init(allocator); defer runtime.deinit(); + try runtime.compileFromSlice(query); + try runtime.start(json); const result = try runtime.next() orelse return error.NoResult; const output = try jv.stringify(allocator, result); return output; } fn testRun(expected: []const u8, allocator: std.mem.Allocator, input: []const u8, query: []const u8) !void { - var compile_allocator = std.heap.ArenaAllocator.init(allocator); - defer compile_allocator.deinit(); - var reader = std.Io.Reader.fixed(query); - const tokens = try jq.tokenize(compile_allocator.allocator(), &reader); - const ast = try jq.parse(compile_allocator.allocator(), tokens); - const instrs = try jq.compile(allocator, compile_allocator.allocator(), ast); - defer allocator.free(instrs); - const parsed = try jv.parse(allocator, input); defer parsed.deinit(); const json = parsed.value; - var runtime = try jq.Runtime.init(allocator, instrs, json); + var runtime = try jq.Runtime.init(allocator); defer runtime.deinit(); + try runtime.compileFromSlice(query); + try runtime.start(json); const result_value = try runtime.next() orelse return error.NoResult; const result = try jv.stringify(allocator, result_value); |
