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

493
mac/klammer.cpp Normal file
View File

@@ -0,0 +1,493 @@
#include <algorithm>
#include "klammer.h"
#include "show.h"
#include "log.h"
#include "argument_set.h"
#include "eval.h"
#include "util.h"
#include "character.h"
using namespace std::literals;
std::regex Klammer::name_re = std::regex(R"((\w+)(?:\.(\w+))?)");
std::tuple<std::string, std::string>
parse_name(Target_set targets, Katom name_katom)
{
std::string name_with_target = trim_char(name_katom.m_text, '@');
std::smatch match {};
if (!std::regex_match(name_with_target, match, Klammer::name_re)) {
throw Parsing_error(
"The klammer name \"" + name_with_target + "\" is not correctly defined. "
"The form is \"<klammer-name>\" for general klammers or \"<klammer-name>.<target-name>\" "
"for a specialized target. The klammer defined as \"<klammer-name>.k\" specifies the "
"arguments and contains a description of the klammer in the definition body.",
name_katom.m_loc);
}
std::string klammer_name = match[1];
std::string target_name = match[2];
if (target_name.empty()) {
target_name = Target_set::general_name;
}
if (!targets.has(target_name)) {
throw Target_error(
"The target \"" + target_name + "\" in klammer definition \"" + name_with_target + "\" "
"is not defined. Enter \"kdesc -t\" to see the targets defined by the Standard Klammer Set.",
name_katom.m_loc);
}
return { klammer_name, target_name };
}
std::tuple<Katom, Parameter_set, katom_list, Locator>
parse_definition_katoms(std::string klammer_name, Argtype_set argtypes, katom_iter& begin, katom_iter& end)
{
(void)K::log(3, *begin, *(end - 1));
katom_iter deftype = std::find_if(
begin, end, [](const Katom& k) { return is_deftype(k.m_type); });
if (deftype == end) {
throw Argument_error(
"The klammer definition does not contain a definition separator that specifies\n"
"how the klammer should be defined. The klammer syntax is:\n\n"
" @@<name>[.<target>] <parameters> : <body> @@ definition\n"
" @@<name>[.<target>] :: <body> @@ instance (uses .k parameters)\n"
" @@<name>[.<target>] ::: <body> @@ override existing definition\n"
" @@<name>[.<target>] <parameters> :::: <body> @@ default (can be overridden)",
begin->m_loc, false);
}
katom_list parameter_katoms(begin, deftype);
// Resolve text-removal markers (#, #[...]#, ##) in the parameter list.
// The interior of a @@...@@ definition is held verbatim until now (so a
// literal klammer body receives its content untouched), which means the
// parameter declarations still contain any removal markers the writer
// used. Apply removal here, to the parameters only -- the body is left
// verbatim by add_target_definition.
mark_ignored_katoms(parameter_katoms);
std::erase_if(parameter_katoms,
[](const Katom& k) { return k.m_type == katom_t::ignored; });
parameter_katoms = trim_whitespace(parameter_katoms);
if ((deftype->m_type == katom_t::klammer_instance ||
deftype->m_type == katom_t::klammer_override) &&
!parameter_katoms.empty()) {
std::string sym = deftype->m_type == katom_t::klammer_instance ? "::" : ":::";
throw Definition_error(
"The \"" + klammer_name + "\" klammer uses the \"" + sym + "\" symbol but defines parameters.",
begin->m_loc);
}
Parameter_set parameters(parameter_katoms, argtypes);
katom_list body_katoms(deftype + 1, end - 1);
body_katoms = trim_whitespace(body_katoms);
return { *deftype, parameters, body_katoms, begin->m_loc };
}
void Klammer::add_target_definition(
std::string target_name, Argtype_set argtypes, katom_iter begin, katom_iter end)
{
(void)K::log(3, *begin, *(end-1));
auto [deftype, parameters, body, loc] =
parse_definition_katoms(m_name, argtypes, begin, end); // targets, begin, end);
// msg() << "Klammer " << m_name << " add: " << target_name << "\n";
// parameters.describe_parameters();
std::regex variable_re(R"(\*(\w+)\*)");
int i = 0;
variable_map_t varmap {};
for (auto k : body) {
std::smatch match {};
std::string txt = k.m_text;
while (std::regex_search(txt, match, variable_re) &&
(k.m_type == katom_t::karg || k.m_type == katom_t::text)) {
//std::cout << " txt: " << txt << "\n";
std::string varname = match[1];
//m_varmap[target_name][varname].push_back(i);
varmap[varname].push_back(i);
txt = std::regex_replace(txt, std::regex(R"(\*)" + varname + R"(\*)"), "");
}
i++;
}
// m_body[target_name] = body;
m_defs.push_back({target_name, deftype.m_initial_type, parameters, body, varmap, begin->m_loc});
m_defloc[target_name] = begin->m_loc;
m_defmode[target_name] = defmode_from_katom(deftype.m_initial_type);
// std::cout << "Klammer " << m_name << "." << target_name << ": " << m_defloc[target_name] << "\n";
}
void Klammer::remove_target_definition(const std::string& target_name)
{
std::erase_if(m_defs,
[&target_name](const auto& def) { return def.target == target_name; });
m_defloc.erase(target_name);
m_defmode.erase(target_name);
m_body.erase(target_name);
m_varmap.erase(target_name);
}
// Rationalize multiple definitions
std::string error_list(std::string label, auto components, std::string after="")
{
std::stringstream ss {};
ss << label << ":\n";
for (auto c : components) {
ss << " " << c.loc.desc() << "\n";
}
ss << after;
return ss.str();
}
auto Klammer::target_defs(std::vector<std::string> target_names)
{
std::vector<Klammer::components> defs {};
for (auto target : target_names) {
auto target_defs = collect_if(
m_defs, [target](const auto& def) { return def.target == target; });
defs.insert(defs.end(), target_defs.begin(), target_defs.end());
}
return defs;
}
auto Klammer::instance_defs()
{
return collect_if(
m_defs,
[] (const auto& def) {
return def.deftype == katom_t::klammer_instance
|| def.deftype == katom_t::klammer_override; });
}
void Klammer::disallow_instances() //Klammer::components declaration)
{
auto instances = instance_defs();
if (!instances.empty()) {
int icount = instances.size();
std::stringstream ss {};
ss << "There " << to_be(icount) << " " << icount << " "
<< plural("instance", icount) << " (defined by \"::\"), but "
<< "no declarations (defined by a \".k\" target)";
throw Definition_error(
error_list(ss.str(), instances), instances[0].loc, false);
}
}
bool Klammer::copy_to_instances(Target_set targets)
{
auto instances = instance_defs();
if (!instances.empty()) {
auto definitions = collect_if(
m_defs,
[] (const auto& def) {
return def.deftype != katom_t::klammer_instance
&& def.deftype != katom_t::klammer_override; });
int dcount = definitions.size();
if (dcount != 1) {
int icount = instances.size();
std::stringstream ss {};
ss << "There " << to_be(icount) << " " << icount << " "
<< plural("instance", icount) << " (defined by \"::\"), but "
<< dcount << " "<< plural("definition", dcount) << " (defined by \":\")";
throw Definition_error(
error_list(ss.str(), definitions), definitions[0].loc, false);
} else {
copy_components(definitions[0].parameters, m_defs, targets);
return true;
}
} else {
return false;
}
}
void Klammer::copy_components(
Parameter_set parameters, std::vector<Klammer::components> cs, Target_set targets)
{
(void)K::log(4);
for (auto target_name : targets.m_names) {
if (target_name == Target_set::declare_name ||
target_name == Target_set::general_name) {
continue;
}
m_parameters = parameters;
}
for (auto c : cs) {
m_body[c.target] = c.body;
m_varmap[c.target] = c.varmap;
}
}
// Verify that there is no more than one general klammer definition
void Klammer::check_for_multiple_general_klammers()
{
auto general_klammers = target_defs({Target_set::general_name});
if (general_klammers.size() > 1) {
throw Definition_error(
error_list(
"There is more than one general klammer (a klammer in which no target is defined)",
general_klammers),
general_klammers[0].loc, false);
}
}
void Klammer::check_for_declaration_and_definitions()
{
auto declares = target_defs({Target_set::declare_name});
if (!declares.empty()) {
std::vector<Klammer::components> definitions {};
for (auto def : m_defs) {
if (def.target != Target_set::declare_name) {
if (def.deftype == katom_t::klammer_definition ||
def.deftype == katom_t::klammer_default) {
msg() << def << "\n";
definitions.push_back(def);
}
}
}
if (!definitions.empty()) {
auto defsize = definitions.size();
std::string desc = defsize == 1 ? "a definition" :
std::to_string(defsize) + " definitions";
throw Definition_error(
error_list("A klammer has both a declaration (.k) as well as " + desc + "\n(instances are defined by \"::\")",
definitions),
declares[0].loc, false);
}
}
}
// If a general definition exists, use it for targets not defined, but check signatures
void Klammer::copy_general_klammer_to_undefined(Target_set targets)
{
(void)K::log(4);
auto general_klammers = target_defs({Target_set::general_name});
auto declares = target_defs({Target_set::declare_name});
// Check matching signatures (though this case already handled)
if (general_klammers.size() == 1) {
if (declares.empty() && !m_parameters.m_katoms.empty()) {
auto general_parameters = general_klammers[0].parameters;
if (general_parameters != m_parameters) {
// m_parameters.describe_parameters();
throw Definition_error(
error_list("The general parameters are different than the defined parameters",
general_klammers),
m_parameters.m_katoms[0].m_loc, false);
}
}
auto [target, deftype, parameters, body, varmap, loc] = general_klammers[0];
if (m_parameters.m_katoms.empty()) {
m_parameters = parameters;
}
for (auto target_name : targets.m_names) {
// std::cout << "General copy, considering " << target_name << "\n";
if (m_body.count(target_name) == 0 && target_name != Target_set::declare_name) {
// std::cout << " Copying to " << target_name << "\n";
m_body[target_name] = body;
m_defloc[target_name] = loc;
m_varmap[target_name] = varmap;
}
}
}
}
// Three declaration cases: none, one, many
void Klammer::no_declarations(Target_set targets)
{
(void)K::log(4);
// std::cout << boldblack << "No declarations\n" << black;
check_for_multiple_general_klammers();
std::vector<Klammer::components> defs {};
std::vector<std::string> target_names = targets.applicable();
std::vector<Parameter_set> all_parameter_sets {};
// Are all parameters the same?
for (auto def : m_defs) {
if (std::ranges::find(target_names, def.target) != target_names.end()) {
// std::cout << " Found: " << def.target << "\n";
all_parameter_sets.push_back(def.parameters);
} else {
// std::cout << " Not found: " << def.target << "\n";
}
}
if (!all_equal<Parameter_set>(all_parameter_sets)) {
// std::cout << " Not all equal\n";
throw Definition_error(
error_list("There is no declaration (.k) target for klammer \"" + m_name + "\"\n"
"but the parameters of all targets are not the same",
m_defs,
"Use a .k klammer to define the parameters and describe the klammer,\n"
"with \"::\" and no parameters for all targets."),
m_defs[0].loc, false);
} else {
// std::cout << " All equal\n";
copy_components(m_defs[0].parameters, m_defs, targets);
}
copy_general_klammer_to_undefined(targets);
}
void Klammer::one_declaration(Target_set targets, Klammer::components declare)
{
(void)K::log(4);
// std::cout << boldblack << "One declaration\n" << black;
check_for_multiple_general_klammers();
check_for_declaration_and_definitions();
copy_components(declare.parameters, m_defs, targets);
copy_general_klammer_to_undefined(targets);
}
void Klammer::many_declarations(std::vector<Klammer::components> declares)
{
(void)K::log(4);
// std::cout << boldblack << "Many declarations\n" << black;
throw Definition_error(
error_list("More than one declaration (.k) klammer", declares),
declares[0].loc, false);
}
void Klammer::rationalize(Target_set targets)
{
(void)K::log(3, m_name);
auto declares = target_defs({Target_set::declare_name});
auto declare_count = declares.size();
if (declare_count == 0) {
disallow_instances();
if (!copy_to_instances(targets)) {
no_declarations(targets);
}
} else if (declare_count == 1) {
one_declaration(targets, declares[0]);
} else {
many_declarations(declares);
}
}
std::string klammer_name_from_katom(std::string s, Locator loc)
{
std::regex rgx(R"(@(\w+).*)");
std::smatch match {};
if (std::regex_match(s, match, rgx)) {
return match[1];
} else {
throw Definition_error("The form of the klammer name " + q_(s) + " is not correct", loc);
}
}
void label(const std::string& s)
{
int w = 13;
std::cout << std::right << std::setw(w) << std::setfill(' ') << s << ": ";
}
void show_args(const std::string& label_text, std::vector<Argument> arguments)
{
if (!arguments.empty()) {
label(label_text);
for (auto a : arguments) {
std::cout << a << " ";
}
std::cout << '\n';
}
}
strings_t Klammer::get_target_names() const
{
strings_t names {};
for (auto [target, body] : m_body) {
std::stringstream ss {};
// ss << name << target.m_loc.m_line;
ss << target;
names.push_back(ss.str());
}
return names; // return join(names, ","s);
}
strings_t Klammer::get_locations()
{
strings_t locs {};
for (auto [target, locator] : m_defloc) {
std::cout << target << right_arrow << locator << "\n";
}
return {};
}
std::string Klammer::signature_text()
{
std::string result {};
bool has_pos = false;
bool has_opt = false;
for (auto pos : m_parameters.m_positional) {
result += pos.m_name;
std::string type = pos.m_argtype.m_name;
if (type != default_argtype) {
result += "." + type;
}
result += " | ";
has_pos = true;
}
if (result.size() >= 2)
result.resize(result.size() - 2);
auto opt_count = m_parameters.m_optional.size();
if (opt_count <= 3 && !has_pos) {
result += " ";
}
for (auto opt : m_parameters.m_optional) {
if (opt_count > 3) {
result += "\n :" + opt.m_name;
} else {
result += ":" + opt.m_name;
}
std::string type = opt.m_argtype.m_name;
if (type != default_argtype) {
result += "." + type;
}
// result += ":" + opt.m_name + "." + opt.m_argtype.m_name;
if (!opt.m_default.empty()) {
result += " " + italic_on() + opt.m_default + italic_off();
}
result += " ";
has_opt = true;
}
result = trim_right(result);
if (has_pos) {
result = " " + result;
}
if (opt_count > 3) {
result += "\n";
} else if (has_opt or has_pos) {
result += " ";
}
result += "@\n";
return result;
}
std::string Klammer::description_text()
{
std::string result = " [" + m_name + ": no description]";
if (m_body.contains("k")) {
result = to_string(m_body["k"], true);
result = justify(result, 80, 1);
}
return result;
}
std::string Klammer::describe(int margin)
{
std::string result {};
result += "@" + m_name + signature_text() + description_text();
result = add_margin(result, margin) + "\n";
return result;
}