Files
klammertext/tst/katomize_test.sh
T

169 lines
7.8 KiB
Bash
Raw Normal View History

#!/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 "-- what abuts a closing ]# --"
# A removal block ends at its "]#"; what follows is the writer's. Until
# 2026-10-02 mark_ignored_katoms() read find_span_katoms()'s half-open end
# as inclusive and removed the katom abutting the close as well: a sentence
# ending "#[ note ]#." lost its period. A following space or newline is
# removed by design, which is what hid it.
check "28. punctuation after ]# survives" 'a . c' 'a #[gone]#. c'
check "29. a letter after ]# survives" 'a x c' 'a #[gone]#x c'
check "30. a space after ]# is removed" 'a . c' 'a #[gone]# . c'
check "31. nested blocks, punctuation after" 'a . c' 'a #[ #[in]# out ]#. c'
echo
echo "================================================================="
echo "${bold}Results: $PASS passed, $FAIL failed${reset}"
[ $FAIL -eq 0 ] && exit 0 || exit 1