aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jq.zig16
-rw-r--r--src/jq/compile.zig22
-rw-r--r--src/jq/execute.zig22
-rw-r--r--src/jq/parse.zig33
-rw-r--r--src/jq/tokenize.zig33
-rw-r--r--src/jv.zig3
-rw-r--r--src/jv/parse.zig6
-rw-r--r--src/jv/stringify.zig6
-rw-r--r--src/jv/value.zig3
-rw-r--r--src/main.zig23
-rw-r--r--src/root.zig48
11 files changed, 178 insertions, 37 deletions
diff --git a/src/jq.zig b/src/jq.zig
new file mode 100644
index 0000000..cf0606b
--- /dev/null
+++ b/src/jq.zig
@@ -0,0 +1,16 @@
+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 execute = @import("./jq/execute.zig").execute;
diff --git a/src/jq/compile.zig b/src/jq/compile.zig
new file mode 100644
index 0000000..d81f8ac
--- /dev/null
+++ b/src/jq/compile.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+const Ast = @import("./parse.zig").Ast;
+
+pub const Opcode = enum {
+ nop,
+ identity,
+};
+
+pub const Instr = struct {
+ op: Opcode,
+};
+
+pub fn compile(allocator: std.mem.Allocator, compile_allocator: std.mem.Allocator, ast: *const Ast) ![]Instr {
+ _ = compile_allocator;
+ var instrs = try std.array_list.Aligned(Instr, null).initCapacity(allocator, 16);
+
+ switch (ast.kind) {
+ .identity => try instrs.append(allocator, .{ .op = .identity }),
+ }
+
+ return instrs.toOwnedSlice(allocator);
+}
diff --git a/src/jq/execute.zig b/src/jq/execute.zig
new file mode 100644
index 0000000..9857d46
--- /dev/null
+++ b/src/jq/execute.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+const jv = @import("../jv.zig");
+const Instr = @import("./compile.zig").Instr;
+
+pub const ExecuteError = error{
+ Unimplemented,
+};
+
+pub fn execute(allocator: std.mem.Allocator, instrs: []const Instr, input: jv.Value) !jv.Value {
+ _ = allocator;
+ const len = instrs.len;
+ var pc: usize = 0;
+ while (pc < len) {
+ const cur = instrs[pc];
+ _ = switch (cur.op) {
+ .nop => void,
+ .identity => return input,
+ };
+ pc += 1;
+ }
+ return ExecuteError.Unimplemented;
+}
diff --git a/src/jq/parse.zig b/src/jq/parse.zig
new file mode 100644
index 0000000..0554dca
--- /dev/null
+++ b/src/jq/parse.zig
@@ -0,0 +1,33 @@
+const std = @import("std");
+const Token = @import("./tokenize.zig").Token;
+
+pub const ParseError = error{
+ UnexpectedEnd,
+ InvalidQuery,
+};
+
+pub const AstKind = enum {
+ identity,
+};
+
+pub const Ast = struct {
+ kind: AstKind,
+};
+
+pub fn parse(allocator: std.mem.Allocator, tokens: []const Token) !*Ast {
+ if (tokens.len != 2) {
+ return ParseError.InvalidQuery;
+ }
+ const t1 = tokens[0];
+ const t2 = tokens[1];
+ if (t1.kind != .identity) {
+ return ParseError.InvalidQuery;
+ }
+ if (t2.kind != .end) {
+ return ParseError.UnexpectedEnd;
+ }
+
+ const root = try allocator.create(Ast);
+ root.kind = .identity;
+ return root;
+}
diff --git a/src/jq/tokenize.zig b/src/jq/tokenize.zig
new file mode 100644
index 0000000..0823ea1
--- /dev/null
+++ b/src/jq/tokenize.zig
@@ -0,0 +1,33 @@
+const std = @import("std");
+
+pub const TokenizeError = error{
+ UnexpectedEnd,
+};
+
+pub const TokenKind = enum {
+ end,
+ identity,
+};
+
+pub const Token = struct {
+ kind: TokenKind,
+};
+
+pub fn tokenize(allocator: std.mem.Allocator, query: []const u8) ![]Token {
+ var tokens = try std.array_list.Aligned(Token, null).initCapacity(allocator, 16);
+
+ const len = query.len;
+ var i: usize = 0;
+ while (i < len) {
+ const c = query[i];
+ if (c == '.') {
+ try tokens.append(allocator, .{ .kind = .identity });
+ } else {
+ return TokenizeError.UnexpectedEnd;
+ }
+ i += 1;
+ }
+
+ try tokens.append(allocator, .{ .kind = .end });
+ return tokens.toOwnedSlice(allocator);
+}
diff --git a/src/jv.zig b/src/jv.zig
new file mode 100644
index 0000000..73509b6
--- /dev/null
+++ b/src/jv.zig
@@ -0,0 +1,3 @@
+pub const Value = @import("./jv/value.zig").Value;
+pub const parse = @import("./jv/parse.zig").parse;
+pub const stringify = @import("./jv/stringify.zig").stringify;
diff --git a/src/jv/parse.zig b/src/jv/parse.zig
new file mode 100644
index 0000000..c1d35fc
--- /dev/null
+++ b/src/jv/parse.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+const Value = @import("./value.zig").Value;
+
+pub fn parse(allocator: std.mem.Allocator, input: []const u8) !std.json.Parsed(Value) {
+ return try std.json.parseFromSlice(Value, allocator, input, .{});
+}
diff --git a/src/jv/stringify.zig b/src/jv/stringify.zig
new file mode 100644
index 0000000..341fed3
--- /dev/null
+++ b/src/jv/stringify.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+const Value = @import("./value.zig").Value;
+
+pub fn stringify(allocator: std.mem.Allocator, value: Value) ![]u8 {
+ return try std.json.Stringify.valueAlloc(allocator, value, .{});
+}
diff --git a/src/jv/value.zig b/src/jv/value.zig
new file mode 100644
index 0000000..075d5fe
--- /dev/null
+++ b/src/jv/value.zig
@@ -0,0 +1,3 @@
+const std = @import("std");
+
+pub const Value = std.json.Value;
diff --git a/src/main.zig b/src/main.zig
index 03f9f52..1d4f8bf 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -2,26 +2,5 @@ const std = @import("std");
const zgjq = @import("zgjq");
pub fn main() !void {
- // Prints to stderr, ignoring potential errors.
- std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
- try zgjq.bufferedPrint();
-}
-
-test "simple test" {
- const gpa = std.testing.allocator;
- var list: std.ArrayList(i32) = .empty;
- defer list.deinit(gpa); // Try commenting this out and see if zig detects the memory leak!
- try list.append(gpa, 42);
- try std.testing.expectEqual(@as(i32, 42), list.pop());
-}
-
-test "fuzz example" {
- const Context = struct {
- fn testOne(context: @This(), input: []const u8) anyerror!void {
- _ = context;
- // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case!
- try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
- }
- };
- try std.testing.fuzz(Context{}, Context.testOne, .{});
+ // TODO
}
diff --git a/src/root.zig b/src/root.zig
index 94c7cd0..61b61f5 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -1,23 +1,41 @@
-//! By convention, root.zig is the root source file when making a library.
const std = @import("std");
+pub const jq = @import("./jq.zig");
+pub const jv = @import("./jv.zig");
-pub fn bufferedPrint() !void {
- // Stdout is for the actual output of your application, for example if you
- // are implementing gzip, then only the compressed bytes should be sent to
- // stdout, not any debugging messages.
- var stdout_buffer: [1024]u8 = undefined;
- var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
- const stdout = &stdout_writer.interface;
+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();
+ const tokens = try jq.tokenize(compile_allocator.allocator(), query);
+ const ast = try jq.parse(compile_allocator.allocator(), tokens);
+ const instrs = try jq.compile(allocator, compile_allocator.allocator(), ast);
+ defer allocator.free(instrs);
- try stdout.print("Run `zig build test` to run the tests.\n", .{});
-
- try stdout.flush(); // Don't forget to flush!
+ const parsed = try jv.parse(allocator, input);
+ defer parsed.deinit();
+ const json = parsed.value;
+ const result = try jq.execute(allocator, instrs, json);
+ const output = try jv.stringify(allocator, result);
+ return output;
}
-pub fn add(a: i32, b: i32) i32 {
- return a + b;
+fn testRun(expected: []const u8, allocator: std.mem.Allocator, input: []const u8, query: []const u8) !void {
+ const result = try run(allocator, input, query);
+ defer allocator.free(result);
+ try std.testing.expectEqualStrings(expected, result);
}
-test "basic add functionality" {
- try std.testing.expect(add(3, 7) == 10);
+test "identity filter" {
+ var debug_allocator = std.heap.DebugAllocator(.{}).init;
+ defer std.debug.assert(debug_allocator.deinit() == .ok);
+ const allocator = debug_allocator.allocator();
+
+ try testRun("null", allocator, "null", ".");
+ try testRun("false", allocator, "false", ".");
+ try testRun("true", allocator, "true", ".");
+ try testRun("123", allocator, "123", ".");
+ try testRun("3.1415", allocator, "3.1415", ".");
+ try testRun("[]", allocator, "[]", ".");
+ try testRun("{}", allocator, "{}", ".");
+ try testRun("[1,2,3]", allocator, "[1,2,3]", ".");
+ try testRun("{\"a\":123}", allocator, "{\"a\":123}", ".");
}