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 0774b9aeb57b54b263db4fa6d28e9134da86922e
parent 6bae612d291e8d715ea35b03873cb846928eca1d
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Wed, 4 Jun 2025 15:22:07 +0200

Modernize with based goodness

Diffstat:
M example/example.cpp | ++ ---
M include/poafloc/error.hpp | +
M include/poafloc/poafloc.hpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++ ------------------------------
M source/help.cpp | ++ --
M source/option.cpp | ++++++++++++++++++++++++++++++++++ ------------------------------------------------
M source/poafloc.cpp | +++++++ -----
M test/source/parser.cpp | +++ -

7 files changed, 118 insertions(+), 103 deletions(-)


diff --git a/ example/example.cpp b/ example/example.cpp

@@ -72,7 +72,7 @@ int main()

},
};

std::vector<std::string_view> cmd_args {
const std::vector<std::string_view> cmd_args {
"--value=150",
"-m1.34",
"--name",

@@ -86,11 +86,10 @@ int main()

arguments args;

program.help_long();
/*

std::cout << args << '\n';
program(args, cmd_args);
std::cout << args << '\n';
*/

return 0;
}

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

@@ -4,6 +4,7 @@

#include <stdexcept>

#include <based/enum/enum.hpp>
#include <based/format.hpp>
#include <based/types/types.hpp>
#include <based/utility/forward.hpp>

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

@@ -9,11 +9,13 @@

#include <sstream>
#include <string>
#include <string_view>
#include <vector>

#include <based/concepts/is/same.hpp>
#include <based/container/array.hpp>
#include <based/container/vector.hpp>
#include <based/trait/integral_constant.hpp>
#include <based/trait/remove/cvref.hpp>
#include <based/types/literals.hpp>
#include <based/types/types.hpp>
#include <based/utility/forward.hpp>
#include <based/utility/move.hpp>

@@ -23,6 +25,8 @@

namespace poafloc
{

using namespace based::literals; // NOLINT(*namespace*)

namespace detail
{

@@ -41,10 +45,13 @@ public:

private:
using func_t = std::function<void(void*, std::string_view)>;

using size_type = based::u64;

type m_type;
func_t m_func;
std::vector<char> m_opts_short;
std::vector<std::string> m_opts_long;

based::vector<based::character, size_type> m_opts_short;
based::vector<std::string, size_type> m_opts_long;

std::string m_name;
std::string m_message;

@@ -238,9 +245,9 @@ struct is_positional<argument<Record, Type>> : based::true_type

template<class T>
concept IsPositional = is_positional<T>::value;

class positional_base : public std::vector<detail::option>
class positional_base : public based::vector<detail::option, based::u64>
{
using base = std::vector<option>;
using base = based::vector<option, based::u64>;

protected:
template<detail::IsPositional Arg, detail::IsPositional... Args>

@@ -250,7 +257,7 @@ protected:

based::forward<Args>(args)...,
})
{
for (std::size_t i = 0; i < base::size() - 1; i++) {
for (size_type i = 0_u; i + 1_u8 < base::size(); i++) {
if (base::operator[](i).get_type() == option::type::list) {
throw runtime_error("invalid positional constructor");
}

@@ -310,9 +317,9 @@ struct is_option<list<Record, Type>> : based::true_type

template<class T>
concept IsOption = is_option<T>::value;

class group_base : public std::vector<detail::option>
class group_base : public based::vector<detail::option, based::u64>
{
using base = std::vector<option>;
using base = based::vector<option, based::u64>;

std::string m_name;

@@ -359,57 +366,78 @@ namespace detail


class option_short
{
static constexpr const auto sentinel = std::size_t {0xFFFFFFFFFFFFFFFF};
using size_type = based::u8;
using value_type = based::u64;
using opt_type = std::optional<value_type>;

static constexpr auto size = static_cast<std::size_t>(2 * 26);
std::array<std::size_t, size> m_opts = {};
static constexpr auto size = size_type(2_u * 26_u);
static constexpr const auto sentinel = based::limits<value_type>::max;

[[nodiscard]] bool has(char chr) const;
using array_type = based::array<value_type, size_type, size>;
array_type m_opts = {};

[[nodiscard]] bool has(based::character chr) const;

public:
option_short() { m_opts.fill(sentinel); }

static constexpr bool is_valid(char chr);
[[nodiscard]] bool set(char chr, std::size_t idx);
[[nodiscard]] std::optional<std::size_t> get(char chr) const;
static constexpr bool is_valid(based::character chr);
[[nodiscard]] bool set(based::character chr, value_type value);
[[nodiscard]] opt_type get(based::character chr) const;
};

class trie_t
{
static constexpr const auto sentinel = std::size_t {0xFFFFFFFFFFFFFFFF};
using size_type = based::u8;
using value_type = based::u64;
using opt_type = std::optional<value_type>;

static constexpr auto size = size_type(26_u + 10_u);
static constexpr const auto sentinel = based::limits<value_type>::max;

using ptr_type = std::unique_ptr<trie_t>;
using array_type = based::array<ptr_type, size_type, size>;
array_type m_children = {};

static constexpr auto size = static_cast<std::size_t>(26ULL + 10ULL);
std::array<std::unique_ptr<trie_t>, size> m_children = {};
value_type m_value = sentinel;
based::u8 m_count = 0_u8;

std::size_t m_value = sentinel;
std::size_t m_count = 0;
bool m_terminal = false;
trie_t* m_parent = nullptr;

static constexpr auto map(char chr);
static constexpr auto map(based::character chr);

public:
static bool set(trie_t& trie, std::string_view key, std::size_t value);
static std::optional<std::size_t> get(
const trie_t& trie, std::string_view key
);
explicit trie_t(trie_t* parent = nullptr)
: m_parent(parent)
{
}

static bool set(trie_t& trie, std::string_view key, value_type value);
static opt_type get(const trie_t& trie, std::string_view key);
};

class option_long
{
using value_type = based::u64;
using opt_type = std::optional<value_type>;

trie_t m_trie;

public:
static constexpr bool is_valid(std::string_view opt);
[[nodiscard]] bool set(std::string_view opt, std::size_t idx);
[[nodiscard]] std::optional<std::size_t> get(std::string_view opt) const;
[[nodiscard]] bool set(std::string_view opt, value_type idx);
[[nodiscard]] opt_type get(std::string_view opt) const;
};

class parser_base
{
std::vector<option> m_options;
using size_type = based::u64;

based::vector<option, size_type> m_options;

using group_t = std::pair<std::size_t, std::string>;
std::vector<group_t> m_groups;
using group_type = std::pair<size_type, std::string>;
based::vector<group_type, size_type> m_groups;

positional_base m_pos;
option_short m_opt_short;

@@ -417,15 +445,15 @@ class parser_base


void process(const option& option);

[[nodiscard]] const option& get_option(char opt) const;
[[nodiscard]] const option& get_option(based::character opt) const;
[[nodiscard]] const option& get_option(std::string_view opt) const;

using next_t = std::span<std::string_view>;
using next_t = std::span<const std::string_view>;

next_t hdl_long_opt(void* record, std::string_view arg, next_t next) const;
next_t hdl_short_opts(void* record, std::string_view arg, next_t next) const;
next_t hdl_short_opt(
void* record, char opt, std::string_view rest, next_t next
void* record, based::character opt, std::string_view rest, next_t next
) const;

protected:

@@ -442,7 +470,7 @@ protected:

: m_pos(based::forward<decltype(positional)>(positional))
{
m_options.reserve(m_options.size() + (groups.size() + ...));
m_groups.reserve(sizeof...(groups));
m_groups.reserve(size_type::underlying_cast(sizeof...(groups)));

const auto process = [&](const auto& group)
{

@@ -455,7 +483,7 @@ protected:

}

void operator()(void* record, int argc, const char** argv);
void operator()(void* record, std::span<std::string_view> args);
void operator()(void* record, std::span<const std::string_view> args);

void help_long() const;
void help_short() const;

@@ -497,7 +525,7 @@ struct parser : detail::parser_base

parser_base::operator()(&record, argc, argv);
}

void operator()(Record& record, std::span<std::string_view> args)
void operator()(Record& record, std::span<const std::string_view> args)
{
parser_base::operator()(&record, args);
}

diff --git a/ source/help.cpp b/ source/help.cpp

@@ -18,7 +18,7 @@ void parser_base::help_long() const

}
std::cerr << '\n';

std::size_t idx = 0;
auto idx = size_type(0_u);
for (const auto& [end_idx, name] : m_groups) {
std::cerr << std::format("\n{}:\n", name);
while (idx < end_idx) {

@@ -27,7 +27,7 @@ void parser_base::help_long() const


line += " ";
for (const auto opt_short : option.opts_short()) {
line += std::format(" -{},", opt_short);
line += std::format(" -{},", opt_short.chr());
}
for (const auto& opt_long : option.opts_long()) {
switch (option.get_type()) {

diff --git a/ source/option.cpp b/ source/option.cpp

@@ -1,37 +1,37 @@

#include <algorithm>

#include <based/char/character.hpp>
#include <based/char/is/alpha.hpp>
#include <based/char/is/alpha_lower.hpp>
#include <based/char/is/digit.hpp>
#include <based/char/mapper.hpp>
#include <based/functional/predicate/not_null.hpp>
#include <based/trait/iterator.hpp>

#include "poafloc/error.hpp"
#include "poafloc/poafloc.hpp"

namespace
{

constexpr bool is_digit(char c)
{
return c >= '0' && c <= '9';
}
constexpr bool is_alpha_lower(char c)
{
return c >= 'a' && c <= 'z';
}
constexpr bool is_alpha_upper(char c)
struct short_map
{
return c >= 'A' && c <= 'Z';
}

constexpr auto convert(char chr)
{
return static_cast<size_t>(static_cast<unsigned char>(chr));
}
constexpr bool operator()(based::character chr) const
{
return based::is_alpha(chr);
}
};

constexpr auto map(char chr)
struct long_map
{
if (is_alpha_lower(chr)) {
return convert(chr) - convert('a');
constexpr bool operator()(based::character chr) const
{
return based::is_alpha_lower(chr) || based::is_digit(chr);
}
};

return convert(chr) - convert('A') + 26; // NOLINT(*magic*)
}
using short_mapper = based::mapper<short_map>;
using long_mapper = based::mapper<long_map>;

} // namespace

@@ -39,17 +39,17 @@ constexpr auto map(char chr)

namespace poafloc::detail
{

constexpr bool option_short::is_valid(char chr)
constexpr bool option_short::is_valid(based::character chr)
{
return is_alpha_lower(chr) || is_alpha_upper(chr);
return short_mapper::predicate(chr);
}

[[nodiscard]] bool option_short::has(char chr) const
bool option_short::has(based::character chr) const
{
return m_opts[map(chr)] != sentinel;
return m_opts[short_mapper::map(chr)] != sentinel;
}

[[nodiscard]] bool option_short::set(char chr, std::size_t idx)
bool option_short::set(based::character chr, value_type value)
{
if (!is_valid(chr)) {
throw error<error_code::invalid_option>(chr);

@@ -59,11 +59,11 @@ constexpr bool option_short::is_valid(char chr)

return false;
}

m_opts[map(chr)] = idx;
m_opts[short_mapper::map(chr)] = value;
return true;
}

[[nodiscard]] std::optional<std::size_t> option_short::get(char chr) const
option_short::opt_type option_short::get(based::character chr) const
{
if (!is_valid(chr)) {
throw error<error_code::invalid_option>(chr);

@@ -73,7 +73,7 @@ constexpr bool option_short::is_valid(char chr)

return {};
}

return m_opts[map(chr)];
return m_opts[short_mapper::map(chr)];
}

} // namespace poafloc::detail

@@ -82,16 +82,7 @@ constexpr bool option_short::is_valid(char chr)

namespace poafloc::detail
{

constexpr auto trie_t::map(char chr)
{
if (is_alpha_lower(chr)) {
return convert(chr) - convert('a');
}

return convert(chr) - convert('0') + 26; // NOLINT(*magic*)
}

bool trie_t::set(trie_t& trie, std::string_view key, std::size_t value)
bool trie_t::set(trie_t& trie, std::string_view key, value_type value)
{
trie_t* crnt = &trie;
for (const auto c : key) {

@@ -100,9 +91,9 @@ bool trie_t::set(trie_t& trie, std::string_view key, std::size_t value)

crnt->m_value = value;
}

const auto idx = map(c);
const auto idx = long_mapper::map(c);
if (crnt->m_children[idx] == nullptr) {
crnt->m_children[idx] = std::make_unique<trie_t>();
crnt->m_children[idx] = std::make_unique<trie_t>(nullptr);
}
crnt = crnt->m_children[idx].get();
}

@@ -116,25 +107,24 @@ bool trie_t::set(trie_t& trie, std::string_view key, std::size_t value)

return true;
}

std::optional<std::size_t> trie_t::get(const trie_t& trie, std::string_view key)
trie_t::opt_type trie_t::get(const trie_t& trie, std::string_view key)
{
const trie_t* crnt = &trie;

for (const auto c : key) {
const auto idx = map(c);
const auto idx = long_mapper::map(c);
if (crnt->m_children[idx] == nullptr) {
return {};
}
crnt = crnt->m_children[idx].get();
}

if (crnt->m_terminal || crnt->m_count == 1) {
if (crnt->m_terminal || crnt->m_count == 1_u8) {
return crnt->m_value;
}

return {};
}

} // namespace poafloc::detail

// option_long

@@ -143,17 +133,11 @@ namespace poafloc::detail


constexpr bool option_long::is_valid(std::string_view opt)
{
return is_alpha_lower(opt.front())
&& std::ranges::all_of(
opt,
[](const char chr)
{
return is_alpha_lower(chr) || is_digit(chr);
}
);
return based::is_alpha_lower(opt.front())
&& std::ranges::all_of(opt, long_mapper::predicate);
}

[[nodiscard]] bool option_long::set(std::string_view opt, std::size_t idx)
bool option_long::set(std::string_view opt, value_type idx)
{
if (!is_valid(opt)) {
throw error<error_code::invalid_option>(opt);

@@ -162,8 +146,7 @@ constexpr bool option_long::is_valid(std::string_view opt)

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

[[nodiscard]] std::optional<std::size_t> option_long::get(std::string_view opt
) const
option_long::opt_type option_long::get(std::string_view opt) const
{
if (!is_valid(opt)) {
throw error<error_code::invalid_option>(opt);

diff --git a/ source/poafloc.cpp b/ source/poafloc.cpp

@@ -12,7 +12,7 @@ constexpr bool is_option(std::string_view arg)

return arg.starts_with("-");
}

constexpr bool is_next_option(std::span<std::string_view> args)
constexpr bool is_next_option(std::span<const std::string_view> args)
{
return args.empty() || is_option(args.front());
}

@@ -76,7 +76,9 @@ void parser_base::operator()(void* record, int argc, const char** argv)

operator()(record, args);
}

void parser_base::operator()(void* record, std::span<std::string_view> args)
void parser_base::operator()(
void* record, std::span<const std::string_view> args
)
{
std::size_t arg_idx = 0;
bool is_term = false;

@@ -105,7 +107,7 @@ void parser_base::operator()(void* record, std::span<std::string_view> args)

arg_idx = std::size(args) - std::size(res);
}

std::size_t count = 0;
auto count = 0_u64;
while (arg_idx != std::size(args)) {
const auto arg = args[arg_idx++];
if (!is_term && arg == "--") {

@@ -133,7 +135,7 @@ void parser_base::operator()(void* record, std::span<std::string_view> args)

}

parser_base::next_t parser_base::hdl_short_opt(
void* record, char opt, std::string_view rest, next_t next
void* record, based::character opt, std::string_view rest, next_t next
) const
{
const auto option = get_option(opt);

@@ -240,7 +242,7 @@ parser_base::next_t parser_base::hdl_long_opt(

return next;
}

[[nodiscard]] const option& parser_base::get_option(char opt) const
[[nodiscard]] const option& parser_base::get_option(based::character opt) const
{
const auto idx = m_opt_short.get(opt);
if (!idx.has_value()) {

diff --git a/ test/source/parser.cpp b/ test/source/parser.cpp

@@ -548,7 +548,9 @@ TEST_CASE("list", "[poafloc/parser]")


SECTION("long terminal three")
{
std::vector<std::string_view> cmdline = {"--list", "one", "two", "three", "--"};
std::vector<std::string_view> cmdline = {
"--list", "one", "two", "three", "--"
};
REQUIRE_NOTHROW(program(args, cmdline));
REQUIRE(std::size(args.list) == 3);
REQUIRE(args.list[0] == "one");