| ... | ... | @@ -1,194 +0,0 @@ |
| 1 | | const std = @import("std"); |
| 2 | | const string = []const u8; |
| 3 | | const extras = @import("extras"); |
| 4 | | const tracer = @import("tracer"); |
| 5 | | |
| 6 | | pub 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 | | |
| 156 | | pub 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 | | } |