Characters that killed a render, what may sit next to a klammer, and ref/

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)
This commit is contained in:
ack committed 2026-09-07 22:37:05 +02:00
1 parent b80e823ab3
commit e4f1307aa9
53 files changed
+2014 -350

No files matched your search

+157
View File
@@ -0,0 +1,157 @@
#!/bin/bash
#
# katomize_test.sh — Katomization: what abuts a klammer delimiter, and the
# katom rewrite rules that decide it.
#
# SKS-independent by construction: every case runs with --klammersets none
# and defines the one klammer it needs inline, because what is under test is
# where katom boundaries fall, not what any klammer renders.
#
# WHAT ABUTS "@" (Andy, 2026-09-03; notes/Klammertext_improvements.md §4.3).
# An author writes a klammer inside brackets, or with a sentence's
# punctuation after it, and expects the delimiter not to swallow the
# neighbour. The rule is a WHITELIST of characters that may abut, and the
# whitelist is safe for a structural reason worth stating once:
#
# None of ( [ { ) ] } . , : ; ? ! ' can appear in a definition_name
# ([a-zA-Z][a-zA-Z0-9_.]*) or in an abbreviated argument, so a delimiter
# meeting one of them has no second reading.
#
# That is also why LETTERS can never join the list, and why §4.3's "clean
# rule" (a delimiter terminates the preceding token unconditionally) cannot
# be unconditional: "x@" is a legitimate token — the named closing
# delimiter — so "x@i" is ambiguous in the grammar, not merely unhandled.
# Case 20 pins that as CURRENT BEHAVIOUR rather than as a defect.
#
# Usage: ./katomize_test.sh (needs KLAMMERTEXT_HOME set; ktext on PATH)
# Exit code: 0 if all tests pass, 1 otherwise.
PASS=0
FAIL=0
KTEXT=ktext
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
red=$'\033[31m'
green=$'\033[32m'
bold=$'\033[1m'
reset=$'\033[0m'
# An inline klammer, so nothing here depends on the SKS. It wraps its
# argument in < > so a swallowed bracket is visible in the result rather
# than merely absent.
DEF='@@w.* arg : <*arg*> @@'
# check NAME EXPECTED INPUT — render INPUT and compare the whole output.
check() {
local name="$1" expected="$2" input="$3"
local out
# A space between the definition and the input: without it the "@@"
# ending the definition abuts the input's first character, which is
# exactly the class of thing under test and breaks the fixture instead
# of the feature. The space does not survive into the output.
out=$("$KTEXT" --klammersets none -s "$DEF $input" -d 2>/dev/null)
if [ "$out" = "$expected" ]; then
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} $name"
echo " expected: [$expected]"
echo " got: [$out]"
FAIL=$((FAIL+1))
fi
}
# check_warn NAME INPUT — the render must warn "Word not parsed". Used for
# the cases that are NOT in the whitelist, so a later widening of the rule
# has to come here and say so deliberately.
check_warn() {
local name="$1" input="$2"
local err
err=$("$KTEXT" --klammersets none -s "$DEF $input" -d 2>&1 >/dev/null \
| tr '\n' ' ' | tr -s ' ')
case "$err" in
*"Word not parsed"*) echo "${green}PASS${reset} $name"; PASS=$((PASS+1));;
*) echo "${red}FAIL${reset} $name — expected a \"Word not parsed\" warning"
echo " got: [$err]"; FAIL=$((FAIL+1));;
esac
}
echo
echo "${bold}Katomization: what may abut a klammer delimiter${reset}"
echo "================================================================="
echo
echo "-- trailing punctuation after each of the four application forms --"
check " 1. abbreviated form, period" '<x>.' '@w-x.'
check " 2. abbreviated form, comma" '<x>,' '@w-x,'
check " 3. abbreviated form, semicolon" '<x>;' '@w-x;'
check " 4. abbreviated form, colon" '<x>:' '@w-x:'
check " 5. abbreviated form, question mark" '<x>?' '@w-x?'
check " 6. abbreviated form, exclamation" '<x>!' '@w-x!'
# The apostrophe is in the whitelist and must stay: "@w x @'s" is ordinary
# English prose and worked before the 2026-09-03 widening. Andy confirmed
# it explicitly when the bracket characters were added.
check " 7. apostrophe (possessive) is in the list" "<x>'s" "@w-x's"
check " 8. bare close, period" '<x>.' '@w x @.'
check " 9. named close, period" '<x>.' '@w x w@.'
check "10. named close, comma" '<x>,' '@w x w@,'
echo
echo "-- brackets around an application (( was honoured; [ and { added 2026-09-03) --"
check "11. parentheses, abbreviated" '(<x>)' '(@w-x)'
check "12. square brackets, abbreviated" '[<x>]' '[@w-x]'
check "13. braces, abbreviated" '{<x>}' '{@w-x}'
check "14. parentheses, bare close" '(<x>)' '(@w x @)'
check "15. square brackets, bare close" '[<x>]' '[@w x @]'
check "16. braces, bare close" '{<x>}' '{@w x @}'
check "17. square brackets, named close" '[<x>]' '[@w x w@]'
check "18. no spurious space is introduced" '[<x>].' '[@w-x].'
echo
echo "-- the boundary of the whitelist: what is NOT allowed to abut --"
# A hyphen after an OPENING name introduces an argument, so it cannot be
# text. After a CLOSING delimiter it is unambiguous and could be allowed;
# it is not, today, and this case records that rather than endorsing it.
check_warn "19. hyphen after a close is still unhandled" '@w x @-y'
# The contradiction that bounds the whole rule: "x@" is the named closing
# delimiter, so a letter before "@" has two readings and no precedence rule
# can separate them. This is the language, not a gap in the whitelist.
check_warn "20. a letter before \"@\" stays ambiguous" 'y@w x @'
echo
echo "-- the rules are AUTHOR-MODE: they never reach @@ or @@@ text --"
# A rewrite rule exists to spare an author typing. A designer writing "@@"
# or a programmer writing "@@@" needs no such convenience, and in fact needs
# none: those forms are whole katom TYPES. Enforced in find_rewrite(), so
# no rule can reach them however it is written.
# no_rewrite NAME INPUT -- kdiag must report no rule firing at all.
no_rewrite() {
local name="$1" input="$2" out
out=$(kdiag --rewrite "$input" 2>&1 | grep -c "Rewrite \[")
if [ "$out" = "0" ]; then
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} $name — $out rule(s) fired on definition-mode text"
FAIL=$((FAIL+1))
fi
}
no_rewrite "21. @@name fires no rule" '@@w.k'
no_rewrite "22. a target list fires no rule" '@@w.k,tex'
no_rewrite "23. @@@ fires no rule" '@@@target'
no_rewrite "24. a named definition close" 'w@@'
# Was the ONE violation: "no argument klammer" found "@w@" inside "@@w@@"
# and split it -- into the right katoms, but by coincidence. The form is
# malformed (a definition needs :, ::, ::: or ::::), so reporting it is
# better than silently splitting it.
no_rewrite "25. @@w@@ fires no rule either" '@@w@@'
check_warn "26. ... and is reported, not split" '@@w@@'
# A word can MIX TIERS, so the test is not "starts with @@". A definition
# closed flush against a whitespace operator is one word and the rules MUST
# see it -- this is real usage, from tst/modulepath_test.sh's fixture, and
# the first version of the guard broke it.
check "27. @@ glued to #- still splits" '<x>' '@@v.* : <x> @@#-@v@'
echo
echo "================================================================="
echo "${bold}Results: $PASS passed, $FAIL failed${reset}"
[ $FAIL -eq 0 ] && exit 0 || exit 1