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