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

420
mac/argument_set.cpp Normal file
View File

@@ -0,0 +1,420 @@
#include <ranges>
#include <algorithm>
#include <utility>
#include "show.h"
#include "log.h"
#include "katom.h"
#include "argtype_set.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)
{
//std::string pattern = R"(([A-Za-z]\w*)(?:(\.\w*)(?:(\.\w*)))?)";
std::string pattern = R"((?:([A-Za-z]\w*))|(?:([A-Za-z]\w*)\.(\w+))|(?:([A-Za-z]\w*)\.(\w+)\.(\w+)))";
if (optional) {
pattern = R"((?::([A-Za-z]\w*))|(?::([A-Za-z]\w*)\.(\w+))|(?::([A-Za-z]\w*)\.(\w*)\.(\w+)))";
// x x
}
// (void)K::log(3, pattern);
return std::regex(pattern);
}
Parameter_set::Parameter_set(const std::string parameter_string)
{
(void)K::log(3);
Argtype_set 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_set argtypes {};
parse_parameters(m_katoms, argtypes);
}
Parameter_set::Parameter_set(const std::vector<Katom>& katoms, const Argtype_set& 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_set& 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";
}
return Parameter(match_name, argtypes.get(match_type, k.m_loc), k.m_loc);
}
}
Parameter parse_optional_parameter(const katom_list& katoms, const Argtype_set& 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";
}
return Parameter(match_name, argtypes.get(match_type, k.m_loc),
k.m_loc, true, default_value);
}
}
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_set& 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);
if (pos.m_argtype.m_name == "rest") {
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(
std::string label,
std::vector<std::vector<Katom>> positional,
std::vector<std::vector<Katom>> optional,
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 {
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) {
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);
}
}
return values;
}
// 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;
}