commit 31224a1328bc4bb1f93fe7735ce5204621e199fa
parent 394aa9e76ad447d967bf40ddef9eecd1fef06735
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Thu, 6 Jun 2024 14:51:00 +0200
Add support for aliases and int keys
Diffstat:
M | args.hpp | | | 57 | +++++++++++++++++++++++++++++++++++++++++---------------- |
M | demo.cpp | | | 5 | +++-- |
2 files changed, 44 insertions(+), 18 deletions(-)
diff --git a/args.hpp b/args.hpp
@@ -6,12 +6,13 @@
#include <exception>
#include <format>
#include <iostream>
+#include <unordered_map>
class Parser {
public:
struct option_t {
const char *name;
- const char key;
+ const int key;
const char *arg;
const uint8_t options;
};
@@ -30,17 +31,41 @@ class Parser {
};
Parser(const argp_t *argp) : argp(argp) {
- for (int i = 0; argp->options[i].key; i++) {
+ int key_last;
+ int i = 0;
+ while (true) {
const auto &option = argp->options[i];
- const uint8_t idx = option.key - 'a';
+ if (!option.name && !option.key) break;
- if (options[idx]) {
- std::cerr << std::format("duplicate key {}\n", option.key);
- throw new std::runtime_error("duplicate key");
+ if (!option.key) {
+ if ((option.options & ALIAS) == 0) {
+ std::cerr << "non alias without a key\n";
+ throw new std::runtime_error("no key");
+ }
+
+ if (!key_last) {
+ std::cerr << "no option to alias\n";
+ throw new std::runtime_error("no alias");
+ }
+
+ // TODO: connect aliases in --help
+
+ trie.insert(option.name, key_last);
+ } else {
+ if (options.count(option.key)) {
+ std::cerr << std::format("duplicate key {}\n", option.key);
+ throw new std::runtime_error("duplicate key");
+ }
+
+ // TODO: connect aliases in --help
+
+ if (option.name) trie.insert(option.name, option.key);
+ options[option.key] = &option;
+
+ key_last = option.key;
}
- if (option.name) trie.insert(option.name, option.key);
- options[idx] = &option;
+ i++;
}
}
@@ -60,8 +85,8 @@ class Parser {
for (int j = 0; opt[j]; j++) {
const char key = opt[j];
- const auto *option = options[key - 'a'];
- if (!option) goto unknown;
+ if (!options.count(key)) goto unknown;
+ const auto *option = options[key];
const char *arg = nullptr;
if (option->arg) {
@@ -80,12 +105,12 @@ class Parser {
const char *opt = argv[i] + 2;
const auto eq = std::strchr(opt, '=');
- const char key =
+ const int key =
trie.get(!eq ? opt : std::string(opt, eq - opt));
if (!key) goto unknown;
- const auto *option = options[key - 'a'];
+ const auto *option = options[key];
const char *arg = nullptr;
if (!option->arg && eq) goto excess;
@@ -129,7 +154,7 @@ class Parser {
}
}
- void insert(const std::string &option, char key) {
+ void insert(const std::string &option, int key) {
trie_t *crnt = this;
for (const char c : option) {
@@ -145,7 +170,7 @@ class Parser {
crnt->key = key;
}
- char get(const std::string &option) const {
+ int get(const std::string &option) const {
const trie_t *crnt = this;
for (const char c : option) {
@@ -161,13 +186,13 @@ class Parser {
private:
trie_t *children[26] = {0};
uint8_t count = 0;
- char key = 0;
+ int key = 0;
bool terminal = false;
};
const argp_t *argp;
- const option_t *options[26] = {0};
+ std::unordered_map<int, const option_t *> options;
trie_t trie;
};
diff --git a/demo.cpp b/demo.cpp
@@ -20,7 +20,7 @@ int parse_opt(int key, const char *arg, void *input) {
auto arguments = (arguments_t *)input;
switch (key) {
- case 'd': arguments->debug = true; break;
+ case 777: arguments->debug = true; break;
case 'h':
if (arguments->relocatable) error("cannot mix -hex and -relocatable");
arguments->hex = true;
@@ -43,8 +43,9 @@ using enum Parser::Option;
static const Parser::option_t options[] = {
{ "output", 'o', "file", ARG_OPTIONAL},
{ 0, 'i', "file", 0},
- { "debug", 'd', 0, 0},
+ { "debug", 777, 0, 0},
{ "hex", 'h', 0, 0},
+ {"hexadecimal", 0, 0, ALIAS},
{"relocatable", 'r', 0, 0},
{0},
};