Initial commit: Klammertext source distribution

Curated source subset assembled by klammertext-dev's doc/make_dist.sh: the Klammermachine (mac), the Standard Klammer Set (sks), the commands (com), editor plugins and install guides (doc), a test subset (tst), and lib/bin placeholders. Builds with 'make -C com'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-18 18:48:23 +02:00
commit 2ba7ceee7a
272 changed files with 27634 additions and 0 deletions

569
mac/argv.cpp Normal file
View File

@@ -0,0 +1,569 @@
#include <numeric>
#include "file.h"
#include "argv.h"
#include "error.h"
#include "log.h"
#include "util.h"
#include "show.h"
std::string Argv::delimiter = "--";
std::ostream& operator<<(std::ostream& os, const Arg& arg)
{
os << "<" << arg.m_type << " " << arg.m_name
<< " " << q_(arg.m_value) << ">";
return os;
}
std::string wrap_around(const std::string& text, std::size_t indent, std::size_t width=96)
{
std::string result {};
std::size_t current = indent;
std::string margin(indent, ' ');
for (const std::string& word : word_split(text)) {
if (current + 1 + word.size() > width) {
result += "\n" + margin;
current = indent;
}
result += word + " ";
current += word.size() + 1;
}
return result;
}
std::string flag_name(const std::string& name)
{
std::string result {};
if (name.size() == 1) {
result = "-" + name;
} else {
result = "--" + name;
}
return result;
}
std::string Arg::symbol()
{
std::string result = m_name;
if (m_type != "req") {
result = flag_name(m_name);
/*
if (m_name.size() == 1) {
result = "-" + m_name;
} else {
result = "--" + m_name;
}
*/
} else {
result = "<" + result + ">";
}
return result;
}
void Arg::make_regex(const std::string& key)
{
std::string pat {};
if (regex_symbols.find(key) != regex_symbols.end()) {
pat = regex_symbols[key];
m_rgx_symbol = key;
// std::cout << "rgx_symbol: " << m_rgx_symbol << "\n";
} else if (key.find("(") != std::string::npos) {
pat = key;
}
if (pat.size() == 0) {
throw Definition_error("No regex pattern defined for \"" + key + "\".");
}
m_pattern = pat;
m_rgx = std::regex(m_pattern);
}
void Argv::update_width(Arg arg)
{
m_syntax_size = std::max(m_syntax_size, arg.m_syntax.size());
}
std::string get_regex_desc(const std::string& desc)
{
if (regex_desc.find(desc) != regex_desc.end()) {
return regex_desc[desc];
} else {
return desc;
}
}
void Argv::flag(const std::string& name, const std::string& desc)
{
(void)K::log(2, name, desc);
Arg arg {};
arg.m_type = "flag";
arg.m_name = name;
arg.m_desc = get_regex_desc(desc);
arg.m_rgx = std::regex(R"((\w+))");
arg.m_syntax = arg.symbol();
arg.m_value = "false";
m_args[name] = arg;
m_names.push_back(name);
m_flag_names.push_back(name);
m_hyphen_markers.push_back(flag_name(name));
update_width(arg);
}
void Argv::req(const std::string& name, const std::string& desc, const std::string& regex_pattern)
{
(void)K::log(2, name, desc, regex_pattern);
Arg arg {};
arg.m_type = "req";
arg.m_name = name;
arg.m_desc = get_regex_desc(desc);
arg.make_regex(regex_pattern);
arg.m_syntax = "<" + name + ">";
m_args[name] = arg;
m_names.push_back(name);
m_req_names.push_back(name);
update_width(arg);
}
void Argv::opt(const std::string& name, const std::string& desc, const std::string& parameter, const std::string& default_value, const std::string& regex_pattern)
{
(void)K::log(2, name, desc, parameter, default_value, regex_pattern);
Arg arg {};
arg.m_type = "opt";
arg.m_name = name;
arg.m_parameter = parameter;
arg.m_default_value = default_value;
arg.m_value = default_value;
arg.m_desc = get_regex_desc(desc);
arg.make_regex(regex_pattern);
arg.m_syntax = arg.symbol() + " <" + arg.m_parameter + ">";
m_args[name] = arg;
m_names.push_back(name);
m_opt_names.push_back(name);
m_hyphen_markers.push_back(flag_name(name));
update_width(arg);
}
void Argv::usage_line(Arg arg)
{
std::cout.fill(' ');
std::cout << " " << std::left << std::setw(m_syntax_size) << arg.m_syntax << " "
<< wrap_around(arg.m_desc, m_syntax_size + 6) << "\n";
}
void Argv::usage(const std::string& command)
{
std::cout << "\nUsage: " << command << " ";
for (const std::string& name : m_req_names) {
std::cout << "<" + name + "> ";
}
if (m_opt_names.size() + m_flag_names.size() > 5) {
std::cout << "[<optional-arguments>]\n";
} else {
for (const std::string& name : m_names) {
if (m_args[name].m_type == "req") {
continue;
}
std::cout << "[" + m_args[name].m_syntax + "] ";
}
std::cout << "\n";
}
if (!m_req_names.empty()) {
//std::cout << "\n" << plural("Argument", m_req_names.size()) << ":\n";
std::cout << "\n" << "Arguments:\n";
for (const std::string& req_name : m_req_names) {
usage_line(m_args[req_name]);
}
}
int flag_count = m_flag_names.size();
int opt_count = m_opt_names.size();
if (flag_count > 0 || opt_count > 0) {
std::cout << "\n" << plural("Option", flag_count + opt_count) << ":\n";
}
for (const auto& name : m_names) {
if (m_args[name].m_type == "req") {
continue;
}
usage_line(m_args[name]);
}
std::cout << "\n";
}
void Argv::check_flags_and_options(std::string command, strings_t& words)
{
std::vector<std::string> not_defined {};
for (auto word : words) {
if (word[0] == '-' && word != Argv::delimiter && !is_in(word, m_hyphen_markers)) {
not_defined.push_back(word);
}
}
if (!not_defined.empty()) {
std::stringstream ss {};
ss << "The following flags were not defined for command " << q_(command) << ":\n";
for (auto w : not_defined) {
ss << " " << w << "\n";
}
throw Argument_error(ss.str(), Locator(), false);
}
}
void Argv::parse_flags(strings_t& words, string_map& named_args)
{
std::vector<std::string> flag_args {};
for (std::string flag : m_flag_names) {
// std::cout << "Flag: " << flag << "\n";
if (is_in(flag_name(flag), words)) {
flag_args.push_back(flag);
remove_element(words, flag_name(flag));
named_args[flag] = "true";
} else {
named_args[flag] = "false";
}
}
/*
std::vector<std::string> not_defined {};
for (auto word : words) {
if (word[0] == '-' && word != Argv::delimiter) {
not_defined.push_back(word);
}
}
if (!not_defined.empty()) {
std::stringstream ss {};
ss << "The following flags were not defined for command " << q_(command) << ":\n";
for (auto w : not_defined) {
ss << " " << w << "\n";
}
throw Argument_error(ss.str(), Locator(), false);
}
*/
// std::cout << "Flags found: " << flag_args << "\n";
}
void Argv::parse_optional(strings_t& words, string_map& named_args)
{
// msg() << "parse_optional: " << words << "\n";
std::map<std::string, std::string> opt_args {};
for (std::string opt : m_opt_names) {
// std::cout << "Opt: " << opt << sp_arrow << m_args[opt].m_pattern << "\n";
auto it = std::ranges::find(words, flag_name(opt));
if (it != words.end()) {
// msg() << "words: " << words.size() << " " << words << "\n";
size_t index = std::distance(words.begin(), it);
// std::cout << " Found: " << words[index] << "\n";
//std::vector<std::string> opt_args = {};
index++;
/*
if (index >= words.size()) {
break;
}
*/
std::string opt_arg = words[index] + " ";
index++;
/*
if (index >= words.size()) {
break;
}
*/
std::regex opt_regex = m_args[opt].m_rgx;
// std::cout << "Regex match? " << std::regex_match(trim(opt_arg), opt_regex) << "\n";
while (index < words.size() && words[index][0] != '-'
// && std::regex_match(trim(opt_arg), opt_regex)
&& std::regex_match(trim(opt_arg + words[index]), opt_regex)
) {
// opt_args.push_back(words[index++]);
opt_arg += words[index++] + " " ;
}
opt_arg = trim(opt_arg);
// std::cout << " Value: " << opt_arg << "[" << index << "]\n";
opt_args[opt] = opt_arg;
words.erase(it, words.begin() + index);
// std::cout << " Remaining: " << words << "\n";
named_args[opt] = opt_arg;
} else { // Not found
}
}
// std::cout << "opt_args:\n";
// if (opt_args.empty()) {
// std::cout << "[none]";
// } else {
// std::cout << opt_args;
// }
// std::cout << "\n";
}
void Argv::parse_positional(std::string command, //strings_t words,
std::string pos_args, string_map& named_args)
{
for (std::string req : m_req_names) {
auto arg = m_args[req];
auto [substring, rest, found] = regex_split_prefix(arg.m_rgx, pos_args);
if (!found) {
std::stringstream ss {};
ss << "The argument " << q_(req) << " was not found in:\n " << command;
throw Argument_error(ss.str(), Locator(), false);
}
named_args[req] = substring;
pos_args = rest;
}
// std::cout << "Remaining words: " << words << "\n" << pos_args << "\n";
}
std::map<std::string, std::string>
Argv::classify_arguments(int argc, char* argv[], bool full_parse)
{
(void)K::log(2, argc);
if (argc == 1) {
return {};
}
std::map<std::string, std::string> named_args {};
std::vector<std::string> words(argv + 1, argv + argc);
if (full_parse) {
check_flags_and_options(argv[0], words);
}
parse_flags(words, named_args);
parse_optional(words, named_args);
parse_positional(argv_to_string(argc, argv), join(words, " "), named_args);
words.erase(std::remove(words.begin(), words.end(), Argv::delimiter), words.end());
// std::cout << "Named args:\n" << named_args << "\n";
return named_args;
}
void Argv::check_required(
const std::vector<std::string>& req_args, const std::string& command)
{
(void)K::log(2);
auto required = m_req_names.size();
auto given = req_args.size();
if (required > given) {
std::string missing = m_req_names[required - given - 1];
if (given == 0 && m_args[missing].m_rgx_symbol == "'list'") {
return;
}
std::stringstream ss {};
ss << "The required argument \"" << missing
<< "\" was not provided in command \"" << command << "\"";
throw Argument_error(ss.str());
} else if (required < given) {
std::stringstream ss {};
ss << "Too many required arguments were given for command \""
<< command << "\" (" << required << " needed but "
<< given << " given" << ")";
throw Argument_error(ss.str());
}
}
void Argv::check_flags(const string_map& arg_map, const std::string& command)
{
(void)K::log(3);
// std::cout << "arg_map:\n" << arg_map << "\n";
strings_t undefined {};
for (const auto& pair : arg_map) {
auto [key, value] = pair;
// std::cout << "m_type: " << m_args[key].m_type << "\n";
if (key[0] != '_') {
if (m_args[key].m_type != "req"
&& !contains(m_flag_names, key)
&& !contains(m_opt_names, key)) {
undefined.push_back(key);
}
}
}
if (!undefined.empty()) {
std::stringstream ss {};
ss << "Undefined arguments given for command \"" << command << "\":";
for (const std::string& undef : undefined) {
ss << " " << flag_name(undef);
}
throw Argument_error(ss.str(), Locator());
}
}
void Argv::parse(int argc, char* argv[], bool full_parse)
{
(void)K::log(2);
command_name = argv[0];
describe();
auto input_args = classify_arguments(argc, argv, full_parse);
// std::cout << "parse() classify:\n" << input_args << "\n";
for (auto [key, value] : input_args) {
m_args[key].m_value = value;
}
// std::cout << "m_flag_names: " << m_flag_names << "\n";
/*
for (const std::string& name : m_flag_names) {
if (input_args.find(name) != input_args.end()) {
m_args[name].m_value = "true";
}
}
*/
/*
for (const std::string& name : m_opt_names) {
if (input_args.find(name) != input_args.end()) {
std::smatch match {};
if (std::regex_match(input_args[name], match, m_args[name].m_rgx)) {
m_args[name].m_value = input_args[name];
} else {
usage(file_basename(argv[0]));
throw Argument_error(
"Incorrect value for option \"" + name + "\":\n" + m_args[name].m_desc + "\n",
Locator(), false);
}
}
}
std::cout << "input_args:\n" << input_args << "\n";
if (full_parse) {
for (auto [key, value] : input_args) {
auto arg = m_args[key];
if (arg.m_type == "req") {
m_args[key].m_value = value;
}
}
*/
/*
std::vector<std::string> required_args {};
for (auto [key, value] : input_args) {
// std::cout << " full_parse: key: " << key << " value: " << value << "\n";
if (key[0] == '_' && !trim(value).empty()) {
required_args.push_back(value);
}
}
check_required(required_args, command_name);
for (unsigned int i = 0; i < required_args.size(); ++i) {
m_args[m_req_names[i]].m_value = required_args[i];
}
*/
//check_flags(input_args, command_name);
// }
// std::cout << "FINAL:\n"
// << m_args;
}
std::string Argv::get(const std::string& name, bool missing_is_error)
{
if (m_args.count(name) > 0) {
return m_args.at(name).m_value;
} else if (missing_is_error) {
throw Argument_error("Command-line argument \"" + name + "\" is not defined");
} else {
return "";
}
}
bool Argv::as_bool(const std::string& name)
{
(void)K::log(2, name);
return get(name) == "true";
}
int Argv::as_int(const std::string& name)
{
(void)K::log(2, name);
auto value = get(name);
if (!std::regex_match(value, std::regex(R"([-+]?\d+)"))) {
throw Argument_error("Argument \"" + value + "\" is not an integer");
}
return std::stoi(value);
}
int Argv::as_integer_range(const std::string& name, int low, int high)
{
(void)K::log(2, name);
auto value = get(name);
std::stringstream ss {};
ss << "Argument \"" << value << "\" is not an integer in the range of "
<< low << " to " << high;
if (!std::regex_match(value, std::regex(R"([-+]?\d+)"))) {
throw Argument_error(ss.str());
}
int result = std::stoi(value);
if (result < low || result > high) {
throw Argument_error(ss.str());
}
return result;
}
int Argv::as_verbosity(const std::string& name)
{
(void)K::log(2, name);
auto value = get(name);
if (!std::regex_match(value, std::regex(regex_symbols["'verbosity'"]))) {
throw Argument_error("Argument \"" + value + "\" is not a verbosity level");
}
return std::stoi(value);
}
std::string Argv::as_string(const std::string& name)
{
(void)K::log(2, name);
return get(name);
}
strings_t Argv::as_vector(const std::string& name)
{
(void)K::log(2, name);
return regex_split(get(name), std::regex(R"(\s+)"));
}
std::pair<std::string, strings_t> Argv::as_input(const std::string& name, bool allow_empty)
{
(void)K::log(2, name);
const std::string& input_arg = get(name);
std::regex filename_rgx(R"(([./\w]+\.kt?))");
strings_t input_filenames = find_all(input_arg, filename_rgx, 1);
const std::string& input_text = trim(std::regex_replace(input_arg, filename_rgx, ""));
if (input_text.empty() && input_filenames.empty()) {
if (allow_empty) {
return {{},{}};
} else {
throw Argument_error("No input text or filenames specified");
}
}
(void)K::log(2, "Input text", input_text, 1);
for (const auto& f : input_filenames) {
(void)K::log(2, "Input filename", f);
}
return { input_text, input_filenames };
}
void Argv::describe()
{
std::size_t width = std::accumulate(
m_names.begin(), m_names.end(), 0,
[&] (size_t w, const std::string& name) {
return std::max(w, m_args[name].symbol().size()); });
for (const std::string& name : m_names) {
std::string value = m_args[name].m_value;
if (value.empty()) {
value = "<none>";
}
std::stringstream ss {};
ss << " "<< std::setfill(' ') << std::setw(width)
<< m_args[name].symbol() << " : " << value;
}
/*
if (verbose_level == 1) {
std::cout << "\n";
}
*/
}