A character written in the ^...^ mnemonic form -- ^oe^, ^zz^, ^hello^ --
used to kill ktext outright: no message, no output, and an exit status
that looked like a crash because it was one. Only the forms that happen
to spell hexadecimal digits escaped, and they were right by luck rather
than by reading. Any caret form that names no character is now a located
error that quotes what was written.
A klammer may now sit inside square brackets and braces as it already
could inside parentheses, and may be followed by the punctuation that
ends a sentence: [@i word@], {@b word@}, @i word@'s. The rule is a fixed
list of characters, because none of them can occur inside a klammer name
and so none of them is ambiguous next to a delimiter. A letter still
cannot: "x@" is the named closing delimiter, so a letter before "@" has
two readings and no rule can separate them.
The abbreviations an author writes -- the hyphen form @i-word, a klammer
glued to its neighbouring punctuation -- are now recognised as author
conveniences only. They are not applied to @@ definitions or @@@ system
commands, which are written by someone who needs no such shorthand and
whose forms are already whole.
Also fixed, each of them silent: an exported shell function in the
environment broke every @eval in the document; a line break written with
@nl was destroyed when it passed through a klammer that generates a
table, printing as \textbackslash{}newline in the finished PDF; and
"kdesc --coverage" shortened its file column by comparing the spelling of
a path rather than the path, so a klammer set reached by a symbolic link
showed a full path where its neighbours showed a short one.
New in the distribution: ref/, a small klammerset that describes the
Klammermachine's own tables in Klammertext. It currently provides the
character tables -- the diacritics and the extended Latin letters -- as
klammers that render to any target, so the documentation of what can be
written cannot drift from what the machine accepts. It is early, and
will grow.
The Klammertext taxonomy now has a section on targets, describes the
@@@klammerset system command alongside the other three, and states the
precedence of "^" -- what a caret means before a letter, a digit, a
punctuation character, and a quote, in one ordered list.
(from dev 6594c7ff8c1f)
266 lines
7.4 KiB
C++
266 lines
7.4 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <ranges>
|
|
#include <algorithm>
|
|
|
|
#include "katom.h"
|
|
#include "ktype.h"
|
|
#include "katomize.h"
|
|
#include "error.h"
|
|
#include "log.h"
|
|
#include "show.h"
|
|
#include "util.h"
|
|
#include "file.h"
|
|
|
|
bool dbg = false;
|
|
|
|
// KATOMIZATION IS NOT HERE. Turning text into katoms moved to
|
|
// katomize.{h,cpp} (2026-09-02), leaving this file the KATOM: what it is,
|
|
// what its type means, and the operations on a list of them. The test that
|
|
// decided the split is in katomize.h -- does a character stream enter?
|
|
// Everything below answers no.
|
|
|
|
size_t Katom::index = 0;
|
|
|
|
// A katom holds its source text and its text, and decides nothing about
|
|
// either. It used to strip the "^" from a quoted special here -- knowledge
|
|
// of what a construct looks like IN SOURCE, inside the datum, which is the
|
|
// one thing the katom/katomization split says does not belong here. That
|
|
// moved to source_katom() in katomize.{h,cpp} on 2026-09-02, the one place
|
|
// a katom is made from matched source text.
|
|
Katom::Katom(const std::string& src, katom_t type, Locator loc)
|
|
: m_index(Katom::index++)
|
|
, m_text(src)
|
|
, m_src(src)
|
|
, m_loc(loc)
|
|
, m_type(type)
|
|
, m_initial_type(type)
|
|
{
|
|
}
|
|
|
|
int active_count(const katom_list& katoms)
|
|
{
|
|
return std::ranges::count_if(
|
|
katoms,
|
|
[] (const Katom& k) {
|
|
return k.m_type != katom_t::replaced
|
|
&& k.m_type != katom_t::ignored
|
|
&& k.m_type != katom_t::space
|
|
&& k.m_type != katom_t::newline;
|
|
});
|
|
}
|
|
|
|
bool active(const katom_list& katoms)
|
|
{
|
|
return active_count(katoms) > 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void restore_initial_type(katom_iter begin, katom_iter end)
|
|
{
|
|
std::for_each(
|
|
begin, end, [](Katom& k) { k.m_type = k.m_initial_type; });
|
|
}
|
|
|
|
void modify_type(katom_t new_type, katom_iter begin, katom_iter end)
|
|
{
|
|
std::for_each(
|
|
begin, end, [&new_type](Katom& k) { k.m_type = new_type; });
|
|
}
|
|
|
|
void modify_type(katom_t old_type, katom_t new_type, katom_iter begin, katom_iter end)
|
|
{
|
|
std::for_each(
|
|
begin, end, [&](Katom& k) { if (k.m_type == old_type) k.m_type = new_type; });
|
|
}
|
|
|
|
void ignore_whitespace(katom_iter& begin, katom_list& katoms)
|
|
{
|
|
constexpr bool _dbg = false;
|
|
if (begin < katoms.end()) {
|
|
katom_iter ki = begin;
|
|
if (_dbg) std::cout << "IGNORE_WHITESPACE: ";
|
|
auto end = katoms.end();
|
|
while (ki < end && ki->is_whitespace()) {
|
|
if (_dbg) std::cout << kall << ktype << *ki << sp_arrow;
|
|
ki->m_type = katom_t::ignored;
|
|
if (_dbg) std::cout << kignored << kall << ktype << *ki << " " << "\n";
|
|
++ki;
|
|
}
|
|
if (_dbg) std::cout << black << kreset;
|
|
}
|
|
}
|
|
|
|
katom_iter after_whitespace(katom_iter begin)
|
|
{
|
|
katom_iter result = begin;
|
|
while (result->is_whitespace()) {
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<Katom> text_katoms(katom_iter& begin, katom_iter& end)
|
|
{
|
|
katom_list result {};
|
|
for (auto ki = begin; ki < end; ki++) {
|
|
if (!ki->is_whitespace()) {
|
|
auto k = *ki;
|
|
//msg() << " push: " << kindex << ktype << kall << kws << k << "\n";
|
|
result.push_back(k);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string as_string(std::vector<Katom>::const_iterator begin, std::vector<Katom>::const_iterator end, bool strip_whitespace)
|
|
{
|
|
auto include =
|
|
[&](katom_t t)
|
|
{ return t != katom_t::replaced and t != katom_t::ignored; };
|
|
std::string result {};
|
|
for (auto ki = begin; ki < end; ki++) {
|
|
katom_t t = ki->m_type;
|
|
if (include(t)) {
|
|
std::string text = !ki->m_display.empty() ? ki->m_display : ki->m_text;
|
|
result += text;
|
|
}
|
|
}
|
|
if (strip_whitespace)
|
|
result = trim(result);
|
|
return result;
|
|
}
|
|
|
|
std::string as_string(const katom_list& ks, bool strip_whitespace)
|
|
{
|
|
return as_string(ks.begin(), ks.end(), strip_whitespace);
|
|
}
|
|
|
|
katom_list trim(katom_list& katoms, std::set<katom_t> trim_types)
|
|
{
|
|
katom_iter begin =
|
|
std::find_if(katoms.begin(), katoms.end(),
|
|
[&trim_types](const Katom& k) { return !trim_types.contains(k.m_type); });
|
|
if (begin == katoms.end()) {
|
|
return katom_list{};
|
|
}
|
|
katom_iter end = katoms.end() - 1;
|
|
while (trim_types.contains(end->m_type)) {
|
|
--end;
|
|
}
|
|
return katom_list(begin, end+1);
|
|
}
|
|
|
|
katom_list trim(const katom_list& katoms, bool trim_inactive)
|
|
{
|
|
katom_list result = katoms;
|
|
std::set<katom_t> trim_types {katom_t::space, katom_t::newline};
|
|
if (trim_inactive) {
|
|
trim_types.insert(katom_t::replaced);
|
|
trim_types.insert(katom_t::ignored);
|
|
}
|
|
return trim(result, trim_types);
|
|
}
|
|
|
|
|
|
// I'm, like, this is
|
|
// some weird shit;
|
|
// what the fuck?
|
|
|
|
|
|
// Whitespace
|
|
|
|
std::pair<katom_iter,katom_iter> whitespace_span(const katom_list& katoms, const katom_iter& start, katom_t start_type)
|
|
{
|
|
katom_iter ki = start;
|
|
if (ki != katoms.begin()) {
|
|
--ki;
|
|
while (ki != katoms.begin() && ki->is_whitespace()) {
|
|
--ki;
|
|
}
|
|
if (!ki->is_whitespace()) {
|
|
++ki;
|
|
}
|
|
}
|
|
katom_iter begin = ki;
|
|
while (ki != katoms.end() && (ki->is_whitespace() || ki->m_type == start_type
|
|
|| ki->m_type == katom_t::ignored || ki->m_type == katom_t::replaced)) {
|
|
++ki;
|
|
}
|
|
//katom_iter end = ki;
|
|
return {begin, ki}; //end};
|
|
}
|
|
|
|
std::vector<Katom> find_katoms_of_type(const katom_list& katoms, katom_t type)
|
|
{
|
|
auto result = katoms | std::views::filter([type](const Katom& k) { return k.m_type == type; });
|
|
return std::vector(result.begin(), result.end());
|
|
}
|
|
|
|
katom_iter find_katom_of_type(katom_iter begin, katom_iter end, katom_t type)
|
|
{
|
|
return std::find_if(begin, end, [&](const Katom& k) { return k.m_type == type; });
|
|
}
|
|
|
|
// #-
|
|
|
|
void remove_whitespace(katom_list& katoms) // Lint error
|
|
{
|
|
(void)K::log(3);
|
|
auto begin = katoms.begin();
|
|
while (begin < katoms.end()) {
|
|
auto ki = find_katom_of_type(begin, katoms.end(), katom_t::ws_remove);
|
|
if (ki == katoms.end()) break;
|
|
auto [b, e] = whitespace_span(katoms, ki, katom_t::ws_remove);
|
|
std::for_each(b, e, [](Katom& k) { k.m_type = katom_t::ignored; });
|
|
begin = e;
|
|
}
|
|
}
|
|
|
|
// #+n and #/n
|
|
|
|
int whitespace_arg(Katom& k)
|
|
{
|
|
int result = 1;
|
|
std::smatch match{};
|
|
if (std::regex_match(k.m_text, match, std::regex(R"(#[+/](\d*))"))) {
|
|
if (!match[1].str().empty()) {
|
|
result = stoi(match[1]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void insert_whitespace(katom_list& katoms, katom_t type, const std::string& c)
|
|
{
|
|
(void)K::log(3);
|
|
for (Katom k : find_katoms_of_type(katoms, type)) {
|
|
katom_iter ki = find_katom(katoms.begin(), katoms.end(), k.m_index);
|
|
auto count = whitespace_arg(k);
|
|
auto [b, e] = whitespace_span(katoms, ki, type);
|
|
std::for_each(b, e, [](Katom& ka) { ka.m_type = katom_t::ignored; });
|
|
katom_list inserted = std::vector<Katom>{};
|
|
for (int i = 0; i < count; ++i) {
|
|
inserted.push_back(Katom(c, katom_t::ws_added, k.m_loc));
|
|
}
|
|
katoms.insert(e, inserted.begin(), inserted.end());
|
|
}
|
|
}
|
|
|
|
void process_whitespace_modifiers(katom_list& katoms)
|
|
{
|
|
(void)K::log(4);
|
|
remove_whitespace(katoms);
|
|
insert_whitespace(katoms, katom_t::ws_space, " ");
|
|
insert_whitespace(katoms, katom_t::ws_newline, "\n");
|
|
}
|
|
|
|
katom_list trim_whitespace(katom_list katoms)
|
|
{
|
|
return trim(katoms, {katom_t::space, katom_t::newline});
|
|
}
|