poafloc

Parser Of Arguments For Lines Of Commands
git clone git://git.dimitrijedobrota.com/poafloc.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

commit 1d6c74943aa185bcde7b33da4cf27d8fcf647e95
parent 44de79c18378b7ed440c782ce1886bf3b2077e91
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Fri, 23 May 2025 22:31:20 +0200

Restrain character set, better saving

Diffstat:
M include/poafloc/poafloc.hpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ------------------

1 files changed, 81 insertions(+), 22 deletions(-)


diff --git a/ include/poafloc/poafloc.hpp b/ include/poafloc/poafloc.hpp

@@ -89,16 +89,71 @@ namespace detail

struct option_base
{
using value_type = std::size_t;
using return_type = std::optional<value_type>;
using optional_type = std::optional<value_type>;

static constexpr const std::size_t size = 256;
static constexpr const auto sentinel = value_type {0xFFFFFFFFFFFFFFFF};

static constexpr bool is_valid(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|| (c >= '0' && c <= '9');
}

static constexpr const auto size_char = 256;
static constexpr const auto size = []()
{
std::size_t count = 0;

for (std::size_t idx = 0; idx < size_char; ++idx) {
const char c = static_cast<char>(idx);
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|| (c >= '0' && c <= '9'))
{
count++;
continue;
}
}
return count;
}();

static constexpr const auto mapping = []()
{
std::array<std::size_t, size_char> res = {};
std::size_t count = 0;

for (std::size_t idx = 0; idx < std::size(res); ++idx) {
const char c = static_cast<char>(idx);
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|| (c >= '0' && c <= '9'))
{
res[idx] = count++;
continue;
}

res[idx] = sentinel;
}

return res;
}();

using container_type = std::array<value_type, size>;

static auto convert(char chr)
{
return static_cast<container_type::size_type>(chr);
if (!is_valid(chr)) {
invalid_char(chr);
}

return mapping[static_cast<container_type::size_type>(
static_cast<unsigned char>(chr)
)];
}

[[noreturn]] static void invalid_char(char c)
{
throw std::runtime_error {
std::format("Invalid char in option: {}", c),
};
}
};

@@ -124,7 +179,7 @@ public:

return true;
}

[[nodiscard]] return_type get(char chr) const
[[nodiscard]] optional_type get(char chr) const
{
if (!has(chr)) {
return {};

@@ -170,7 +225,7 @@ class option_long : option_base

return true;
}

static return_type get(const trie_t& trie, std::string_view key)
static optional_type get(const trie_t& trie, std::string_view key)
{
const trie_t* crnt = &trie;

@@ -198,7 +253,7 @@ public:

return trie_t::set(m_trie, opt, idx);
}

[[nodiscard]] return_type get(std::string_view opt) const
[[nodiscard]] optional_type get(std::string_view opt) const
{
return trie_t::get(m_trie, opt);
}

@@ -221,26 +276,26 @@ class parser

std::string str;

while (std::getline(istr, str, ' ')) {
if (str.size() < 2 || str[0] != '-') {
if (std::size(str) < 2 || str[0] != '-') {
continue;
}

if (str[1] != '-') {
if (str.size() != 2) {
if (std::size(str) != 2) {
throw std::runtime_error {std::format(
"Short option requires one character: {}", str.substr(1)
)};
}

const auto opt = str[1];
if (!m_opt_short.set(opt, m_options.size())) {
if (!m_opt_short.set(opt, std::size(m_options))) {
throw std::runtime_error {
std::format("Duplicate short option: {}", opt)
};
}
} else {
const auto opt = str.substr(2);
if (!m_opt_long.set(opt, m_options.size())) {
if (!m_opt_long.set(opt, std::size(m_options))) {
throw std::runtime_error {
std::format("Duplicate long option: {}", opt)
};

@@ -270,7 +325,7 @@ class parser

}

template<class T>
[[noreturn]] void missing_argument(T opt) const
[[noreturn]] static void missing_argument(T opt)
{
throw std::runtime_error {
std::format("Missing argument for option: {}", opt)

@@ -278,14 +333,22 @@ class parser

}

template<class T>
[[noreturn]] void unknown_option(T opt) const
[[noreturn]] static void superfluous_argument(T opt)
{
throw std::runtime_error {
std::format("Option doesn't require an argument: {}", opt)
};
}

template<class T>
[[noreturn]] static void unknown_option(T opt)
{
throw std::runtime_error {
std::format("Unknown option: {}", opt),
};
}

[[noreturn]] void unhandled_positional(std::string_view arg) const
[[noreturn]] static void unhandled_positional(std::string_view arg)
{
throw std::runtime_error {
std::format("Unhandled positional arg: {}", arg),

@@ -361,16 +424,12 @@ class parser

const auto option = get_option(opt);

if (!option.argument()) {
throw std::runtime_error {
std::format("Option doesn't require an argumente: {}", opt),
};
superfluous_argument(opt);
}

const auto arg = mix.substr(equal + 1);
if (arg.empty()) {
throw std::runtime_error {
std::format("Option requires an argumente: {}", opt),
};
missing_argument(opt);
}

option(record, arg);

@@ -396,7 +455,7 @@ public:

{
std::size_t arg_idx = 0;

for (; arg_idx != args.size(); ++arg_idx) {
for (; arg_idx != std::size(args); ++arg_idx) {
const auto arg_raw = args[arg_idx];

if (arg_raw.size() < 2) {

@@ -415,7 +474,7 @@ public:

if (arg_raw[1] != '-') {
// short options
auto arg = arg_raw.substr(1);
for (std::size_t opt_idx = 0; opt_idx < arg.size(); opt_idx++) {
for (std::size_t opt_idx = 0; opt_idx < std::size(arg); opt_idx++) {
const auto res = handle_short(
record,
arg[opt_idx],

@@ -462,7 +521,7 @@ public:

}
}

for (; arg_idx != args.size(); ++arg_idx) {
for (; arg_idx != std::size(args); ++arg_idx) {
unhandled_positional(args[arg_idx]);
}
}