authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2021-04-29 22:33:18 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2021-04-29 22:33:18 -07:00
loge02a50af83dab38ee4df026397f0c04109c8452c
tree923f78ef1cb52d15657b92712b9f9fcb05a9e50d
parent6b6af7ecf52e77861775dd3d1b2c37ca9f100ab9

initial code commit


3 files changed, 194 insertions(+), 0 deletions(-)

.gitignore created+3
...@@ -0,0 +1,3 @@
1/.zigmod
2/deps.zig
3/zig-*
src/lib.zig created+184
...@@ -0,0 +1,184 @@
1const std = @import("std");
2
3pub const Value = union(enum) {
4 Object: []Member,
5 Array: []Value,
6 String: []const u8,
7 Number: f64,
8 Bool: bool,
9 Null: void,
10
11 pub fn get(self: Value, key: []const u8) ?Value {
12 if (self == .Object) {
13 for (self.Object) |member| {
14 if (std.mem.eql(u8, member.key, key)) {
15 return member.value;
16 }
17 }
18 }
19 return null;
20 }
21
22 pub fn fetch(self: Value, query: anytype) ?Value {
23 return self.fetch_inner(query, 0);
24 }
25
26 fn fetch_inner(self: Value, query: anytype, comptime n: usize) ?Value {
27 if (query.len == n) {
28 return self;
29 }
30 const i = query[n];
31 const TO = @TypeOf(i);
32 const TI = @typeInfo(TO);
33 if (comptime std.meta.trait.isZigString(TO)) {
34 if (self.get(i)) |v| {
35 return v.fetch_inner(query, n + 1);
36 }
37 }
38 if (TI == .Int or TI == .ComptimeInt) {
39 if (self == .Array) {
40 return self.Array[i].fetch_inner(query, n + 1);
41 }
42 }
43 return null;
44 }
45
46 pub fn format(self: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
47 switch (self) {
48 .Object => {
49 try writer.writeAll("{");
50 for (self.Object) |member, i| {
51 if (i > 0) {
52 try writer.writeAll(", ");
53 }
54 try writer.print("{}", .{member});
55 }
56 try writer.writeAll("}");
57 },
58 .Array => {
59 try writer.writeAll("[");
60 for (self.Array) |val, i| {
61 if (i > 0) {
62 try writer.writeAll(", ");
63 }
64 try writer.print("{}", .{val});
65 }
66 try writer.writeAll("]");
67 },
68 .String => {
69 try writer.print("\"{s}\"", .{self.String});
70 },
71 .Number => {
72 try writer.print("{}", .{self.Number});
73 },
74 .Bool => {
75 try writer.print("{}", .{self.Bool});
76 },
77 .Null => {
78 try writer.writeAll("null");
79 },
80 }
81 }
82};
83
84pub const Member = struct {
85 key: []const u8,
86 value: Value,
87
88 pub fn format(self: Member, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
89 try writer.print("\"{s}\": {}", .{ self.key, self.value });
90 }
91};
92
93const Parser = struct {
94 alloc: *std.mem.Allocator,
95 p: std.json.StreamingParser,
96 input: []const u8,
97 index: usize,
98 tok2: ?std.json.Token,
99
100 pub fn next(self: *Parser) !?std.json.Token {
101 if (self.tok2) |t2| {
102 defer self.tok2 = null;
103 return t2;
104 }
105 while (self.index < self.input.len) {
106 var tok: ?std.json.Token = null;
107 var tok2: ?std.json.Token = null;
108 const b = self.input[self.index];
109 try self.p.feed(b, &tok, &tok2);
110 self.index += 1;
111 if (tok) |_| {} else {
112 continue;
113 }
114 if (tok2) |t2| {
115 self.tok2 = t2;
116 }
117 return tok;
118 }
119 return null;
120 }
121
122 pub const Error =
123 std.fs.File.OpenError ||
124 std.json.StreamingParser.Error ||
125 std.mem.Allocator.Error ||
126 error{InvalidCharacter} ||
127 error{ JsonExpectedObjKey, JsonExpectedValueStartGotEnd };
128};
129
130pub fn parse(alloc: *std.mem.Allocator, input: []const u8) Parser.Error!Value {
131 const p = &Parser{
132 .alloc = alloc,
133 .p = std.json.StreamingParser.init(),
134 .input = input,
135 .index = 0,
136 .tok2 = null,
137 };
138 return try parse_value(p, null);
139}
140
141fn parse_value(p: *Parser, start: ?std.json.Token) Parser.Error!Value {
142 const tok = start orelse try p.next();
143 return switch (tok.?) {
144 .ObjectBegin => |t| Value{ .Object = try parse_object(p) },
145 .ObjectEnd => |t| error.JsonExpectedValueStartGotEnd,
146 .ArrayBegin => |t| Value{ .Array = try parse_array(p) },
147 .ArrayEnd => |t| error.JsonExpectedValueStartGotEnd,
148 .String => |t| Value{ .String = t.slice(p.input, p.index - 1) },
149 .Number => |t| Value{ .Number = try std.fmt.parseFloat(f64, t.slice(p.input, p.index - 1)) },
150 .True => Value{ .Bool = true },
151 .False => Value{ .Bool = false },
152 .Null => Value{ .Null = {} },
153 };
154}
155
156fn parse_object(p: *Parser) Parser.Error![]Member {
157 const array = &std.ArrayList(Member).init(p.alloc);
158 defer array.deinit();
159 while (true) {
160 const tok = try p.next();
161 if (tok.? == .ObjectEnd) {
162 return array.toOwnedSlice();
163 }
164 if (tok.? != .String) {
165 return error.JsonExpectedObjKey;
166 }
167 try array.append(Member{
168 .key = tok.?.String.slice(p.input, p.index - 1),
169 .value = try parse_value(p, null),
170 });
171 }
172}
173
174fn parse_array(p: *Parser) Parser.Error![]Value {
175 const array = &std.ArrayList(Value).init(p.alloc);
176 defer array.deinit();
177 while (true) {
178 const tok = try p.next();
179 if (tok.? == .ArrayEnd) {
180 return array.toOwnedSlice();
181 }
182 try array.append(try parse_value(p, tok.?));
183 }
184}
zig.mod created+7
...@@ -0,0 +1,7 @@
1id: ocmr9rtohgccd6gm6tp8b1yzylyzkqwvo1q4btrsvj0cse9y
2name: json
3main: src/lib.zig
4license: MIT
5description: "A JSON library for inspecting arbitrary values"
6dev_dependencies:
7 - src: git https://github.com/truemedian/zfetch