Support complex syntax for command line arguments
Support parameters prefixed by '-' or '--'. Support parameters in form 'key=value'
This commit is contained in:
parent
aaf6e6ec0c
commit
83ede4ef81
@ -13,7 +13,7 @@ VERSION := $(MAJOR).$(MINOR).$(PATCH)-dev+$(COMMIT)
|
|||||||
|
|
||||||
CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result
|
CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result
|
||||||
CPPFLAGS:=$(CPPFLAGS) -DMusique_Version='"$(VERSION)"' \
|
CPPFLAGS:=$(CPPFLAGS) -DMusique_Version='"$(VERSION)"' \
|
||||||
-Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/ -Ilib/link/include -Ilib/asio/include/
|
-Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/ -Ilib/link/include -Ilib/asio/include/ -Ilib/edit_distance.cc/
|
||||||
LDFLAGS=-flto
|
LDFLAGS=-flto
|
||||||
LDLIBS= -lpthread
|
LDLIBS= -lpthread
|
||||||
|
|
||||||
|
@ -50,7 +50,6 @@ static constexpr std::array all_parameters = [] {
|
|||||||
std::cout << *maybe_docs << std::endl;
|
std::cout << *maybe_docs << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "musique: error: cannot find documentation for given builtin" << std::endl;
|
std::cerr << "musique: error: cannot find documentation for given builtin" << std::endl;
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
};
|
};
|
||||||
@ -99,42 +98,89 @@ static constexpr std::array all_parameters = [] {
|
|||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
|
||||||
bool cmd::accept_commandline_argument(std::vector<cmd::Run> &runnables, std::span<char const*> &args)
|
|
||||||
|
// Supported types of argument input:
|
||||||
|
// With arity = 0
|
||||||
|
// -i -j -k ≡ --i --j --k ≡ i j k
|
||||||
|
// With arity = 1
|
||||||
|
// -i 1 -j 2 ≡ --i 1 --j 2 ≡ i 1 j 2 ≡ --i=1 --j=2
|
||||||
|
// Arity ≥ 2 is not supported
|
||||||
|
std::optional<std::string_view> cmd::accept_commandline_argument(std::vector<cmd::Run> &runnables, std::span<char const*> &args)
|
||||||
{
|
{
|
||||||
if (args.empty())
|
if (args.empty()) {
|
||||||
return false;
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This struct when function returns automatically ajust number of arguments used
|
||||||
|
struct Fix_Args_Array
|
||||||
|
{
|
||||||
|
std::string_view name() const { return m_name; }
|
||||||
|
|
||||||
|
std::optional<std::string_view> value() const
|
||||||
|
{
|
||||||
|
if (m_has_value) { m_success = m_value_consumed = true; return m_value; }
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_success() { m_success = true; }
|
||||||
|
|
||||||
|
~Fix_Args_Array() { args = args.subspan(int(m_success) + (!m_packed * int(m_value_consumed))); }
|
||||||
|
|
||||||
|
std::span<char const*> &args;
|
||||||
|
std::string_view m_name = {};
|
||||||
|
std::string_view m_value = {};
|
||||||
|
bool m_has_value = false;
|
||||||
|
bool m_packed = false;
|
||||||
|
mutable bool m_success = false;
|
||||||
|
mutable bool m_value_consumed = false;
|
||||||
|
} state { .args = args };
|
||||||
|
|
||||||
|
std::string_view s = args.front();
|
||||||
|
if (s.starts_with("--")) s.remove_prefix(2);
|
||||||
|
if (s.starts_with('-')) s.remove_prefix(1);
|
||||||
|
|
||||||
|
state.m_name = s;
|
||||||
|
|
||||||
|
if (auto p = s.find('='); p != std::string_view::npos) {
|
||||||
|
state.m_name = s.substr(0, p);
|
||||||
|
state.m_value = s.substr(p+1);
|
||||||
|
state.m_has_value = true;
|
||||||
|
state.m_packed = true;
|
||||||
|
} else if (args.size() >= 2) {
|
||||||
|
state.m_has_value = true;
|
||||||
|
state.m_value = args[1];
|
||||||
|
}
|
||||||
|
|
||||||
for (auto const& [name, handler] : all_parameters) {
|
for (auto const& [name, handler] : all_parameters) {
|
||||||
// TODO Parameters starting with - or -- should be considered equal
|
if (name != state.name()) {
|
||||||
if (name != args.front()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
args = args.subspan(1);
|
|
||||||
std::visit(Overloaded {
|
std::visit(Overloaded {
|
||||||
[](Empty_Argument const& h) {
|
[&state](Empty_Argument const& h) {
|
||||||
|
state.mark_success();
|
||||||
h();
|
h();
|
||||||
},
|
},
|
||||||
[&args, name=name](Requires_Argument const& h) {
|
[&state, name=name](Requires_Argument const& h) {
|
||||||
if (args.empty()) {
|
auto arg = state.value();
|
||||||
|
if (!arg) {
|
||||||
std::cerr << "musique: error: option " << std::quoted(name) << " requires an argument" << std::endl;
|
std::cerr << "musique: error: option " << std::quoted(name) << " requires an argument" << std::endl;
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
h(args.front());
|
h(*arg);
|
||||||
args = args.subspan(1);
|
|
||||||
},
|
},
|
||||||
[&, name=name](Defines_Code const& h) {
|
[&state, &runnables, name=name](Defines_Code const& h) {
|
||||||
if (args.empty()) {
|
auto arg = state.value();
|
||||||
|
if (!arg) {
|
||||||
std::cerr << "musique: error: option " << std::quoted(name) << " requires an argument" << std::endl;
|
std::cerr << "musique: error: option " << std::quoted(name) << " requires an argument" << std::endl;
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
runnables.push_back(h(args.front()));
|
runnables.push_back(h(*arg));
|
||||||
args = args.subspan(1);
|
|
||||||
}
|
}
|
||||||
}, handler);
|
}, handler);
|
||||||
return true;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return state.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef MUSIQUE_CMD_HH
|
#ifndef MUSIQUE_CMD_HH
|
||||||
#define MUSIQUE_CMD_HH
|
#define MUSIQUE_CMD_HH
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
@ -22,7 +23,7 @@ namespace cmd
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Accept and execute next command line argument with its parameters if it has any
|
/// Accept and execute next command line argument with its parameters if it has any
|
||||||
bool accept_commandline_argument(std::vector<cmd::Run> &runnables, std::span<char const*> &args);
|
std::optional<std::string_view> accept_commandline_argument(std::vector<cmd::Run> &runnables, std::span<char const*> &args);
|
||||||
|
|
||||||
/// Print all arguments that are similar to one provided
|
/// Print all arguments that are similar to one provided
|
||||||
void print_close_matches(std::string_view arg);
|
void print_close_matches(std::string_view arg);
|
||||||
|
@ -321,34 +321,20 @@ static Result<bool> handle_repl_session_commands(std::string_view input, Runner
|
|||||||
/// Fancy main that supports Result forwarding on error (Try macro)
|
/// Fancy main that supports Result forwarding on error (Try macro)
|
||||||
static std::optional<Error> Main(std::span<char const*> args)
|
static std::optional<Error> Main(std::span<char const*> args)
|
||||||
{
|
{
|
||||||
|
enable_repl = args.empty();
|
||||||
|
|
||||||
if (cmd::is_tty() && getenv("NO_COLOR") == nullptr) {
|
if (cmd::is_tty() && getenv("NO_COLOR") == nullptr) {
|
||||||
pretty::terminal_mode();
|
pretty::terminal_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<cmd::Run> runnables;
|
std::vector<cmd::Run> runnables;
|
||||||
|
|
||||||
|
|
||||||
for (;;) if (!cmd::accept_commandline_argument(runnables, args)) {
|
while (args.size()) if (auto failed = cmd::accept_commandline_argument(runnables, args)) {
|
||||||
if (args.size()) {
|
std::cerr << "musique: error: Failed to recognize parameter " << std::quoted(*failed) << std::endl;
|
||||||
std::cerr << "musique: error: Failed to recognize parameter " << std::quoted(args.front()) << std::endl;
|
|
||||||
cmd::print_close_matches(args.front());
|
cmd::print_close_matches(args.front());
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (arg == "--ast") {
|
|
||||||
ast_only_mode = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg == "--version" || arg == "-v") {
|
|
||||||
std::cout << Musique_Version << std::endl;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Runner runner;
|
Runner runner;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user