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 |

commit166d7e74f5d7a5c3adc525595375c5fe34ab36b6
parent36fd7278d9c884f92d36fa9a886e0c420dc36551
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateThu, 6 Jun 2024 02:00:57 +0200

Add uncomplete matching to long options * parse is a member function instread of static * cleaner error handling and messaging

Diffstat:
MMakefile|+-
Margs.hpp|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mdemo.cpp|+++++++++-----

3 files changed, 110 insertions(+), 46 deletions(-)


diff --git a/Makefile b/Makefile

@@ -2,7 +2,7 @@

all: demo
demo: demo.cpp args.hpp
g++ -o $@ $< -std=c++20 -Wall -Werror
g++ -o $@ $< -std=c++20 -Wall -Werror -ggdb
clean:
rm -rf demo

diff --git a/args.hpp b/args.hpp

@@ -1,7 +1,9 @@

#ifndef ARGS_HPP
#define ARGS_HPP
#include <cstdint>
#include <cstring>
#include <exception>
#include <format>
#include <iostream>

@@ -20,70 +22,128 @@ class Parser {

const parse_f parser;
};
static int parse(argp_t *argp, int argc, char *argv[], void *input) {
for (int i = 1; i < argc; i++) {
bool opt_short = false, opt_long = false;
Parser(const argp_t *argp) : argp(argp) {
for (int i = 0; argp->options[i].key; i++) {
const auto &option = argp->options[i];
const uint8_t idx = option.key - 'a';
if (options[idx]) {
std::cerr << std::format("duplicate key {}\n", option.key);
throw new std::runtime_error("duplicate key");
}
options[idx] = &option;
if (option.name) trie.insert(option.name, option.key);
}
}
int parse(int argc, char *argv[], void *input) {
const char *arg = nullptr;
char key;
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
argp->parser(-1, argv[i], input);
continue;
}
if (argv[i][1] != '-') opt_short = true;
else opt_long = true;
const char *opt = argv[i] + opt_long + 1;
if (argv[i][1] != '-') {
const char *opt = argv[i] + 1;
key = opt[0];
bool found = false;
for (int j = 0; argp->options[j].key; j++) {
const auto &option = argp->options[j];
const char *arg = 0;
const auto *option = options[key - 'a'];
if (!option) goto unknown;
if (opt_short && opt[0] != option.key) continue;
if (option->arg) {
if (i == argc) goto missing;
arg = argv[++i];
}
} else {
const char *opt = argv[i] + 2;
const auto eq = std::strchr(opt, '=');
if (opt_long) {
if(!option.name) continue;
key = trie.get(!eq ? opt : std::string(opt, eq - opt));
const auto n = std::strlen(option.name);
if (std::strncmp(argv[i] + 2, option.name, n)) continue;
if (!key) goto unknown;
if (opt[n] == '=') {
if (!option.arg) {
std::cerr << "option doesn't require a value\n";
exit(1);
}
const auto *option = options[key - 'a'];
arg = opt + n + 1;
}
if (eq) {
if (!option->arg) goto excess;
arg = eq + 1;
} else if (option->arg) {
if (i == argc) goto missing;
arg = argv[++i];
}
}
if (option.arg && !arg) {
if (i == argc) {
std::cerr << "option missing a value\n";
exit(1);
}
argp->parser(key, arg, input);
}
arg = argv[++i];
}
return 0;
argp->parser(option.key, arg, input);
unknown:
std::cerr << std::format("unknown option {}\n", argv[i]);
return 1;
found = true;
break;
missing:
std::cerr << std::format("option {} missing a value\n", argv[i]);
return 2;
excess:
std::cerr << std::format("option {} don't require a value\n", argv[i]);
return 3;
}
private:
class trie_t {
public:
~trie_t() noexcept {
for (uint8_t i = 0; i < 26; i++) {
delete children[i];
}
}
void insert(const std::string &option, char key) {
trie_t *crnt = this;
if (found) continue;
for (const char c : option) {
crnt->count++;
if (!crnt->terminal) crnt->key = key;
if (argv[i][0] == '-') {
std::cerr << std::format("unknown option {}\n", argv[i]);
return 1;
const uint8_t idx = c - 'a';
if (!crnt->children[idx]) crnt->children[idx] = new trie_t();
crnt = crnt->children[idx];
}
crnt->terminal = true;
crnt->key = key;
}
return 0;
}
char get(const std::string &option) const {
const trie_t *crnt = this;
private:
for (const char c : option) {
const uint8_t idx = c - 'a';
if (!crnt->children[idx]) return 0;
crnt = crnt->children[idx];
}
if (!crnt->terminal && crnt->count > 1) return 0;
return crnt->key;
}
private:
trie_t *children[26] = {0};
uint8_t count = 0;
char key = 0;
bool terminal = false;
};
const argp_t *argp;
const option_t *options[26] = {0};
trie_t trie;
};
#endif

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

@@ -3,13 +3,11 @@

#include <cstdint>
#include <vector>
void error(const std::string &message) {
std::cerr << message << std::endl;
exit(1);
}
void error(const std::string &message) { std::cerr << message << std::endl; }
struct arguments_t {
const char *output_file = 0;
const char *input_file = 0;
bool debug = 0;
bool hex = 0;

@@ -32,6 +30,7 @@ int parse_opt(int key, const char *arg, void *input) {

arguments->relocatable = true;
break;
case 'o': arguments->output_file = arg; break;
case 'i': arguments->input_file = arg; break;
default: arguments->args.push_back(arg);
}

@@ -41,6 +40,7 @@ int parse_opt(int key, const char *arg, void *input) {

// clang-format off
static const Parser::option_t options[] = {
{ 0, 'o', "file"},
{ "input", 'i', "file"},
{ "debug", 'd', 0},
{ "hex", 'h', 0},
{"relocatable", 'r', 0},

@@ -50,14 +50,18 @@ static const Parser::option_t options[] = {

int main(int argc, char *argv[]) {
Parser::argp_t argp = {options, parse_opt};
Parser parser(&argp);
arguments_t arguments;
if (Parser::parse(&argp, argc, argv, &arguments)) {
if (parser.parse(argc, argv, &arguments)) {
error("There was an error while parsing arguments");
return 1;
}
std::cout << "Command line options: " << std::endl;
std::cout << "\t input: " << arguments.input_file << std::endl;
std::cout << "\t output: " << arguments.output_file << std::endl;
std::cout << "\t hex: " << arguments.hex << std::endl;
std::cout << "\t debug: " << arguments.debug << std::endl;