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>
311 lines
15 KiB
C++
311 lines
15 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
#include <map>
|
|
#include <string>
|
|
#include <regex>
|
|
#include <tuple>
|
|
#include <set>
|
|
|
|
inline const std::string at_s {"@"};
|
|
inline const std::string at2_s { "@@" };
|
|
inline const std::string at3_s { "@@@" };
|
|
inline const std::string bar_s { "|" };
|
|
inline const std::string open_s { R"(\()" };
|
|
inline const std::string close_s { R"(\))" };
|
|
inline const std::string karg_s { "*" };
|
|
inline const std::string read_s { "read" };
|
|
inline const std::string eval_s { "eval" };
|
|
inline const std::string cond_s { "cond" };
|
|
inline const std::string lit_s { "lit" };
|
|
|
|
inline const std::string hat_s { "^" };
|
|
|
|
inline const std::string ignore_line_s { "#" };
|
|
inline const std::string ignore_begin_s { R"(#\[)" };
|
|
inline const std::string ignore_end_s { R"(\]#)" };
|
|
inline const std::string ignore_rest_s { "##" };
|
|
|
|
inline const std::string ws_remove_s { "#-" };
|
|
inline const std::string ws_space_s { R"(#\+\d*)" };
|
|
inline const std::string ws_newline_s { R"(#/\d*)" };
|
|
|
|
// const std::string kname = "[a-zA-Z]+[a-zA-Z0-9_.]*";
|
|
// const std::string dname = "[a-zA-Z]+[a-zA-Z0-9_]*";
|
|
|
|
// const std::string k_name = R"([^^@#|\:]+)";
|
|
inline const std::string definition_name = R"([a-zA-Z][a-zA-Z0-9_.]*)";
|
|
// The name at the START of a definition may carry a comma-separated list of
|
|
// targets ("@@table.html,tex"), so its katom runs through commas. This is
|
|
// deliberately looser than the grammar: katomization's job is to delimit
|
|
// the katom, and parse_name() in klammer.cpp validates the list, where a
|
|
// malformed one can be reported with its location. The comma stays out of
|
|
// definition_name itself -- that pattern also delimits klammer APPLICATIONS,
|
|
// option names and "*arg*" variables, where a comma is ordinary writer text.
|
|
// "*" is here for the same reason: "@@date.*" declares a klammer for every
|
|
// target explicitly, and "*" is a Klammertext special everywhere else.
|
|
inline const std::string definition_begin_name = R"([a-zA-Z][a-zA-Z0-9_.,*]*)";
|
|
|
|
|
|
enum class katom_t {
|
|
space,
|
|
word,
|
|
newline,
|
|
text,
|
|
apply_begin,
|
|
apply_end,
|
|
bar,
|
|
double_bar,
|
|
option_name,
|
|
read_begin,
|
|
eval_begin,
|
|
cond_begin,
|
|
define_begin,
|
|
define_end,
|
|
klammer_definition,
|
|
klammer_instance,
|
|
klammer_override,
|
|
klammer_default,
|
|
karg,
|
|
machine_begin,
|
|
machine_end,
|
|
ignore_line,
|
|
ignore_begin,
|
|
ignore_end,
|
|
ignore_rest,
|
|
ws_remove,
|
|
ws_space,
|
|
ws_newline,
|
|
special,
|
|
nonascii,
|
|
literal_begin,
|
|
literal_end,
|
|
ws_added,
|
|
literal,
|
|
eval_result,
|
|
replaced,
|
|
ignored,
|
|
};
|
|
|
|
|
|
class Ktype;
|
|
|
|
inline std::map<katom_t, std::string> katom_type_names {};
|
|
inline std::vector<katom_t> katom_type_list {};
|
|
|
|
class Ktype {
|
|
public:
|
|
// Ktype() = default;
|
|
Ktype(katom_t type, std::string name, std::string pattern, bool use_equal = false)
|
|
: m_type(type),
|
|
m_name(name),
|
|
m_pattern(pattern),
|
|
m_use_equal(use_equal),
|
|
m_rgx(std::regex(pattern)) {
|
|
katom_type_list.push_back(type);
|
|
katom_type_names.insert({m_type, m_name});
|
|
}
|
|
// 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;
|
|
bool m_use_equal;
|
|
std::regex m_rgx;
|
|
};
|
|
|
|
inline const
|
|
std::vector<Ktype> katom_types {
|
|
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 +"\\*"),
|
|
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
|
|
// class is [A-Za-z0-9], not [A-Fa-f0-9], because a mnemonic is not
|
|
// hex: "^oe^" matched NEITHER alternative and so was not a katom at
|
|
// all, fell through to the "non-diacritic non-ascii" rewrite rule,
|
|
// which pads it with whitespace and hands back the same single word --
|
|
// and make_katoms_from_word() then recursed on the identical string
|
|
// until the stack died (SIGSEGV, no message). The two entries that
|
|
// appeared to work, ^ae^ and ^AE^, did so only because "ae" happens to
|
|
// be hex digits; they were being katomized as CODE POINTS and rendered
|
|
// correctly only because encode() runs process_extended_latin() before
|
|
// process_unicode_codepoint(). Which of the two a form means is
|
|
// 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}\^))"),
|
|
|
|
// 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"(\^')"),
|
|
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"),
|
|
Ktype(katom_t::literal, "literal", ""),
|
|
Ktype(katom_t::eval_result, "eval-result", ""),
|
|
|
|
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 {
|
|
katom_t::word,
|
|
katom_t::space,
|
|
katom_t::newline,
|
|
katom_t::literal,
|
|
katom_t::ws_added
|
|
};
|
|
|
|
inline const
|
|
std::set<katom_t> open_level {
|
|
katom_t::literal_begin,
|
|
katom_t::read_begin,
|
|
katom_t::eval_begin,
|
|
katom_t::cond_begin,
|
|
katom_t::apply_begin,
|
|
katom_t::define_begin,
|
|
katom_t::machine_begin,
|
|
katom_t::ignore_begin
|
|
};
|
|
|
|
inline const
|
|
std::set<katom_t> close_level {
|
|
katom_t::literal_end,
|
|
katom_t::apply_end,
|
|
katom_t::apply_end,
|
|
katom_t::apply_end,
|
|
katom_t::apply_end,
|
|
katom_t::define_end,
|
|
katom_t::machine_end,
|
|
katom_t::ignore_end
|
|
};
|
|
|
|
inline
|
|
std::map<katom_t, katom_t> katom_spans {
|
|
{ katom_t::literal_begin, katom_t::literal_end },
|
|
{ katom_t::read_begin, katom_t::apply_end },
|
|
{ katom_t::eval_begin, katom_t::apply_end },
|
|
{ katom_t::cond_begin, katom_t::apply_end },
|
|
{ katom_t::apply_begin, katom_t::apply_end },
|
|
{ katom_t::define_begin, katom_t::define_end },
|
|
{ katom_t::machine_begin, katom_t::machine_end },
|
|
{ katom_t::ignore_begin, katom_t::ignore_end }
|
|
};
|
|
|
|
|
|
bool is_active(katom_t t);
|
|
bool is_printable(katom_t type);
|
|
|
|
bool is_deftype(katom_t type);
|
|
|
|
const Ktype& type_to_katom_instance(katom_t type);
|
|
std::string type_to_name(katom_t type);
|