| ... | ... | @@ -11,7 +11,6 @@ const AllocWhen = @import("./scanner.zig").AllocWhen; |
| 11 | 11 | const Diagnostics = @import("./scanner.zig").Diagnostics; |
| 12 | 12 | const default_max_value_len = @import("./scanner.zig").default_max_value_len; |
| 13 | 13 | const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger; |
| 14 | | const maybeRecordDiagnosticContext = @import("./scanner.zig").maybeRecordDiagnosticContext; |
| 15 | 14 | |
| 16 | 15 | const Value = @import("./dynamic.zig").Value; |
| 17 | 16 | const Array = @import("./dynamic.zig").Array; |
| ... | ... | @@ -220,13 +219,13 @@ pub fn innerParse( |
| 220 | 219 | options: ParseOptions, |
| 221 | 220 | ) ParseError(@TypeOf(source.*))!T { |
| 222 | 221 | errdefer source.saveDiagnostics(); |
| 223 | | errdefer maybeRecordDiagnosticContext(allocator, options.diagnostics, @typeName(T)); |
| 222 | errdefer if (options.diagnostics) |diag| diag.recordContext(@typeName(T)); |
| 224 | 223 | switch (@typeInfo(T)) { |
| 225 | 224 | .Bool => { |
| 226 | 225 | return switch (try source.next()) { |
| 227 | 226 | .true => true, |
| 228 | 227 | .false => false, |
| 229 | | else => |t| return typeError(allocator, options.diagnostics, t, "bool"), |
| 228 | else => |t| return typeError(options.diagnostics, t, "bool"), |
| 230 | 229 | }; |
| 231 | 230 | }, |
| 232 | 231 | .Float, .ComptimeFloat => { |
| ... | ... | @@ -234,7 +233,7 @@ pub fn innerParse( |
| 234 | 233 | defer freeAllocated(allocator, token); |
| 235 | 234 | const slice = switch (token) { |
| 236 | 235 | inline .number, .allocated_number, .string, .allocated_string => |slice| slice, |
| 237 | | else => |t| return typeError(allocator, options.diagnostics, t, "float"), |
| 236 | else => |t| return typeError(options.diagnostics, t, "float"), |
| 238 | 237 | }; |
| 239 | 238 | return try std.fmt.parseFloat(T, slice); |
| 240 | 239 | }, |
| ... | ... | @@ -243,7 +242,7 @@ pub fn innerParse( |
| 243 | 242 | defer freeAllocated(allocator, token); |
| 244 | 243 | const slice = switch (token) { |
| 245 | 244 | inline .number, .allocated_number, .string, .allocated_string => |slice| slice, |
| 246 | | else => |t| return typeError(allocator, options.diagnostics, t, "int"), |
| 245 | else => |t| return typeError(options.diagnostics, t, "int"), |
| 247 | 246 | }; |
| 248 | 247 | return sliceToInt(T, slice); |
| 249 | 248 | }, |
| ... | ... | @@ -267,7 +266,7 @@ pub fn innerParse( |
| 267 | 266 | defer freeAllocated(allocator, token); |
| 268 | 267 | const slice = switch (token) { |
| 269 | 268 | inline .number, .allocated_number, .string, .allocated_string => |slice| slice, |
| 270 | | else => |t| return typeError(allocator, options.diagnostics, t, "enum (number or string)"), |
| 269 | else => |t| return typeError(options.diagnostics, t, "enum (number or string)"), |
| 271 | 270 | }; |
| 272 | 271 | return sliceToEnum(T, slice); |
| 273 | 272 | }, |
| ... | ... | @@ -280,7 +279,7 @@ pub fn innerParse( |
| 280 | 279 | |
| 281 | 280 | switch (try source.next()) { |
| 282 | 281 | .object_begin => {}, |
| 283 | | else => |t| return typeError(allocator, options.diagnostics, t, "union (object with one field)"), |
| 282 | else => |t| return typeError(options.diagnostics, t, "union (object with one field)"), |
| 284 | 283 | } |
| 285 | 284 | |
| 286 | 285 | var result: ?T = null; |
| ... | ... | @@ -300,7 +299,7 @@ pub fn innerParse( |
| 300 | 299 | // void isn't really a json type, but we can support void payload union tags with {} as a value. |
| 301 | 300 | switch (try source.next()) { |
| 302 | 301 | .object_begin => {}, |
| 303 | | else => |t| return typeError(allocator, options.diagnostics, t, "void payload ('{}')"), |
| 302 | else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"), |
| 304 | 303 | } |
| 305 | 304 | if (.object_end != try source.next()) return error.UnknownField; |
| 306 | 305 | result = @unionInit(T, u_field.name, {}); |
| ... | ... | @@ -324,7 +323,7 @@ pub fn innerParse( |
| 324 | 323 | if (structInfo.is_tuple) { |
| 325 | 324 | switch (try source.next()) { |
| 326 | 325 | .array_begin => {}, |
| 327 | | else => |t| return typeError(allocator, options.diagnostics, t, "tuple (array of values)"), |
| 326 | else => |t| return typeError(options.diagnostics, t, "tuple (array of values)"), |
| 328 | 327 | } |
| 329 | 328 | |
| 330 | 329 | var r: T = undefined; |
| ... | ... | @@ -344,7 +343,7 @@ pub fn innerParse( |
| 344 | 343 | |
| 345 | 344 | switch (try source.next()) { |
| 346 | 345 | .object_begin => {}, |
| 347 | | else => |t| return typeError(allocator, options.diagnostics, t, "struct ('{...}')"), |
| 346 | else => |t| return typeError(options.diagnostics, t, "struct ('{...}')"), |
| 348 | 347 | } |
| 349 | 348 | |
| 350 | 349 | var r: T = undefined; |
| ... | ... | @@ -366,7 +365,7 @@ pub fn innerParse( |
| 366 | 365 | // Free the name token now in case we're using an allocator that optimizes freeing the last allocated object. |
| 367 | 366 | // (Recursing into innerParse() might trigger more allocations.) |
| 368 | 367 | freeAllocated(allocator, name_token.?); |
| 369 | | errdefer maybeRecordDiagnosticContext(allocator, options.diagnostics, @typeName(T) ++ "." ++ field.name); |
| 368 | errdefer if (options.diagnostics) |diag| diag.recordContext(@typeName(T) ++ "." ++ field.name); |
| 370 | 369 | name_token = null; |
| 371 | 370 | if (fields_seen[i]) { |
| 372 | 371 | switch (options.duplicate_field_behavior) { |
| ... | ... | @@ -405,7 +404,7 @@ pub fn innerParse( |
| 405 | 404 | return internalParseArray(T, arrayInfo.child, arrayInfo.len, allocator, source, options); |
| 406 | 405 | }, |
| 407 | 406 | .string => { |
| 408 | | if (arrayInfo.child != u8) return typeError(allocator, options.diagnostics, .string, "array"); |
| 407 | if (arrayInfo.child != u8) return typeError(options.diagnostics, .string, "array"); |
| 409 | 408 | // Fixed-length string. |
| 410 | 409 | |
| 411 | 410 | var r: T = undefined; |
| ... | ... | @@ -449,7 +448,7 @@ pub fn innerParse( |
| 449 | 448 | return r; |
| 450 | 449 | }, |
| 451 | 450 | |
| 452 | | else => |t| return typeError(allocator, options.diagnostics, t, "array"), |
| 451 | else => |t| return typeError(options.diagnostics, t, "array"), |
| 453 | 452 | } |
| 454 | 453 | }, |
| 455 | 454 | |
| ... | ... | @@ -458,7 +457,7 @@ pub fn innerParse( |
| 458 | 457 | .array_begin => { |
| 459 | 458 | return internalParseArray(T, vecInfo.child, vecInfo.len, allocator, source, options); |
| 460 | 459 | }, |
| 461 | | else => |t| return typeError(allocator, options.diagnostics, t, "array"), |
| 460 | else => |t| return typeError(options.diagnostics, t, "array"), |
| 462 | 461 | } |
| 463 | 462 | }, |
| 464 | 463 | |
| ... | ... | @@ -497,7 +496,7 @@ pub fn innerParse( |
| 497 | 496 | return try arraylist.toOwnedSlice(); |
| 498 | 497 | }, |
| 499 | 498 | .string => { |
| 500 | | if (ptrInfo.child != u8) return typeError(allocator, options.diagnostics, .string, "array"); |
| 499 | if (ptrInfo.child != u8) return typeError(options.diagnostics, .string, "array"); |
| 501 | 500 | |
| 502 | 501 | // Dynamic length string. |
| 503 | 502 | if (ptrInfo.sentinel) |sentinel_ptr| { |
| ... | ... | @@ -519,7 +518,7 @@ pub fn innerParse( |
| 519 | 518 | } |
| 520 | 519 | } |
| 521 | 520 | }, |
| 522 | | else => |t| return typeError(allocator, options.diagnostics, t, "array"), |
| 521 | else => |t| return typeError(options.diagnostics, t, "array"), |
| 523 | 522 | } |
| 524 | 523 | }, |
| 525 | 524 | else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"), |
| ... | ... | @@ -582,24 +581,21 @@ fn coerceToTokenType(token: anytype) TokenType { |
| 582 | 581 | .end_of_document => .end_of_document, |
| 583 | 582 | }; |
| 584 | 583 | } |
| 585 | | fn typeError(allocator: Allocator, diagnostics: ?*Diagnostics, token: anytype, expected: []const u8) error{UnexpectedToken} { |
| 584 | fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []const u8) error{UnexpectedToken} { |
| 586 | 585 | if (diagnostics) |diag| { |
| 587 | | if (std.fmt.allocPrint(allocator, "expected: {s}, found: {s}", .{ |
| 588 | | expected, |
| 589 | | switch (coerceToTokenType(token)) { |
| 590 | | .object_begin => "'{'", |
| 591 | | .array_begin => "'['", |
| 592 | | .true, .false => "bool", |
| 593 | | .null => "null", |
| 594 | | .number => "number", |
| 595 | | .string => "string", |
| 596 | | .object_end => unreachable, // type errors happen at the start of a value. |
| 597 | | .array_end => unreachable, // type errors happen at the start of a value. |
| 598 | | .end_of_document => unreachable, // type errors happen at the start of a value. |
| 599 | | }, |
| 600 | | })) |s| { |
| 601 | | diag.recordContext(allocator, s) catch {}; |
| 602 | | } else |_| {} |
| 586 | const prefix = "expected: " ++ expected ++ ", found: "; |
| 587 | const s = switch (coerceToTokenType(token)) { |
| 588 | .object_begin => prefix ++ "'{'", |
| 589 | .array_begin => prefix ++ "'['", |
| 590 | .true, .false => prefix ++ "bool", |
| 591 | .null => prefix ++ "null", |
| 592 | .number => prefix ++ "number", |
| 593 | .string => prefix ++ "string", |
| 594 | .object_end => unreachable, // type errors happen at the start of a value. |
| 595 | .array_end => unreachable, // type errors happen at the start of a value. |
| 596 | .end_of_document => unreachable, // type errors happen at the start of a value. |
| 597 | }; |
| 598 | diag.recordContext(s); |
| 603 | 599 | } |
| 604 | 600 | return error.UnexpectedToken; |
| 605 | 601 | } |