Make JSON parser perfectly conformant

This commit is contained in:
Justine Tunney 2022-07-13 23:02:19 -07:00
parent 60164a7266
commit b707fca77a
7 changed files with 260 additions and 183 deletions

View file

@ -30,165 +30,266 @@
-- ljson should reject all of them as invalid -- ljson should reject all of them as invalid
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_no-colon.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_no-colon.json
assert(not DecodeJson(' {"a" ')) val, err = DecodeJson(' {"a" ')
assert(val == nil)
assert(err == "unexpected eof")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_value.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_value.json
assert(not DecodeJson(' {"a": ')) val, err = DecodeJson(' {"a": ')
assert(val == nil)
assert(err == "unexpected eof")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_key.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_key.json
assert(not DecodeJson(' {:"b"} ')) val, err = DecodeJson(' {:"b"} ')
assert(val == nil)
assert(err == "unexpected ':'")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_colon.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_colon.json
assert(not DecodeJson(' {"a" b} ')) val, err = DecodeJson(' {"a" b} ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_key_with_single_quotes.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_key_with_single_quotes.json
assert(not DecodeJson(' {key: \'value\'} ')) val, err = DecodeJson(' {key: \'value\'} ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_garbage_at_end.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_garbage_at_end.json
assert(not DecodeJson(' {"a":"a" 123} ')) val, err = DecodeJson(' {"a":"a" 123} ')
assert(val == nil)
assert(err == "object key must be string")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_emoji.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_emoji.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x7b\xf0\x9f\x87\xa8\xf0\x9f\x87\xad\x7d ')) val, err = DecodeJson(' \x7b\xf0\x9f\x87\xa8\xf0\x9f\x87\xad\x7d ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_bracket_key.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_bracket_key.json
assert(not DecodeJson(' {[: "x"} ')) val, err = DecodeJson(' {[: "x"} ')
assert(val == nil)
assert(err == "object key must be string")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_with_alpha_char.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_with_alpha_char.json
assert(not DecodeJson(' [1.8011670033376514H-308] ')) val, err = DecodeJson(' [1.8011670033376514H-308] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_with_alpha.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_with_alpha.json
assert(not DecodeJson(' [1.2a-3] ')) val, err = DecodeJson(' [1.2a-3] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_starting_with_dot.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_starting_with_dot.json
assert(not DecodeJson(' [.123] ')) val, err = DecodeJson(' [.123] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_real_with_invalid_utf8_after_e.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_real_with_invalid_utf8_after_e.json
-- (converted to binary for safety) val, err = DecodeJson(" [1e\xe5] ")
assert(not DecodeJson(' \x5b\x31\x65\xe5\x5d ')) assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_real_garbage_after_e.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_real_garbage_after_e.json
assert(not DecodeJson(' [1ea] ')) val, err = DecodeJson(' [1ea] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_neg_with_garbage_at_end.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_neg_with_garbage_at_end.json
assert(not DecodeJson(' [-1x] ')) val, err = DecodeJson(' [-1x] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_neg_real_without_int_part.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_neg_real_without_int_part.json
assert(not DecodeJson(' [-.123] ')) val, err = DecodeJson(' [-.123] ')
assert(val == nil)
assert(err == "bad negative")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_minus_sign_with_trailing_garbage.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_minus_sign_with_trailing_garbage.json
assert(not DecodeJson(' [-foo] ')) val, err = DecodeJson(' [-foo] ')
assert(val == nil)
assert(err == "bad negative")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_minus_infinity.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_minus_infinity.json
assert(not DecodeJson(' [-Infinity] ')) val, err = DecodeJson(' [-Infinity] ')
assert(val == nil)
assert(err == "bad negative")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-utf-8-in-int.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-utf-8-in-int.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x5b\x30\xe5\x5d ')) val, err = DecodeJson(' \x5b\x30\xe5\x5d ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-utf-8-in-exponent.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-utf-8-in-exponent.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x5b\x31\x65\x31\xe5\x5d ')) val, err = DecodeJson(' \x5b\x31\x65\x31\xe5\x5d ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-utf-8-in-bigger-int.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-utf-8-in-bigger-int.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x5b\x31\x32\x33\xe5\x5d ')) val, err = DecodeJson(' \x5b\x31\x32\x33\xe5\x5d ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-negative-real.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid-negative-real.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x5b\x2d\x31\x32\x33\x2e\x31\x32\x33\x66\x6f\x6f\x5d ')) val, err = DecodeJson(' \x5b\x2d\x31\x32\x33\x2e\x31\x32\x33\x66\x6f\x6f\x5d ')
assert(val == nil)
assert(err == "missing ','")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid+-.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_invalid+-.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x5b\x30\x65\x2b\x2d\x31\x5d ')) val, err = DecodeJson(" [0e+-1] ")
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_infinity.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_infinity.json
assert(not DecodeJson(' [Infinity] ')) val, err = DecodeJson(' [Infinity] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_hex_2_digits.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_hex_2_digits.json
assert(not DecodeJson(' [0x42] ')) val, err = DecodeJson(' [0x42] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_hex_1_digit.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_hex_1_digit.json
assert(not DecodeJson(' [0x1] ')) val, err = DecodeJson(' [0x1] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_expression.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_expression.json
assert(not DecodeJson(' [1+2] ')) val, err = DecodeJson(' [1+2] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_U+FF11_fullwidth_digit_one.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_U+FF11_fullwidth_digit_one.json
-- (converted to binary for safety) -- (converted to binary for safety)
assert(not DecodeJson(' \x5b\xef\xbc\x91\x5d ')) val, err = DecodeJson(' \x5b\xef\xbc\x91\x5d ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_NaN.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_NaN.json
assert(not DecodeJson(' [NaN] ')) val, err = DecodeJson(' [NaN] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_Inf.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_Inf.json
assert(not DecodeJson(' [Inf] ')) val, err = DecodeJson(' [Inf] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_9.e+.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_9.e+.json
assert(not DecodeJson(' [9.e+] ')) val, err = DecodeJson(' [9.e+] ')
assert(val == nil)
assert(err == "bad double")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1eE2.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1eE2.json
assert(not DecodeJson(' [1eE2] ')) val, err = DecodeJson(' [1eE2] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1.0e.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1.0e.json
assert(not DecodeJson(' [1.0e] ')) val, err = DecodeJson(' [1.0e] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1.0e-.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1.0e-.json
assert(not DecodeJson(' [1.0e-] ')) val, err = DecodeJson(' [1.0e-] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1.0e+.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_1.0e+.json
assert(not DecodeJson(' [1.0e+] ')) val, err = DecodeJson(' [1.0e+] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0e.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0e.json
assert(not DecodeJson(' [0e] ')) val, err = DecodeJson(' [0e] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0e+.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0e+.json
assert(not DecodeJson(' [0e+] ')) val, err = DecodeJson(' [0e+] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0_capital_E.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0_capital_E.json
assert(not DecodeJson(' [0E] ')) val, err = DecodeJson(' [0E] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0_capital_E+.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0_capital_E+.json
assert(not DecodeJson(' [0E+] ')) val, err = DecodeJson(' [0E+] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.3e.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.3e.json
assert(not DecodeJson(' [0.3e] ')) val, err = DecodeJson(' [0.3e] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.3e+.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.3e+.json
assert(not DecodeJson(' [0.3e+] ')) val, err = DecodeJson(' [0.3e+] ')
assert(val == nil)
assert(err == "bad exponent")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.1.2.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.1.2.json
assert(not DecodeJson(' [0.1.2] ')) val, err = DecodeJson(' [0.1.2] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_.2e-3.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_.2e-3.json
assert(not DecodeJson(' [.2e-3] ')) val, err = DecodeJson(' [.2e-3] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_.-1.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_.-1.json
assert(not DecodeJson(' [.-1] ')) val, err = DecodeJson(' [.-1] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_-NaN.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_-NaN.json
assert(not DecodeJson(' [-NaN] ')) val, err = DecodeJson(' [-NaN] ')
assert(val == nil)
assert(err == "bad negative")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_-1.0..json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_-1.0..json
assert(not DecodeJson(' [-1.0.] ')) val, err = DecodeJson(' [-1.0.] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_+Inf.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_+Inf.json
assert(not DecodeJson(' [+Inf] ')) val, err = DecodeJson(' [+Inf] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_+1.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_+1.json
assert(not DecodeJson(' [+1] ')) val, err = DecodeJson(' [+1] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_++.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_++.json
assert(not DecodeJson(' [++1234] ')) val, err = DecodeJson(' [++1234] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_incomplete_true.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_incomplete_true.json
assert(not DecodeJson(' [tru] ')) val, err = DecodeJson(' [tru] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_incomplete_null.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_incomplete_null.json
assert(not DecodeJson(' [nul] ')) val, err = DecodeJson(' [nul] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_incomplete_false.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_incomplete_false.json
assert(not DecodeJson(' [fals] ')) val, err = DecodeJson(' [fals] ')
assert(val == nil)
assert(err == "illegal character")
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_array_unclosed_with_object_inside.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_array_unclosed_with_object_inside.json
assert(not DecodeJson(' [{} ')) assert(not DecodeJson(' [{} '))
@ -311,3 +412,24 @@ assert(not DecodeJson(' [1 true] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_semicolon.json -- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_object_missing_semicolon.json
assert(not DecodeJson(' {"a" "b"} ')) assert(not DecodeJson(' {"a" "b"} '))
-- lool
assert(not DecodeJson(' [--2.] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_real_without_fractional_part.json
assert(not DecodeJson(' [1.] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_2.e3.json
assert(not DecodeJson(' [2.e3] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_2.e-3.json
assert(not DecodeJson(' [2.e-3] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_2.e+3.json
assert(not DecodeJson(' [2.e+3] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.e1.json
assert(not DecodeJson(' [0.e1] '))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_-2..json
assert(not DecodeJson(' [-2.] '))

View file

@ -1,61 +0,0 @@
--
-- Nicolas Seriot's JSONTestSuite
-- https://github.com/nst/JSONTestSuite
-- commit d64aefb55228d9584d3e5b2433f720ea8fd00c82
--
-- MIT License
--
-- Copyright (c) 2016 Nicolas Seriot
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
-- [jart] these tests deviate from the expectations of the upstream test
-- suite. most of these failures are because we're more permissive
-- about the encoding of double exponents and empty double fraction.
-- from fail1.lua
--------------------------------------------------------------------------------
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_real_without_fractional_part.json
assert(DecodeJson(' [1.] '))
assert(EncodeLua(DecodeJson(' [1.] ')) == EncodeLua({1.0}))
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_2.e3.json
assert(DecodeJson(' [2.e3] '))
assert(EncodeLua(DecodeJson(' [2.e3] ')) == '{2000.}')
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_2.e-3.json
assert(DecodeJson(' [2.e-3] '))
assert(EncodeLua(DecodeJson(' [2.e-3] ')) == '{0.002}')
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_2.e+3.json
assert(DecodeJson(' [2.e+3] '))
assert(EncodeLua(DecodeJson(' [2.e+3] ')) == '{2000.}')
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_0.e1.json
assert(DecodeJson(' [0.e1] '))
assert(EncodeLua(DecodeJson(' [0.e1] ')) == '{0.}')
-- https://github.com/nst/JSONTestSuite/tree/d64aefb55228d9584d3e5b2433f720ea8fd00c82/test_parsing/n_number_-2..json
assert(DecodeJson(' [-2.] '))
assert(EncodeLua(DecodeJson(' [-2.] ')) == '{-2.}')
-- lool
assert(not DecodeJson(' [--2.] '))

View file

@ -20,7 +20,7 @@ assert(EncodeLua(assert(DecodeJson[[ [1,3,2] ]])) == '{1, 3, 2}')
assert(EncodeLua(assert(DecodeJson[[ {"foo": 2, "bar": 4} ]])) == '{bar=4, foo=2}') assert(EncodeLua(assert(DecodeJson[[ {"foo": 2, "bar": 4} ]])) == '{bar=4, foo=2}')
assert(EncodeLua(assert(DecodeJson[[ -123 ]])) == '-123') assert(EncodeLua(assert(DecodeJson[[ -123 ]])) == '-123')
assert(EncodeLua(assert(DecodeJson[[ 1e6 ]])) == '1000000.') assert(EncodeLua(assert(DecodeJson[[ 1e6 ]])) == '1000000.')
assert(EncodeLua(assert(DecodeJson[[ 1.e-6 ]])) == '0.000001') assert(EncodeLua(assert(DecodeJson[[ 1e-6 ]])) == '0.000001')
assert(EncodeLua(assert(DecodeJson[[ 1e-06 ]])) == '0.000001') assert(EncodeLua(assert(DecodeJson[[ 1e-06 ]])) == '0.000001')
assert(EncodeLua(assert(DecodeJson[[ 9.123e6 ]])) == '9123000.') assert(EncodeLua(assert(DecodeJson[[ 9.123e6 ]])) == '9123000.')
assert(EncodeLua(assert(DecodeJson[[ [{"heh": [1,3,2]}] ]])) == '{{heh={1, 3, 2}}}') assert(EncodeLua(assert(DecodeJson[[ [{"heh": [1,3,2]}] ]])) == '{{heh={1, 3, 2}}}')

View file

@ -24,6 +24,7 @@
#include "libc/log/rop.h" #include "libc/log/rop.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/gc.internal.h" #include "libc/runtime/gc.internal.h"
#include "libc/runtime/stack.h"
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
#include "libc/stdio/strlist.internal.h" #include "libc/stdio/strlist.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
@ -93,7 +94,7 @@ static int SerializeArray(lua_State *L, char **buf, struct Serializer *z,
size_t i; size_t i;
RETURN_ON_ERROR(appendw(buf, '[')); RETURN_ON_ERROR(appendw(buf, '['));
for (i = 1; i <= tbllen; i++) { for (i = 1; i <= tbllen; i++) {
lua_rawgeti(L, -1, i); lua_rawgeti(L, -1, i); // +2
if (i > 1) RETURN_ON_ERROR(appendw(buf, ',')); if (i > 1) RETURN_ON_ERROR(appendw(buf, ','));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, level - 1)); RETURN_ON_ERROR(Serialize(L, buf, -1, z, level - 1));
lua_pop(L, 1); lua_pop(L, 1);
@ -108,8 +109,8 @@ static int SerializeObject(lua_State *L, char **buf, struct Serializer *z,
int level) { int level) {
bool comma = false; bool comma = false;
RETURN_ON_ERROR(appendw(buf, '{')); RETURN_ON_ERROR(appendw(buf, '{'));
lua_pushnil(L); lua_pushnil(L); // +2
while (lua_next(L, -2)) { while (lua_next(L, -2)) { // +3
if (lua_type(L, -2) == LUA_TSTRING) { if (lua_type(L, -2) == LUA_TSTRING) {
if (comma) { if (comma) {
RETURN_ON_ERROR(appendw(buf, ',')); RETURN_ON_ERROR(appendw(buf, ','));
@ -164,9 +165,13 @@ static int SerializeTable(lua_State *L, char **buf, int idx,
int rc; int rc;
bool isarray; bool isarray;
lua_Unsigned n; lua_Unsigned n;
if ((intptr_t)__builtin_frame_address(0) < GetStackAddr() + PAGESIZE * 2) {
z->reason = "out of stack";
return -1;
}
RETURN_ON_ERROR(rc = LuaPushVisit(&z->visited, lua_topointer(L, idx))); RETURN_ON_ERROR(rc = LuaPushVisit(&z->visited, lua_topointer(L, idx)));
if (!rc) { if (!rc) {
lua_pushvalue(L, idx); lua_pushvalue(L, idx); // +1
if ((n = lua_rawlen(L, -1)) > 0) { if ((n = lua_rawlen(L, -1)) > 0) {
isarray = true; isarray = true;
} else { } else {
@ -250,7 +255,7 @@ static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
int LuaEncodeJsonData(lua_State *L, char **buf, int idx, bool sorted) { int LuaEncodeJsonData(lua_State *L, char **buf, int idx, bool sorted) {
int rc, depth = 64; int rc, depth = 64;
struct Serializer z = {.reason = "out of memory", .sorted = sorted}; struct Serializer z = {.reason = "out of memory", .sorted = sorted};
if (lua_checkstack(L, depth * 4)) { if (lua_checkstack(L, depth * 3 + LUA_MINSTACK)) {
rc = Serialize(L, buf, idx, &z, depth); rc = Serialize(L, buf, idx, &z, depth);
free(z.visited.p); free(z.visited.p);
free(z.strbuf); free(z.strbuf);

View file

@ -22,6 +22,7 @@
#include "libc/log/rop.h" #include "libc/log/rop.h"
#include "libc/math.h" #include "libc/math.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/stack.h"
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
#include "libc/stdio/strlist.internal.h" #include "libc/stdio/strlist.internal.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -334,6 +335,11 @@ OnError:
static int SerializeTable(lua_State *L, char **buf, int idx, static int SerializeTable(lua_State *L, char **buf, int idx,
struct Serializer *z, int depth) { struct Serializer *z, int depth) {
int rc; int rc;
intptr_t rsp, bot;
if ((intptr_t)__builtin_frame_address(0) < GetStackAddr() + PAGESIZE * 2) {
z->reason = "out of stack";
return -1;
}
RETURN_ON_ERROR(rc = LuaPushVisit(&z->visited, lua_topointer(L, idx))); RETURN_ON_ERROR(rc = LuaPushVisit(&z->visited, lua_topointer(L, idx)));
if (rc) return SerializeOpaque(L, buf, idx, "cyclic"); if (rc) return SerializeOpaque(L, buf, idx, "cyclic");
lua_pushvalue(L, idx); // idx becomes invalid once we change stack lua_pushvalue(L, idx); // idx becomes invalid once we change stack
@ -400,7 +406,7 @@ static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
int LuaEncodeLuaData(lua_State *L, char **buf, int idx, bool sorted) { int LuaEncodeLuaData(lua_State *L, char **buf, int idx, bool sorted) {
int rc, depth = 64; int rc, depth = 64;
struct Serializer z = {.reason = "out of memory", .sorted = sorted}; struct Serializer z = {.reason = "out of memory", .sorted = sorted};
if (lua_checkstack(L, depth * 4)) { if (lua_checkstack(L, depth * 3 + LUA_MINSTACK)) {
rc = Serialize(L, buf, idx, &z, depth); rc = Serialize(L, buf, idx, &z, depth);
free(z.visited.p); free(z.visited.p);
if (rc == -1) { if (rc == -1) {

View file

@ -725,16 +725,12 @@ FUNCTIONS
output is valid. Please note that invalid utf-8 could still output is valid. Please note that invalid utf-8 could still
happen if it's encoded as utf-8. happen if it's encoded as utf-8.
This parser is lenient about commas and colons. For example
it's permissible to say `DecodeJson('[1 2 3 4]')`. Trailing
commas are allowed. Even prefix commas are allowed. However
it's not recommended that you rely on this behavior, and it
won't round-trip with EncodeJson() currently.
When objects are parsed, your Lua object can't preserve the When objects are parsed, your Lua object can't preserve the
the original ordering of fields. As such, they'll be sorted the original ordering of fields. As such, they'll be sorted
by EncodeJson() and may not round-trip with original intent by EncodeJson() and may not round-trip with original intent
This parser has perfect conformance with JSONTestSuite.
EncodeJson(value[, options:table]) EncodeJson(value[, options:table])
├─→ json:str ├─→ json:str
├─→ true [if useoutput] ├─→ true [if useoutput]

View file

@ -21,6 +21,7 @@
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/str/tpenc.h" #include "libc/str/tpenc.h"
#include "libc/str/utf16.h" #include "libc/str/utf16.h"
@ -31,27 +32,28 @@
#include "third_party/lua/lua.h" #include "third_party/lua/lua.h"
#include "tool/net/ljson.h" #include "tool/net/ljson.h"
#define AFTER_VALUE 0x01u #define KEY 1
#define ARRAY_SINGLE 0x02u #define COMMA 2
#define ARRAY_END 0x04u #define COLON 4
#define OBJECT_KEY 0x10u #define ARRAY 8
#define OBJECT_VAL 0x20u #define OBJECT 16
#define OBJECT_END 0x40u #define DEPTH 64
#define TOP_LEVEL 64
static struct DecodeJson Parse(struct lua_State *L, const char *p, static struct DecodeJson Parse(struct lua_State *L, const char *p,
const char *e, int context, int depth) { const char *e, int context, int depth) {
long x; long x;
char w[4]; char w[4];
const char *a;
luaL_Buffer b; luaL_Buffer b;
const char *reason;
struct DecodeJson r; struct DecodeJson r;
const char *a, *reason;
int A, B, C, D, c, d, i, u; int A, B, C, D, c, d, i, u;
if (UNLIKELY(!depth)) { if (UNLIKELY(!depth)) {
return (struct DecodeJson){-1, "maximum depth exceeded"}; return (struct DecodeJson){-1, "maximum depth exceeded"};
} }
if (UNLIKELY((intptr_t)__builtin_frame_address(0) <
GetStackAddr() + PAGESIZE * 2)) {
return (struct DecodeJson){-1, "out of stack"};
}
for (a = p, d = +1; p < e;) { for (a = p, d = +1; p < e;) {
switch ((c = *p++ & 255)) { switch ((c = *p++ & 255)) {
case ' ': // spaces case ' ': // spaces
@ -62,7 +64,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
break; break;
case ',': // present in list and object case ',': // present in list and object
if (0 != (context & AFTER_VALUE)) { if (context & COMMA) {
context = 0; context = 0;
a = p; a = p;
break; break;
@ -71,7 +73,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
case ':': // present only in object after key case ':': // present only in object after key
if (0 != (context & OBJECT_VAL)) { if (context & COLON) {
context = 0; context = 0;
a = p; a = p;
break; break;
@ -80,8 +82,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
case 'n': // null case 'n': // null
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (KEY | COLON | COMMA)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation;
if (p + 3 <= e && READ32LE(p - 1) == READ32LE("null")) { if (p + 3 <= e && READ32LE(p - 1) == READ32LE("null")) {
lua_pushnil(L); lua_pushnil(L);
return (struct DecodeJson){1, p + 3}; return (struct DecodeJson){1, p + 3};
@ -90,8 +91,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
case 'f': // false case 'f': // false
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (KEY | COLON | COMMA)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation;
if (p + 4 <= e && READ32LE(p) == READ32LE("alse")) { if (p + 4 <= e && READ32LE(p) == READ32LE("alse")) {
lua_pushboolean(L, false); lua_pushboolean(L, false);
return (struct DecodeJson){1, p + 4}; return (struct DecodeJson){1, p + 4};
@ -100,8 +100,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
case 't': // true case 't': // true
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (KEY | COLON | COMMA)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation;
if (p + 3 <= e && READ32LE(p - 1) == READ32LE("true")) { if (p + 3 <= e && READ32LE(p - 1) == READ32LE("true")) {
lua_pushboolean(L, true); lua_pushboolean(L, true);
return (struct DecodeJson){1, p + 3}; return (struct DecodeJson){1, p + 3};
@ -109,14 +108,21 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
goto IllegalCharacter; goto IllegalCharacter;
} }
default:
IllegalCharacter:
return (struct DecodeJson){-1, "illegal character"};
OnColonCommaKey:
if (context & KEY) goto BadObjectKey;
OnColonComma:
if (context & COLON) goto MissingColon;
return (struct DecodeJson){-1, "missing ','"};
MissingColon:
return (struct DecodeJson){-1, "missing ':'"};
BadObjectKey: BadObjectKey:
return (struct DecodeJson){-1, "object key must be string"}; return (struct DecodeJson){-1, "object key must be string"};
MissingPunctuation:
return (struct DecodeJson){-1, "missing ',' or ':'"};
case '-': // negative case '-': // negative
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (COLON | COMMA | KEY)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation;
if (p < e && isdigit(*p)) { if (p < e && isdigit(*p)) {
d = -1; d = -1;
break; break;
@ -125,10 +131,14 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
case '0': // zero or number case '0': // zero or number
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (COLON | COMMA | KEY)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation;
if (p < e) { if (p < e) {
if ((*p == '.' || *p == 'e' || *p == 'E')) { if (*p == '.') {
if (p + 1 == e || !isdigit(p[1])) {
return (struct DecodeJson){-1, "bad double"};
}
goto UseDubble;
} else if (*p == 'e' || *p == 'E') {
goto UseDubble; goto UseDubble;
} else if (isdigit(*p)) { } else if (isdigit(*p)) {
return (struct DecodeJson){-1, "unexpected octal"}; return (struct DecodeJson){-1, "unexpected octal"};
@ -138,8 +148,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
return (struct DecodeJson){1, p}; return (struct DecodeJson){1, p};
case '1' ... '9': // integer case '1' ... '9': // integer
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (COLON | COMMA | KEY)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation;
for (x = (c - '0') * d; p < e; ++p) { for (x = (c - '0') * d; p < e; ++p) {
c = *p & 255; c = *p & 255;
if (isdigit(c)) { if (isdigit(c)) {
@ -147,7 +156,12 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
__builtin_add_overflow(x, (c - '0') * d, &x)) { __builtin_add_overflow(x, (c - '0') * d, &x)) {
goto UseDubble; goto UseDubble;
} }
} else if (c == '.' || c == 'e' || c == 'E') { } else if (c == '.') {
if (p + 1 == e || !isdigit(p[1])) {
return (struct DecodeJson){-1, "bad double"};
}
goto UseDubble;
} else if (c == 'e' || c == 'E') {
goto UseDubble; goto UseDubble;
} else { } else {
break; break;
@ -159,15 +173,16 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
UseDubble: // number UseDubble: // number
lua_pushnumber(L, StringToDouble(a, e - a, &c)); lua_pushnumber(L, StringToDouble(a, e - a, &c));
DCHECK(c > 0, "paranoid avoiding infinite loop"); DCHECK(c > 0, "paranoid avoiding infinite loop");
if (a + c < e && (a[c] == 'e' || a[c] == 'E')) {
return (struct DecodeJson){-1, "bad exponent"};
}
return (struct DecodeJson){1, a + c}; return (struct DecodeJson){1, a + c};
case '[': // Array case '[': // Array
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (COLON | COMMA | KEY)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation; lua_newtable(L); // +1
lua_newtable(L); for (context = ARRAY, i = 0;;) {
i = 0; r = Parse(L, p, e, context, depth - 1); // +2
r = Parse(L, p, e, ARRAY_SINGLE | ARRAY_END, depth - 1);
for (;;) {
if (UNLIKELY(r.rc == -1)) { if (UNLIKELY(r.rc == -1)) {
lua_pop(L, 1); lua_pop(L, 1);
return r; return r;
@ -177,44 +192,44 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
break; break;
} }
lua_rawseti(L, -2, i++ + 1); lua_rawseti(L, -2, i++ + 1);
r = Parse(L, p, e, AFTER_VALUE | ARRAY_END, depth - 1); context = ARRAY | COMMA;
} }
if (!i) { if (!i) {
// we need this kludge so `[]` won't round-trip as `{}` // we need this kludge so `[]` won't round-trip as `{}`
lua_pushboolean(L, false); lua_pushboolean(L, false); // +2
lua_rawseti(L, -2, 0); lua_rawseti(L, -2, 0);
} }
return (struct DecodeJson){1, p}; return (struct DecodeJson){1, p};
case ']': case ']':
if (0 != (context & ARRAY_END)) { if (context & ARRAY) {
return (struct DecodeJson){0, p}; return (struct DecodeJson){0, p};
} else { } else {
return (struct DecodeJson){-1, "unexpected ']'"}; return (struct DecodeJson){-1, "unexpected ']'"};
} }
case '}': case '}':
if (0 != (context & OBJECT_END)) { if (context & OBJECT) {
return (struct DecodeJson){0, p}; return (struct DecodeJson){0, p};
} else { } else {
return (struct DecodeJson){-1, "unexpected '}'"}; return (struct DecodeJson){-1, "unexpected '}'"};
} }
case '{': // Object case '{': // Object
if (UNLIKELY(0 != (context & OBJECT_KEY))) goto BadObjectKey; if (context & (COLON | COMMA | KEY)) goto OnColonCommaKey;
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation; lua_newtable(L); // +1
lua_newtable(L); context = KEY | OBJECT;
r = Parse(L, p, e, OBJECT_KEY | OBJECT_END, depth - 1);
for (;;) { for (;;) {
r = Parse(L, p, e, context, depth - 1); // +2
if (r.rc == -1) { if (r.rc == -1) {
lua_pop(L, 1); lua_pop(L, 1);
return r; return r;
} }
p = r.p; p = r.p;
if (!r.rc) { if (!r.rc) {
break; return (struct DecodeJson){1, p};
} }
r = Parse(L, p, e, OBJECT_VAL, depth - 1); r = Parse(L, p, e, COLON, depth - 1); // +3
if (r.rc == -1) { if (r.rc == -1) {
lua_pop(L, 2); lua_pop(L, 2);
return r; return r;
@ -225,12 +240,11 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
p = r.p; p = r.p;
lua_settable(L, -3); lua_settable(L, -3);
r = Parse(L, p, e, OBJECT_KEY | AFTER_VALUE | OBJECT_END, depth - 1); context = KEY | COMMA | OBJECT;
} }
return (struct DecodeJson){1, p};
case '"': // string case '"': // string
if (UNLIKELY(0 != (context & (OBJECT_VAL | AFTER_VALUE)))) goto MissingPunctuation; if (context & (COLON | COMMA)) goto OnColonComma;
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
for (;;) { for (;;) {
if (UNLIKELY(p >= e)) { if (UNLIKELY(p >= e)) {
@ -252,7 +266,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
} }
continue; continue;
HandleEscape: HandleEscape:
if (UNLIKELY(p >= e)) { if (p >= e) {
goto UnexpectedEofString; goto UnexpectedEofString;
} }
switch ((c = *p++ & 255)) { switch ((c = *p++ & 255)) {
@ -331,7 +345,7 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
w[1] = 0200 | (c & 077); w[1] = 0200 | (c & 077);
i = 2; i = 2;
} else if (c <= 0xffff) { } else if (c <= 0xffff) {
if (UNLIKELY(IsSurrogate(c))) { if (IsSurrogate(c)) {
ReplacementCharacter: ReplacementCharacter:
c = 0xfffd; c = 0xfffd;
} }
@ -368,13 +382,9 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
luaL_pushresultsize(&b, 0); luaL_pushresultsize(&b, 0);
lua_pop(L, 1); lua_pop(L, 1);
return (struct DecodeJson){-1, reason}; return (struct DecodeJson){-1, reason};
default:
IllegalCharacter:
return (struct DecodeJson){-1, "illegal character"};
} }
} }
if (UNLIKELY(depth == TOP_LEVEL)) { if (depth == DEPTH) {
return (struct DecodeJson){0, 0}; return (struct DecodeJson){0, 0};
} else { } else {
return (struct DecodeJson){-1, "unexpected eof"}; return (struct DecodeJson){-1, "unexpected eof"};
@ -405,10 +415,9 @@ static struct DecodeJson Parse(struct lua_State *L, const char *p,
* @return r.p is string describing error if `rc < 0` * @return r.p is string describing error if `rc < 0`
*/ */
struct DecodeJson DecodeJson(struct lua_State *L, const char *p, size_t n) { struct DecodeJson DecodeJson(struct lua_State *L, const char *p, size_t n) {
int depth = TOP_LEVEL;
if (n == -1) n = p ? strlen(p) : 0; if (n == -1) n = p ? strlen(p) : 0;
if (lua_checkstack(L, depth * 4)) { if (lua_checkstack(L, DEPTH * 3 + LUA_MINSTACK)) {
return Parse(L, p, p + n, 0, depth); return Parse(L, p, p + n, 0, DEPTH);
} else { } else {
return (struct DecodeJson){-1, "can't set stack depth"}; return (struct DecodeJson){-1, "can't set stack depth"};
} }