Ship the escaping test suite; add Provenance to the README
tst/ now carries all three engine regression suites (cond, deftype, escape) and the Makefile runs them. The README gains a Provenance section: this repository is a curated snapshot of the private development tree, stamped with the development commit it was assembled from. (from dev f6463478da4c)
This commit is contained in:
11
README.md
11
README.md
@@ -30,6 +30,17 @@ on).
|
|||||||
Syntax highlighting and editing support for Emacs and Sublime Text are in
|
Syntax highlighting and editing support for Emacs and Sublime Text are in
|
||||||
[`doc/edit/`](doc/edit/).
|
[`doc/edit/`](doc/edit/).
|
||||||
|
|
||||||
|
## Provenance
|
||||||
|
|
||||||
|
This repository is a curated snapshot of Klammertext's private development
|
||||||
|
tree, assembled by a manifest-driven script. Its history is a series of
|
||||||
|
release snapshots, not a mirror of the development history, and files here
|
||||||
|
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 `f6463478da4c`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
See [`LICENSE.md`](LICENSE.md).
|
See [`LICENSE.md`](LICENSE.md).
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Klammertext distribution test suite (subset).
|
# Klammertext distribution test suite (subset).
|
||||||
#
|
#
|
||||||
# Runs the two shell regression suites:
|
# Runs the three shell regression suites:
|
||||||
# cond_test.sh — @cond argument delimitation
|
# cond_test.sh — @cond argument delimitation
|
||||||
# deftype_test.sh — the four klammer definition modes + redefinition table
|
# deftype_test.sh — the four klammer definition modes + redefinition table
|
||||||
|
# escape_test.sh — target character escaping and quoted specials
|
||||||
#
|
#
|
||||||
# Requires KLAMMERTEXT_HOME set and `ktext` on PATH (build it with `make -C com`).
|
# Requires KLAMMERTEXT_HOME set and `ktext` on PATH (build it with `make -C com`).
|
||||||
|
|
||||||
@@ -10,3 +11,4 @@
|
|||||||
test:
|
test:
|
||||||
./cond_test.sh
|
./cond_test.sh
|
||||||
./deftype_test.sh
|
./deftype_test.sh
|
||||||
|
./escape_test.sh
|
||||||
|
|||||||
132
tst/escape_test.sh
Executable file
132
tst/escape_test.sh
Executable file
@@ -0,0 +1,132 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# escape_test.sh — Regression tests for the Klammermachine's target character
|
||||||
|
# escaping MECHANISM.
|
||||||
|
#
|
||||||
|
# Kept SKS-INDEPENDENT on purpose: tst/ tests the engine only (mac/target.cpp,
|
||||||
|
# mac/machine.cpp), not the SKS. A target is a MACHINE construct — declared
|
||||||
|
# with the @@@target system command, not owned by any klammer set — so the
|
||||||
|
# idiomatic engine-level test defines its own fixture target inline and loads
|
||||||
|
# no klammer set (`-k none`); it does not "avoid" the SKS so much as have no
|
||||||
|
# need of it. Target `t` here escapes `& -> AMP`, `_ -> UND`, `\ -> BSL`
|
||||||
|
# (arbitrary tokens, easy to assert). The SKS's own targets (tex, html) and
|
||||||
|
# the specific characters they declare are exercised by the SKS suite.
|
||||||
|
#
|
||||||
|
# The `:escape` parameter on `@@@target` declares characters special in a
|
||||||
|
# target's output and their replacements. Escaping must reach writer content
|
||||||
|
# wherever it appears — including the body of a GENERAL klammer (no target
|
||||||
|
# suffix), which is target-agnostic writer text — while leaving target-native
|
||||||
|
# content alone:
|
||||||
|
#
|
||||||
|
# - general klammer body -> writer content, ESCAPE it
|
||||||
|
# - target-specific body (.t) -> already in target form, LEAVE it
|
||||||
|
# - ^'...'^ literal span -> raw target markup, LEAVE it
|
||||||
|
# - nested target-native klammer -> e.g. @n@ -> \newline, LEAVE it
|
||||||
|
# - @eval/@read/@cond arg span -> code/path/predicate, LEAVE it
|
||||||
|
# - klammer-producing @eval result -> klammer output, LEAVE it
|
||||||
|
#
|
||||||
|
# Cases 8-9 pin a regression: escaping a general klammer body once corrupted
|
||||||
|
# the @eval CODE inside it (an underscore in "offer.Price_list(K)" became a
|
||||||
|
# KTESC marker -> Python AttributeError). Only the general body's own literal
|
||||||
|
# writer text is escaped; @eval/@read/@cond argument spans (code, filenames,
|
||||||
|
# predicates) are skipped, and the Klammertext a nested @eval produces is
|
||||||
|
# klammer output, not writer text, so it is never escaped. Mechanism:
|
||||||
|
# Klammer::m_body_generic + the hook in Machine::apply_klammer; see the
|
||||||
|
# "Target character escaping" section of CLAUDE.md.
|
||||||
|
#
|
||||||
|
# Usage: ./escape_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}
|
||||||
|
ERR=/tmp/escape_test_err.$$
|
||||||
|
|
||||||
|
red=$'\033[31m'
|
||||||
|
green=$'\033[32m'
|
||||||
|
bold=$'\033[1m'
|
||||||
|
reset=$'\033[0m'
|
||||||
|
|
||||||
|
# strip leading/trailing blank lines and trailing whitespace
|
||||||
|
trim() { awk '{ sub(/[ \t\r]+$/, "") } { line[NR]=$0 } END { f=1; while (f<=NR && line[f]=="") f++; l=NR; while (l>=1 && line[l]=="") l--; for (i=f;i<=l;i++) print line[i] }'; }
|
||||||
|
|
||||||
|
# check_eq NAME EXPECTED KTEXT_ARGS... — exit 0 and stdout==EXPECTED.
|
||||||
|
check_eq() {
|
||||||
|
local name="$1" expected="$2"; shift 2
|
||||||
|
local out status err
|
||||||
|
out=$("$KTEXT" "$@" 2>"$ERR"); status=$?
|
||||||
|
err=$(cat "$ERR")
|
||||||
|
out=$(printf '%s' "$out" | trim)
|
||||||
|
if [ $status -ne 0 ]; then
|
||||||
|
echo "${red}FAIL${reset} $name — ktext exited $status"
|
||||||
|
echo " stderr: $(echo "$err" | head -2)"; FAIL=$((FAIL+1)); return
|
||||||
|
fi
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "${bold}Target character escaping tests (Machine mechanism, fixture target 't')${reset}"
|
||||||
|
echo "======================================================================="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# A self-contained fixture target, defined inline via the @@@target system
|
||||||
|
# command, escaping & _ \ to distinct tokens. -k none loads no klammer set,
|
||||||
|
# so nothing below depends on the SKS.
|
||||||
|
T='@@@target t | test target :escape & AMP _ UND \ BSL @@@'
|
||||||
|
|
||||||
|
check_eq " 1. top-level text: & escaped" 'A AMP B' -k none -t t -s "$T A & B"
|
||||||
|
check_eq " 2. general klammer body: & escaped" 'A AMP B' -k none -t t -s "$T @@g : A & B @@ @g@"
|
||||||
|
check_eq " 3. general klammer body: _ escaped" 'AUNDB' -k none -t t -s "$T @@g : A_B @@ @g@"
|
||||||
|
check_eq " 4. general klammer body: backslash escaped" 'aBSLb' -k none -t t -s "$T @@g : a\\b @@ @g@"
|
||||||
|
check_eq " 5. target-specific body: NOT escaped" 'A & B' -k none -t t -s "$T @@g.k : d @@ @@g.t :: A & B @@ @g@"
|
||||||
|
check_eq " 6. general body ^'...'^ literal: NOT escaped" 'a&b' -k none -t t -s "$T @@g : ^'a&b'^ @@ @g@"
|
||||||
|
check_eq " 7. nested target-native klammer survives escape" 'X AMP Y \newline Z' -k none -t t -s "$T @@n.t : \\newline @@ @@g : X & Y @n@ Z @@ @g@"
|
||||||
|
# 8-9: @eval inside a general body. Code (with underscores) must not be
|
||||||
|
# escaped or Python breaks; the Klammertext it returns is klammer output and
|
||||||
|
# must not be escaped either. chr(64) builds a literal '@' so the returned
|
||||||
|
# klammer call is not parsed as one in this source line.
|
||||||
|
check_eq " 8. general body @eval code NOT escaped" '3' -k none -t t -s "$T @@g : @eval (1).__add__(2) @ @@ @g@"
|
||||||
|
check_eq " 9. general body @eval klammer result NOT escaped" '\textbf{hi}' -k none -t t -s "$T @@b.k s : d @@ @@b.t :: \\textbf{*s*} @@ @@g : @eval chr(64)+'b hi '+chr(64) @ @@ @g@"
|
||||||
|
# 10-11: an @eval result that still holds klammers is a GENERATOR (Klammertext
|
||||||
|
# with data) -- its writer text is escaped for the target before the klammers
|
||||||
|
# are applied; a result with no klammers is a RENDERER (final markup) -- left
|
||||||
|
# untouched. The signal is "does the read-back result contain a klammer".
|
||||||
|
check_eq "10. @eval generator: klammer result's data escaped" '[a AMP b]' -k none -t t -s "$T @@wrap z : [*z*] @@ @@g : @eval chr(64)+'wrap a & b '+chr(64) @ @@ @g@"
|
||||||
|
check_eq "11. @eval renderer: final markup NOT escaped" 'raw & markup' -k none -t t -s "$T @@g : @eval 'raw & markup' @ @@ @g@"
|
||||||
|
|
||||||
|
# 12-18: quoted KLAMMERTEXT specials (^@ ^| ^# ^^ ^: ^*) and ^'...'^ literal
|
||||||
|
# regions. The katomizer strips the "^"; hide_special_katoms() and
|
||||||
|
# mark_literal_katoms() then hold the character as a KTESC marker so it stays
|
||||||
|
# inert through sub-Machine re-katomization and the @eval result read-back,
|
||||||
|
# resolving to the literal character at final processing. Regression: after
|
||||||
|
# the marker mechanism replaced the old hide/restore pass, a quoted "@"
|
||||||
|
# leaked as a bare apply-end katom into re-read text ("A klammer ends
|
||||||
|
# without a beginning"). Exact-match expectations also guard against KTESC
|
||||||
|
# markers leaking into output.
|
||||||
|
check_eq "12. quoted @ | # resolve to the characters" 'x @ | # y' -k none -t t -s "$T x ^@ ^| ^# y"
|
||||||
|
check_eq "13. quoted ^ : * resolve to the characters" 'x ^ : * y' -k none -t t -s "$T x ^^ ^: ^* y"
|
||||||
|
check_eq "14. ^'...'^ region: specials stay literal" 'a @ | b' -k none -t t -s "$T a ^' @ | '^ b"
|
||||||
|
check_eq "15. general body: quoted @ resolves" 'x @ y' -k none -t t -s "$T @@g : x ^@ y @@ @g@"
|
||||||
|
# 16: inside an @eval argument span a quoted special reaches the CODE as the
|
||||||
|
# character (the span is skipped by hide_special_katoms, like the escape pass).
|
||||||
|
check_eq "16. @eval code: quoted : reaches shell as ':'" 'x:y' -k none -t t -s "$T @@g : @eval :shell echo x^:y @ @@ @g@"
|
||||||
|
# 17: an @eval result emitting the two characters ^ @ is re-read as a quoted
|
||||||
|
# special and survives to the output as a literal @ (the generator idiom for
|
||||||
|
# a literal @; a bare @ in a result is a parse error by design).
|
||||||
|
check_eq "17. @eval result ^@ survives read-back as @" '@' -k none -t t -s "$T @@g : @eval chr(94)+chr(64) @ @@ @g@"
|
||||||
|
# 18: a bare-Python :after_apply phase receives the RESOLVED result text
|
||||||
|
# (K_result) and its return is taken as raw target text, not re-read as
|
||||||
|
# Klammertext -- a resolved @ in the result must not be re-parsed.
|
||||||
|
check_eq "18. :after_apply phase: raw result, @ intact" 'A @ B' -k none -t u -s '@@@target u | up :after_apply string.capwords @@@ a ^@ b'
|
||||||
|
|
||||||
|
rm -f "$ERR"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "============================================"
|
||||||
|
echo "Results: ${green}$PASS passed${reset}, ${red}$FAIL failed${reset}"
|
||||||
|
[ $FAIL -eq 0 ]
|
||||||
Reference in New Issue
Block a user