mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-24 06:12:27 +00:00
Make JSON parser perfectly conformant
This commit is contained in:
parent
60164a7266
commit
b707fca77a
7 changed files with 260 additions and 183 deletions
|
@ -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.] '))
|
||||||
|
|
|
@ -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.] '))
|
|
|
@ -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}}}')
|
||||||
|
|
15
third_party/lua/luaencodejsondata.c
vendored
15
third_party/lua/luaencodejsondata.c
vendored
|
@ -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);
|
||||||
|
|
8
third_party/lua/luaencodeluadata.c
vendored
8
third_party/lua/luaencodeluadata.c
vendored
|
@ -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) {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
123
tool/net/ljson.c
123
tool/net/ljson.c
|
@ -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"};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue