authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2024-04-27 09:58:32-04:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2024-05-29 08:22:00-04:00
loge60cd8058091e6c97743cd56206072ba5d1d8fce
tree57348386f02a72126a50dde69a99f27f5133e5b4
parentd750a78b2c0265bd7bb2b924c4c739cad5aa6666

first attempt


3 files changed, 62 insertions(+), 4 deletions(-)

lib/std/json/scanner.zig+34-4
...@@ -196,7 +196,12 @@ pub const Diagnostics = struct {...@@ -196,7 +196,12 @@ pub const Diagnostics = struct {
196 line_number: u64 = 1,196 line_number: u64 = 1,
197 line_start_cursor: usize = @as(usize, @bitCast(@as(isize, -1))), // Start just "before" the input buffer to get a 1-based column for line 1.197 line_start_cursor: usize = @as(usize, @bitCast(@as(isize, -1))), // Start just "before" the input buffer to get a 1-based column for line 1.
198 total_bytes_before_current_input: u64 = 0,198 total_bytes_before_current_input: u64 = 0,
199 cursor_pointer: *const usize = undefined,199 /// While the source is operational, this is a pointer into it.
200 /// If the source is destroyed, this becomes a literal value.
201 cursor: union(enum) {
202 pointer: *const usize,
203 value: usize,
204 } = undefined,
200205
201 /// Starts at 1.206 /// Starts at 1.
202 pub fn getLine(self: *const @This()) u64 {207 pub fn getLine(self: *const @This()) u64 {
...@@ -204,11 +209,22 @@ pub const Diagnostics = struct {...@@ -204,11 +209,22 @@ pub const Diagnostics = struct {
204 }209 }
205 /// Starts at 1.210 /// Starts at 1.
206 pub fn getColumn(self: *const @This()) u64 {211 pub fn getColumn(self: *const @This()) u64 {
207 return self.cursor_pointer.* -% self.line_start_cursor;212 return self.getCursor() -% self.line_start_cursor;
208 }213 }
209 /// Starts at 0. Measures the byte offset since the start of the input.214 /// Starts at 0. Measures the byte offset since the start of the input.
210 pub fn getByteOffset(self: *const @This()) u64 {215 pub fn getByteOffset(self: *const @This()) u64 {
211 return self.total_bytes_before_current_input + self.cursor_pointer.*;216 return self.total_bytes_before_current_input + self.getCursor();
217 }
218
219 fn getCursor(self: *const @This()) usize {
220 return switch (self.cursor) {
221 .pointer => |p| p.*,
222 .value => |v| v,
223 };
224 }
225 fn saveCursor(self: *@This()) void {
226 const value = self.getCursor();
227 self.cursor = .{ .value = value };
212 }228 }
213};229};
214230
...@@ -244,6 +260,10 @@ pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {...@@ -244,6 +260,10 @@ pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {
244 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {260 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
245 self.scanner.enableDiagnostics(diagnostics);261 self.scanner.enableDiagnostics(diagnostics);
246 }262 }
263 /// Calls `std.json.Scanner.saveDiagnostics`.
264 pub fn saveDiagnostics(self: *const @This()) void {
265 self.scanner.saveDiagnostics();
266 }
247267
248 pub const NextError = ReaderType.Error || Error || Allocator.Error;268 pub const NextError = ReaderType.Error || Error || Allocator.Error;
249 pub const SkipError = NextError;269 pub const SkipError = NextError;
...@@ -446,10 +466,20 @@ pub const Scanner = struct {...@@ -446,10 +466,20 @@ pub const Scanner = struct {
446 self.* = undefined;466 self.* = undefined;
447 }467 }
448468
469 /// See also `saveDiagnostics()`.
449 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {470 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
450 diagnostics.cursor_pointer = &self.cursor;471 diagnostics.cursor = .{ .pointer = &self.cursor };
472 std.log.warn("cursor(enableDiagnostics): {}", .{diagnostics.getCursor()});
451 self.diagnostics = diagnostics;473 self.diagnostics = diagnostics;
452 }474 }
475 /// Call this just before `deinit()` to make the diagnostics available after the `deinit()`.
476 pub fn saveDiagnostics(self: *const @This()) void {
477 if (self.diagnostics) |diag| {
478 std.log.warn("cursor(deinit presave): {}", .{diag.getCursor()});
479 diag.saveCursor();
480 std.log.warn("cursor(deinit postsave): {}", .{diag.getCursor()});
481 }
482 }
453483
454 /// Call this whenever you get `error.BufferUnderrun` from `next()`.484 /// Call this whenever you get `error.BufferUnderrun` from `next()`.
455 /// When there is no more input to provide, call `endInput()`.485 /// When there is no more input to provide, call `endInput()`.
lib/std/json/static.zig+13
...@@ -7,6 +7,7 @@ const ArrayList = std.ArrayList;...@@ -7,6 +7,7 @@ const ArrayList = std.ArrayList;
7const Scanner = @import("./scanner.zig").Scanner;7const Scanner = @import("./scanner.zig").Scanner;
8const Token = @import("./scanner.zig").Token;8const Token = @import("./scanner.zig").Token;
9const AllocWhen = @import("./scanner.zig").AllocWhen;9const AllocWhen = @import("./scanner.zig").AllocWhen;
10const Diagnostics = @import("./scanner.zig").Diagnostics;
10const default_max_value_len = @import("./scanner.zig").default_max_value_len;11const default_max_value_len = @import("./scanner.zig").default_max_value_len;
11const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger;12const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger;
1213
...@@ -42,6 +43,10 @@ pub const ParseOptions = struct {...@@ -42,6 +43,10 @@ pub const ParseOptions = struct {
42 /// The default with a `*std.json.Reader` input is `.alloc_always`.43 /// The default with a `*std.json.Reader` input is `.alloc_always`.
43 /// Ignored for `parseFromValue` and `parseFromValueLeaky`.44 /// Ignored for `parseFromValue` and `parseFromValueLeaky`.
44 allocate: ?AllocWhen = null,45 allocate: ?AllocWhen = null,
46
47 /// Enable diagnostics with `var diagnostics: json.Diagnostics = undefined;`,
48 /// and set this option to `&diagnostics`.
49 diagnostics: ?*Diagnostics = null,
45};50};
4651
47pub fn Parsed(comptime T: type) type {52pub fn Parsed(comptime T: type) type {
...@@ -136,6 +141,14 @@ pub fn parseFromTokenSourceLeaky(...@@ -136,6 +141,14 @@ pub fn parseFromTokenSourceLeaky(
136 resolved_options.allocate = .alloc_always;141 resolved_options.allocate = .alloc_always;
137 }142 }
138 }143 }
144 if (resolved_options.diagnostics) |diag| {
145 scanner_or_reader.enableDiagnostics(diag);
146 }
147 defer {
148 if (resolved_options.diagnostics) |_| {
149 scanner_or_reader.saveDiagnostics();
150 }
151 }
139152
140 const value = try innerParse(T, allocator, scanner_or_reader, resolved_options);153 const value = try innerParse(T, allocator, scanner_or_reader, resolved_options);
141154
lib/std/json/static_test.zig+15
...@@ -925,3 +925,18 @@ test "parse at comptime" {...@@ -925,3 +925,18 @@ test "parse at comptime" {
925 };925 };
926 comptime testing.expectEqual(@as(u64, 9999), config.uptime) catch unreachable;926 comptime testing.expectEqual(@as(u64, 9999), config.uptime) catch unreachable;
927}927}
928
929test "parse with diagnostics" {
930 const doc =
931 \\{
932 \\ "common": "mistake",
933 \\}
934 ;
935 var diagnostics: Diagnostics = undefined;
936 try testing.expectError(error.SyntaxError, parseFromSlice(Value, testing.allocator, doc, .{
937 .diagnostics = &diagnostics,
938 }));
939 try std.testing.expectEqual(3, diagnostics.getLine());
940 try std.testing.expectEqual(1, diagnostics.getColumn());
941 try std.testing.expectEqual(25, diagnostics.getByteOffset());
942}