Files
klammertext/mac/argument_set.cpp

527 lines
19 KiB
C++
Raw Permalink Normal View History

#include <ranges>
#include <algorithm>
#include <utility>
#include "show.h"
#include "log.h"
#include "katom.h"
#include "argtype_registry.h"
#include "argument_set.h"
#include "util.h"
bool operator==(Parameter_set lhs, Parameter_set rhs)
{
return as_string(lhs.m_katoms.begin(), lhs.m_katoms.end(), true) ==
as_string(rhs.m_katoms.begin(), rhs.m_katoms.end(), true);
}
std::regex parameter_regex(bool optional=false)
{
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
// The type component may carry a numeric type parameter, e.g.
// rows.rest(2) (see resolve_argtype below). In the optional
// three-part form the type may be empty (:paper_size..tex).
std::string type = R"(\w+(?:\(\d+\))?)";
std::string opt_type = R"(\w*(?:\(\d+\))?)";
std::string pattern = R"((?:([A-Za-z]\w*))|(?:([A-Za-z]\w*)\.()" + type +
R"())|(?:([A-Za-z]\w*)\.()" + type + R"()\.(\w+)))";
if (optional) {
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
pattern = R"((?::([A-Za-z]\w*))|(?::([A-Za-z]\w*)\.()" + type +
R"())|(?::([A-Za-z]\w*)\.()" + opt_type + R"()\.(\w+)))";
}
// (void)K::log(3, pattern);
return std::regex(pattern);
}
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
// Look up an argument type, specializing a parameterized use such as
// rest(2): the base type is copied, its type parameter set, and its
// display name extended, so kdesc signatures show rows.rest(2).
static Argtype resolve_argtype(
const std::string& type_text, const Argtype_registry& argtypes, const Locator& loc)
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
{
static const std::regex parameterized(R"((\w+)\((\d+)\))");
std::smatch match {};
if (std::regex_match(type_text, match, parameterized)) {
Argtype argtype = argtypes.get(match[1], loc);
argtype.m_parameter = match[2];
argtype.m_name += "(" + std::string(match[2]) + ")";
return argtype;
}
return argtypes.get(type_text, loc);
}
// True for the rest type in any parameterization (rest, rest(2), ...).
static bool is_rest(const Argtype& argtype)
{
return argtype.m_name == "rest" || argtype.m_name.rfind("rest(", 0) == 0;
}
Parameter_set::Parameter_set(const std::string parameter_string)
{
(void)K::log(3);
Argtype_registry argtypes {};
parse_parameters(
katomize(line_split(parameter_string), Locator().str()),
argtypes);
}
Parameter_set::Parameter_set(const std::vector<Katom>& katoms)
: m_katoms(katoms)
{
(void)K::log(3);
Argtype_registry argtypes {};
parse_parameters(m_katoms, argtypes);
}
Parameter_set::Parameter_set(const std::vector<Katom>& katoms, const Argtype_registry& argtypes)
: m_katoms(katoms)
{
(void)K::log(3);
parse_parameters(m_katoms, argtypes);
}
// Parameter parsing
Parameter parse_positional_parameter(const katom_list& katoms, const Argtype_registry& argtypes)
{
// (void)K::log(3, katoms);
if (katoms.size() > 1) {
throw Argument_error("Multiple katoms for positional argument: " +
as_string(katoms.begin(), katoms.end(), true) +
"\nPositional arguments are separated by the bar (|) character.",
katoms[0].m_loc, false);
}
Katom k = katoms[0];
std::string name = k.m_text;
std::smatch match {};
if (!std::regex_match(name, match, parameter_regex())) {
throw Argument_error(
"The structure of the word \"" + name + "\" is not correct for a positional parameter",
k.m_loc);
} else {
std::string match_name = std::string(match[1]) + std::string(match[2]) + std::string(match[4]);
std::string match_type = std::string(match[3]) + std::string(match[5]);
std::string match_target = match[6];
if (match_type.empty()) {
match_type = "string";
}
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
return Parameter(match_name, resolve_argtype(match_type, argtypes, k.m_loc), k.m_loc);
}
}
Parameter parse_optional_parameter(const katom_list& katoms, const Argtype_registry& argtypes)
{
//(void)K::log(3);
Katom k = katoms[0];
std::string default_value {};
if (katoms.size() > 1) {
default_value = to_string(katoms.cbegin() + 1, katoms.cend(), true);
}
std::string name = k.m_text;
std::smatch match {};
if (!std::regex_match(name, match, parameter_regex(true))) {
throw Argument_error(
"The structure of the word \"" + name +
"\" is not correct for an optional parameter",
k.m_loc);
} else {
std::string match_name = std::string(match[1]) + std::string(match[2]) + std::string(match[4]);
std::string match_type = std::string(match[3]) + std::string(match[5]);
std::string match_target = match[6];
if (match_type.empty()) {
match_type = "string";
}
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
Parameter parameter(match_name, resolve_argtype(match_type, argtypes, k.m_loc),
k.m_loc, true, default_value);
// Two-level default resolution: a default declared in the
// parameter list wins; otherwise the argument type's :default
// fills in. Both are validated here, at definition time, so an
// invalid default cannot reach an application.
if (default_value.empty() && !parameter.m_argtype.m_default.empty()) {
parameter.m_default = parameter.m_argtype.m_default;
parameter.m_default_from_type = true;
} else if (!default_value.empty()) {
Parameter_set::validate(parameter, default_value, k.m_loc);
}
return parameter;
}
}
void check_for_missing_parameter(const katom_list& katoms)
{
(void)K::log(3, katoms.size());
// Yeah, yeah, "algorithms."
auto ki = katoms.begin();
while (ki < katoms.end() - 1) {
ki = std::find_if(ki, katoms.end(), [](const Katom& k) {
return k.m_type == katom_t::bar; });
if (ki == katoms.end()) {
break;
}
auto kstart = ki;
ki = std::find_if(ki + 1, katoms.end(), [](const Katom& k) {
return !k.is_whitespace(); });
if (ki == katoms.end()) {
throw Argument_error(
"A parameter list ends with a bar character", kstart->m_loc);
}
auto type_after_bar = ki->m_type;
if (type_after_bar == katom_t::bar) {
throw Argument_error(
"A parameter name was missing between two bar characters", kstart->m_loc);
} else if (type_after_bar == katom_t::option_name) {
throw Argument_error(
"A parameter name was missing between a bar character and an option name",
kstart->m_loc);
}
++ki;
}
}
bool is_boundary(katom_list::const_iterator ki)
{
return ki->m_type == katom_t::option_name || ki->m_type == katom_t::bar;
}
std::vector<std::vector<Katom>>
function_symbol_parts(katom_list::const_iterator kbegin, katom_list::const_iterator kend)
{
std::vector<std::vector<Katom>> parts;
katom_list part {};
auto ki = kbegin;
while (ki != kend && ki->is_whitespace()) {
ki++;
}
if (ki == kend) {
return {};
}
if (ki->m_type != katom_t::option_name) {
part.push_back(Katom("|", katom_t::bar, kbegin->m_loc));
}
while (ki < kend) {
if (!part.empty() && is_boundary(ki)) {
parts.push_back(trim(part));
part = {};
}
part.push_back(*ki);
ki++;
}
if (!part.empty()) {
parts.push_back(trim(part));
}
// std::cout << "PARTS:\n";
// for (size_t i = 0; i < parts.size(); i++) {
// std::cout << i << sp_arrow << parts[i] << "\n";
// }
return parts;
}
katom_list trim_part(katom_list part)
{
return trim(part, {katom_t::space, katom_t::newline, katom_t::bar});
}
std::tuple<katom_lists,katom_lists>
parameter_split(katom_list::const_iterator kbegin, katom_list::const_iterator kend)
{
(void)K::log(3); //, "begin:", *kbegin, "end:", *(kend - 1));
// "distance:", std::distance(kbegin, kend));
auto parts = function_symbol_parts(kbegin, kend);
// std::cout << "parts: " << parts << "\n";
katom_lists positional {};
katom_lists optional {};
for (auto p : parts) {
if (p[0].m_type == katom_t::option_name) {
optional.push_back(p);
} else {
positional.push_back(trim_part(p));
}
}
return {positional, optional};
}
void Parameter_set::parse_parameters(const katom_list& katoms, const Argtype_registry& argtypes)
{
(void)K::log(3, trim(katoms));
if (katoms.empty()) {
return;
}
check_for_missing_parameter(katoms);
auto [positional, optional] = parameter_split(katoms.cbegin(), katoms.cend());
for (auto req : positional) {
auto pos = parse_positional_parameter(req, argtypes);
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
if (is_rest(pos.m_argtype)) {
m_rest.push_back(pos);
} else {
m_positional.push_back(pos);
}
}
for (auto opt : optional) {
auto param = parse_optional_parameter(opt, argtypes);
if (std::ranges::count(m_optional_names, param.m_name) > 0) {
throw Argument_error(
"Optional parameter \":" + param.m_name + "\" already defined",
katoms[0].m_loc);
}
m_optional.push_back(param);
m_optional_names.push_back(param.m_name);
}
if (!m_rest.empty()) {
m_positional_count = m_positional.size();
}
}
void describe_arguments(
const std::string& label,
const std::vector<std::vector<Katom>>& positional,
const std::vector<std::vector<Katom>>& optional,
const std::vector<Katom>& rest)
{
std::cout << label << ":\n"
<< " positional: " << positional << "\n"
<< " optional: " << optional << "\n"
<< " rest: " << rest << "\n";
}
void Parameter_set::describe_parameters()
{
(void)K::log(3);
std::cout << " positional: ";
if (!m_positional.empty()) {
for (auto p : m_positional) {
std::cout << p << " ";
}
} else {
std::cout << "[none]";
}
std::cout << "\n optional: ";
if (!m_optional.empty()) {
for (auto p : m_optional) {
std::cout << p << " ";
}
} else {
std::cout << "[none]";
}
std::cout << "\n rest: ";
if (!m_rest.empty()) {
std::cout << kall << m_rest << kreset << "\n";
} else {
std::cout << "[none]";
}
std::cout << "\n";
}
std::tuple<katom_lists,katom_lists,katom_list>
argument_split(katom_list::const_iterator kbegin, katom_list::const_iterator kend,
long unsigned int positional_limit)
{
// (void)K::log(3, "begin:", *(kbegin+1), "end:", *(kend - 1),
// "distance:", std::distance(kbegin, kend), "limit:", positional_limit);
(void)K::log(3);
// msg() << std::pair(kbegin, kend) << "\n";
auto parts = function_symbol_parts(kbegin, kend);
katom_lists positional {};
katom_lists optional {};
katom_list rest {};
for (auto p : parts) {
if (p[0].m_type == katom_t::option_name) {
optional.push_back(p);
} else if (positional.size() < positional_limit) {
positional.push_back(trim_part(p));
} else {
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
// Parts were trimmed, so adjacent parts would abut their bar
// katoms (a row separator "||" next to an empty cell's "|"
// would serialize as "|||"). A space keeps the writer's
// bar/double-bar distinction parseable.
if (!rest.empty()) {
rest.push_back(Katom(" ", katom_t::space, p[0].m_loc));
}
rest.insert(rest.end(), p.begin(), p.end());
}
}
return {positional, optional, trim_part(rest)};
}
// Parameter/argument mapping
void Parameter_set::check_positional(const katom_lists& positional_arguments, const Locator& loc)
{
(void)K::log(3, "required:", m_positional.size(), positional_arguments.size()); //, positional_arguments);
auto positional_count = m_positional.size();
auto given_count = positional_arguments.size();
if (positional_count > given_count) {
// std::cout << "Given less than required\n";
std::vector<Parameter> missing(m_positional.begin() + given_count, m_positional.end());
//std::cout << "missing: " << missing << "\n";
auto missing_count = missing.size();
std::stringstream ss {};
ss << "Positional " << plural("argument", missing_count) << " " << to_be(missing_count)
<< " missing:\n";
std::cout << ss.str();
for (auto arg : missing) {
ss << " " << arg.m_name << "\n";
}
//std::cout << ss.str();
throw Argument_error(ss.str(), loc, false);
} else if (positional_count < given_count) {
//std::cout << "DESCRIBE\n";
//describe_parameters();
std::stringstream ss {};
ss << "Too many positional arguments were given; "
<< positional_count << " needed but " << given_count << " given";
throw Argument_error(ss.str(), loc);
}
}
std::map<std::string, std::string>
Parameter_set::check_optional(const katom_lists& optional_arguments, const Locator& loc)
{
(void)K::log(3, optional_arguments.size());
std::vector<std::string> optional_names_used {};
std::map<std::string, std::string> values {};
for (const auto& opt : optional_arguments) {
std::string name(opt[0].m_text, 1);
if (std::ranges::count(m_optional_names, name) == 0) {
throw Argument_error("Optional argument \":" + name + "\" not defined", loc);
}
if (std::ranges::count(optional_names_used, name) > 0) {
throw Argument_error("Optional argument \":" + name + "\" already provided "
+ "with a value of:\n" + values[name], loc, false);
}
katom_list value_katoms(opt.begin()+1, opt.end());
std::string value = trim(to_string(value_katoms));
values[name] = value;
optional_names_used.push_back(name);
}
return values;
}
const std::map<std::string, std::string>
Parameter_set::value_map(
const katom_lists& positional, const katom_lists& optional, const katom_list& rest,
const Locator& loc)
{
(void)K::log(3, "positional:", positional.size(), "optional:", optional.size(), "rest:", rest.size());
std::map<std::string, std::string> values {};
check_positional(positional, loc);
for (size_t i = 0; i < m_positional.size(); ++i) {
auto param = m_positional[i];
std::string arg = as_string(positional[i].begin(), positional[i].end(), true);
values[param.m_name] = arg;
}
auto optional_values = check_optional(optional, loc);
for (auto [key, value] : optional_values) {
// check_optional returns only the options that were actually
// written, so an empty value here means the name was written alone
// (":number" rather than ":number 10") — distinct from the option
// being absent, which is filled from the default below. The
// argument type supplies the alone value; bool declares "true",
// which is what makes a bare boolean option mean true.
const Parameter* parameter = find(key);
if (value.empty() && parameter && !parameter->m_argtype.m_alone.empty()) {
value = parameter->m_argtype.m_alone;
}
values[key] = value;
}
for (auto opt : m_optional) {
values.try_emplace(opt.m_name, opt.m_default);
}
if (active(rest)) {
if (!m_rest.empty()) {
values[m_rest[0].m_name] = as_string(rest.begin(), rest.end(), true);
} else {
std::stringstream ss {};
ss << "More positional arguments were given (" << positional.size() + rest.size()
<< ") than defined (" << m_positional.size() << ")";
throw Argument_error(ss.str(), loc);
}
}
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
for (const auto& [name, value] : values) {
const Parameter* parameter = find(name);
if (parameter) {
validate(*parameter, value, loc);
}
}
return values;
}
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
// Check an argument value against its argument type's pattern. An empty
// value (an unsupplied optional argument without a default) is not checked.
// The error message includes the argument type's description from its
// @@@argtype definition, so the .k description text is what the writer
// sees when a complicated value (e.g. a table line specification) is wrong.
// The value-size limit guards against std::regex stack overflow: the
// libstdc++ executor recurses per character, so a pattern applied to a
// very large value crashes. Typed argument values are short; large
// values are content (rest, :text) whose types match everything and are
// excluded by matches_all() anyway.
const size_t validation_size_limit = 4096;
void Parameter_set::validate(
const Parameter& parameter, const std::string& value, const Locator& loc)
{
if (value.empty() || value.size() > validation_size_limit) {
return;
}
const Argtype& argtype = parameter.m_argtype;
if (argtype.matches_all()) {
return;
}
if (!std::regex_match(value, argtype.m_regex)) {
std::stringstream ss {};
ss << "The value \"" << value << "\" given for the argument \""
<< parameter.m_name << "\" does not match the \"" << argtype.m_name
<< "\" argument type:\n\n"
<< trim(argtype.m_desc) << "\n";
throw Argument_error(ss.str(), loc, false);
}
}
const Parameter* Parameter_set::find(const std::string& name) const
{
for (const auto& params : {&m_positional, &m_optional, &m_rest}) {
for (const Parameter& p : *params) {
if (p.m_name == name) {
return &p;
}
}
}
return nullptr;
}
// Parameter/argument substitution
std::string replace_arguments(
const std::map<std::string, std::string>& values,
const std::string& parameterized_text, const Locator& loc)
{
(void)K::log(3);
std::string result = parameterized_text;
for (auto [name, value] : values) {
result = string_replace(result, '*' + name + '*', value);
}
auto matches = find_all(result, std::regex(R"((\*.*?\*))"));
std::vector<std::string> unmatched;
unmatched.reserve(matches.size());
std::copy(matches.begin(), matches.end(), std::back_inserter(unmatched));
auto unmatched_count = unmatched.size();
if (unmatched_count > 0) {
std::stringstream ss {};
ss << "Undefined " << plural("argument", unmatched_count) << " in klammer:\n";
for (const auto& arg : unmatched) {
ss << " " << arg << "\n";
}
ss << "To prevent the \"*\" character from specifying an argument, "
<< "precede it with the \"^\" character.";
throw Argument_error(ss.str(), loc, false);
}
return result;
}