authorgravatar for mail@linusgroh.deLinus Groh <mail@linusgroh.de> 2023-10-27 19:05:51+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-27 18:05:51+00:00
log772636ed0d4a2051955197999c9ddf2906384806
treead6779833aa096513c83f20e846993f745cafa1d
parenta4cffd80bdc9bf879d1048b52aeba5716723803d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.json: Parse -0 as a float instead of an integer (#17729)

This is consistent with `JSON.parse("-0")` in JavaScript, RFC 8259 doesn't specifically mention what to do in this case. If a negative zero is encoded the intention is likely to preserve the sign.

3 files changed, 26 insertions(+), 1 deletions(-)

lib/std/json/dynamic_test.zig+11
......@@ -339,6 +339,17 @@ test "many object keys" {
339339 try testing.expectEqualStrings("v5", parsed.value.object.get("k5").?.string);
340340}
341341
342test "negative zero" {
343 const doc = "-0";
344 var fbs = std.io.fixedBufferStream(doc);
345 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
346 defer reader.deinit();
347 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
348 defer parsed.deinit();
349
350 try testing.expect(parsed.value.float == 0 and std.math.signbit(parsed.value.float));
351}
352
342353fn smallBufferJsonReader(allocator: Allocator, io_reader: anytype) JsonReader(16, @TypeOf(io_reader)) {
343354 return JsonReader(16, @TypeOf(io_reader)).init(allocator, io_reader);
344355}
lib/std/json/scanner.zig+2-1
......@@ -1700,11 +1700,12 @@ fn appendSlice(list: *std.ArrayList(u8), buf: []const u8, max_value_len: usize)
17001700}
17011701
17021702/// For the slice you get from a `Token.number` or `Token.allocated_number`,
1703/// this function returns true if the number doesn't contain any fraction or exponent components.
1703/// this function returns true if the number doesn't contain any fraction or exponent components, and is not `-0`.
17041704/// Note, the numeric value encoded by the value may still be an integer, such as `1.0`.
17051705/// This function is meant to give a hint about whether integer parsing or float parsing should be used on the value.
17061706/// This function will not give meaningful results on non-numeric input.
17071707pub fn isNumberFormattedLikeAnInteger(value: []const u8) bool {
1708 if (std.mem.eql(u8, value, "-0")) return false;
17081709 return std.mem.indexOfAny(u8, value, ".eE") == null;
17091710}
17101711
lib/std/json/scanner_test.zig+13
......@@ -7,6 +7,7 @@ const TokenType = @import("./scanner.zig").TokenType;
77const Diagnostics = @import("./scanner.zig").Diagnostics;
88const Error = @import("./scanner.zig").Error;
99const validate = @import("./scanner.zig").validate;
10const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger;
1011
1112const example_document_str =
1213 \\{
......@@ -465,3 +466,15 @@ test "enableDiagnostics" {
465466 try testDiagnostics(error.SyntaxError, 1, s.len, s.len - 1, s);
466467 }
467468}
469
470test isNumberFormattedLikeAnInteger {
471 try std.testing.expect(isNumberFormattedLikeAnInteger("0"));
472 try std.testing.expect(isNumberFormattedLikeAnInteger("1"));
473 try std.testing.expect(isNumberFormattedLikeAnInteger("123"));
474 try std.testing.expect(!isNumberFormattedLikeAnInteger("-0"));
475 try std.testing.expect(!isNumberFormattedLikeAnInteger("0.0"));
476 try std.testing.expect(!isNumberFormattedLikeAnInteger("1.0"));
477 try std.testing.expect(!isNumberFormattedLikeAnInteger("1.23"));
478 try std.testing.expect(!isNumberFormattedLikeAnInteger("1e10"));
479 try std.testing.expect(!isNumberFormattedLikeAnInteger("1E10"));
480}