mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 07:04:39 +00:00
Merge pull request #306 from p-ranav/feature/binary_number_parsing
Added support for binary notation, e.g., 0b101
This commit is contained in:
commit
af442b4da0
@ -148,6 +148,7 @@ constexpr bool standard_unsigned_integer<unsigned long long int> = true;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
constexpr int radix_2 = 2;
|
||||||
constexpr int radix_8 = 8;
|
constexpr int radix_8 = 8;
|
||||||
constexpr int radix_10 = 10;
|
constexpr int radix_10 = 10;
|
||||||
constexpr int radix_16 = 16;
|
constexpr int radix_16 = 16;
|
||||||
@ -183,12 +184,28 @@ constexpr bool starts_with(std::basic_string_view<CharT, Traits> prefix,
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class chars_format {
|
enum class chars_format {
|
||||||
scientific = 0x1,
|
scientific = 0xf1,
|
||||||
fixed = 0x2,
|
fixed = 0xf2,
|
||||||
hex = 0x4,
|
hex = 0xf4,
|
||||||
|
binary = 0xf8,
|
||||||
general = fixed | scientific
|
general = fixed | scientific
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ConsumeBinaryPrefixResult {
|
||||||
|
bool is_binary;
|
||||||
|
std::string_view rest;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto consume_binary_prefix(std::string_view s)
|
||||||
|
-> ConsumeBinaryPrefixResult {
|
||||||
|
if (starts_with(std::string_view{"0b"}, s) ||
|
||||||
|
starts_with(std::string_view{"0B"}, s)) {
|
||||||
|
s.remove_prefix(2);
|
||||||
|
return {true, s};
|
||||||
|
}
|
||||||
|
return {false, s};
|
||||||
|
}
|
||||||
|
|
||||||
struct ConsumeHexPrefixResult {
|
struct ConsumeHexPrefixResult {
|
||||||
bool is_hexadecimal;
|
bool is_hexadecimal;
|
||||||
std::string_view rest;
|
std::string_view rest;
|
||||||
@ -232,6 +249,15 @@ template <class T, auto Param = 0> struct parse_number {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T> struct parse_number<T, radix_2> {
|
||||||
|
auto operator()(std::string_view s) -> T {
|
||||||
|
if (auto [ok, rest] = consume_binary_prefix(s); ok) {
|
||||||
|
return do_from_chars<T, radix_2>(rest);
|
||||||
|
}
|
||||||
|
throw std::invalid_argument{"pattern not found"};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <class T> struct parse_number<T, radix_16> {
|
template <class T> struct parse_number<T, radix_16> {
|
||||||
auto operator()(std::string_view s) -> T {
|
auto operator()(std::string_view s) -> T {
|
||||||
if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
|
if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
|
||||||
@ -280,6 +306,19 @@ template <class T> struct parse_number<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto [ok_binary, rest_binary] = consume_binary_prefix(s);
|
||||||
|
if (ok_binary) {
|
||||||
|
try {
|
||||||
|
return do_from_chars<T, radix_2>(rest_binary);
|
||||||
|
} catch (const std::invalid_argument &err) {
|
||||||
|
throw std::invalid_argument("Failed to parse '" + std::string(s) +
|
||||||
|
"' as binary: " + err.what());
|
||||||
|
} catch (const std::range_error &err) {
|
||||||
|
throw std::range_error("Failed to parse '" + std::string(s) +
|
||||||
|
"' as binary: " + err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (starts_with("0"sv, s)) {
|
if (starts_with("0"sv, s)) {
|
||||||
try {
|
try {
|
||||||
return do_from_chars<T, radix_8>(rest);
|
return do_from_chars<T, radix_8>(rest);
|
||||||
@ -342,6 +381,10 @@ template <class T> struct parse_number<T, chars_format::general> {
|
|||||||
throw std::invalid_argument{
|
throw std::invalid_argument{
|
||||||
"chars_format::general does not parse hexfloat"};
|
"chars_format::general does not parse hexfloat"};
|
||||||
}
|
}
|
||||||
|
if (auto r = consume_binary_prefix(s); r.is_binary) {
|
||||||
|
throw std::invalid_argument{
|
||||||
|
"chars_format::general does not parse binfloat"};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return do_strtod<T>(s);
|
return do_strtod<T>(s);
|
||||||
@ -360,6 +403,9 @@ template <class T> struct parse_number<T, chars_format::hex> {
|
|||||||
if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) {
|
if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) {
|
||||||
throw std::invalid_argument{"chars_format::hex parses hexfloat"};
|
throw std::invalid_argument{"chars_format::hex parses hexfloat"};
|
||||||
}
|
}
|
||||||
|
if (auto r = consume_binary_prefix(s); r.is_binary) {
|
||||||
|
throw std::invalid_argument{"chars_format::hex does not parse binfloat"};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return do_strtod<T>(s);
|
return do_strtod<T>(s);
|
||||||
@ -373,12 +419,30 @@ template <class T> struct parse_number<T, chars_format::hex> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T> struct parse_number<T, chars_format::binary> {
|
||||||
|
auto operator()(std::string const &s) -> T {
|
||||||
|
if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
|
||||||
|
throw std::invalid_argument{
|
||||||
|
"chars_format::binary does not parse hexfloat"};
|
||||||
|
}
|
||||||
|
if (auto r = consume_binary_prefix(s); !r.is_binary) {
|
||||||
|
throw std::invalid_argument{"chars_format::binary parses binfloat"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return do_strtod<T>(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <class T> struct parse_number<T, chars_format::scientific> {
|
template <class T> struct parse_number<T, chars_format::scientific> {
|
||||||
auto operator()(std::string const &s) -> T {
|
auto operator()(std::string const &s) -> T {
|
||||||
if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
|
if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
|
||||||
throw std::invalid_argument{
|
throw std::invalid_argument{
|
||||||
"chars_format::scientific does not parse hexfloat"};
|
"chars_format::scientific does not parse hexfloat"};
|
||||||
}
|
}
|
||||||
|
if (auto r = consume_binary_prefix(s); r.is_binary) {
|
||||||
|
throw std::invalid_argument{
|
||||||
|
"chars_format::scientific does not parse binfloat"};
|
||||||
|
}
|
||||||
if (s.find_first_of("eE") == std::string::npos) {
|
if (s.find_first_of("eE") == std::string::npos) {
|
||||||
throw std::invalid_argument{
|
throw std::invalid_argument{
|
||||||
"chars_format::scientific requires exponent part"};
|
"chars_format::scientific requires exponent part"};
|
||||||
@ -402,6 +466,10 @@ template <class T> struct parse_number<T, chars_format::fixed> {
|
|||||||
throw std::invalid_argument{
|
throw std::invalid_argument{
|
||||||
"chars_format::fixed does not parse hexfloat"};
|
"chars_format::fixed does not parse hexfloat"};
|
||||||
}
|
}
|
||||||
|
if (auto r = consume_binary_prefix(s); r.is_binary) {
|
||||||
|
throw std::invalid_argument{
|
||||||
|
"chars_format::fixed does not parse binfloat"};
|
||||||
|
}
|
||||||
if (s.find_first_of("eE") != std::string::npos) {
|
if (s.find_first_of("eE") != std::string::npos) {
|
||||||
throw std::invalid_argument{
|
throw std::invalid_argument{
|
||||||
"chars_format::fixed does not parse exponent part"};
|
"chars_format::fixed does not parse exponent part"};
|
||||||
@ -629,6 +697,9 @@ public:
|
|||||||
} else if constexpr (is_one_of(Shape, 'u') &&
|
} else if constexpr (is_one_of(Shape, 'u') &&
|
||||||
details::standard_unsigned_integer<T>) {
|
details::standard_unsigned_integer<T>) {
|
||||||
action(details::parse_number<T, details::radix_10>());
|
action(details::parse_number<T, details::radix_10>());
|
||||||
|
} else if constexpr (is_one_of(Shape, 'b') &&
|
||||||
|
details::standard_unsigned_integer<T>) {
|
||||||
|
action(details::parse_number<T, details::radix_2>());
|
||||||
} else if constexpr (is_one_of(Shape, 'o') &&
|
} else if constexpr (is_one_of(Shape, 'o') &&
|
||||||
details::standard_unsigned_integer<T>) {
|
details::standard_unsigned_integer<T>) {
|
||||||
action(details::parse_number<T, details::radix_8>());
|
action(details::parse_number<T, details::radix_8>());
|
||||||
|
@ -209,6 +209,49 @@ TEST_CASE_TEMPLATE("Parse integer argument of any format" * test_suite("scan"),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_TEMPLATE("Parse a binary argument" * test_suite("scan"), T, uint8_t,
|
||||||
|
uint16_t, uint32_t, uint64_t) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("-n").scan<'b', T>();
|
||||||
|
|
||||||
|
SUBCASE("with binary digit") {
|
||||||
|
program.parse_args({"test", "-n", "0b101"});
|
||||||
|
REQUIRE(program.get<T>("-n") == 0b101);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("minus sign produces an optional argument") {
|
||||||
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-0b101"}),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("plus sign is not allowed") {
|
||||||
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+0b101"}),
|
||||||
|
std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("does not fit") {
|
||||||
|
REQUIRE_THROWS_AS(
|
||||||
|
program.parse_args(
|
||||||
|
{"test", "-n",
|
||||||
|
"0b111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"11111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"1111111111111111111111111111111111111111111111111111111111111111"
|
||||||
|
"1"}),
|
||||||
|
std::range_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define FLOAT_G(t, literal) \
|
#define FLOAT_G(t, literal) \
|
||||||
([] { \
|
([] { \
|
||||||
if constexpr (std::is_same_v<t, float>) \
|
if constexpr (std::is_same_v<t, float>) \
|
||||||
|
Loading…
Reference in New Issue
Block a user