From c016b3e5f34fe798c17c99ed84a01513ba379969 Mon Sep 17 00:00:00 2001 From: Andy Kopra Date: Tue, 22 Sep 2026 22:53:13 +0200 Subject: [PATCH] kdesc --rewrite from the reflection klammerset; txt caption rules the block's width Assembled from klammertext-dev e99530408517. The katom rewrite patterns are rendered from ref/ (a table in dispatch order, patterns through @c), the procedural listing having left mac/; a plain-text caption's rule is as long as the table or listing it captions; the editor-support comments name the tools' new dev/ location. --- README.md | 2 +- com/kdesc.cpp | 9 +++- doc/edit/README.md | 2 +- doc/edit/shared/klammertext_edit.py | 2 +- doc/edit/sublime/Klammertext.py | 2 +- doc/edit/sublime/Klammertext_align.py | 2 +- doc/edit/sublime/Klammertext_indent.py | 2 +- env/makefile.env | 2 +- kpy/Makefile | 2 +- mac/eval_python.cpp | 2 +- mac/katom_rewrite_rules.cpp | 58 ---------------------- mac/katom_rewrite_rules.h | 6 --- ref/ref.k | 7 +++ ref/reflect.cpp | 69 +++++++++++++++++++++----- sks/target/phases.py | 20 ++++++++ 15 files changed, 101 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 0ae6e3d..70d6bfc 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ are regenerated on each release — patches cannot be merged directly. Report problems (or send patches) to the author; accepted changes are applied to the development tree and appear in a following snapshot. -This snapshot was assembled from development commit `e727ce1d248d`. +This snapshot was assembled from development commit `e99530408517`. ## License diff --git a/com/kdesc.cpp b/com/kdesc.cpp index a59a44c..719102f 100644 --- a/com/kdesc.cpp +++ b/com/kdesc.cpp @@ -201,6 +201,13 @@ void describe_katoms(bool show_regex) std::cout << "\n" << M.apply(text_target()) << "\n\n"; } +void show_katom_rewrite_patterns() +{ + Machine M = ref_machine(); + M.read_string("@katom_rewrite_patterns@"); + std::cout << "\n" << M.apply(text_target()) << "\n\n"; +} + int main(int argc, char* argv[]) { try { @@ -269,7 +276,7 @@ int main(int argc, char* argv[]) } if (p("rewrite")) { - describe_rewrite_patterns(); + show_katom_rewrite_patterns(); } // The --typeface subcommands operate on the Klammertext font store diff --git a/doc/edit/README.md b/doc/edit/README.md index 7a4aec2..f9f04a2 100644 --- a/doc/edit/README.md +++ b/doc/edit/README.md @@ -75,7 +75,7 @@ Two kinds of artifact cannot read it and restate parts of it by hand: ## Testing -`tst/editor_test.sh` (run by `dbg/rebuild.sh` and `make -C tst test`) +`tst/editor_test.sh` (run by `dev/rebuild.sh` and `make -C tst test`) drives the fixture pairs in `tst/editor/` through the shared core's API and CLI, checks idempotence, checks the Emacs implementation for byte-equality, runs the language server through a scripted LSP client diff --git a/doc/edit/shared/klammertext_edit.py b/doc/edit/shared/klammertext_edit.py index f246ff4..4ffccfb 100644 --- a/doc/edit/shared/klammertext_edit.py +++ b/doc/edit/shared/klammertext_edit.py @@ -42,7 +42,7 @@ # * the '@code'/'@c' rules in doc/edit/vscode/syntaxes/klammertext.tmLanguage.json # # Installation note: editors locate this file either next to their own plugin -# files (a vendored copy, placed there by doc/make_editing_zip.sh), as +# files (a vendored copy, placed there by dev/make_editing_zip.sh), as # ../shared/klammertext_edit.py relative to the plugin directory (the layout # of this repository), or under $KLAMMERTEXT_HOME/doc/edit/shared/. # diff --git a/doc/edit/sublime/Klammertext.py b/doc/edit/sublime/Klammertext.py index b16bfb5..6f409bb 100644 --- a/doc/edit/sublime/Klammertext.py +++ b/doc/edit/sublime/Klammertext.py @@ -27,7 +27,7 @@ # policy list. This file is only the Sublime wrapper. # # The shared core is located next to this file (a vendored copy — the -# installed-package layout produced by doc/make_editing_zip.sh), or in +# installed-package layout produced by dev/make_editing_zip.sh), or in # ../shared (the repository layout), or under $KLAMMERTEXT_HOME. # # SYNC: the literal-klammer set in the shared core must agree with the @code diff --git a/doc/edit/sublime/Klammertext_align.py b/doc/edit/sublime/Klammertext_align.py index 01863a9..92ed5ec 100644 --- a/doc/edit/sublime/Klammertext_align.py +++ b/doc/edit/sublime/Klammertext_align.py @@ -23,7 +23,7 @@ # Sublime command wrapper. # # The shared core is located next to this file (a vendored copy — the -# installed-package layout produced by doc/make_editing_zip.sh), or in +# installed-package layout produced by dev/make_editing_zip.sh), or in # ../shared (the repository layout), or under $KLAMMERTEXT_HOME. import os diff --git a/doc/edit/sublime/Klammertext_indent.py b/doc/edit/sublime/Klammertext_indent.py index c30d2c6..fdc0ec0 100644 --- a/doc/edit/sublime/Klammertext_indent.py +++ b/doc/edit/sublime/Klammertext_indent.py @@ -18,7 +18,7 @@ # the language server. This file is only the Sublime command wrapper. # # The shared core is located next to this file (a vendored copy — the -# installed-package layout produced by doc/make_editing_zip.sh), or in +# installed-package layout produced by dev/make_editing_zip.sh), or in # ../shared (the repository layout), or under $KLAMMERTEXT_HOME. import os diff --git a/env/makefile.env b/env/makefile.env index 2fedfa3..bac46e9 100644 --- a/env/makefile.env +++ b/env/makefile.env @@ -18,7 +18,7 @@ UNAME_S := $(shell uname -s) CPP_VERSION := c++20 # Default compiler: clang on macOS, gcc elsewhere. gcc-built Klammertext # binaries crash at runtime on macOS (a documented gcc/macOS codegen issue), so -# gcc there is only a compile-time conformance check (via `./dbg/rebuild.sh +# gcc there is only a compile-time conformance check (via `./dev/rebuild.sh # gcc`, which compiles under g++ then rebuilds with clang). Override with # `make COMPILER=...`. ifeq ($(UNAME_S),Darwin) diff --git a/kpy/Makefile b/kpy/Makefile index fe1d1de..fb93ec4 100644 --- a/kpy/Makefile +++ b/kpy/Makefile @@ -4,7 +4,7 @@ # Like ref/reflect.so and sks/document/document.so it links against # libklammertext.so and shares the engine's class layouts, so it is rebuilt # whenever they are (mac/Makefile's "all" runs "make -C kpy"; com/Makefile's -# "redo" and dbg/rebuild.sh clean it -- a clean that misses a directory is a +# "redo" and dev/rebuild.sh clean it -- a clean that misses a directory is a # mixed-object hazard, not a tidiness one). # # The module's filename carries the interpreter's ABI tag diff --git a/mac/eval_python.cpp b/mac/eval_python.cpp index 395f0b4..f56f8a4 100644 --- a/mac/eval_python.cpp +++ b/mac/eval_python.cpp @@ -261,7 +261,7 @@ std::string Eval_python::eval(std::string code) (void)K::log(3, code); code = m_machine.m_state.subst(code, true); // Kept as a record of a value worth watching; line comments rather than a - // block, so the msg() guard can see it is inert (doc/check_output_policy.sh + // block, so the msg() guard can see it is inert (dev/check_output_policy.sh // is line-based and cannot tell it is inside a /* */). // msg() << "\n" // << std::string(80, '-') << "\n" diff --git a/mac/katom_rewrite_rules.cpp b/mac/katom_rewrite_rules.cpp index 8274bac..22ff2e7 100644 --- a/mac/katom_rewrite_rules.cpp +++ b/mac/katom_rewrite_rules.cpp @@ -321,61 +321,3 @@ bool category_order_holds(const std::string& word, std::string& why, rewrite_ord } return true; } - -void describe_rewrite_patterns() -{ - if (verbose_level > 0) { - std::string desc_text = "doc/katom_patterns.desc"; - std::cout << "\n" - << justify(string_from_file(klammertext_filename(desc_text))) - << "\n\n"; - } - size_t width0 = 0; - size_t width1 = 0; - size_t width2 = 0; - for (const Rewrite_rule& rule : rewrite_rules) { - width0 = std::max(width0, category_name(rule.category).size()); - width1 = std::max(width1, rule.description.size()); - width2 = std::max(width2, rule.pattern.m_pattern.size()); - } - width0 += 2; - width1 += 2; - width2 += 2; - - std::cout << boldblack << std::setfill(' ') - << std::setw(width0) << std::left << " Category" - << std::setw(width1) << std::left << "Description" - << std::setw(width2) << std::left << "Pattern" - << "Replacement" << black << "\n"; - - // Dispatch order, not declaration order: what a reader needs is the - // sequence the rules are actually TRIED in, since that is what decides - // which one fires. - rule_category shown {}; - bool first = true; - for (size_t i : rule_order(rewrite_order::by_category)) { - const Rewrite_rule& rule = rewrite_rules[i]; - std::string category = category_name(rule.category); - if (first || rule.category != shown) { - shown = rule.category; - first = false; - } else { - category = ""; // name a category once, at its head - } - std::string desc = rule.description; - desc[0] = static_cast(toupper(desc[0])); - std::cout << " " - << std::left - << std::setw(width0) << category - << std::setw(width1) << desc - << std::setw(width2) << rule.pattern.m_pattern - << (rule.special ? "(not a pattern replacement)" : rule.replacement) - << "\n"; - } - if (verbose_level > 0) { - std::string notes_text = "doc/katom_patterns.notes"; - std::cout << "\n\n" - << justify(string_from_file(klammertext_filename(notes_text))) - << "\n\n"; - } -} diff --git a/mac/katom_rewrite_rules.h b/mac/katom_rewrite_rules.h index aa236ae..10d5bb5 100644 --- a/mac/katom_rewrite_rules.h +++ b/mac/katom_rewrite_rules.h @@ -142,9 +142,3 @@ std::vector matching_rules(const std::string& word, rewrite_order order // state an invariant across. bool category_order_holds(const std::string& word, std::string& why, rewrite_order order = rewrite_order::by_category); - -// The "kdesc --rewrite" listing. It moved here with the table it reads, -// and gained the CATEGORY column -- which is the point of the categories -// reaching the user rather than only the maintainer: a reader sees the nine -// groups and their order instead of thirty positions. -void describe_rewrite_patterns(); diff --git a/ref/ref.k b/ref/ref.k index 8b123c0..3bab904 100644 --- a/ref/ref.k +++ b/ref/ref.k @@ -48,3 +48,10 @@ @@katom_descriptions.* :: @eval :cpp *KLAMMERTEXT_HOME*/ref/reflect katom_descriptions @ @@ + +@@katom_rewrite_patterns.k : + Show the rewrite patterns for parsed text that is not a katom @@ + +@@katom_rewrite_patterns.* :: + @eval :cpp *KLAMMERTEXT_HOME*/ref/reflect katom_rewrite_patterns @ @@ + diff --git a/ref/reflect.cpp b/ref/reflect.cpp index 2e9ae3e..c9ebffa 100644 --- a/ref/reflect.cpp +++ b/ref/reflect.cpp @@ -194,6 +194,24 @@ std::string argtype_definitions(Machine& machine) } +// Verbatim text as a table cell: a regex, a replacement string. It goes +// through @c, whose literal content is exempt from every special (a bar, a +// hash, an at sign) -- but a literal value has its caret forms processed as +// it is built, so a caret is written quoted: bare, "[^^@#|]+" would display +// as "[^@#|]+" and "[@|#^:*]" would lose its caret to the colon. The +// exception is a caret before an apostrophe: "^'" is not a quoting form (it +// opens a literal span elsewhere) and passes through as written. +static std::string code_cell(const std::string& text) +{ + std::string quoted {}; + for (size_t i = 0; i < text.size(); i++) { + const char c = text[i]; + const bool before_apostrophe = i + 1 < text.size() && text[i + 1] == '\''; + quoted += (c == '^' && !before_apostrophe) ? "^^" : std::string(1, c); + } + return "@c :nd | " + quoted + " c@"; +} + // The Regex cell of a katom type. A type the Machine assigns is matched by // no regex and its cell is empty -- ws_added among them, whose m_pattern is a // description and not a regex (see the note in mac/katom_matching.cpp). The @@ -209,18 +227,7 @@ static std::string katom_regex_cell(const Ktype& k) if (k.m_type == katom_t::newline) { return "@t \\n @"; } - // A literal value has its caret forms processed as it is built, so a - // caret is written quoted: bare, "[^^@#|]+" would display as "[^@#|]+" - // and "[@|#^:*]" would lose its caret to the colon. - // The exception is a caret before an apostrophe: "^'" is not a quoting - // form (it opens a literal span elsewhere) and passes through as written. - std::string quoted {}; - for (size_t i = 0; i < k.m_pattern.size(); i++) { - const char c = k.m_pattern[i]; - const bool before_apostrophe = i + 1 < k.m_pattern.size() && k.m_pattern[i + 1] == '\''; - quoted += (c == '^' && !before_apostrophe) ? "^^" : std::string(1, c); - } - return "@c :nd | " + quoted + " c@"; + return code_cell(k.m_pattern); } extern "C" @@ -290,3 +297,41 @@ characters can be displayed by the @t-kdesc command: @t kdesc -c -u @ kt << rows.str() << "table@\n"; return kt.str(); } + + +extern "C" +std::string katom_rewrite_patterns([[maybe_unused]] Machine& machine) +{ + std::stringstream kt {}; + kt << "@s2 Katom rewrite patterns @\n\n" + << R"(The table lists how a text is reformed or split into one or more katoms. + @i [Need a longer description here.] @ + +)" + << "@table :caption The rewriting patterns for elements that are not katoms | " + << "Category | Description | Pattern | Replacement ||\n"; + + // Dispatch order, not declaration order: what a reader needs is the + // sequence the rules are actually TRIED in, since that is what decides + // which one fires. + rule_category shown {}; + bool first = true; + std::string b = " | "; + for (size_t i : rule_order(rewrite_order::by_category)) { + const Rewrite_rule& rule = rewrite_rules[i]; + std::string category = category_name(rule.category); + if (first || rule.category != shown) { + shown = rule.category; + first = false; + } else { + category = ""; // name a category once, at its head + } + std::string desc = rule.description; + desc[0] = static_cast(toupper(desc[0])); + kt << category << b << desc << b << code_cell(rule.pattern.m_pattern) << b + << (rule.special ? "(hyphens separate word arguments)" : code_cell(rule.replacement)) + << " ||\n"; + } + kt << "table@\n"; + return kt.str(); +} diff --git a/sks/target/phases.py b/sks/target/phases.py index 1909754..d7d6fd8 100644 --- a/sks/target/phases.py +++ b/sks/target/phases.py @@ -244,10 +244,29 @@ def txt_insert_captions(text, K): # "Listing 1", not "Listing 2". It was one counter for every label. caption_numbers = {} width = int(K.Target_txt_width) + verbatim = txt_verbatim_marker() + def block_width(lines): + # The visible width of the laid-out block adjacent to a caption: + # its lines carry the verbatim marker, and the first line that does + # not (or a blank line) ends the block. + widest = 0 + for line in lines: + if not line.startswith(verbatim) or not line.strip(): + break + widest = max(widest, txt_visible_length(line)) + return widest def replace(m): indent_run, name, text = m.groups() indent = len(indent_run) // len(nbsp) indented_width = max(1, width - indent) + # The block the caption belongs to: below a top caption, above a + # bottom one. Its width is what the caption's rule is drawn to, + # so the rule under "Table 1 - Prices" is as long as the table's + # own rules, not as long as those words. (Before the match sits + # the caption line's own verbatim marker: drop that line.) + after = m.string[m.end():].split("\n")[1:] + before = m.string[:m.start()].split("\n")[:-1] + block = max(block_width(after), block_width(reversed(before))) # An empty label means an unnumbered caption (":number false"). It # must NOT take a number from the sequence: a reader counting the # captions they can see would then find the next one a number @@ -263,6 +282,7 @@ def txt_insert_captions(text, K): if txt_visible_length(caption) > indented_width: lines = txt_wrap(caption, indented_width) line_length = max(txt_visible_length(e) for e in lines) + line_length = max(line_length, block - indent) lines.append("-" * line_length) lead = txt_verbatim_marker() + indent_run # The first line's marker is already in the text, before the indent