argparsingtest 1.0.0: per-style parser sets opts/tkstyle/tclstyle with N/A capability rows
Restructure of the parser-timing module around three calling styles:
- parser procs move into per-style child namespaces argparsingtest::opts /
::tkstyle (2 leading positionals) / ::tclstyle (2 trailing positionals),
with identical short proc names across styles; the test1_ prefix is gone
- hand-rolled parsers share a manual_ prefix (manual_ni, manual_switch,
manual_prefix, ...) so they group visually in the comparison tables;
test1_prefix2 removed (not meaningfully distinct from test1_prefix)
- tkstyle/tclstyle carry a representative roster (manual_switch baseline +
punkargs/punkargs_by_id/punkargs_parsecache + opt + cmdline + tepam);
style parsers return a {p1 p2 opts} list
- new 'unsupported' discovery status for library/style combos the backing
library cannot express: cmdline with leading positionals (getoptions stops
at the first non-option word - probe verified), argp with any positionals
(parseArgs walks the whole list as option pairs - source verified).
compare renders these as N/A rows below the timed rows so every library
still appears in every style's table
- harness procs (discover_parsers/timeit/first_call/compare) gain
-style all|opts|tkstyle|tclstyle and return results keyed by style;
-args requires a single -style (each style has its own default vector);
external probe keys drop the test1_ prefix (getopt/parse_args/argparse/cmdr)
- punkargs_by_id variants keep the eager punk::args::define as sole doc
source (the former duplicate argdoc lappend made the lazy namespace scan
undefine/re-register the id on first introspection); unused struct::set
require dropped; opt/cmdline/tepam requires hoisted to module top;
first_call library warm-ups extended with positional coverage
- test suite rewritten: 17 tests including per-parser {p1 p2 opts} contract
checks across both positional styles, N/A table rendering, roster counts
and the -style/-args error paths
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
#1.0.0 - BREAKING restructure: parser procs moved into per-style child namespaces (argparsingtest::opts, ::tkstyle, ::tclstyle) and renamed - test1_ prefix dropped, hand-rolled parsers now share a manual_ prefix (e.g. test1_switch -> opts::manual_switch, test1_punkargs -> opts::punkargs); test1_prefix2 removed (not meaningfully distinct from test1_prefix); unused struct::set require dropped; opt/cmdline/tepam requires hoisted to module top
#1.0.0 - new tkstyle (2 positionals first) and tclstyle (2 positionals last) parser sets returning {p1 p2 opts}; harness procs (discover_parsers/timeit/first_call/compare) gain -style and return results keyed by style; new 'unsupported' discovery status for library/style combos the backing library cannot express (cmdline in tkstyle, argp in both positional styles) - rendered as N/A rows in compare tables; external probe keys renamed (test1_getopt -> getopt etc)
#0.2.0 - added timeit/compare/discover_parsers procs for rough parser timing comparison; PUNKARGS argdoc blocks added to all procs
#0.2.0 - fixed timeit to call parsers with namespace-qualified name (workaround for upstream argp 0.2 caller-normalisation bug); noted the bug at the test1_argp registration site
#0.2.0 - first_call now times cold AND warm first calls per parser (warm = backing library pre-compiled via per-library sacrificial warm-up scripts in library_warmups); replaces the punk::args-only 'punk::args::parse {} withdef {}' warm hack which was unfair to other libraries; return shape now {parser {cold <us> warm <us>}}; compare table gains first_cold/first_warm columns
if {![dict exists $r $key]} { set ok "missing $key" ; break }
}
if {$ok eq 1 && ![dict exists $r results opts manual_switch us_per_call]} { set ok "missing results.opts.manual_switch" }
if {$ok eq 1 && [dict get $r unsupported opts] ne ""} { set ok "unexpected unsupported list" }
set ok
} -result 1
#added 2026-07-18 (agent)
test compare_json {Test that compare -format json round-trips with style-keyed unsupported and first_call structure} -body {
set r [argparsingtest::compare -style tkstyle -iterations 20 -parsers {manual_switch cmdline_typed} -format json]
set ok 1
if {[catch {set d [json::json2dict $r]} _err]} { set ok "json parse: $_err" }
if {$ok eq 1 && ![dict exists $d results tkstyle manual_switch us_per_call]} { set ok "missing results" }
if {$ok eq 1 && [dict get $d unsupported tkstyle] ne "cmdline_typed"} { set ok "unsupported: [dict get $d unsupported tkstyle]" }
if {$ok eq 1 && ![dict exists $d first_call tkstyle manual_switch cold]} { set ok "missing first_call cold" }
if {$ok eq 1 && ![dict exists $d first_call tkstyle manual_switch warm]} { set ok "missing first_call warm" }
set ok
} -result 1
@ -61,23 +152,23 @@ namespace eval ::testspace {
set ok
} -result 1
#added 2026-07-17 (agent)
test discover_parsers_basic {Test that discover_parsers returns a dict with test1_switch loaded and 21 entries} -body {
set r [argparsingtest::discover_parsers]
#added 2026-07-18 (agent)
test compare_badstyle {Test that compare -style bogus raises an error containing choiceviolation} -body {
set caught [catch {argparsingtest::compare -style bogus} err]
set ok 0
if {$caught && ([string first "choiceviolation" $err] >= 0 || [string first "must be one of" $err] >= 0)} {
set ok 1
if {![dict exists $r test1_switch]} { set ok 0 }
if {$ok && [dict get $r test1_switch] ne "loaded"} { set ok 0 }
if {$ok && [dict size $r] != 21} { set ok 0 }
}
set ok
} -result 1
#added 2026-07-17 (agent)
test first_call_handrolled {Test that first_call returns cold and warm float timings for a hand-rolled parser} -body {
set r [argparsingtest::first_call -parsers {test1_switch}]
set r [argparsingtest::first_call -style opts -parsers {manual_switch}]
set ok 1
if {![dict exists $r test1_switch]} { set ok 0 }
if {![dict exists $r opts manual_switch]} { set ok 0 }
if {$ok} {
set sub [dict get $r test1_switch]
set sub [dict get $r opts manual_switch]
foreach mode {cold warm} {
if {![dict exists $sub $mode]} { set ok 0 ; break }
if {![string is double -strict [dict get $sub $mode]]} { set ok 0 ; break }
@ -88,9 +179,9 @@ namespace eval ::testspace {
#added 2026-07-17 (agent)
test first_call_library_backed {Test that first_call returns cold and warm float timings for library-backed parsers (warm-up scripts must run cleanly)} -body {
set r [argparsingtest::first_call -parsers {test1_punkargs test1_punkargs_by_id test1_opt test1_cmdline_untyped test1_cmdline_typed test1_tepam}]
set r [argparsingtest::first_call -style opts -parsers {punkargs punkargs_by_id opt cmdline_untyped cmdline_typed tepam}]
set ok 1
foreach {pname sub} $r {
foreach {pname sub} [dict get $r opts] {
foreach mode {cold warm} {
if {![dict exists $sub $mode]} { set ok "missing $mode for $pname" ; break }
if {![string is double -strict [dict get $sub $mode]]} { set ok "$pname $mode: [dict get $sub $mode]" ; break }
@ -100,45 +191,40 @@ namespace eval ::testspace {
set ok
} -result 1
#added 2026-07-17 (agent)
test first_call_argp {Test first_call cold and warm for test1_argp when argp is loaded} -constraints argp_loaded -body {
set r [argparsingtest::first_call -parsers {test1_argp}]
set sub [dict get $r test1_argp]
#added 2026-07-18 (agent) - positional-style parsers must also time cleanly in child interps
test first_call_positional_styles {Test first_call cold and warm for tkstyle and tclstyle parsers} -body {