2026-07-18 18:48:23 +02:00
|
|
|
#!/bin/bash
|
|
|
|
|
#
|
|
|
|
|
# cond_test.sh — Regression tests for @cond argument delimitation.
|
|
|
|
|
#
|
|
|
|
|
# These tests pin down the fix for the bug witnessed by tst/cond_test.kt:
|
|
|
|
|
# a defined klammer that contains its own bar separators (e.g. @frac a | b @)
|
|
|
|
|
# nested inside a @cond branch caused @cond to miscount bars and reject the
|
|
|
|
|
# input with "There should only be one or two bar characters".
|
|
|
|
|
#
|
|
|
|
|
# Root cause: @cond delimited its arguments by counting EVERY bar in its flat
|
|
|
|
|
# katom range, conflating the inner klammer's bars (which belong to the inner
|
|
|
|
|
# klammer's arity) with @cond's own separators. The fix counts only the bars
|
|
|
|
|
# at nesting depth 0 within the @cond span (cond_separator_bars() in
|
|
|
|
|
# mac/machine.cpp), so argument boundaries follow the span tree.
|
|
|
|
|
#
|
|
|
|
|
# See doc/cond_evaluation_order.md for the full description and the
|
|
|
|
|
# theoretical basis (operadic arity, the precedence-order proposition, and
|
|
|
|
|
# @cond as a non-strict special form).
|
|
|
|
|
#
|
|
|
|
|
# Usage: ./cond_test.sh (LSan suppressions come from mac/env/runtime.env.*)
|
|
|
|
|
# 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'
|
|
|
|
|
|
|
|
|
|
# strip leading/trailing blank lines and surrounding whitespace
|
2026-07-19 17:44:03 +02:00
|
|
|
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] }'; }
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
# check_eq TEST_NAME EXPECTED KTEXT_ARGS...
|
|
|
|
|
# Runs ktext, expects exit status 0, and compares trimmed stdout to EXPECTED.
|
|
|
|
|
check_eq() {
|
|
|
|
|
local test_name="$1"
|
|
|
|
|
local expected="$2"
|
|
|
|
|
shift 2
|
|
|
|
|
|
|
|
|
|
local output status
|
|
|
|
|
output=$("$KTEXT" "$@" 2>/tmp/cond_test_err.$$)
|
|
|
|
|
status=$?
|
|
|
|
|
output=$(printf '%s' "$output" | trim)
|
|
|
|
|
|
|
|
|
|
if [ $status -ne 0 ]; then
|
|
|
|
|
echo "${red}FAIL${reset} $test_name — ktext exited $status"
|
|
|
|
|
echo " stderr: $(head -3 /tmp/cond_test_err.$$)"
|
|
|
|
|
FAIL=$((FAIL + 1))
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
if [ "$output" = "$expected" ]; then
|
|
|
|
|
echo "${green}PASS${reset} $test_name"
|
|
|
|
|
PASS=$((PASS + 1))
|
|
|
|
|
else
|
|
|
|
|
echo "${red}FAIL${reset} $test_name"
|
|
|
|
|
echo " expected: [$expected]"
|
|
|
|
|
echo " got: [$output]"
|
|
|
|
|
FAIL=$((FAIL + 1))
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# check_contains TEST_NAME SUBSTRING KTEXT_ARGS...
|
|
|
|
|
# Runs ktext, expects exit status 0, and checks that stdout contains SUBSTRING.
|
|
|
|
|
check_contains() {
|
|
|
|
|
local test_name="$1"
|
|
|
|
|
local needle="$2"
|
|
|
|
|
shift 2
|
|
|
|
|
|
|
|
|
|
local output status
|
|
|
|
|
output=$("$KTEXT" "$@" 2>&1)
|
|
|
|
|
status=$?
|
|
|
|
|
|
|
|
|
|
if [ $status -ne 0 ]; then
|
|
|
|
|
echo "${red}FAIL${reset} $test_name — ktext exited $status"
|
|
|
|
|
echo " output: $(echo "$output" | head -3)"
|
|
|
|
|
FAIL=$((FAIL + 1))
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
if echo "$output" | grep -qF "$needle"; then
|
|
|
|
|
echo "${green}PASS${reset} $test_name"
|
|
|
|
|
PASS=$((PASS + 1))
|
|
|
|
|
else
|
|
|
|
|
echo "${red}FAIL${reset} $test_name — expected to contain [$needle]"
|
|
|
|
|
echo " output: $(echo "$output" | head -3)"
|
|
|
|
|
FAIL=$((FAIL + 1))
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# check_error TEST_NAME PATTERN KTEXT_ARGS...
|
|
|
|
|
# Runs ktext, expects a NONZERO exit status and PATTERN in the message.
|
|
|
|
|
check_error() {
|
|
|
|
|
local test_name="$1"
|
|
|
|
|
local pattern="$2"
|
|
|
|
|
shift 2
|
|
|
|
|
|
|
|
|
|
local output status
|
|
|
|
|
output=$("$KTEXT" "$@" 2>&1)
|
|
|
|
|
status=$?
|
|
|
|
|
|
|
|
|
|
if [ $status -eq 0 ]; then
|
|
|
|
|
echo "${red}FAIL${reset} $test_name — expected an error but ktext succeeded"
|
|
|
|
|
FAIL=$((FAIL + 1))
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
if echo "$output" | grep -qF "$pattern"; then
|
|
|
|
|
echo "${green}PASS${reset} $test_name"
|
|
|
|
|
PASS=$((PASS + 1))
|
|
|
|
|
else
|
|
|
|
|
echo "${red}FAIL${reset} $test_name — expected error to contain [$pattern]"
|
|
|
|
|
echo " output: $(echo "$output" | head -3)"
|
|
|
|
|
FAIL=$((FAIL + 1))
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FRAC='@@frac a | b : *a*/*b* @@'
|
|
|
|
|
|
|
|
|
|
echo "${bold}@cond argument delimitation tests${reset}"
|
|
|
|
|
echo "================================="
|
|
|
|
|
echo
|
|
|
|
|
|
|
|
|
|
# --- The reported regression (defined klammer with bars inside a branch) ---
|
|
|
|
|
|
|
|
|
|
check_eq \
|
|
|
|
|
" 1. inner klammer with bars, true branch" \
|
|
|
|
|
"1/2" \
|
|
|
|
|
-s "$FRAC @cond true | @frac 1 | 2 @ | @frac 2 | 1 @ @" -d
|
|
|
|
|
|
|
|
|
|
check_eq \
|
|
|
|
|
" 2. inner klammer with bars, false branch" \
|
|
|
|
|
"2/1" \
|
|
|
|
|
-s "$FRAC @cond false | @frac 1 | 2 @ | @frac 2 | 1 @ @" -d
|
|
|
|
|
|
|
|
|
|
# --- The exact witness file from the bug report ---
|
|
|
|
|
|
|
|
|
|
check_contains \
|
|
|
|
|
" 3. tst/cond_test.kt @cond_klammer_test true" \
|
|
|
|
|
"1/2" \
|
|
|
|
|
"$K/tst/cond_test.kt" -d -s '@cond_klammer_test true @'
|
|
|
|
|
|
|
|
|
|
check_contains \
|
|
|
|
|
" 4. tst/cond_test.kt @cond_klammer_test false" \
|
|
|
|
|
"2/1" \
|
|
|
|
|
"$K/tst/cond_test.kt" -d -s '@cond_klammer_test false @'
|
|
|
|
|
|
|
|
|
|
# --- Plain @cond unaffected by the change ---
|
|
|
|
|
|
|
|
|
|
check_eq " 5. plain two-bar, true" "yes" -s '@cond true | yes | no @' -d
|
|
|
|
|
check_eq " 6. plain two-bar, false" "no" -s '@cond false | yes | no @' -d
|
|
|
|
|
check_eq " 7. one-bar, true" "shown" -s '@cond true | shown @' -d
|
|
|
|
|
check_eq " 8. one-bar, false (empty)" "" -s '@cond false | shown @' -d
|
|
|
|
|
|
|
|
|
|
# --- Primitives and nesting inside @cond ---
|
|
|
|
|
|
|
|
|
|
check_eq " 9. @eval in a branch" "42" -s '@cond true | @eval 6*7 @ | no @' -d
|
|
|
|
|
check_eq "10. @eval as the predicate" "yes" -s '@cond @eval 1==1 @ | yes | no @' -d
|
|
|
|
|
check_eq "11. nested @cond in a branch" "B" -s '@cond true | @cond false | A | B @ | C @' -d
|
|
|
|
|
|
|
|
|
|
# --- A klammer with its own bars (double-bar / cells) inside a branch ---
|
|
|
|
|
|
|
|
|
|
check_eq "12. klammer with internal bar in a branch" \
|
|
|
|
|
"x+y" \
|
|
|
|
|
-s '@@two a | b : *a*+*b* @@ @cond true | @two x | y @ | z @' -d
|
|
|
|
|
|
|
|
|
|
# --- Genuine arity errors must still be rejected ---
|
|
|
|
|
|
|
|
|
|
check_error "13. three top-level bars is still an error" \
|
|
|
|
|
"one or two bar characters" \
|
|
|
|
|
-s '@cond true | a | b | c @' -d
|
|
|
|
|
|
|
|
|
|
check_error "14. zero bars is still an error" \
|
|
|
|
|
"one or two bar characters" \
|
|
|
|
|
-s '@cond true @' -d
|
|
|
|
|
|
|
|
|
|
rm -f /tmp/cond_test_err.$$
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "================================="
|
|
|
|
|
echo "Results: ${green}$PASS passed${reset}, ${red}$FAIL failed${reset}"
|
|
|
|
|
[ $FAIL -eq 0 ]
|