authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2024-06-04 12:46:27 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2024-06-04 12:46:27 -07:00
log7025bfa226566b84347680c600ad7d416ba51917
treeb94f53bdad0fbfd8925a7efc2992206653dfdcba
parentf4dcf2bc324672fb2bdce20c634d9c5b6d064cc5

make intrusive-parser its own package


4 files changed, 9 insertions(+), 195 deletions(-)

intrusive_parser.zig deleted-194
......@@ -1,194 +0,0 @@
1const std = @import("std");
2const string = []const u8;
3const extras = @import("extras");
4const tracer = @import("tracer");
5
6pub const Parser = struct {
7 any: std.io.AnyReader,
8 allocator: std.mem.Allocator,
9 temp: std.ArrayListUnmanaged(u8) = .{},
10 idx: usize = 0,
11 end: bool = false,
12 line: usize = 1,
13 col: usize = 1,
14 data: std.ArrayListUnmanaged(u8) = .{},
15 strings_map: std.StringArrayHashMapUnmanaged(usize) = .{},
16 string_tag: u8,
17
18 pub fn init(allocator: std.mem.Allocator, any: std.io.AnyReader, string_tag: u8) Parser {
19 return .{
20 .any = any,
21 .allocator = allocator,
22 .string_tag = string_tag,
23 };
24 }
25
26 pub fn deinit(p: *Parser) void {
27 p.temp.deinit(p.allocator);
28 p.data.deinit(p.allocator);
29 p.strings_map.deinit(p.allocator);
30 }
31
32 pub fn avail(p: *Parser) usize {
33 return p.temp.items.len - p.idx;
34 }
35
36 pub fn slice(p: *Parser) []const u8 {
37 return p.temp.items[p.idx..];
38 }
39
40 pub fn eat(p: *Parser, comptime test_s: string) !?void {
41 if (test_s.len == 1) {
42 _ = try p.eatByte(test_s[0]);
43 return;
44 }
45 try p.peekAmt(test_s.len) orelse return null;
46 if (std.mem.eql(u8, p.slice()[0..test_s.len], test_s)) {
47 p.idx += test_s.len;
48 return;
49 }
50 return null;
51 }
52
53 fn peekAmt(p: *Parser, amt: usize) !?void {
54 if (p.avail() >= amt) return;
55 const buf_size = std.mem.page_size;
56 const diff_amt = amt - p.avail();
57 std.debug.assert(diff_amt <= buf_size);
58 var buf: [buf_size]u8 = undefined;
59 const len = try p.any.readAll(&buf);
60 if (len == 0) p.end = true;
61 if (len == 0) return null;
62 try p.temp.appendSlice(p.allocator, buf[0..len]);
63 if (amt > len) return null;
64 }
65
66 pub fn eatByte(p: *Parser, test_c: u8) !?u8 {
67 const t = tracer.trace(@src(), " '{c}'", .{test_c});
68 defer t.end();
69
70 try p.peekAmt(1) orelse return null;
71 if (p.slice()[0] == test_c) {
72 p.idx += 1;
73 return test_c;
74 }
75 return null;
76 }
77
78 pub fn eatRange(p: *Parser, comptime from: u8, comptime to: u8) !?u8 {
79 const t = tracer.trace(@src(), " ({d},{d})", .{ from, to });
80 defer t.end();
81
82 try p.peekAmt(1) orelse return null;
83 if (p.slice()[0] >= from and p.slice()[0] <= to) {
84 defer p.idx += 1;
85 return p.slice()[0];
86 }
87 return null;
88 }
89
90 pub fn eatAnyScalar(p: *Parser, test_s: string) !?u8 {
91 const t = tracer.trace(@src(), " ({s})", .{test_s});
92 defer t.end();
93
94 std.debug.assert(extras.matchesAll(u8, test_s, std.ascii.isASCII));
95 try p.peekAmt(1) orelse return null;
96 if (std.mem.indexOfScalar(u8, test_s, p.slice()[0])) |idx| {
97 p.idx += 1;
98 return test_s[idx];
99 }
100 return null;
101 }
102
103 pub fn shift(p: *Parser) !u21 {
104 try p.peekAmt(1) orelse return error.EndOfStream;
105 const len = std.unicode.utf8ByteSequenceLength(p.slice()[0]) catch return error.MalformedJson;
106 try p.peekAmt(len) orelse return error.EndOfStream;
107 defer p.idx += len;
108 return std.unicode.utf8Decode(p.slice()[0..len]) catch return error.MalformedJson;
109 }
110
111 pub fn shiftBytesN(p: *Parser, comptime n: usize) ![n]u8 {
112 try p.peekAmt(n) orelse return error.EndOfStream;
113 defer p.idx += n;
114 return p.slice()[0..n].*;
115 }
116
117 // tag(u8) + len(u32) + bytes(N)
118 pub fn addStr(p: *Parser, alloc: std.mem.Allocator, str: string) !usize {
119 const t = tracer.trace(@src(), "({d})", .{str.len});
120 defer t.end();
121
122 const adapter: AdapterStr = .{ .p = p };
123 const res = try p.strings_map.getOrPutAdapted(alloc, str, adapter);
124 if (res.found_existing) return res.value_ptr.*;
125 errdefer p.strings_map.orderedRemoveAt(res.index);
126 const r = p.data.items.len;
127 const l = str.len;
128 try p.data.ensureUnusedCapacity(alloc, 1 + 4 + l);
129 p.data.appendAssumeCapacity(p.string_tag);
130 p.data.appendSliceAssumeCapacity(&std.mem.toBytes(@as(u32, @intCast(l))));
131 p.data.appendSliceAssumeCapacity(str);
132 res.value_ptr.* = r;
133 return r;
134 }
135
136 const AdapterStr = struct {
137 p: *const Parser,
138
139 pub fn hash(ctx: @This(), a: string) u32 {
140 _ = ctx;
141 var hasher = std.hash.Wyhash.init(0);
142 hasher.update(a);
143 return @truncate(hasher.final());
144 }
145
146 pub fn eql(ctx: @This(), a: string, _: string, b_index: usize) bool {
147 const i = ctx.p.strings_map.values()[b_index];
148 std.debug.assert(ctx.p.data.items[i] == ctx.p.string_tag);
149 const l: u32 = @bitCast(ctx.p.data.items[i..][1..][0..4].*);
150 const b = ctx.p.data.items[i..][1..][4..][0..l];
151 return std.mem.eql(u8, a, b);
152 }
153 };
154};
155
156pub fn Mixin(comptime T: type) type {
157 return struct {
158 pub fn avail(pp: *T) usize {
159 return pp.parser.avail();
160 }
161
162 pub fn slice(pp: *T) []const u8 {
163 return pp.parser.slice();
164 }
165
166 pub fn eat(pp: *T, comptime test_s: string) !?void {
167 return pp.parser.eat(test_s);
168 }
169
170 fn peekAmt(pp: *T, amt: usize) !?void {
171 return pp.parser.peekAmt(amt);
172 }
173
174 pub fn eatByte(pp: *T, test_c: u8) !?u8 {
175 return pp.parser.eatByte(test_c);
176 }
177
178 pub fn eatRange(pp: *T, comptime from: u8, comptime to: u8) !?u8 {
179 return pp.parser.eatRange(from, to);
180 }
181
182 pub fn eatAnyScalar(pp: *T, test_s: string) !?u8 {
183 return pp.parser.eatAnyScalar(test_s);
184 }
185
186 pub fn shift(pp: *T) !u21 {
187 return pp.parser.shift();
188 }
189
190 pub fn shiftBytesN(pp: *T, comptime n: usize) ![n]u8 {
191 return pp.parser.shiftBytesN(n);
192 }
193 };
194}
json.zig+4-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22const string = []const u8;
33const extras = @import("extras");
44const tracer = @import("tracer");
5const intrusive_parser = @import("./intrusive_parser.zig");
5const intrusive_parser = @import("intrusive-parser");
66
77const Error = error{ OutOfMemory, EndOfStream, MalformedJson };
88const ObjectHashMap = std.AutoArrayHashMapUnmanaged(StringIndex, ValueIndex);
......@@ -304,6 +304,9 @@ const Parser = struct {
304304
305305 // tag(u8) + len(u32) + bytes(N)
306306 pub fn addStr(p: *Parser, alloc: std.mem.Allocator, str: string) !StringIndex {
307 const t = tracer.trace(@src(), "({d})", .{str.len});
308 defer t.end();
309
307310 return @enumFromInt(try p.parser.addStr(alloc, str));
308311 }
309312
licenses.txt+4
......@@ -4,3 +4,7 @@ MIT:
44- git https://github.com/nektro/zig-extras
55- git https://github.com/nektro/zig-tracer
66- git https://github.com/nst/JSONTestSuite
7
8MPL-2.0:
9= https://spdx.org/licenses/MPL-2.0
10- git https://github.com/nektro/zig-intrusive-parser
zig.mod+1
......@@ -6,6 +6,7 @@ description: "A JSON library for inspecting arbitrary values"
66dependencies:
77 - src: git https://github.com/nektro/zig-extras
88 - src: git https://github.com/nektro/zig-tracer
9 - src: git https://github.com/nektro/zig-intrusive-parser
910
1011root_dependencies:
1112 - src: git https://github.com/nst/JSONTestSuite commit-984defc2deaa653cb73cd29f4144a720ec9efe7c