2026-07-18 18:48:23 +02:00
|
|
|
#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);
|
|
|
|
|
}
|
|
|
|
|
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
void Argv::var(const std::string& name, const std::string& desc,
|
|
|
|
|
const std::string& parameter)
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
{
|
|
|
|
|
(void)K::log(2, name, desc);
|
|
|
|
|
Arg arg {};
|
|
|
|
|
arg.m_type = "var";
|
|
|
|
|
arg.m_name = name;
|
|
|
|
|
arg.m_desc = get_regex_desc(desc);
|
|
|
|
|
arg.m_syntax = arg.symbol();
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
if (!parameter.empty()) {
|
|
|
|
|
arg.m_syntax += " [<" + parameter + ">]";
|
|
|
|
|
}
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
m_args[name] = arg;
|
|
|
|
|
m_names.push_back(name);
|
|
|
|
|
m_var_names.push_back(name);
|
|
|
|
|
m_hyphen_markers.push_back(flag_name(name));
|
|
|
|
|
update_width(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Argv::parse_vars(strings_t& words, string_map& named_args)
|
|
|
|
|
{
|
|
|
|
|
for (const std::string& name : m_var_names) {
|
|
|
|
|
auto it = std::ranges::find(words, flag_name(name));
|
|
|
|
|
named_args[name] = "";
|
|
|
|
|
if (it == words.end()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
m_given.insert(name);
|
|
|
|
|
auto first = it + 1;
|
|
|
|
|
auto last = first;
|
|
|
|
|
while (last != words.end() && !(*last).empty() && (*last)[0] != '-') {
|
|
|
|
|
last++;
|
|
|
|
|
}
|
|
|
|
|
named_args[name] = join(strings_t(first, last), " ");
|
2026-07-26 23:22:59 +02:00
|
|
|
m_vectors[name] = strings_t(first, last);
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
words.erase(it, last);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
void Argv::check_flags_and_options(const std::string& command, strings_t& words)
|
2026-07-18 18:48:23 +02:00
|
|
|
{
|
|
|
|
|
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++;
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
// An option declared with opt() takes a value, so the word after
|
|
|
|
|
// the flag must exist. Written last with nothing after it -- a
|
|
|
|
|
// bare "kdesc -v", or "ktext doc.kt -t" -- this read past the end
|
|
|
|
|
// of the vector and the command died with SIGSEGV, naming
|
|
|
|
|
// nothing. (Two bounds checks used to sit here, commented out;
|
|
|
|
|
// they would have returned a HALF-PARSED option rather than
|
|
|
|
|
// reporting the mistake, so this reports it instead.) A valueless
|
|
|
|
|
// option is declared with flag(), not opt().
|
2026-07-18 18:48:23 +02:00
|
|
|
if (index >= words.size()) {
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
throw Argument_error(
|
|
|
|
|
"The option " + flag_name(opt) + " needs a value: " +
|
|
|
|
|
m_args[opt].m_syntax + ". Enter \"" +
|
|
|
|
|
file_basename(command_name) + "\" for the list of arguments.",
|
|
|
|
|
Locator(), false);
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
|
|
|
|
std::string opt_arg = words[index] + " ";
|
|
|
|
|
index++;
|
|
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
|
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
void Argv::parse_positional(const std::string& command, //strings_t words,
|
2026-07-18 18:48:23 +02:00
|
|
|
std::string pos_args, string_map& named_args)
|
|
|
|
|
{
|
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
for (const std::string& req : m_req_names) {
|
2026-07-18 18:48:23 +02:00
|
|
|
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;
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
// Drop the whitespace that separated this positional from the next.
|
|
|
|
|
// regex_split_prefix() requires its match at position 0 and returns
|
|
|
|
|
// the remainder verbatim, so without this the SECOND required
|
|
|
|
|
// argument is always "not found": its pattern is offered " second"
|
|
|
|
|
// and cannot match a leading space.
|
|
|
|
|
//
|
|
|
|
|
// Trimming here rather than at the top of the loop is deliberate: the
|
|
|
|
|
// first positional still receives pos_args exactly as before, so a
|
|
|
|
|
// command with ONE required argument -- which is every command that
|
|
|
|
|
// ships (ktext's `filenames`, kdiag's `input`) -- parses
|
|
|
|
|
// bit-identically. Only the case that never worked changes.
|
|
|
|
|
size_t next = rest.find_first_not_of(" \t");
|
|
|
|
|
pos_args = (next == std::string::npos) ? "" : rest.substr(next);
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
parse_vars(words, named_args);
|
2026-07-18 18:48:23 +02:00
|
|
|
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());
|
2026-07-26 23:22:59 +02:00
|
|
|
if (m_req_names.size() == 1) {
|
|
|
|
|
// A single positional argument owns all remaining words; keep the
|
|
|
|
|
// original argv boundaries alongside the joined named_args value.
|
|
|
|
|
m_vectors[m_req_names[0]] = words;
|
|
|
|
|
}
|
2026-07-18 18:48:23 +02:00
|
|
|
// 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];
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
// No describe() here. A call sat at this point, before any value has
|
|
|
|
|
// been assigned, so it could only ever print a table of "<none>" -- and
|
|
|
|
|
// because set_verbose_level() parses a throwaway Argv before the real
|
|
|
|
|
// one, EVERY command would print that table on every run. Silencing
|
|
|
|
|
// Argv::describe() itself was the wrong half of the fix: it also silenced
|
|
|
|
|
// the callers that legitimately want it (kdesc -v, argv_test). The
|
|
|
|
|
// caller decides; parse() does not print.
|
2026-07-18 18:48:23 +02:00
|
|
|
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);
|
2026-07-26 23:22:59 +02:00
|
|
|
if (m_vectors.count(name) > 0) {
|
|
|
|
|
return m_vectors.at(name);
|
|
|
|
|
}
|
|
|
|
|
// No stored boundaries (e.g. an opt, whose value is a single argv
|
|
|
|
|
// word): the value is one element, spaces and all -- never re-split.
|
|
|
|
|
std::string value = get(name);
|
|
|
|
|
if (value.empty()) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
return { value };
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
// The line was built and then dropped, so this printed nothing at
|
|
|
|
|
// all -- which is why "kdesc -v 1" showed no arguments and argv_test,
|
|
|
|
|
// whose whole job is to display what Argv parsed, was silent. The
|
|
|
|
|
// callers already decide whether to call it (the commands gate it on
|
|
|
|
|
// verbose_level > 0), so it prints unconditionally here.
|
|
|
|
|
std::cout << ss.str() << "\n";
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
|
|
|
|
}
|