katom types from ref/ in groups, @table :leading in lines of text and :rulesep, @code under txt, one table style attribute, literal-klammer options (from dev 2e453744f5e9)

kdesc --katoms is Klammertext rendered from ref/, the types in titled groups,
with --katoms full for the regex column.  @table :leading L is L lines of the
surrounding text in html as in pdf (default 1), and :rulesep spaces the
horizontal rules.  The html table has one style attribute, so :column_width
fractions take effect again.  @code and @source_listing render in plain
text; captions are numbered per kind there, and a table needs no blank line
around it.  @c takes :nd, and an option-shaped word before a bar inside
literal code is code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ackandClaude Fable 5.1 committed 2026-09-21 22:37:13 +02:00
1 parent 63956c00e5
commit 217c0f3fc5
23 files changed
+625 -290

No files matched your search

+11 -116
View File
@@ -2,6 +2,7 @@
#include <iomanip>
#include <algorithm>
#include <utility>
#include <stdexcept>
#include "locator.h"
#include "ktype.h"
@@ -43,127 +44,21 @@ bool is_deftype(katom_t type)
type == katom_t::klammer_default;
}
std::string pattern_display(Ktype t)
const Ktype& type_to_katom_instance(katom_t type)
{
std::string pat{};
if (t.m_type == katom_t::space) {
pat = "<space>";
} else if (t.m_type == katom_t::newline) {
pat = "\\n";
} else if (t.m_type == katom_t::ws_space) {
pat = "#+ or #+<number>";
} else if (t.m_type == katom_t::ws_newline) {
pat = "#/ or #/<number>";
} else if (t.m_type == katom_t::special) {
pat = "^@, ^|, ^#, ^:, or ^^";
} else if (t.m_type == katom_t::apply_end) {
pat = "@ or <name>@";
} else if (t.m_type == katom_t::define_end) {
pat = "@@ or <name>@@";
} else if (t.m_type == katom_t::machine_end) {
pat = "@@@ or <name>@@@";
} else if (t.m_type == katom_t::ws_added) {
pat = "<space> or \\n";
} else if (t.m_type == katom_t::word) {
pat = "<only-letters>";
} else if (t.m_type == katom_t::text) {
pat = "<no-special-chars>";
} else if (t.m_type == katom_t::nonascii) {
pat = "^<code> or ^<code>^";
} else {
pat = t.m_pattern;
pat = string_replace(pat, definition_name, "<name>");
pat = string_replace(pat, "\\", "");
auto var = std::find_if(katom_types.begin(), katom_types.end(),
[&] (const Ktype& t) { return t.m_type == type; });
if (var == katom_types.end()) {
throw std::logic_error(
"type_to_katom_instance: katom type " +
std::to_string(static_cast<int>(type)) +
" has no entry in katom_types. See file mac/ktype.h.");
}
return pat;
}
std::string regex_display(Ktype t)
{
std::string rgx{};
if (t.m_type == katom_t::space) {
rgx = "<space>";
} else if (t.m_type == katom_t::newline) {
rgx = "\\n";
} else {
rgx = t.m_pattern;
}
return rgx;
}
Ktype find_katom_type(katom_t type)
{
for (auto t : katom_types) {
if (t.m_type == type) {
return t;
}
}
throw Internal_error("Unknown katom_t: " + std::to_string((int)type));
}
void describe_katoms(bool show_regex)
{
size_t name_w = 0;
size_t pattern_w = 0;
size_t regex_w = 0;
size_t description_w = 0;
std::stringstream ss {};
ss << R"(
A "katom" is an individual element in the Klammertext input text.
Each katom has a type, listed below. You can see how Klammertext
divides up text into katoms with the "kdiag" command.)" << "\n\n";
if (show_regex) {
ss << R"(The "Regex" column is the argument to the std::regex C++ function.)";
} else {
ss << R"(In the katom patterns, "<name>" is a word that begins with a letter
and only contains letters, numbers, or the underscore (_) or period (.) characters. )";
}
ss << "A <number> is an integer greater than or equal to 1. "
"A <code> is one of the non-ASCII codes that are displayed by the command "
"\"kdesc -c\".";
for (auto t : katom_types) {
name_w = std::max(name_w, t.m_name.size());
pattern_w = std::max(pattern_w, pattern_display(t).size());
regex_w = std::max(regex_w, regex_display(t).size());
description_w = std::max(description_w, t.m_description.size());
}
pattern_w++;
regex_w++;
std::cout << justify(ss.str()) << "\n\n"
<< std::setfill(' ') << boldblack << std::right << std::setw(6) << "Index"
<< std::left
<< std::setw(name_w) << " Name" << " ";
if (show_regex) {
std::cout << std::setw(regex_w) << " Regex" << " ";
} else {
std::cout << std::setw(pattern_w) << " Pattern" << " ";
}
std::cout << std::setw(description_w) << "Description"
<< "\n" << black;
for (auto typ : katom_type_display_order) {
auto t = find_katom_type(typ);
std::cout << " " << std::right << std::setw(3)
<< static_cast<int>(t.m_type) << " "
<< std::left
<< std::setw(name_w) << t.m_name << " ";
if (show_regex) {
std::cout << std::setw(regex_w) << regex_display(t) << " ";
} else {
std::cout << std::setw(pattern_w) << pattern_display(t) << " ";
}
std::cout << std::setw(description_w) << t.m_description << "\n";
}
std::cout << "\n";
return *var;
}
std::string type_to_name(katom_t type)
{
auto var = std::find_if(katom_types.begin(), katom_types.end(),
[&] (Ktype t) { return t.m_type == type; });
return var->m_name;
return type_to_katom_instance(type).m_name;
}
+112 -86
View File
@@ -69,10 +69,10 @@ enum class katom_t {
karg,
machine_begin,
machine_end,
ignore_line,
ignore_begin,
ignore_end,
ignore_rest,
ignore_line,
ws_remove,
ws_space,
ws_newline,
@@ -88,109 +88,67 @@ enum class katom_t {
};
inline const
std::vector<katom_t> katom_type_display_order {
katom_t::space,
katom_t::word,
katom_t::newline,
katom_t::text,
katom_t::apply_begin,
katom_t::apply_end,
katom_t::bar,
katom_t::double_bar,
katom_t::option_name,
katom_t::read_begin,
katom_t::eval_begin,
katom_t::cond_begin,
katom_t::define_begin,
katom_t::define_end,
katom_t::klammer_definition,
katom_t::klammer_instance,
katom_t::klammer_override,
katom_t::klammer_default,
katom_t::karg,
katom_t::machine_begin,
katom_t::machine_end,
katom_t::ignore_begin,
katom_t::ignore_end,
katom_t::ignore_rest,
katom_t::ignore_line,
katom_t::ws_remove,
katom_t::ws_space,
katom_t::ws_newline,
katom_t::special,
katom_t::nonascii,
katom_t::literal_begin,
katom_t::literal_end,
katom_t::ws_added,
katom_t::literal,
katom_t::eval_result,
katom_t::replaced,
katom_t::ignored
};
class Ktype;
inline std::map<katom_t, std::string> katom_type_names {};
inline std::map<katom_t, std::string> katom_type_descs {};
inline std::vector<katom_t> katom_type_list {};
class Ktype {
public:
// Ktype() = default;
Ktype(katom_t type, std::string name, std::string pattern, std::string description, bool use_equal = false)
Ktype(katom_t type, std::string name, std::string pattern, bool use_equal = false)
: m_type(type),
m_name(name),
m_pattern(pattern),
m_description(description),
m_use_equal(use_equal),
m_rgx(std::regex(pattern)) {
katom_type_list.push_back(type);
katom_type_names.insert({m_type, m_name});
katom_type_descs.insert({m_type, m_description});
}
// A description used to be the fourth argument. A string literal there
// would now convert to bool and silently set use_equal, so it is refused.
Ktype(katom_t, std::string, std::string, const char*) = delete;
bool match(const std::string& s) const {
return m_use_equal ? s == m_pattern : std::regex_match(s, m_rgx);
}
katom_t m_type;
std::string m_name;
std::string m_pattern;
std::string m_description;
bool m_use_equal;
std::regex m_rgx;
};
inline const
std::vector<Ktype> katom_types {
Ktype(katom_t::space, "space", " ", "One space character", false),
Ktype(katom_t::word, "word", "[a-zA-Z]+", "Text only containing letters", false),
Ktype(katom_t::newline, "newline", "\n", "One newline character", false),
Ktype(katom_t::bar, "bar", "\\|", "Bar character used as positional argument separator", false),
Ktype(katom_t::double_bar, "double-bar", "\\|\\|", "Separator for compound positional arguments", false),
Ktype(katom_t::read_begin, "read-begin", at_s+read_s, "Beginning of the file input klammer", false),
Ktype(katom_t::eval_begin, "eval-begin", at_s+eval_s, "Beginning of the evaluation klammer", false),
Ktype(katom_t::cond_begin, "cond-begin", at_s+cond_s, "Beginning of the conditional (if/then/else) klammer", false),
Ktype(katom_t::apply_begin, "apply-begin", at_s + definition_name, "Beginning of a klammer call"),
Ktype(katom_t::apply_end, "apply-end", "(" + definition_name + ")?" + at_s, "End of a klammer call"),
Ktype(katom_t::option_name, "option-name", ":" + definition_name, "Optional argument name"),
Ktype(katom_t::define_begin, "define-begin", at2_s + definition_begin_name, "Beginning of a klammer definition"),
Ktype(katom_t::define_end, "define-end", "(" + definition_name + ")?" + at2_s, "End of a klammer definition"),
Ktype(katom_t::klammer_default, "klammer-default", "::::", "Define klammer default value for possible override"),
Ktype(katom_t::klammer_override, "klammer-override", ":::", "Override existing klammer definition"),
Ktype(katom_t::klammer_definition, "klammer-definition", ":", "Klammer definition, including parameters"),
Ktype(katom_t::klammer_instance, "klammer-instance", "::", "Klammer definition using previously defined parameters"),
Ktype(katom_t::space, "space", " ", false),
Ktype(katom_t::word, "word", "[a-zA-Z]+", false),
Ktype(katom_t::newline, "newline", "\n", false),
Ktype(katom_t::bar, "bar", "\\|", false),
Ktype(katom_t::double_bar, "double-bar", "\\|\\|", false),
Ktype(katom_t::read_begin, "read-begin", at_s+read_s, false),
Ktype(katom_t::eval_begin, "eval-begin", at_s+eval_s, false),
Ktype(katom_t::cond_begin, "cond-begin", at_s+cond_s, false),
Ktype(katom_t::apply_begin, "apply-begin", at_s + definition_name),
Ktype(katom_t::apply_end, "apply-end", "(" + definition_name + ")?" + at_s),
Ktype(katom_t::option_name, "option-name", ":" + definition_name),
Ktype(katom_t::define_begin, "define-begin", at2_s + definition_begin_name),
Ktype(katom_t::define_end, "define-end", "(" + definition_name + ")?" + at2_s),
Ktype(katom_t::klammer_default, "klammer-default", "::::"),
Ktype(katom_t::klammer_override, "klammer-override", ":::"),
Ktype(katom_t::klammer_definition, "klammer-definition", ":"),
Ktype(katom_t::klammer_instance, "klammer-instance", "::"),
Ktype(katom_t::karg, "klammer-arg", "\\*" + definition_name +"\\*", "Klammer argument in klammer body definition"),
Ktype(katom_t::machine_begin, "machine-begin", at3_s + definition_name, "Beginning of a processor modification definition"),
Ktype(katom_t::machine_end, "machine-end", "(" + definition_name + ")?" + at3_s, "End of a processor modification definition"),
Ktype(katom_t::ignore_begin, "ignore-begin", ignore_begin_s, "Beginning of text to remove"),
Ktype(katom_t::ignore_end, "ignore-end", ignore_end_s, "End of text to remove"),
Ktype(katom_t::ignore_rest, "ignore-rest", ignore_rest_s, "Remove all text to the end of file or string"),
Ktype(katom_t::ignore_line, "ignore-line", ignore_line_s, "Remove all text to the first newline, inclusive"),
Ktype(katom_t::ws_remove, "ws-remove", ws_remove_s, "Remove all whitespace at this point"),
Ktype(katom_t::ws_space, "ws-space", ws_space_s, "Remove all whitespace, leaving <number> spaces (default: 1)"),
Ktype(katom_t::ws_newline, "ws-newline", ws_newline_s, "Remove all whitespace, leaving <number> newlines (default: 1)"),
Ktype(katom_t::special, "special-char", R"(\^[@|#^:*])", "Klammertext special character treated as regular text"),
Ktype(katom_t::karg, "klammer-arg", "\\*" + definition_name +"\\*"),
Ktype(katom_t::machine_begin, "machine-begin", at3_s + definition_name),
Ktype(katom_t::machine_end, "machine-end", "(" + definition_name + ")?" + at3_s),
Ktype(katom_t::ignore_begin, "ignore-begin", ignore_begin_s),
Ktype(katom_t::ignore_end, "ignore-end", ignore_end_s),
Ktype(katom_t::ignore_rest, "ignore-rest", ignore_rest_s),
Ktype(katom_t::ignore_line, "ignore-line", ignore_line_s),
Ktype(katom_t::ws_remove, "ws-remove", ws_remove_s),
Ktype(katom_t::ws_space, "ws-space", ws_space_s),
Ktype(katom_t::ws_newline, "ws-newline", ws_newline_s),
Ktype(katom_t::special, "special-char", R"(\^[@|#^:*])"),
//Ktype(katom_t::nonascii, "non-ascii-char", R"(\^\w(?:.|\^))", "Non-ASCII character"),
// The second alternative is the "^...^" delimited form: a code point
// (^263A^, 4-6 hex digits) OR a mnemonic (^s^, ^oe^). Its character
@@ -207,26 +165,95 @@ std::vector<Ktype> katom_types {
// decided there, by the tables, not here -- katomization's job is only to
// recognize the shape. A shape that resolves to no character at all is
// a located error, raised at the end of encode().
Ktype(katom_t::nonascii, "non-ascii-char", R"((\^(\w(?:.|\^)))|(\^[A-Za-z0-9]{1,6}\^))", "Non-ASCII character"),
Ktype(katom_t::nonascii, "non-ascii-char", R"((\^(\w(?:.|\^)))|(\^[A-Za-z0-9]{1,6}\^))"),
// Ktype(katom_t::word, "word", R"([a-z][a-z0-9_]*)", "Lower-case letters, numbers, or underscore"),
// Ktype(katom_t::text, "text",
Ktype(katom_t::literal_begin, "literal-begin", R"(\^')", "Begin unprocessed text"),
Ktype(katom_t::literal_end, "literal-end", R"('\^)", "End unprocessed text"),
Ktype(katom_t::text, "text", R"([^^@#|]+)", "No special characters, whitespace, or \":\" at the beginning"), // spaces, @, #, |, or ^,
Ktype(katom_t::literal_begin, "literal-begin", R"(\^')"),
Ktype(katom_t::literal_end, "literal-end", R"('\^)"),
Ktype(katom_t::text, "text", R"([^^@#|]+)"), // spaces, @, #, |, or ^,
Ktype(katom_t::ws_added, "ws-added", "<space> or \\n", "Added whitespace characters from #+ and #/"),
Ktype(katom_t::literal, "literal", "", "Literal katom (changed from its original type by ^'...'^)"),
Ktype(katom_t::eval_result, "eval-result", "", "Katom produced by @eval klammer"),
Ktype(katom_t::ws_added, "ws-added", "<space> or \\n"),
Ktype(katom_t::literal, "literal", ""),
Ktype(katom_t::eval_result, "eval-result", ""),
Ktype(katom_t::replaced, "replaced", "", "A katom replaced by definitions, applications, or file input"),
Ktype(katom_t::ignored, "ignored", "", "A katom ignored by the action of a \"#\" katom"),
Ktype(katom_t::replaced, "replaced", ""),
Ktype(katom_t::ignored, "ignored", ""),
// Ktype(katom_t::undefined, "undefined", "", "Undefined pattern")
};
// The human-facing description of the katom types, and the ONLY one: class
// Ktype above holds what the program needs (type, name, regex), this holds
// what a reader is told, and katom_t joins them. The pattern and the
// description are Klammertext, rendered by katom_descriptions() in
// ref/reflect.cpp (kdesc --katoms; a Handbook chapter). A title is
// Klammertext too, without styling -- the generator styles every title alike.
//
// The ORDER is the display order, from what every author meets to what only
// a programmer does, and it is also the order of the katom_t enumerators:
// the table's Index column is the enum value, which is the subscript kdiag
// prints. tst/ktype_test.cpp asserts both halves of the tie -- every type
// in katom_types described exactly once, and the indices increasing.
struct Katom_type_description {
katom_t m_type;
std::string m_pattern;
std::string m_description;
};
struct Katom_type_group {
std::string m_title;
std::vector<Katom_type_description> m_types;
};
inline const
std::vector<Katom_type_group> katom_type_groups {
{"Plain text", {
{katom_t::space, "@symb-space", "One space character"},
{katom_t::word, "@symb only-letters @", "Text only containing letters"},
{katom_t::newline, "\\n", "One newline character"},
{katom_t::text, "@symb no-special-chars @", "No special characters, whitespace, or an initial colon"}}},
{"Klammer application", {
{katom_t::apply_begin, "^@ #- @symb-name", "Beginning of a klammer call"},
{katom_t::apply_end, "^@ @ri-or @symb-name #- ^@", "End of a klammer call"},
{katom_t::bar, "^|", "Bar character used as positional argument separator"},
{katom_t::double_bar, "^|^|", "Separator for compound positional arguments"},
{katom_t::option_name, ": #- @symb-name", "Name of an optional argument"},
{katom_t::read_begin, "@t ^@read @", "Beginning of the file input klammer"},
{katom_t::eval_begin, "@t ^@eval @", "Beginning of the evaluation klammer"},
{katom_t::cond_begin, "@t ^@cond @", "Beginning of the conditional (if/then/else) klammer"}}},
{"Klammer definition", {
{katom_t::define_begin, "^@^@ #- @symb-name", "Beginning of a klammer definition"},
{katom_t::define_end, "^@^@ @ri-or @symb-name #- ^@^@", "End of a klammer definition"},
{katom_t::klammer_definition, ":", "Klammer definition, including parameters"},
{katom_t::klammer_instance, "::", "Klammer definition using previously defined parameters"},
{katom_t::klammer_override, ":::", "Override existing klammer definition"},
{katom_t::klammer_default, "::::", "Define klammer default value for possible override"},
{katom_t::karg, "* #- @symb-name #- *", "Klammer argument in klammer body definition"}}},
{"System commands", {
{katom_t::machine_begin, "^@^@^@ #- @symb-name", "Beginning of a system command"},
{katom_t::machine_end, "^@^@^@ @ri-or @symb-name #- ^@^@^@", "End of a system command"}}},
{"Text removal and whitespace", {
{katom_t::ignore_line, "^#", "Remove all text to the first newline, inclusive"},
{katom_t::ignore_begin, "^#[", "Beginning of text to remove"},
{katom_t::ignore_end, "]^#", "End of text to remove"},
{katom_t::ignore_rest, "^#^#", "Remove all text to the end of file or string"},
{katom_t::ws_remove, "^#-", "Remove all whitespace at this point"},
{katom_t::ws_space, "^#+ @ri-or ^#+ #- @symb-n", "Remove all whitespace, leaving @symb-n spaces (default: 1)"},
{katom_t::ws_newline, "^#/ @ri-or ^#/ #- @symb-n", "Remove all whitespace, leaving @symb-n newlines (default: 1)"}}},
{"Special characters and literal text", {
{katom_t::special, "^@, ^|, ^#, ^:, @ri-or ^^", "Klammertext special character treated as regular text"},
{katom_t::nonascii, "^^ #- @symb-code @ri-or ^^ #- @symb-code #- ^^", "Non-ASCII character"},
{katom_t::literal_begin, "^^ #- '", "Begin unprocessed text"},
{katom_t::literal_end, "'#- ^^", "End unprocessed text"}}},
{"Types assigned during processing", {
{katom_t::ws_added, "@symb-space @ri-or \\n", "Added whitespace characters from @t ^#+ @ and @t ^#/ @"},
{katom_t::literal, "@symb any-text @", "Literal katom (type changed by @t ^^ #- '...'#- ^^ @ #- )"},
{katom_t::eval_result, "@symb any-text @", "Katom produced by @t ^@eval @ klammer"},
{katom_t::replaced, "@symb any-text @", "A katom replaced by definitions or klammers"},
{katom_t::ignored, "@symb any-text @", "A katom ignored by the action of a text-removal katom"}}}
};
inline const
std::set<katom_t> printable_katom_types {
@@ -277,8 +304,7 @@ std::map<katom_t, katom_t> katom_spans {
bool is_active(katom_t t);
bool is_printable(katom_t type);
void describe_katoms(bool show_regex = false);
bool is_deftype(katom_t type);
const Ktype& type_to_katom_instance(katom_t type);
std::string type_to_name(katom_t type);
+11 -2
View File
@@ -1,3 +1,4 @@
#include <algorithm>
#include <set>
#include <utility>
#include "machine.h"
@@ -402,11 +403,19 @@ void Machine::mark_literal_klammer_content(katom_list& katoms)
// Find where literal content starts.
// Skip bars_before_literal bars (separating preceding positional args).
// If options are present, skip past the bar after them.
// Options are identified by :name katoms before any bar.
// Options are identified by :name katoms before any bar -- but only
// a name THE KLAMMER DECLARES. Any ":word" counted until 2026-09-20,
// so code holding one before a bar ("@c ls :w | wc c@", a regex's
// "(?:a|b)") had its text up to the bar taken for options and the
// file failed with 'Optional argument ":w" not defined' -- an error
// about an option the author never wrote, in content that is
// promised to be read as nothing but text.
const auto& optional_names = klammer.m_parameters.m_optional_names;
auto literal_start = k + 1;
bool has_options = false;
for (auto j = k + 1; j < close; ++j) {
if (j->m_type == katom_t::option_name) {
if (j->m_type == katom_t::option_name &&
std::ranges::count(optional_names, std::string(j->m_text, 1)) > 0) {
has_options = true;
}
if (j->m_type == katom_t::bar) {