| #include <cstdlib> | |
| #include <iostream> | |
| #include <vector> | |
| #include "fast_float/fast_float.h" | |
| int main_readme() { | |
| std::string const input = "+.1"; // not valid | |
| double result; | |
| fast_float::parse_options options{ | |
| fast_float::chars_format::json | | |
| fast_float::chars_format::allow_leading_plus}; // should be ignored | |
| auto answer = fast_float::from_chars_advanced( | |
| input.data(), input.data() + input.size(), result, options); | |
| if (answer.ec == std::errc()) { | |
| std::cerr << "should have failed\n"; | |
| return EXIT_FAILURE; | |
| } | |
| return EXIT_SUCCESS; | |
| } | |
| int main_readme2() { | |
| std::string const input = "inf"; // not valid in JSON | |
| double result; | |
| fast_float::parse_options options{ | |
| fast_float::chars_format::json | | |
| fast_float::chars_format::allow_leading_plus}; // should be ignored | |
| auto answer = fast_float::from_chars_advanced( | |
| input.data(), input.data() + input.size(), result, options); | |
| if (answer.ec == std::errc()) { | |
| std::cerr << "should have failed\n"; | |
| return EXIT_FAILURE; | |
| } | |
| return EXIT_SUCCESS; | |
| } | |
| int main_readme3() { | |
| std::string const input = | |
| "inf"; // not valid in JSON but we allow it with json_or_infnan | |
| double result; | |
| fast_float::parse_options options{ | |
| fast_float::chars_format::json_or_infnan | | |
| fast_float::chars_format::allow_leading_plus}; // should be ignored | |
| auto answer = fast_float::from_chars_advanced( | |
| input.data(), input.data() + input.size(), result, options); | |
| if (answer.ec != std::errc() || (!std::isinf(result))) { | |
| std::cerr << "should have parsed infinity\n"; | |
| return EXIT_FAILURE; | |
| } | |
| return EXIT_SUCCESS; | |
| } | |
| struct ExpectedResult { | |
| double value; | |
| std::string junk_chars; | |
| }; | |
| struct AcceptedValue { | |
| std::string input; | |
| ExpectedResult expected; | |
| }; | |
| struct RejectReason { | |
| fast_float::parse_error error; | |
| intptr_t location_offset; | |
| }; | |
| struct RejectedValue { | |
| std::string input; | |
| RejectReason reason; | |
| }; | |
| int main() { | |
| std::vector<AcceptedValue> const accept{ | |
| {"-0.2", {-0.2, ""}}, | |
| {"0.02", {0.02, ""}}, | |
| {"0.002", {0.002, ""}}, | |
| {"1e+0000", {1., ""}}, | |
| {"0e-2", {0., ""}}, | |
| {"1e", {1., "e"}}, | |
| {"1e+", {1., "e+"}}, | |
| {"inf", {std::numeric_limits<double>::infinity(), ""}}}; | |
| std::vector<RejectedValue> const reject{ | |
| {"-.2", {fast_float::parse_error::missing_integer_after_sign, 1}}, | |
| {"00.02", {fast_float::parse_error::leading_zeros_in_integer_part, 0}}, | |
| {"0.e+1", {fast_float::parse_error::no_digits_in_fractional_part, 2}}, | |
| {"00.e+1", {fast_float::parse_error::leading_zeros_in_integer_part, 0}}, | |
| {".25", {fast_float::parse_error::no_digits_in_integer_part, 0}}, | |
| // The following cases already start as invalid JSON, so they are | |
| // handled as trailing junk and the error is for not having digits in the | |
| // empty string before the invalid token. | |
| {"+0.25", {fast_float::parse_error::no_digits_in_integer_part, 0}}, | |
| {"inf", {fast_float::parse_error::no_digits_in_integer_part, 0}}, | |
| {"nan(snan)", {fast_float::parse_error::no_digits_in_integer_part, 0}}}; | |
| for (std::size_t i = 0; i < accept.size(); ++i) { | |
| auto const &s = accept[i].input; | |
| auto const &expected = accept[i].expected; | |
| double result; | |
| auto answer = | |
| fast_float::from_chars(s.data(), s.data() + s.size(), result, | |
| fast_float::chars_format::json_or_infnan); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "json fmt rejected valid json " << s << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| if (result != expected.value) { | |
| std::cerr << "json fmt gave wrong result " << s << " (expected " | |
| << expected.value << " got " << result << ")" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| if (std::string(answer.ptr) != expected.junk_chars) { | |
| std::cerr << "json fmt has wrong trailing characters " << s | |
| << " (expected " << expected.junk_chars << " got " << answer.ptr | |
| << ")" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| for (std::size_t i = 0; i < reject.size(); ++i) { | |
| auto const &s = reject[i].input; | |
| double result; | |
| auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, | |
| fast_float::chars_format::json); | |
| if (answer.ec == std::errc()) { | |
| std::cerr << "json fmt accepted invalid json " << s << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| for (std::size_t i = 0; i < reject.size(); ++i) { | |
| auto const &f = reject[i].input; | |
| auto const &expected_reason = reject[i].reason; | |
| auto answer = fast_float::parse_number_string<true>( | |
| f.data(), f.data() + f.size(), | |
| fast_float::parse_options( | |
| fast_float::chars_format::json | | |
| fast_float::chars_format::allow_leading_plus)); // should be ignored | |
| if (answer.valid) { | |
| std::cerr << "json parse accepted invalid json " << f << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| if (answer.error != expected_reason.error) { | |
| std::cerr << "json parse failure had invalid error reason " << f | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| intptr_t error_location = answer.lastmatch - f.data(); | |
| if (error_location != expected_reason.location_offset) { | |
| std::cerr << "json parse failure had invalid error location " << f | |
| << " (expected " << expected_reason.location_offset << " got " | |
| << error_location << ")" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| if (main_readme() != EXIT_SUCCESS) { | |
| return EXIT_FAILURE; | |
| } | |
| if (main_readme2() != EXIT_SUCCESS) { | |
| return EXIT_FAILURE; | |
| } | |
| #ifndef __FAST_MATH__ | |
| if (main_readme3() != EXIT_SUCCESS) { | |
| return EXIT_FAILURE; | |
| } | |
| #endif | |
| return EXIT_SUCCESS; | |
| } |