From 14c03b7c26ff06835fca631eed6a3e308946321a Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 18 Oct 2026 08:16:01 +0000 Subject: feat(lib): add experimental _EXP_STRING backed by std::string Co-Authored-By: Claude Opus 5.5 --- tests/CMakeLists.txt | 14 ++++++++++ tests/README.md | 2 +- tests/i/class-copy/notes.md | 5 ++++ tests/i/escape-sequences/notes.md | 5 ++++ tests/i/extern-cc-runtime/notes.md | 5 ++++ tests/i/library-lines/notes.md | 5 ++++ tests/i/operator-assign/notes.md | 5 ++++ tests/i/underscore-identifiers/notes.md | 5 ++++ tests/library/exp-string/case.json | 1 + tests/library/exp-string/expected-stderr.txt | 2 ++ tests/library/exp-string/expected.txt | 10 +++++++ tests/library/exp-string/notes.md | 5 ++++ tests/library/exp-string/program.cps | 41 ++++++++++++++++++++++++++++ 13 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tests/i/class-copy/notes.md create mode 100644 tests/i/escape-sequences/notes.md create mode 100644 tests/i/extern-cc-runtime/notes.md create mode 100644 tests/i/library-lines/notes.md create mode 100644 tests/i/operator-assign/notes.md create mode 100644 tests/i/underscore-identifiers/notes.md create mode 100644 tests/library/exp-string/case.json create mode 100644 tests/library/exp-string/expected-stderr.txt create mode 100644 tests/library/exp-string/expected.txt create mode 100644 tests/library/exp-string/notes.md create mode 100644 tests/library/exp-string/program.cps (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1f13dea..c5b33a1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,3 +30,17 @@ foreach(suite i ii iii) LABELS "${suite}" TIMEOUT 900) endforeach() endforeach() + +# Keep the library beside its built objects without assuming a build directory name. +set(cpsc_library_case "${CMAKE_BINARY_DIR}/test-cases/i/exp-string") +foreach(file program.cps case.json expected.txt expected-stderr.txt) + configure_file("library/exp-string/${file}" "${cpsc_library_case}/${file}" COPYONLY) +endforeach() +add_test(NAME "i/exp-string" COMMAND "${PERL_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/run.pl" + --case "${cpsc_library_case}/case.json" --cpsc "$" + --run-mode "${CPSC_RUN_MODE}" --perl "${PERL_EXECUTABLE}" + --cc "${CMAKE_C_COMPILER}" --cxx "${CMAKE_CXX_COMPILER}" + --seed "${CPSC_TEST_SEED}" --count "${CPSC_TEST_COUNT}" + --results "${CMAKE_BINARY_DIR}/test-results") +set_tests_properties("i/exp-string" PROPERTIES LABELS "i" TIMEOUT 900) diff --git a/tests/README.md b/tests/README.md index 3b9cfc7..ab05c48 100644 --- a/tests/README.md +++ b/tests/README.md @@ -11,7 +11,7 @@ perl tests/summarize.pl build/test-results Use `ctest --test-dir build -L '^ii$' --output-on-failure` for one suite, or `-R '^i/arrays$'` for one case. CTest returns failure if any case fails; it continues through the other cases. There are no expected-failure exemptions. Rejection of valid CIE syntax must not be treated as a successful negative test. -The suites are **i**: independently traced fixed outputs, **ii**: C++ differential tests with Perl inputs, and **iii**: rejected programs with required causes and source lines. Fixed expectations are **pending human review**; each case's notes explain the trace and distinguish CIE rules from project extensions. REAL calculations are observed through BOOLEAN/INTEGER output; REAL text and REAL-to-string formatting remain outside this suite. +The suites are **i**: independently traced fixed outputs, **ii**: C++ differential tests with Perl inputs, and **iii**: rejected programs with required causes and source lines. Fixed expectations are **pending human review**; each case's notes explain the trace and distinguish CIE rules from project extensions. REAL calculations are observed through BOOLEAN/INTEGER output; The experimental string-library fixture additionally checks its specified C++17 numeric text formatting. The default is 10,000 random records per differential group, plus fixed boundary records. Both generated native programs and the configured `-r` path are checked; NONE builds only run native programs. Each source is compiled once per execution path and processes its records in one batch. Tests normalize only CRLF to LF; spaces, blank lines, case and final newlines remain significant. Generated text files use the same comparison rule. diff --git a/tests/i/class-copy/notes.md b/tests/i/class-copy/notes.md new file mode 100644 index 0000000..eab1cbf --- /dev/null +++ b/tests/i/class-copy/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +Each completed default/copy construction increments Live; only copy construction increments Copies. Pass destroys its independent modified copy. Make copies its local into the result before destroying the local. Forward transfers that result. Discarded results, temporary receivers and constructor-bound results are destroyed at statement end. A later argument throwing destroys the already-created argument before entering CATCH. At the final count, nine copies have completed and only A, B and C remain live; reverse destruction removes values 8, 1 and 1. Remaining is zero. This is the class lifetime extension. diff --git a/tests/i/escape-sequences/notes.md b/tests/i/escape-sequences/notes.md new file mode 100644 index 0000000..432300e --- /dev/null +++ b/tests/i/escape-sequences/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +Simple escapes produce a literal quote, backslash and question mark. Octal 101 and hexadecimal 42 add A and B after the leading A; Unicode escapes encode the displayed accented letter and emoji. Escaped CHAR byte values are 10, 9 and 255. Built-in STRING stops at the embedded NUL. diff --git a/tests/i/extern-cc-runtime/notes.md b/tests/i/extern-cc-runtime/notes.md new file mode 100644 index 0000000..7ea9429 --- /dev/null +++ b/tests/i/extern-cc-runtime/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +A C++ string starts with 4096 x bytes and appends the three digits 123, giving 4099. The second EXTERN function catches and rethrows invalid_argument; pseudocode catches it once. These are C++ object and exception extensions. diff --git a/tests/i/library-lines/notes.md b/tests/i/library-lines/notes.md new file mode 100644 index 0000000..b18e19c --- /dev/null +++ b/tests/i/library-lines/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +User calls on lines 3, 8 and 13 enter nested library array access, an array-argument contract and a close of an unopened file. The declaration on line 18 automatically invokes a library constructor that divides by zero. Each error uses that user line and is caught. These are library and exception extensions. diff --git a/tests/i/operator-assign/notes.md b/tests/i/operator-assign/notes.md new file mode 100644 index 0000000..da63a77 --- /dev/null +++ b/tests/i/operator-assign/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +Assignments copy 3 into B, self-assign 3, then copy the returned 7. C_STRING assignment sets A to length 5 without a copy trace. Record-field, array-element and dereferenced assignments trace 7, 5 and 7. Final fields are 7, 7, 7 and 5. This is the member assignment extension. diff --git a/tests/i/underscore-identifiers/notes.md b/tests/i/underscore-identifiers/notes.md new file mode 100644 index 0000000..1941f03 --- /dev/null +++ b/tests/i/underscore-identifiers/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +The underscore-prefixed class holds an INTEGER. The underscore-prefixed function adds one to 41 and the underscore-prefixed variable receives 42. This is a compiler identifier extension. diff --git a/tests/library/exp-string/case.json b/tests/library/exp-string/case.json new file mode 100644 index 0000000..a558ad6 --- /dev/null +++ b/tests/library/exp-string/case.json @@ -0,0 +1 @@ +{"sources":["program.cps","../../../pseudocode-lib/_exp_string.cps"]} diff --git a/tests/library/exp-string/expected-stderr.txt b/tests/library/exp-string/expected-stderr.txt new file mode 100644 index 0000000..6dc689f --- /dev/null +++ b/tests/library/exp-string/expected-stderr.txt @@ -0,0 +1,2 @@ +[Fatal] line 27: unconsumed characters in string conversion +[Fatal] line 32: negative string length diff --git a/tests/library/exp-string/expected.txt b/tests/library/exp-string/expected.txt new file mode 100644 index 0000000..fab16b1 --- /dev/null +++ b/tests/library/exp-string/expected.txt @@ -0,0 +1,10 @@ +helloworld +0 +3 +20500 +20500 +421.250000 +TRUE +conversion caught +length caught +heap diff --git a/tests/library/exp-string/notes.md b/tests/library/exp-string/notes.md new file mode 100644 index 0000000..ca7d489 --- /dev/null +++ b/tests/library/exp-string/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Review status: pending human review. + +Copies of hello are independent; assigning world to the copy gives helloworld when concatenated. Empty length is zero, and three NUL bytes retain length three. One hundred appended five-byte words grow 20000 bytes to 20500, preserved by BYVAL and RETURN. C++17 formatting gives 42 and 1.250000. Parsing 12.5 compares equal to 12.5. Trailing junk and negative counts emit the explicit library reasons at user lines 27 and 32, then are caught. Discarded numeric and Echo results must release their owned strings at statement end. The explicitly deleted heap object prints heap. This fixture covers compiler/library extensions, including numeric text formatting. diff --git a/tests/library/exp-string/program.cps b/tests/library/exp-string/program.cps new file mode 100644 index 0000000..719d9a4 --- /dev/null +++ b/tests/library/exp-string/program.cps @@ -0,0 +1,41 @@ +INCLUDE "../../../pseudocode-lib/_exp_string.cps" +FUNCTION Echo(S : _EXP_STRING) RETURNS _EXP_STRING + RETURN S +ENDFUNCTION +DECLARE A : _EXP_STRING((C_STRING)"hello") +DECLARE B : _EXP_STRING(A) +B <- (C_STRING)"world" +B <- B +CALL _EXP_STRING_OUTPUT(A & B) +DECLARE Empty : _EXP_STRING +OUTPUT _EXP_STRING_LENGTH(Empty) +DECLARE Nuls : _EXP_STRING(3, (C_CHAR)'\0') +OUTPUT _EXP_STRING_LENGTH(Nuls) +DECLARE Long : _EXP_STRING(20000, (C_CHAR)'x') +DECLARE I : INTEGER +FOR I <- 1 TO 100 + Long <- Long & A +NEXT I +OUTPUT _EXP_STRING_LENGTH(Long) +Long <- Echo(Long) +OUTPUT _EXP_STRING_LENGTH(Long) +CALL _EXP_STRING_OUTPUT(_EXP_STRING_NUM_TO_STR(42) & _EXP_STRING_NUM_TO_STR(1.25)) +DECLARE Number : _EXP_STRING((C_STRING)"12.5") +OUTPUT _EXP_STRING_STR_TO_NUM(Number) = 12.5 +TRY + Number <- (C_STRING)"12x" + OUTPUT _EXP_STRING_STR_TO_NUM(Number) +CATCH + OUTPUT "conversion caught" +ENDTRY +TRY + DECLARE Invalid : _EXP_STRING(-1, (C_CHAR)'x') +CATCH + OUTPUT "length caught" +ENDTRY +CALL _EXP_STRING_NUM_TO_STR(99) +CALL Echo(Long) +DECLARE Heap : _EXP_STRING +Heap <- NEW _EXP_STRING((C_STRING)"heap") +CALL _EXP_STRING_OUTPUT(Heap) +DELETE Heap -- cgit v1.3.1-4-g156e