Browse Source
G-071 flipped proposed -> active (user direction 2026-07-12).
New allocation.test (7 tests) with reduced fixtures isolating the failing
shape from the tclcore moduledoc: the lseq-range shape (start ?sep? end
?by-step clause?) reproduces the moduledoc failures exactly. Current
mis-allocation pinned as GAP tests ("1 2 3" -> invalid blaming sep,
"1 2 by 3" -> incomplete blaming end, "1 2 3 4" correctly invalid but
misblaming sep), noise-word variants and the if-shape noise-word clauses
pinned as regression guards, parse_status -form absence pinned as GAP.
Also silenced four more unconditional debug puts on get_dict allocation
retreat paths ((111)/(222)/(333)/(444)) - these printed to stderr during
NORMAL successful parses at every optional-skip retreat, polluting
interactive help/parse output for noise-word-modelled commands. Behaviour
unchanged; the retreat logic they marked is exactly this goal fix target.
Full punk::args suite 193 pass / 1 pre-existing skip / 0 fail (tclsh
9.0.3).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
7 changed files with 190 additions and 8 deletions
@ -1,4 +1,4 @@
|
||||
[project] |
||||
name = "punkshell" |
||||
version = "0.12.9" |
||||
version = "0.12.10" |
||||
license = "BSD-2-Clause" |
||||
|
||||
@ -0,0 +1,144 @@
|
||||
package require tcltest |
||||
|
||||
package require punk::args |
||||
|
||||
#Value-allocation characterization for optional standalone values and |
||||
#optional-member clauses (G-071) - added 2026-07-12 before any allocator changes |
||||
#(tests-first per the punk::args convention). Reduced fixtures isolate the |
||||
#lseq-range shape (start ?sep? end ?by-step clause?) and the if shape (noise-word |
||||
#clauses) from the tclcore moduledoc, so the pins do not depend on moduledoc |
||||
#availability or vintage. |
||||
# |
||||
#GAP-labelled tests pin the CURRENT broken allocation and flip when G-071 lands: |
||||
#with the optional 'sep' noise word absent, the allocator force-assigns the word |
||||
#after start to 'sep' (whose -choices then reject it) instead of yielding it to |
||||
#later elements/clauses - so '1 2 3' and '1 2 by 3' fail in-form while every |
||||
#variant carrying the noise word parses. '1 2 3 4' is genuinely invalid but the |
||||
#blame lands on 'sep' rather than the failing clause (misblame pin). |
||||
|
||||
namespace eval ::testspace { |
||||
namespace import ::tcltest::* |
||||
variable common { |
||||
set result "" |
||||
punk::args::define { |
||||
@id -id ::testspace::alloc_range |
||||
@values -min 2 -max 5 |
||||
start -type int |
||||
sep -type string -choices {.. to} -optional 1 |
||||
end -type int |
||||
"by step" -type {?literalprefix(by)? int} -optional 1 |
||||
} |
||||
punk::args::define { |
||||
@id -id ::testspace::alloc_if |
||||
@values -min 2 -max -1 |
||||
expr1 -type int -optional 0 |
||||
then -type literal(then) -optional 1 |
||||
body1 -type int -optional 0 |
||||
"elseif_clause" -type {literal(elseif) int ?literal(then)? int} -optional 1 -multiple 1 |
||||
"else_clause" -type {?literal(else)? int} -optional 1 -multiple 0 |
||||
} |
||||
} |
||||
variable cleanup { |
||||
punk::args::undefine ::testspace::alloc_range 1 |
||||
punk::args::undefine ::testspace::alloc_if 1 |
||||
} |
||||
#ok/status plus badarg (failed) or receivednames (ok) - compact per-case signature |
||||
proc pstat {arglist id} { |
||||
set st [punk::args::parse_status $arglist withid $id] |
||||
if {[dict get $st ok]} { |
||||
return [list 1 [dict get $st status] [dict get $st receivednames]] |
||||
} |
||||
return [list 0 [dict get $st status] [dict get $st badarg]] |
||||
} |
||||
|
||||
#added 2026-07-12 (agent, G-071) - regression guards: allocation with the |
||||
#optional noise word present (or no step) works today and must keep working |
||||
test allocation_range_noiseword_variants {lseq-range shape: variants with the sep noise word present (or only start end) allocate correctly}\ |
||||
-setup $common -body { |
||||
lappend result [pstat {1 2} ::testspace::alloc_range] |
||||
lappend result [pstat {1 to 2} ::testspace::alloc_range] |
||||
lappend result [pstat {1 to 2 3} ::testspace::alloc_range] |
||||
lappend result [pstat {1 .. 2 by 3} ::testspace::alloc_range] |
||||
lappend result [pstat {1 to 2 by 3} ::testspace::alloc_range] |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result [list\ |
||||
{1 valid {start end}}\ |
||||
{1 valid {start sep end}}\ |
||||
{1 valid {start sep end {by step}}}\ |
||||
{1 valid {start sep end {by step}}}\ |
||||
{1 valid {start sep end {by step}}}\ |
||||
] |
||||
|
||||
#added 2026-07-12 (agent, G-071) |
||||
test allocation_range_optskip_bare_step_GAP {GAP: sep absent + bare step ('1 2 3') fails - word 2 force-fed to the skippable optional 'sep' instead of yielding to end + clause}\ |
||||
-setup $common -body { |
||||
pstat {1 2 3} ::testspace::alloc_range |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result {0 invalid sep} |
||||
|
||||
#added 2026-07-12 (agent, G-071) |
||||
test allocation_range_optskip_by_clause_GAP {GAP: sep absent + by-clause ('1 2 by 3') fails as incomplete blaming 'end'}\ |
||||
-setup $common -body { |
||||
pstat {1 2 by 3} ::testspace::alloc_range |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result {0 incomplete end} |
||||
|
||||
#added 2026-07-12 (agent, G-071) - '1 2 3 4' must STAY invalid when the |
||||
#allocator is fixed (per lseq.n no form accepts it) but the blame should move |
||||
#off the unrelated optional 'sep' to the genuinely failing element |
||||
test allocation_range_excess_invalid_misblame_GAP {GAP: genuinely invalid '1 2 3 4' is rejected (correct) but blames the unrelated optional 'sep' (misblame)}\ |
||||
-setup $common -body { |
||||
pstat {1 2 3 4} ::testspace::alloc_range |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result {0 invalid sep} |
||||
|
||||
#added 2026-07-12 (agent, G-071) - guard: with the noise word present the |
||||
#equivalent excess arglist is rejected without touching 'sep' |
||||
test allocation_range_excess_with_noiseword_invalid {lseq-range shape: '1 to 2 3 4' rejected (excess values)}\ |
||||
-setup $common -body { |
||||
set st [punk::args::parse_status {1 to 2 3 4} withid ::testspace::alloc_range] |
||||
list [dict get $st ok] [dict get $st status] |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result {0 invalid} |
||||
|
||||
#added 2026-07-12 (agent, G-071) - regression guards: the ::if noise-word |
||||
#shapes (mid-clause ?literal(then)?, clause-leading ?literal(else)?) allocate |
||||
#correctly today and must not regress with the allocator fix |
||||
test allocation_if_noiseword_guards {if shape: mid-clause and clause-leading optional literals allocate correctly}\ |
||||
-setup $common -body { |
||||
foreach c { |
||||
{1 2} |
||||
{1 then 2} |
||||
{1 2 else 3} |
||||
{1 2 3} |
||||
{1 2 elseif 3 4} |
||||
{1 2 elseif 3 then 4 else 5} |
||||
{1 then 2 elseif 3 then 4 elseif 5 6 else 7} |
||||
} { |
||||
set st [punk::args::parse_status $c withid ::testspace::alloc_if] |
||||
lappend result [list [dict get $st ok] [dict get $st status]] |
||||
} |
||||
#incomplete elseif clause is rejected |
||||
set st [punk::args::parse_status {1 2 elseif 3} withid ::testspace::alloc_if] |
||||
lappend result [list [dict get $st ok] [dict get $st status]] |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result [list {1 valid} {1 valid} {1 valid} {1 valid} {1 valid} {1 valid} {1 valid} {0 invalid}] |
||||
|
||||
#added 2026-07-12 (agent, G-071) |
||||
test parsestatus_form_option_GAP {GAP: parse_status does not accept -form - per-form status probing raises instead of parsing}\ |
||||
-setup $common -body { |
||||
set rc [catch {punk::args::parse_status {1 to 2} withid ::testspace::alloc_range -form 0} r] |
||||
list $rc [string match "Bad number of values*" $r] |
||||
}\ |
||||
-cleanup $cleanup\ |
||||
-result {1 1} |
||||
|
||||
cleanupTests |
||||
} |
||||
namespace delete ::testspace |
||||
Loading…
Reference in new issue