Browse Source

update bootsupport + vfs + layout bootsupport: punk::args 0.5.0, punk::ns 0.2.0

punkcheck-managed regeneration propagating the G-049 module releases (parse-status data model, machine-parsable cmdhelp returns) into src/bootsupport/modules, the punk.project-0.1 and punk.shell-0.1 layout bootsupport copies, and _vfscommon.vfs/modules.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 5 days ago
parent
commit
296e1b876a
  1. 468
      src/bootsupport/modules/punk/args-0.5.0.tm
  2. 141
      src/bootsupport/modules/punk/ns-0.2.0.tm
  3. 468
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.5.0.tm
  4. 141
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.2.0.tm
  5. 468
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.5.0.tm
  6. 141
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.2.0.tm
  7. 468
      src/vfs/_vfscommon.vfs/modules/punk/args-0.5.0.tm
  8. 141
      src/vfs/_vfscommon.vfs/modules/punk/ns-0.2.0.tm

468
src/bootsupport/modules/punk/args-0.4.2.tm → src/bootsupport/modules/punk/args-0.5.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::args 0.4.2
# Application punk::args 0.5.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args 0 0.4.2]
#[manpage_begin punkshell_module_punk::args 0 0.5.0]
#[copyright "2024"]
#[titledesc {args parsing}] [comment {-- Name section and table of contents description --}]
#[moddesc {args to nested dict of opts and values}] [comment {-- Description at end of page heading --}]
@ -4624,8 +4624,17 @@ tcl::namespace::eval punk::args {
-badarg -type string -help\
"name of an argument to highlight"
-parsedargs -type dict -help\
"Result of successful punk::pargs::parse
(currently only looks at 'received')"
"Result of successful punk::args::parse.
Together with -badarg this is converted internally to the
parse-status structure the renderers consume (received argument
rows get the goodarg style, choice words matching an argument's
value-in-effect are highlighted)."
-parsestatus -type dict -help\
"Parse-status structure as returned by punk::args::parse_status.
The renderers derive goodarg/badarg row marking and choice
value-in-effect highlighting from it. Takes precedence over
-badarg/-parsedargs. Should be built for the same form the
display renders (see -form)."
-aserror -type boolean -help\
"If true, the usage table is raised as an error message,
otherwise it is returned as a value."
@ -4770,17 +4779,22 @@ tcl::namespace::eval punk::args {
}
#set arg_error_CLR(testsinglecolour) [a+ brightred]
upvar ::punk::args::arg_error_CLR CLR
variable arg_error_CLR
set forcereload 0 ;#no need for forcereload to be true for initial run - empty array will trigger initial load
if {[info exists CLR(testsinglecolour)]} {
if {[info exists arg_error_CLR(testsinglecolour)]} {
set terminal_colour_is_on [expr {[string length [a+ yellow]]}]
set error_colour_is_on [expr {[string length $CLR(testsinglecolour)]}]
set error_colour_is_on [expr {[string length $arg_error_CLR(testsinglecolour)]}]
if {$terminal_colour_is_on ^ $error_colour_is_on} {
#results differ
set forcereload 1
}
}
private::argerror_load_colours $forcereload
#per-render colour resolution: CLR is proc-local, seeded from the shared base array;
#a scheme's overrides are merged into this local copy only. (Merging into the shared
#arg_error_CLR leaked scheme overrides into every subsequent render until a colour
#on/off state flip forced an array reload.)
array set CLR [array get arg_error_CLR]
if {[llength $args] %2 != 0} {
set arg_error_isrunning 0
@ -4791,7 +4805,7 @@ tcl::namespace::eval punk::args {
set badarg ""
set parsedargs [dict create] ;#dict with keys: leaders,opts,values,received,solos,multis (as from punk::args::parse)
set goodargs [list]
set parsestatus "" ;#parse-status structure (see ::punk::args::parse_status) - takes precedence over -badarg/-parsedargs
#-----------------------
#todo!! make changeable from config file
#JJJ 2025-07-16
@ -4802,26 +4816,17 @@ tcl::namespace::eval punk::args {
set scheme error
set form 0
dict for {k v} $args {
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -aserror -return -scheme -form} $k]
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -parsestatus -aserror -return -scheme -form} $k]
switch -- $fullk {
-badarg {
set badarg $v
}
-parsedargs {
#todo?
dict for {set setinfo} $v {
switch -- $set {
received {
foreach {r rpos} $setinfo {
if {$r ni $goodargs} {
lappend goodargs $r
}
}
}
}
}
set parsedargs $v
}
-parsestatus {
set parsestatus $v
}
-aserror {
if {![string is boolean -strict $v]} {
set arg_error_isrunning 0
@ -4844,13 +4849,15 @@ tcl::namespace::eval punk::args {
}
default {
set arg_error_isrunning 0
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -aserror -scheme -return -form"
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -parsestatus -aserror -scheme -return -form"
}
}
}
#todo - scheme - use config and iterm toml definitions etc
switch -- $scheme {
"" - -nocolor - -nocolour {
"" - nocolor - nocolour - -nocolor - -nocolour {
#the documented choice value is 'nocolour' - dash spellings and 'nocolor'
#accepted for backward compatibility
set scheme nocolour
}
info - error {}
@ -4883,7 +4890,7 @@ tcl::namespace::eval punk::args {
#hack some basics for now.
#for coloured schemes - use bold as well as brightcolour in case colour off.
#CLR has been defaulted to arg_error_CLR for its defaults
#CLR is a proc-local copy of arg_error_CLR (see above)
#The nocolour,info,error arrays have overrides for some keys.
switch -- $scheme {
nocolour {
@ -4902,6 +4909,18 @@ tcl::namespace::eval punk::args {
}
}
#parse-status structure the renderers derive goodarg/badarg row marking and choice
#value-in-effect highlighting from (see ::punk::args::parse_status for the shape).
#An explicit -parsestatus takes precedence; otherwise build one for the displayed
#form from the -parsedargs/-badarg primitives.
if {$parsestatus ne ""} {
set PSTAT $parsestatus
} else {
set PSTAT [private::parse_status_build $spec_dict [lindex $selected_forms 0] -badarg $badarg -parsedargs $parsedargs]
}
set argstatusd [dict get $PSTAT argstatus]
set receivednames [dict get $PSTAT receivednames]
#set RST [a]
set RST "\x1b\[0m"
@ -5192,17 +5211,9 @@ tcl::namespace::eval punk::args {
#goodargs
}
}
set full_goodargs [list]
#goodargs may have simplified entries for received opts of form -alias1|-alias2|-realname
#map -realname to full argname
foreach g $goodargs {
if {[string match -* $g] && [dict exists $lookup_optset $g]} {
lappend full_goodargs [dict get $lookup_optset $g]
} else {
lappend full_goodargs $g
}
}
set goodargs $full_goodargs
#received-name normalization to optionset names (formerly done here on the
#transient goodargs local) lives in private::parse_status_build - the
#renderers below consume $argstatusd/$receivednames from the structure
if {![catch {package require punk::trie}]} {
#todo - reservelist for future options - or just to affect the prefix calculation
# (similar to -choiceprefixreservelist)
@ -5304,18 +5315,25 @@ tcl::namespace::eval punk::args {
set leading_val_names_hints {}
set trailing_val_names_display $trailing_val_names
set trailing_val_names_hints {}
#puts "--> parsedargs: $parsedargs"
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
#display options first then values
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names $parsed_leaders] [list opts $opt_names_display $opt_names_hints $opt_names $parsed_opts] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names $parsed_values]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames parsedvalues
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names] [list opts $opt_names_display $opt_names_hints $opt_names] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames
set lastgroup ""
set lastgroup_parsekey ""
foreach argshow $argnames_display hint $argnames_hints arg $argnames {
set arginfo [dict get $form_dict ARG_INFO $arg]
#this argument's entry in the parse-status structure: row marking from
#its status, choice value-in-effect highlighting from hasvalue/value
if {[dict exists $argstatusd $arg]} {
set arg_status [dict get $argstatusd $arg status]
set arg_hasvalue [dict get $argstatusd $arg hasvalue]
set arg_value [dict get $argstatusd $arg value]
} else {
set arg_status unparsed
set arg_hasvalue 0
set arg_value ""
}
if {$argumentclass eq "opts"} {
set thisgroup [dict get $arginfo -group]
@ -5354,9 +5372,9 @@ tcl::namespace::eval punk::args {
}
if {$use_table} {
$t add_row [list " $thisgroup" $groupinfo "" "" $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs || $thisgroup_parsekey in $goodargs} {
} elseif {$arg_status eq "ok" || $thisgroup_parsekey in $receivednames} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5364,9 +5382,9 @@ tcl::namespace::eval punk::args {
#set arghelp "[a+ bold] $thisgroup$RST $groupinfo"
set arghelp [textblock::join -- "[a+ bold] $thisgroup$RST" " " $groupinfo]
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -5527,9 +5545,9 @@ tcl::namespace::eval punk::args {
}
append cdisplay \n $ctext
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5547,9 +5565,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5613,9 +5631,9 @@ tcl::namespace::eval punk::args {
append cdisplay \n $ctext
}
#puts "-- parsed:$parsedvalues arg:$arg c:$c"
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5640,7 +5658,7 @@ tcl::namespace::eval punk::args {
append cdisplay \n [dict get $choicelabeldict $c]
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5657,9 +5675,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5809,9 +5827,9 @@ tcl::namespace::eval punk::args {
set col1 $argshow
}
$t add_row [list $col1 $typeshow $default $multiple $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5823,9 +5841,9 @@ tcl::namespace::eval punk::args {
set arghelp "[a+ bold]$argshow$RST $linetail"
}
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -6116,6 +6134,13 @@ tcl::namespace::eval punk::args {
#default to enhanced errorstyle despite slow 'catch' (unhappy path) performance
#todo - configurable per interp/namespace
-errorstyle -type string -default enhanced -choices {enhanced standard basic minimal}
-caller -type string -default "" -help\
"Caller attribution for validation error messages.
When non-empty, this string replaces the %caller% placeholder in
validation failure messages instead of the automatic call-frame
walk - use it when parsing on behalf of another command (e.g a
usage/help display naming the queried command rather than the
internal parse call site)."
-cache -type boolean -default 0 -help\
{Use sparingly.
This caches the entire parse result or formatted validation error for the
@ -6200,6 +6225,7 @@ tcl::namespace::eval punk::args {
-form {*}
-errorstyle standard
-cache 0
-caller {}
}]
#todo - load override_errorstyle from configuration
@ -6208,7 +6234,7 @@ tcl::namespace::eval punk::args {
set opts [dict merge $defaultopts $opts]
dict for {k v} $opts {
switch -- $k {
-form - -errorstyle - -cache {
-form - -errorstyle - -cache - -caller {
}
default {
#punk::args::usage $args withid ::punk::args::parse ??
@ -6246,7 +6272,7 @@ tcl::namespace::eval punk::args {
set result [punk::args::get_dict $deflist $parseargs -form [dict get $opts -form]]
} else {
variable parse_cache
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
if {[dict exists $parse_cache $key]} {
set cached [dict get $parse_cache $key]
if {[dict get $cached type] eq "result"} {
@ -6272,7 +6298,13 @@ tcl::namespace::eval punk::args {
set ecode [dict get $erroropts -errorcode]
#punk ecode is of form PUNKARGS VALIDATION {description..} -key val ...
set msg [string map [list %caller% [Get_caller]] $msg]
if {[dict get $opts -caller] ne ""} {
#explicit caller attribution supplied (e.g punk::ns::cmdhelp usage display
#naming the queried command) - frame walking would name internal call sites.
set msg [string map [list %caller% [dict get $opts -caller]] $msg]
} else {
set msg [string map [list %caller% [Get_caller]] $msg]
}
switch -- $matched_errorstyle {
minimal {
#return -options [list -code error -errorcode $ecode] $msg
@ -6337,7 +6369,7 @@ tcl::namespace::eval punk::args {
}
}
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
dict set parse_cache $key [dict create type "error" value $elist]
return {*}$elist
} trap {PUNKARGS} {msg erropts} {
@ -6352,6 +6384,312 @@ tcl::namespace::eval punk::args {
return $result
}
#classify a PUNKARGS VALIDATION failure class-word into the parse-status overall status
#payload = elements of the errorcode description after the class word
proc private::parse_status_classify {failureclass payload} {
switch -- $failureclass {
missingrequiredleader - missingrequiredvalue - leadermissing - optionmissing - valuemissing - missingoptionvalue {
return incomplete
}
leadingvaluecount - trailingvaluecount {
#payload: <num> min <min> max <max>
set num [lindex $payload 0]
set min [Dict_getdef [lrange $payload 1 end] min ""]
if {[string is integer -strict $num] && [string is integer -strict $min] && $num < $min} {
return incomplete
}
return invalid
}
default {
return invalid
}
}
}
#build the parse-status structure documented in the ::punk::args::parse_status definition.
#fid selects the form whose argument set the per-argument statuses cover (the form usage
#displays render). Options are unvalidated internal inputs - see parse_status/arg_error.
proc private::parse_status_build {spec_dict fid args} {
set defaults [dict create {*}{
-ok ""
-status ""
-scheme ""
-message ""
-errorcode ""
-failureclass ""
-badarg ""
-parsedargs {}
}]
set opts [dict merge $defaults $args]
set parsedargs [dict get $opts -parsedargs]
set badarg [dict get $opts -badarg]
set form_dict [dict get $spec_dict FORMS $fid]
#map each member of an -alias1|-alias2|-realname optionset to the optionset (the ARG_INFO key)
set lookup_optset [dict create]
foreach optionset [dict get $form_dict OPT_NAMES] {
foreach o [split $optionset |] {
dict set lookup_optset $o $optionset
}
}
#received argument names normalized to definition argument names.
#received is dict-like but may repeat keys for -multiple arguments; values are clause ordinals.
set receivednames [list]
set positions [dict create] ;#argname -> list of clause ordinals
foreach {r rpos} [Dict_getdef $parsedargs received {}] {
if {[string match -* $r] && [dict exists $lookup_optset $r]} {
set r [dict get $lookup_optset $r]
}
if {$r ni $receivednames} {
lappend receivednames $r
}
dict lappend positions $r $rpos
}
if {$badarg ne "" && [string match -* $badarg] && [dict exists $lookup_optset $badarg]} {
set badarg [dict get $lookup_optset $badarg]
}
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
set argstatus [dict create]
foreach {names class parsedvalues} [list {*}{
} [dict get $form_dict LEADER_NAMES] leader {*}{
} $parsed_leaders {*}{
} [dict get $form_dict OPT_NAMES] option {*}{
} $parsed_opts {*}{
} [dict get $form_dict VAL_NAMES] value {*}{
} $parsed_values {*}{
}] {
foreach arg $names {
set argpositions [Dict_getdef $positions $arg {}]
set received [llength $argpositions]
if {$arg eq $badarg} {
set status bad
set argfailclass [dict get $opts -failureclass]
} elseif {$received > 0} {
set status ok
set argfailclass ""
} else {
set status unparsed
set argfailclass ""
}
#value-in-effect (includes values the parse filled from -default).
#Direct lookup by definition argument name, matching the display renderers:
#aliased optionsets store under their canonical name and are not resolved here.
if {[dict exists $parsedvalues $arg]} {
set hasvalue 1
set value [dict get $parsedvalues $arg]
} else {
set hasvalue 0
set value ""
}
dict set argstatus $arg [dict create class $class status $status received $received positions $argpositions hasvalue $hasvalue value $value failureclass $argfailclass]
}
}
return [dict create {*}{
} ok [dict get $opts -ok] {*}{
} status [dict get $opts -status] {*}{
} scheme [dict get $opts -scheme] {*}{
} message [dict get $opts -message] {*}{
} errorcode [dict get $opts -errorcode] {*}{
} failureclass [dict get $opts -failureclass] {*}{
} badarg $badarg {*}{
} id [Dict_getdef $spec_dict id ""] {*}{
} form $fid {*}{
} receivednames $receivednames {*}{
} argstatus $argstatus {*}{
}]
}
#resolve the form name whose argument set parse-status per-argument entries cover
#(first form matching the -form selection - mirrors arg_error's display-form resolution)
proc private::parse_status_formname {spec_dict formselection} {
set formnames [dict get $spec_dict form_names]
set first [lindex $formselection 0]
if {$formselection eq "*" || $formselection eq ""} {
return [lindex $formnames 0]
}
if {[string is integer -strict $first]} {
set fid [lindex $formnames $first]
if {$fid ne ""} {
return $fid
}
return [lindex $formnames 0]
}
set fmatch [tcl::prefix::match -error "" $formnames $first]
if {$fmatch ne ""} {
return $fmatch
}
return [lindex $formnames 0]
}
lappend PUNKARGS [list {
@id -id ::punk::args::parse_status
@cmd -name punk::args::parse_status\
-summary\
"Parse-status structure from a parse attempt (validation failures reported, not raised)."\
-help\
"Run a parse attempt of arglist against a punk::args definition and
return a parse-status dict describing the outcome - for success AND
for validation failure (a validation failure is reported in the
returned structure instead of being raised as an error; other errors
such as an unknown id still raise).
This is the machine-parsable form of the information behind the
usage-display argument marking (punk::ns::cmdhelp / 'i <cmd> <args...>'):
which supplied arguments validated, which argument failed and why,
and which display scheme applies. punk::args::arg_error accepts the
structure via its -parsestatus option and derives its goodarg/badarg
row marking and choice value-in-effect highlighting from it.
Overall keys:
ok 1 parse succeeded, 0 validation failure
status valid | invalid | incomplete
invalid - a supplied word failed validation
incomplete - required arguments missing (a count or
allocation shortfall). Note a supplied word
failing its -type check can also surface as
an allocation shortfall (missingrequiredvalue)
- badarg and the per-argument statuses carry
the specifics in both situations.
scheme suggested display scheme: info (ok) | error (failure)
message empty | the validation failure message
errorcode empty | the -errorcode of the validation failure with
the bulky -argspecs payload removed
failureclass empty | first word of the errorcode description
(e.g choiceviolation, typemismatch, missingrequiredvalue)
badarg empty | name of the offending/unfillable argument
id the definition id
form form name the per-argument statuses were built for
(the first form matching -form - the form usage
displays render)
receivednames received argument names normalized to definition
argument names (opt aliases folded to their
-alias|-name optionset)
argstatus dict keyed by definition argument name, each value a
dict with keys:
class leader | option | value
status ok (received and validated) |
bad (the offending argument) |
unparsed (not received)
received count of received clauses (0 if unreceived)
positions ordinal positions of received clauses
hasvalue 1 if a value-in-effect is known
value value-in-effect - includes values the
parse filled from -default; empty when
hasvalue is 0. No values are reported
for a failed parse.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\
"Arguments to parse - supplied as a single list"
@opts -prefix 0
-form -type list -default * -help\
"Restrict parsing to the set of forms listed (see punk::args::parse).
The per-argument statuses are built for the first matching form."
-caller -type string -default "" -help\
"Caller attribution for the validation failure message
(see punk::args::parse -caller). When empty, defaults to the
definition's @cmd -name (or its id) rather than the call-frame
walk parse would perform."
@values -min 2
@form -form withid -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withid $id"
@values -max 2
withid -type literal(withid) -help\
"The literal value 'withid'"
id -type string -help\
"id of punk::args definition for a command"
@form -form withdef -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withdef $def ?$def?"
withdef -type literal(withdef) -help\
"The literal value 'withdef'"
def -type string -multiple 1 -optional 0 -help\
"Each remaining argument is a block of text
defining argument definitions."
}]
proc parse_status {args} {
if {[llength $args] < 3} {
punk::args::parse $args withid ::punk::args::parse_status
}
set opts_and_vals $args
set arglist [lpop opts_and_vals 0]
set opts [dict create {*}{
-form {*}
-caller {}
}]
while {[llength $opts_and_vals] && [string match -* [lindex $opts_and_vals 0]]} {
set k [lpop opts_and_vals 0]
switch -- $k {
-form - -caller {
if {![llength $opts_and_vals]} {
punk::args::parse $args withid ::punk::args::parse_status
}
dict set opts $k [lpop opts_and_vals 0]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
}
set tailtype [lpop opts_and_vals 0]
switch -- $tailtype {
withid {
if {[llength $opts_and_vals] != 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set id [lindex $opts_and_vals 0]
set spec_dict [get_spec $id]
if {$spec_dict eq ""} {
error "punk::args::parse_status - no such id: $id"
}
}
withdef {
if {[llength $opts_and_vals] < 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set spec_dict [resolve {*}$opts_and_vals]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
set fid [private::parse_status_formname $spec_dict [dict get $opts -form]]
set caller [dict get $opts -caller]
if {$caller eq ""} {
#the frame walk parse would do by default names parse_status's own internal
#parse call - the definition's command name is the meaningful attribution here
set caller [Dict_getdef $spec_dict cmd_info -name [Dict_getdef $spec_dict id ""]]
}
if {[catch {
punk::args::parse $arglist -form [dict get $opts -form] -errorstyle minimal -caller $caller $tailtype {*}$opts_and_vals
} r ropts]} {
set ecode [Dict_getdef $ropts -errorcode ""]
if {[lrange $ecode 0 1] ne [list PUNKARGS VALIDATION]} {
#not an argument validation failure (e.g definition error) - propagate
return -options $ropts $r
}
set classinfo [lindex $ecode 2]
set failureclass [lindex $classinfo 0]
set payload [lrange $classinfo 1 end]
set customdict [lrange $ecode 3 end]
set badarg [Dict_getdef $customdict -badarg ""]
set status [private::parse_status_classify $failureclass $payload]
#the -argspecs payload (the whole resolved spec) is display machinery - too bulky
#for a status structure; keep the classification and the small custom keys
set ecode_slim [lrange $ecode 0 2]
foreach {k v} $customdict {
if {$k ne "-argspecs"} {
lappend ecode_slim $k $v
}
}
return [private::parse_status_build $spec_dict $fid -ok 0 -status $status -scheme error -message $r -errorcode $ecode_slim -failureclass $failureclass -badarg $badarg]
}
return [private::parse_status_build $spec_dict $fid -ok 1 -status valid -scheme info -parsedargs $r]
}
#return number of values we can assign to cater for variable length clauses such as:
# {"elseif" expr "?then?" body}
#review - efficiency? each time we call this - we are looking ahead at the same info
@ -10202,7 +10540,7 @@ tcl::namespace::eval punk::args {
if {$leadername ni $leadernames_received} {
#puts stderr "private::get_dict_can_assign_value $ldridx $values $nameidx $VAL_NAMES"
set msg "Bad number of leaders for %caller%. Not enough remaining values to assign to required arguments (fail on $leadername)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -badarg $leadername -argspecs $argspecs]] $msg
} else {
puts stderr "get_dict cannot assign val:$ldr to leadername:$leadername (222)"
#return -options [list -code error -errorcode [list PUNKARGS UNCONSUMED -argspecs $argspecs]] "private::get_dict_can_assign_value consumed 0 unexpected 2?"
@ -10413,7 +10751,7 @@ tcl::namespace::eval punk::args {
if {$valname ni $valnames_received} {
#puts stderr "private::get_dict_can_assign_value $validx $values $nameidx $VAL_NAMES"
set msg "Bad number of values for %caller%. Not enough remaining values to assign to required arguments (fail on $valname)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -badarg $valname -argspecs $argspecs]] $msg
} else {
#error 444
puts stderr "get_dict cannot assign val:$val to valname:$valname (444)"
@ -13332,7 +13670,7 @@ package provide punk::args [tcl::namespace::eval punk::args {
tcl::namespace::path {::punk::args::lib ::punk::args::system}
variable pkg punk::args
variable version
set version 0.4.2
set version 0.5.0
}]
return

141
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.1.4.tm → src/bootsupport/modules/punk/ns-0.2.0.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application punk::ns 0.1.4
# Application punk::ns 0.2.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -5503,7 +5503,20 @@ y" {return quirkykeyscript}
usage information as this is inherently risky. (could create a file,
exit the interp etc)
"
-return -type string -default table -choices {string table tableobject}
-return -type string -default table -choices {string table tableobject dict} -choicelabels {
string "no table layout"
table "full table layout"
tableobject "table object cmd"
dict "machine-parsable resolution + parse-status"
} -help\
"Return form of the usage information.
'dict' returns a dict with keys origin, docid, cmdtype,
args_remaining and parsestatus - where parsestatus is the
parse-status structure of the supplied argument words against
the resolved definition (see punk::args::parse_status for the
documented structure; empty if the command is undocumented).
Its per-argument statuses distinguish a fully-valid, an invalid
and an incomplete argument set machine-parsably."
} {${[punk::args::resolved_def -types opts ::punk::args::arg_error -scheme]}} {
@ -5564,17 +5577,6 @@ y" {return quirkykeyscript}
set opt_grepstr [dict get $opts -grepstr]
set opt_form [dict get $opts -form]
set opt_return [dict get $opts -return]
switch -- $opt_return {
string {
set estyle "basic"
}
tableobject {
set estyle "minimal"
}
default {
set estyle "standard"
}
}
set nextopts [dict remove $opts -grepstr]
#JJJ
@ -5597,35 +5599,37 @@ y" {return quirkykeyscript}
if {[llength $testresolved] == 1} {
#only the command itself is in the args_resolved list - so we can't resolve to a deeper subcommand
ledit queryargs -1 -1 {*}$args_remaining ;#prepend
#advisory goodargs parse (marking of supplied words). With NO supplied args
#a failure only reflects missing required leaders/values - nothing to mark and
#nothing wrong with the user's (absent) input, so we show plain usage in the
#info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the
#full usage table INSIDE the raised error only for it to be discarded here -
#doubling display time for large argdocs (e.g 'i punk::args::define').
if {![llength $queryargs]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $queryargs -form $opt_form -errorstyle $advisory_estyle withid $rootdoc} parseresult]} {
if {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally
#-errorstyle minimal so no usage table is built inside a discarded error).
#-caller attributes any failure message to the queried command rather than
#an internal parse call site.
set pstatus [punk::args::parse_status $queryargs -form $opt_form -caller $querycommand withid $rootdoc]
#With NO supplied args a failure only reflects missing required
#leaders/values - nothing to mark and nothing wrong with the user's (absent)
#input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $queryargs]} {
dict set pstatus scheme info
}
} else {
return [dict create origin $rootorigin docid $rootdoc cmdtype $rootorigintype args_remaining $queryargs parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5695,36 +5699,44 @@ y" {return quirkykeyscript}
}
}
if {$origindoc ne ""} {
#important not to use "-cache 1" for this parse - need to reflect dynamically updated ensembles etc
#advisory goodargs parse (marking of supplied words). With NO supplied trailing args
#a failure only reflects missing required leaders/values (e.g 'i string is') - nothing
#to mark and nothing wrong with the user's (absent) input, so we show plain usage in
#the info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the full
#usage table INSIDE the raised error only for it to be discarded here - doubling
#display time for large argdocs (e.g 'i punk::args::define').
if {![llength $args_remaining]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $args_remaining -cache 0 -form $opt_form -errorstyle $advisory_estyle withid $origindoc} parseresult]} {
if {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
}
#caller attribution for any validation failure message: the queried command plus
#the subcommand words consumed by resolution (not the trailing argument words)
if {[llength $args_remaining]} {
set consumedwords [lrange $queryargs 0 end-[llength $args_remaining]]
} else {
set consumedwords $queryargs
}
set caller_display [string trim "$querycommand [join $consumedwords { }]"]
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally uncached and
#-errorstyle minimal so no usage table is built inside a discarded error and
#dynamically updated ensembles are reflected).
set pstatus [punk::args::parse_status $args_remaining -form $opt_form -caller $caller_display withid $origindoc]
#With NO supplied trailing args a failure only reflects missing required
#leaders/values (e.g 'i string is') - nothing to mark and nothing wrong with the
#user's (absent) input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $args_remaining]} {
dict set pstatus scheme info
}
return [dict create origin $origin docid $origindoc cmdtype $origintype args_remaining $args_remaining parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5735,6 +5747,9 @@ y" {return quirkykeyscript}
}
return $result
} else {
if {$opt_return eq "dict"} {
return [dict create origin $origin docid "" cmdtype $origintype args_remaining $args_remaining parsestatus {}]
}
return "Undocumented command $origin. Type: $origintype"
}
@ -7642,6 +7657,6 @@ namespace eval ::punk::args::register {
## Ready
package provide punk::ns [tcl::namespace::eval punk::ns {
variable version
set version 0.1.4
set version 0.2.0
}]
return

468
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.4.2.tm → src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.5.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::args 0.4.2
# Application punk::args 0.5.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args 0 0.4.2]
#[manpage_begin punkshell_module_punk::args 0 0.5.0]
#[copyright "2024"]
#[titledesc {args parsing}] [comment {-- Name section and table of contents description --}]
#[moddesc {args to nested dict of opts and values}] [comment {-- Description at end of page heading --}]
@ -4624,8 +4624,17 @@ tcl::namespace::eval punk::args {
-badarg -type string -help\
"name of an argument to highlight"
-parsedargs -type dict -help\
"Result of successful punk::pargs::parse
(currently only looks at 'received')"
"Result of successful punk::args::parse.
Together with -badarg this is converted internally to the
parse-status structure the renderers consume (received argument
rows get the goodarg style, choice words matching an argument's
value-in-effect are highlighted)."
-parsestatus -type dict -help\
"Parse-status structure as returned by punk::args::parse_status.
The renderers derive goodarg/badarg row marking and choice
value-in-effect highlighting from it. Takes precedence over
-badarg/-parsedargs. Should be built for the same form the
display renders (see -form)."
-aserror -type boolean -help\
"If true, the usage table is raised as an error message,
otherwise it is returned as a value."
@ -4770,17 +4779,22 @@ tcl::namespace::eval punk::args {
}
#set arg_error_CLR(testsinglecolour) [a+ brightred]
upvar ::punk::args::arg_error_CLR CLR
variable arg_error_CLR
set forcereload 0 ;#no need for forcereload to be true for initial run - empty array will trigger initial load
if {[info exists CLR(testsinglecolour)]} {
if {[info exists arg_error_CLR(testsinglecolour)]} {
set terminal_colour_is_on [expr {[string length [a+ yellow]]}]
set error_colour_is_on [expr {[string length $CLR(testsinglecolour)]}]
set error_colour_is_on [expr {[string length $arg_error_CLR(testsinglecolour)]}]
if {$terminal_colour_is_on ^ $error_colour_is_on} {
#results differ
set forcereload 1
}
}
private::argerror_load_colours $forcereload
#per-render colour resolution: CLR is proc-local, seeded from the shared base array;
#a scheme's overrides are merged into this local copy only. (Merging into the shared
#arg_error_CLR leaked scheme overrides into every subsequent render until a colour
#on/off state flip forced an array reload.)
array set CLR [array get arg_error_CLR]
if {[llength $args] %2 != 0} {
set arg_error_isrunning 0
@ -4791,7 +4805,7 @@ tcl::namespace::eval punk::args {
set badarg ""
set parsedargs [dict create] ;#dict with keys: leaders,opts,values,received,solos,multis (as from punk::args::parse)
set goodargs [list]
set parsestatus "" ;#parse-status structure (see ::punk::args::parse_status) - takes precedence over -badarg/-parsedargs
#-----------------------
#todo!! make changeable from config file
#JJJ 2025-07-16
@ -4802,26 +4816,17 @@ tcl::namespace::eval punk::args {
set scheme error
set form 0
dict for {k v} $args {
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -aserror -return -scheme -form} $k]
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -parsestatus -aserror -return -scheme -form} $k]
switch -- $fullk {
-badarg {
set badarg $v
}
-parsedargs {
#todo?
dict for {set setinfo} $v {
switch -- $set {
received {
foreach {r rpos} $setinfo {
if {$r ni $goodargs} {
lappend goodargs $r
}
}
}
}
}
set parsedargs $v
}
-parsestatus {
set parsestatus $v
}
-aserror {
if {![string is boolean -strict $v]} {
set arg_error_isrunning 0
@ -4844,13 +4849,15 @@ tcl::namespace::eval punk::args {
}
default {
set arg_error_isrunning 0
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -aserror -scheme -return -form"
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -parsestatus -aserror -scheme -return -form"
}
}
}
#todo - scheme - use config and iterm toml definitions etc
switch -- $scheme {
"" - -nocolor - -nocolour {
"" - nocolor - nocolour - -nocolor - -nocolour {
#the documented choice value is 'nocolour' - dash spellings and 'nocolor'
#accepted for backward compatibility
set scheme nocolour
}
info - error {}
@ -4883,7 +4890,7 @@ tcl::namespace::eval punk::args {
#hack some basics for now.
#for coloured schemes - use bold as well as brightcolour in case colour off.
#CLR has been defaulted to arg_error_CLR for its defaults
#CLR is a proc-local copy of arg_error_CLR (see above)
#The nocolour,info,error arrays have overrides for some keys.
switch -- $scheme {
nocolour {
@ -4902,6 +4909,18 @@ tcl::namespace::eval punk::args {
}
}
#parse-status structure the renderers derive goodarg/badarg row marking and choice
#value-in-effect highlighting from (see ::punk::args::parse_status for the shape).
#An explicit -parsestatus takes precedence; otherwise build one for the displayed
#form from the -parsedargs/-badarg primitives.
if {$parsestatus ne ""} {
set PSTAT $parsestatus
} else {
set PSTAT [private::parse_status_build $spec_dict [lindex $selected_forms 0] -badarg $badarg -parsedargs $parsedargs]
}
set argstatusd [dict get $PSTAT argstatus]
set receivednames [dict get $PSTAT receivednames]
#set RST [a]
set RST "\x1b\[0m"
@ -5192,17 +5211,9 @@ tcl::namespace::eval punk::args {
#goodargs
}
}
set full_goodargs [list]
#goodargs may have simplified entries for received opts of form -alias1|-alias2|-realname
#map -realname to full argname
foreach g $goodargs {
if {[string match -* $g] && [dict exists $lookup_optset $g]} {
lappend full_goodargs [dict get $lookup_optset $g]
} else {
lappend full_goodargs $g
}
}
set goodargs $full_goodargs
#received-name normalization to optionset names (formerly done here on the
#transient goodargs local) lives in private::parse_status_build - the
#renderers below consume $argstatusd/$receivednames from the structure
if {![catch {package require punk::trie}]} {
#todo - reservelist for future options - or just to affect the prefix calculation
# (similar to -choiceprefixreservelist)
@ -5304,18 +5315,25 @@ tcl::namespace::eval punk::args {
set leading_val_names_hints {}
set trailing_val_names_display $trailing_val_names
set trailing_val_names_hints {}
#puts "--> parsedargs: $parsedargs"
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
#display options first then values
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names $parsed_leaders] [list opts $opt_names_display $opt_names_hints $opt_names $parsed_opts] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names $parsed_values]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames parsedvalues
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names] [list opts $opt_names_display $opt_names_hints $opt_names] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames
set lastgroup ""
set lastgroup_parsekey ""
foreach argshow $argnames_display hint $argnames_hints arg $argnames {
set arginfo [dict get $form_dict ARG_INFO $arg]
#this argument's entry in the parse-status structure: row marking from
#its status, choice value-in-effect highlighting from hasvalue/value
if {[dict exists $argstatusd $arg]} {
set arg_status [dict get $argstatusd $arg status]
set arg_hasvalue [dict get $argstatusd $arg hasvalue]
set arg_value [dict get $argstatusd $arg value]
} else {
set arg_status unparsed
set arg_hasvalue 0
set arg_value ""
}
if {$argumentclass eq "opts"} {
set thisgroup [dict get $arginfo -group]
@ -5354,9 +5372,9 @@ tcl::namespace::eval punk::args {
}
if {$use_table} {
$t add_row [list " $thisgroup" $groupinfo "" "" $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs || $thisgroup_parsekey in $goodargs} {
} elseif {$arg_status eq "ok" || $thisgroup_parsekey in $receivednames} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5364,9 +5382,9 @@ tcl::namespace::eval punk::args {
#set arghelp "[a+ bold] $thisgroup$RST $groupinfo"
set arghelp [textblock::join -- "[a+ bold] $thisgroup$RST" " " $groupinfo]
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -5527,9 +5545,9 @@ tcl::namespace::eval punk::args {
}
append cdisplay \n $ctext
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5547,9 +5565,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5613,9 +5631,9 @@ tcl::namespace::eval punk::args {
append cdisplay \n $ctext
}
#puts "-- parsed:$parsedvalues arg:$arg c:$c"
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5640,7 +5658,7 @@ tcl::namespace::eval punk::args {
append cdisplay \n [dict get $choicelabeldict $c]
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5657,9 +5675,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5809,9 +5827,9 @@ tcl::namespace::eval punk::args {
set col1 $argshow
}
$t add_row [list $col1 $typeshow $default $multiple $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5823,9 +5841,9 @@ tcl::namespace::eval punk::args {
set arghelp "[a+ bold]$argshow$RST $linetail"
}
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -6116,6 +6134,13 @@ tcl::namespace::eval punk::args {
#default to enhanced errorstyle despite slow 'catch' (unhappy path) performance
#todo - configurable per interp/namespace
-errorstyle -type string -default enhanced -choices {enhanced standard basic minimal}
-caller -type string -default "" -help\
"Caller attribution for validation error messages.
When non-empty, this string replaces the %caller% placeholder in
validation failure messages instead of the automatic call-frame
walk - use it when parsing on behalf of another command (e.g a
usage/help display naming the queried command rather than the
internal parse call site)."
-cache -type boolean -default 0 -help\
{Use sparingly.
This caches the entire parse result or formatted validation error for the
@ -6200,6 +6225,7 @@ tcl::namespace::eval punk::args {
-form {*}
-errorstyle standard
-cache 0
-caller {}
}]
#todo - load override_errorstyle from configuration
@ -6208,7 +6234,7 @@ tcl::namespace::eval punk::args {
set opts [dict merge $defaultopts $opts]
dict for {k v} $opts {
switch -- $k {
-form - -errorstyle - -cache {
-form - -errorstyle - -cache - -caller {
}
default {
#punk::args::usage $args withid ::punk::args::parse ??
@ -6246,7 +6272,7 @@ tcl::namespace::eval punk::args {
set result [punk::args::get_dict $deflist $parseargs -form [dict get $opts -form]]
} else {
variable parse_cache
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
if {[dict exists $parse_cache $key]} {
set cached [dict get $parse_cache $key]
if {[dict get $cached type] eq "result"} {
@ -6272,7 +6298,13 @@ tcl::namespace::eval punk::args {
set ecode [dict get $erroropts -errorcode]
#punk ecode is of form PUNKARGS VALIDATION {description..} -key val ...
set msg [string map [list %caller% [Get_caller]] $msg]
if {[dict get $opts -caller] ne ""} {
#explicit caller attribution supplied (e.g punk::ns::cmdhelp usage display
#naming the queried command) - frame walking would name internal call sites.
set msg [string map [list %caller% [dict get $opts -caller]] $msg]
} else {
set msg [string map [list %caller% [Get_caller]] $msg]
}
switch -- $matched_errorstyle {
minimal {
#return -options [list -code error -errorcode $ecode] $msg
@ -6337,7 +6369,7 @@ tcl::namespace::eval punk::args {
}
}
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
dict set parse_cache $key [dict create type "error" value $elist]
return {*}$elist
} trap {PUNKARGS} {msg erropts} {
@ -6352,6 +6384,312 @@ tcl::namespace::eval punk::args {
return $result
}
#classify a PUNKARGS VALIDATION failure class-word into the parse-status overall status
#payload = elements of the errorcode description after the class word
proc private::parse_status_classify {failureclass payload} {
switch -- $failureclass {
missingrequiredleader - missingrequiredvalue - leadermissing - optionmissing - valuemissing - missingoptionvalue {
return incomplete
}
leadingvaluecount - trailingvaluecount {
#payload: <num> min <min> max <max>
set num [lindex $payload 0]
set min [Dict_getdef [lrange $payload 1 end] min ""]
if {[string is integer -strict $num] && [string is integer -strict $min] && $num < $min} {
return incomplete
}
return invalid
}
default {
return invalid
}
}
}
#build the parse-status structure documented in the ::punk::args::parse_status definition.
#fid selects the form whose argument set the per-argument statuses cover (the form usage
#displays render). Options are unvalidated internal inputs - see parse_status/arg_error.
proc private::parse_status_build {spec_dict fid args} {
set defaults [dict create {*}{
-ok ""
-status ""
-scheme ""
-message ""
-errorcode ""
-failureclass ""
-badarg ""
-parsedargs {}
}]
set opts [dict merge $defaults $args]
set parsedargs [dict get $opts -parsedargs]
set badarg [dict get $opts -badarg]
set form_dict [dict get $spec_dict FORMS $fid]
#map each member of an -alias1|-alias2|-realname optionset to the optionset (the ARG_INFO key)
set lookup_optset [dict create]
foreach optionset [dict get $form_dict OPT_NAMES] {
foreach o [split $optionset |] {
dict set lookup_optset $o $optionset
}
}
#received argument names normalized to definition argument names.
#received is dict-like but may repeat keys for -multiple arguments; values are clause ordinals.
set receivednames [list]
set positions [dict create] ;#argname -> list of clause ordinals
foreach {r rpos} [Dict_getdef $parsedargs received {}] {
if {[string match -* $r] && [dict exists $lookup_optset $r]} {
set r [dict get $lookup_optset $r]
}
if {$r ni $receivednames} {
lappend receivednames $r
}
dict lappend positions $r $rpos
}
if {$badarg ne "" && [string match -* $badarg] && [dict exists $lookup_optset $badarg]} {
set badarg [dict get $lookup_optset $badarg]
}
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
set argstatus [dict create]
foreach {names class parsedvalues} [list {*}{
} [dict get $form_dict LEADER_NAMES] leader {*}{
} $parsed_leaders {*}{
} [dict get $form_dict OPT_NAMES] option {*}{
} $parsed_opts {*}{
} [dict get $form_dict VAL_NAMES] value {*}{
} $parsed_values {*}{
}] {
foreach arg $names {
set argpositions [Dict_getdef $positions $arg {}]
set received [llength $argpositions]
if {$arg eq $badarg} {
set status bad
set argfailclass [dict get $opts -failureclass]
} elseif {$received > 0} {
set status ok
set argfailclass ""
} else {
set status unparsed
set argfailclass ""
}
#value-in-effect (includes values the parse filled from -default).
#Direct lookup by definition argument name, matching the display renderers:
#aliased optionsets store under their canonical name and are not resolved here.
if {[dict exists $parsedvalues $arg]} {
set hasvalue 1
set value [dict get $parsedvalues $arg]
} else {
set hasvalue 0
set value ""
}
dict set argstatus $arg [dict create class $class status $status received $received positions $argpositions hasvalue $hasvalue value $value failureclass $argfailclass]
}
}
return [dict create {*}{
} ok [dict get $opts -ok] {*}{
} status [dict get $opts -status] {*}{
} scheme [dict get $opts -scheme] {*}{
} message [dict get $opts -message] {*}{
} errorcode [dict get $opts -errorcode] {*}{
} failureclass [dict get $opts -failureclass] {*}{
} badarg $badarg {*}{
} id [Dict_getdef $spec_dict id ""] {*}{
} form $fid {*}{
} receivednames $receivednames {*}{
} argstatus $argstatus {*}{
}]
}
#resolve the form name whose argument set parse-status per-argument entries cover
#(first form matching the -form selection - mirrors arg_error's display-form resolution)
proc private::parse_status_formname {spec_dict formselection} {
set formnames [dict get $spec_dict form_names]
set first [lindex $formselection 0]
if {$formselection eq "*" || $formselection eq ""} {
return [lindex $formnames 0]
}
if {[string is integer -strict $first]} {
set fid [lindex $formnames $first]
if {$fid ne ""} {
return $fid
}
return [lindex $formnames 0]
}
set fmatch [tcl::prefix::match -error "" $formnames $first]
if {$fmatch ne ""} {
return $fmatch
}
return [lindex $formnames 0]
}
lappend PUNKARGS [list {
@id -id ::punk::args::parse_status
@cmd -name punk::args::parse_status\
-summary\
"Parse-status structure from a parse attempt (validation failures reported, not raised)."\
-help\
"Run a parse attempt of arglist against a punk::args definition and
return a parse-status dict describing the outcome - for success AND
for validation failure (a validation failure is reported in the
returned structure instead of being raised as an error; other errors
such as an unknown id still raise).
This is the machine-parsable form of the information behind the
usage-display argument marking (punk::ns::cmdhelp / 'i <cmd> <args...>'):
which supplied arguments validated, which argument failed and why,
and which display scheme applies. punk::args::arg_error accepts the
structure via its -parsestatus option and derives its goodarg/badarg
row marking and choice value-in-effect highlighting from it.
Overall keys:
ok 1 parse succeeded, 0 validation failure
status valid | invalid | incomplete
invalid - a supplied word failed validation
incomplete - required arguments missing (a count or
allocation shortfall). Note a supplied word
failing its -type check can also surface as
an allocation shortfall (missingrequiredvalue)
- badarg and the per-argument statuses carry
the specifics in both situations.
scheme suggested display scheme: info (ok) | error (failure)
message empty | the validation failure message
errorcode empty | the -errorcode of the validation failure with
the bulky -argspecs payload removed
failureclass empty | first word of the errorcode description
(e.g choiceviolation, typemismatch, missingrequiredvalue)
badarg empty | name of the offending/unfillable argument
id the definition id
form form name the per-argument statuses were built for
(the first form matching -form - the form usage
displays render)
receivednames received argument names normalized to definition
argument names (opt aliases folded to their
-alias|-name optionset)
argstatus dict keyed by definition argument name, each value a
dict with keys:
class leader | option | value
status ok (received and validated) |
bad (the offending argument) |
unparsed (not received)
received count of received clauses (0 if unreceived)
positions ordinal positions of received clauses
hasvalue 1 if a value-in-effect is known
value value-in-effect - includes values the
parse filled from -default; empty when
hasvalue is 0. No values are reported
for a failed parse.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\
"Arguments to parse - supplied as a single list"
@opts -prefix 0
-form -type list -default * -help\
"Restrict parsing to the set of forms listed (see punk::args::parse).
The per-argument statuses are built for the first matching form."
-caller -type string -default "" -help\
"Caller attribution for the validation failure message
(see punk::args::parse -caller). When empty, defaults to the
definition's @cmd -name (or its id) rather than the call-frame
walk parse would perform."
@values -min 2
@form -form withid -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withid $id"
@values -max 2
withid -type literal(withid) -help\
"The literal value 'withid'"
id -type string -help\
"id of punk::args definition for a command"
@form -form withdef -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withdef $def ?$def?"
withdef -type literal(withdef) -help\
"The literal value 'withdef'"
def -type string -multiple 1 -optional 0 -help\
"Each remaining argument is a block of text
defining argument definitions."
}]
proc parse_status {args} {
if {[llength $args] < 3} {
punk::args::parse $args withid ::punk::args::parse_status
}
set opts_and_vals $args
set arglist [lpop opts_and_vals 0]
set opts [dict create {*}{
-form {*}
-caller {}
}]
while {[llength $opts_and_vals] && [string match -* [lindex $opts_and_vals 0]]} {
set k [lpop opts_and_vals 0]
switch -- $k {
-form - -caller {
if {![llength $opts_and_vals]} {
punk::args::parse $args withid ::punk::args::parse_status
}
dict set opts $k [lpop opts_and_vals 0]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
}
set tailtype [lpop opts_and_vals 0]
switch -- $tailtype {
withid {
if {[llength $opts_and_vals] != 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set id [lindex $opts_and_vals 0]
set spec_dict [get_spec $id]
if {$spec_dict eq ""} {
error "punk::args::parse_status - no such id: $id"
}
}
withdef {
if {[llength $opts_and_vals] < 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set spec_dict [resolve {*}$opts_and_vals]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
set fid [private::parse_status_formname $spec_dict [dict get $opts -form]]
set caller [dict get $opts -caller]
if {$caller eq ""} {
#the frame walk parse would do by default names parse_status's own internal
#parse call - the definition's command name is the meaningful attribution here
set caller [Dict_getdef $spec_dict cmd_info -name [Dict_getdef $spec_dict id ""]]
}
if {[catch {
punk::args::parse $arglist -form [dict get $opts -form] -errorstyle minimal -caller $caller $tailtype {*}$opts_and_vals
} r ropts]} {
set ecode [Dict_getdef $ropts -errorcode ""]
if {[lrange $ecode 0 1] ne [list PUNKARGS VALIDATION]} {
#not an argument validation failure (e.g definition error) - propagate
return -options $ropts $r
}
set classinfo [lindex $ecode 2]
set failureclass [lindex $classinfo 0]
set payload [lrange $classinfo 1 end]
set customdict [lrange $ecode 3 end]
set badarg [Dict_getdef $customdict -badarg ""]
set status [private::parse_status_classify $failureclass $payload]
#the -argspecs payload (the whole resolved spec) is display machinery - too bulky
#for a status structure; keep the classification and the small custom keys
set ecode_slim [lrange $ecode 0 2]
foreach {k v} $customdict {
if {$k ne "-argspecs"} {
lappend ecode_slim $k $v
}
}
return [private::parse_status_build $spec_dict $fid -ok 0 -status $status -scheme error -message $r -errorcode $ecode_slim -failureclass $failureclass -badarg $badarg]
}
return [private::parse_status_build $spec_dict $fid -ok 1 -status valid -scheme info -parsedargs $r]
}
#return number of values we can assign to cater for variable length clauses such as:
# {"elseif" expr "?then?" body}
#review - efficiency? each time we call this - we are looking ahead at the same info
@ -10202,7 +10540,7 @@ tcl::namespace::eval punk::args {
if {$leadername ni $leadernames_received} {
#puts stderr "private::get_dict_can_assign_value $ldridx $values $nameidx $VAL_NAMES"
set msg "Bad number of leaders for %caller%. Not enough remaining values to assign to required arguments (fail on $leadername)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -badarg $leadername -argspecs $argspecs]] $msg
} else {
puts stderr "get_dict cannot assign val:$ldr to leadername:$leadername (222)"
#return -options [list -code error -errorcode [list PUNKARGS UNCONSUMED -argspecs $argspecs]] "private::get_dict_can_assign_value consumed 0 unexpected 2?"
@ -10413,7 +10751,7 @@ tcl::namespace::eval punk::args {
if {$valname ni $valnames_received} {
#puts stderr "private::get_dict_can_assign_value $validx $values $nameidx $VAL_NAMES"
set msg "Bad number of values for %caller%. Not enough remaining values to assign to required arguments (fail on $valname)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -badarg $valname -argspecs $argspecs]] $msg
} else {
#error 444
puts stderr "get_dict cannot assign val:$val to valname:$valname (444)"
@ -13332,7 +13670,7 @@ package provide punk::args [tcl::namespace::eval punk::args {
tcl::namespace::path {::punk::args::lib ::punk::args::system}
variable pkg punk::args
variable version
set version 0.4.2
set version 0.5.0
}]
return

141
src/bootsupport/modules/punk/ns-0.1.4.tm → src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.2.0.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application punk::ns 0.1.4
# Application punk::ns 0.2.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -5503,7 +5503,20 @@ y" {return quirkykeyscript}
usage information as this is inherently risky. (could create a file,
exit the interp etc)
"
-return -type string -default table -choices {string table tableobject}
-return -type string -default table -choices {string table tableobject dict} -choicelabels {
string "no table layout"
table "full table layout"
tableobject "table object cmd"
dict "machine-parsable resolution + parse-status"
} -help\
"Return form of the usage information.
'dict' returns a dict with keys origin, docid, cmdtype,
args_remaining and parsestatus - where parsestatus is the
parse-status structure of the supplied argument words against
the resolved definition (see punk::args::parse_status for the
documented structure; empty if the command is undocumented).
Its per-argument statuses distinguish a fully-valid, an invalid
and an incomplete argument set machine-parsably."
} {${[punk::args::resolved_def -types opts ::punk::args::arg_error -scheme]}} {
@ -5564,17 +5577,6 @@ y" {return quirkykeyscript}
set opt_grepstr [dict get $opts -grepstr]
set opt_form [dict get $opts -form]
set opt_return [dict get $opts -return]
switch -- $opt_return {
string {
set estyle "basic"
}
tableobject {
set estyle "minimal"
}
default {
set estyle "standard"
}
}
set nextopts [dict remove $opts -grepstr]
#JJJ
@ -5597,35 +5599,37 @@ y" {return quirkykeyscript}
if {[llength $testresolved] == 1} {
#only the command itself is in the args_resolved list - so we can't resolve to a deeper subcommand
ledit queryargs -1 -1 {*}$args_remaining ;#prepend
#advisory goodargs parse (marking of supplied words). With NO supplied args
#a failure only reflects missing required leaders/values - nothing to mark and
#nothing wrong with the user's (absent) input, so we show plain usage in the
#info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the
#full usage table INSIDE the raised error only for it to be discarded here -
#doubling display time for large argdocs (e.g 'i punk::args::define').
if {![llength $queryargs]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $queryargs -form $opt_form -errorstyle $advisory_estyle withid $rootdoc} parseresult]} {
if {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally
#-errorstyle minimal so no usage table is built inside a discarded error).
#-caller attributes any failure message to the queried command rather than
#an internal parse call site.
set pstatus [punk::args::parse_status $queryargs -form $opt_form -caller $querycommand withid $rootdoc]
#With NO supplied args a failure only reflects missing required
#leaders/values - nothing to mark and nothing wrong with the user's (absent)
#input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $queryargs]} {
dict set pstatus scheme info
}
} else {
return [dict create origin $rootorigin docid $rootdoc cmdtype $rootorigintype args_remaining $queryargs parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5695,36 +5699,44 @@ y" {return quirkykeyscript}
}
}
if {$origindoc ne ""} {
#important not to use "-cache 1" for this parse - need to reflect dynamically updated ensembles etc
#advisory goodargs parse (marking of supplied words). With NO supplied trailing args
#a failure only reflects missing required leaders/values (e.g 'i string is') - nothing
#to mark and nothing wrong with the user's (absent) input, so we show plain usage in
#the info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the full
#usage table INSIDE the raised error only for it to be discarded here - doubling
#display time for large argdocs (e.g 'i punk::args::define').
if {![llength $args_remaining]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $args_remaining -cache 0 -form $opt_form -errorstyle $advisory_estyle withid $origindoc} parseresult]} {
if {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
}
#caller attribution for any validation failure message: the queried command plus
#the subcommand words consumed by resolution (not the trailing argument words)
if {[llength $args_remaining]} {
set consumedwords [lrange $queryargs 0 end-[llength $args_remaining]]
} else {
set consumedwords $queryargs
}
set caller_display [string trim "$querycommand [join $consumedwords { }]"]
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally uncached and
#-errorstyle minimal so no usage table is built inside a discarded error and
#dynamically updated ensembles are reflected).
set pstatus [punk::args::parse_status $args_remaining -form $opt_form -caller $caller_display withid $origindoc]
#With NO supplied trailing args a failure only reflects missing required
#leaders/values (e.g 'i string is') - nothing to mark and nothing wrong with the
#user's (absent) input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $args_remaining]} {
dict set pstatus scheme info
}
return [dict create origin $origin docid $origindoc cmdtype $origintype args_remaining $args_remaining parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5735,6 +5747,9 @@ y" {return quirkykeyscript}
}
return $result
} else {
if {$opt_return eq "dict"} {
return [dict create origin $origin docid "" cmdtype $origintype args_remaining $args_remaining parsestatus {}]
}
return "Undocumented command $origin. Type: $origintype"
}
@ -7642,6 +7657,6 @@ namespace eval ::punk::args::register {
## Ready
package provide punk::ns [tcl::namespace::eval punk::ns {
variable version
set version 0.1.4
set version 0.2.0
}]
return

468
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.4.2.tm → src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.5.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::args 0.4.2
# Application punk::args 0.5.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args 0 0.4.2]
#[manpage_begin punkshell_module_punk::args 0 0.5.0]
#[copyright "2024"]
#[titledesc {args parsing}] [comment {-- Name section and table of contents description --}]
#[moddesc {args to nested dict of opts and values}] [comment {-- Description at end of page heading --}]
@ -4624,8 +4624,17 @@ tcl::namespace::eval punk::args {
-badarg -type string -help\
"name of an argument to highlight"
-parsedargs -type dict -help\
"Result of successful punk::pargs::parse
(currently only looks at 'received')"
"Result of successful punk::args::parse.
Together with -badarg this is converted internally to the
parse-status structure the renderers consume (received argument
rows get the goodarg style, choice words matching an argument's
value-in-effect are highlighted)."
-parsestatus -type dict -help\
"Parse-status structure as returned by punk::args::parse_status.
The renderers derive goodarg/badarg row marking and choice
value-in-effect highlighting from it. Takes precedence over
-badarg/-parsedargs. Should be built for the same form the
display renders (see -form)."
-aserror -type boolean -help\
"If true, the usage table is raised as an error message,
otherwise it is returned as a value."
@ -4770,17 +4779,22 @@ tcl::namespace::eval punk::args {
}
#set arg_error_CLR(testsinglecolour) [a+ brightred]
upvar ::punk::args::arg_error_CLR CLR
variable arg_error_CLR
set forcereload 0 ;#no need for forcereload to be true for initial run - empty array will trigger initial load
if {[info exists CLR(testsinglecolour)]} {
if {[info exists arg_error_CLR(testsinglecolour)]} {
set terminal_colour_is_on [expr {[string length [a+ yellow]]}]
set error_colour_is_on [expr {[string length $CLR(testsinglecolour)]}]
set error_colour_is_on [expr {[string length $arg_error_CLR(testsinglecolour)]}]
if {$terminal_colour_is_on ^ $error_colour_is_on} {
#results differ
set forcereload 1
}
}
private::argerror_load_colours $forcereload
#per-render colour resolution: CLR is proc-local, seeded from the shared base array;
#a scheme's overrides are merged into this local copy only. (Merging into the shared
#arg_error_CLR leaked scheme overrides into every subsequent render until a colour
#on/off state flip forced an array reload.)
array set CLR [array get arg_error_CLR]
if {[llength $args] %2 != 0} {
set arg_error_isrunning 0
@ -4791,7 +4805,7 @@ tcl::namespace::eval punk::args {
set badarg ""
set parsedargs [dict create] ;#dict with keys: leaders,opts,values,received,solos,multis (as from punk::args::parse)
set goodargs [list]
set parsestatus "" ;#parse-status structure (see ::punk::args::parse_status) - takes precedence over -badarg/-parsedargs
#-----------------------
#todo!! make changeable from config file
#JJJ 2025-07-16
@ -4802,26 +4816,17 @@ tcl::namespace::eval punk::args {
set scheme error
set form 0
dict for {k v} $args {
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -aserror -return -scheme -form} $k]
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -parsestatus -aserror -return -scheme -form} $k]
switch -- $fullk {
-badarg {
set badarg $v
}
-parsedargs {
#todo?
dict for {set setinfo} $v {
switch -- $set {
received {
foreach {r rpos} $setinfo {
if {$r ni $goodargs} {
lappend goodargs $r
}
}
}
}
}
set parsedargs $v
}
-parsestatus {
set parsestatus $v
}
-aserror {
if {![string is boolean -strict $v]} {
set arg_error_isrunning 0
@ -4844,13 +4849,15 @@ tcl::namespace::eval punk::args {
}
default {
set arg_error_isrunning 0
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -aserror -scheme -return -form"
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -parsestatus -aserror -scheme -return -form"
}
}
}
#todo - scheme - use config and iterm toml definitions etc
switch -- $scheme {
"" - -nocolor - -nocolour {
"" - nocolor - nocolour - -nocolor - -nocolour {
#the documented choice value is 'nocolour' - dash spellings and 'nocolor'
#accepted for backward compatibility
set scheme nocolour
}
info - error {}
@ -4883,7 +4890,7 @@ tcl::namespace::eval punk::args {
#hack some basics for now.
#for coloured schemes - use bold as well as brightcolour in case colour off.
#CLR has been defaulted to arg_error_CLR for its defaults
#CLR is a proc-local copy of arg_error_CLR (see above)
#The nocolour,info,error arrays have overrides for some keys.
switch -- $scheme {
nocolour {
@ -4902,6 +4909,18 @@ tcl::namespace::eval punk::args {
}
}
#parse-status structure the renderers derive goodarg/badarg row marking and choice
#value-in-effect highlighting from (see ::punk::args::parse_status for the shape).
#An explicit -parsestatus takes precedence; otherwise build one for the displayed
#form from the -parsedargs/-badarg primitives.
if {$parsestatus ne ""} {
set PSTAT $parsestatus
} else {
set PSTAT [private::parse_status_build $spec_dict [lindex $selected_forms 0] -badarg $badarg -parsedargs $parsedargs]
}
set argstatusd [dict get $PSTAT argstatus]
set receivednames [dict get $PSTAT receivednames]
#set RST [a]
set RST "\x1b\[0m"
@ -5192,17 +5211,9 @@ tcl::namespace::eval punk::args {
#goodargs
}
}
set full_goodargs [list]
#goodargs may have simplified entries for received opts of form -alias1|-alias2|-realname
#map -realname to full argname
foreach g $goodargs {
if {[string match -* $g] && [dict exists $lookup_optset $g]} {
lappend full_goodargs [dict get $lookup_optset $g]
} else {
lappend full_goodargs $g
}
}
set goodargs $full_goodargs
#received-name normalization to optionset names (formerly done here on the
#transient goodargs local) lives in private::parse_status_build - the
#renderers below consume $argstatusd/$receivednames from the structure
if {![catch {package require punk::trie}]} {
#todo - reservelist for future options - or just to affect the prefix calculation
# (similar to -choiceprefixreservelist)
@ -5304,18 +5315,25 @@ tcl::namespace::eval punk::args {
set leading_val_names_hints {}
set trailing_val_names_display $trailing_val_names
set trailing_val_names_hints {}
#puts "--> parsedargs: $parsedargs"
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
#display options first then values
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names $parsed_leaders] [list opts $opt_names_display $opt_names_hints $opt_names $parsed_opts] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names $parsed_values]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames parsedvalues
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names] [list opts $opt_names_display $opt_names_hints $opt_names] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames
set lastgroup ""
set lastgroup_parsekey ""
foreach argshow $argnames_display hint $argnames_hints arg $argnames {
set arginfo [dict get $form_dict ARG_INFO $arg]
#this argument's entry in the parse-status structure: row marking from
#its status, choice value-in-effect highlighting from hasvalue/value
if {[dict exists $argstatusd $arg]} {
set arg_status [dict get $argstatusd $arg status]
set arg_hasvalue [dict get $argstatusd $arg hasvalue]
set arg_value [dict get $argstatusd $arg value]
} else {
set arg_status unparsed
set arg_hasvalue 0
set arg_value ""
}
if {$argumentclass eq "opts"} {
set thisgroup [dict get $arginfo -group]
@ -5354,9 +5372,9 @@ tcl::namespace::eval punk::args {
}
if {$use_table} {
$t add_row [list " $thisgroup" $groupinfo "" "" $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs || $thisgroup_parsekey in $goodargs} {
} elseif {$arg_status eq "ok" || $thisgroup_parsekey in $receivednames} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5364,9 +5382,9 @@ tcl::namespace::eval punk::args {
#set arghelp "[a+ bold] $thisgroup$RST $groupinfo"
set arghelp [textblock::join -- "[a+ bold] $thisgroup$RST" " " $groupinfo]
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -5527,9 +5545,9 @@ tcl::namespace::eval punk::args {
}
append cdisplay \n $ctext
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5547,9 +5565,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5613,9 +5631,9 @@ tcl::namespace::eval punk::args {
append cdisplay \n $ctext
}
#puts "-- parsed:$parsedvalues arg:$arg c:$c"
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5640,7 +5658,7 @@ tcl::namespace::eval punk::args {
append cdisplay \n [dict get $choicelabeldict $c]
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5657,9 +5675,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5809,9 +5827,9 @@ tcl::namespace::eval punk::args {
set col1 $argshow
}
$t add_row [list $col1 $typeshow $default $multiple $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5823,9 +5841,9 @@ tcl::namespace::eval punk::args {
set arghelp "[a+ bold]$argshow$RST $linetail"
}
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -6116,6 +6134,13 @@ tcl::namespace::eval punk::args {
#default to enhanced errorstyle despite slow 'catch' (unhappy path) performance
#todo - configurable per interp/namespace
-errorstyle -type string -default enhanced -choices {enhanced standard basic minimal}
-caller -type string -default "" -help\
"Caller attribution for validation error messages.
When non-empty, this string replaces the %caller% placeholder in
validation failure messages instead of the automatic call-frame
walk - use it when parsing on behalf of another command (e.g a
usage/help display naming the queried command rather than the
internal parse call site)."
-cache -type boolean -default 0 -help\
{Use sparingly.
This caches the entire parse result or formatted validation error for the
@ -6200,6 +6225,7 @@ tcl::namespace::eval punk::args {
-form {*}
-errorstyle standard
-cache 0
-caller {}
}]
#todo - load override_errorstyle from configuration
@ -6208,7 +6234,7 @@ tcl::namespace::eval punk::args {
set opts [dict merge $defaultopts $opts]
dict for {k v} $opts {
switch -- $k {
-form - -errorstyle - -cache {
-form - -errorstyle - -cache - -caller {
}
default {
#punk::args::usage $args withid ::punk::args::parse ??
@ -6246,7 +6272,7 @@ tcl::namespace::eval punk::args {
set result [punk::args::get_dict $deflist $parseargs -form [dict get $opts -form]]
} else {
variable parse_cache
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
if {[dict exists $parse_cache $key]} {
set cached [dict get $parse_cache $key]
if {[dict get $cached type] eq "result"} {
@ -6272,7 +6298,13 @@ tcl::namespace::eval punk::args {
set ecode [dict get $erroropts -errorcode]
#punk ecode is of form PUNKARGS VALIDATION {description..} -key val ...
set msg [string map [list %caller% [Get_caller]] $msg]
if {[dict get $opts -caller] ne ""} {
#explicit caller attribution supplied (e.g punk::ns::cmdhelp usage display
#naming the queried command) - frame walking would name internal call sites.
set msg [string map [list %caller% [dict get $opts -caller]] $msg]
} else {
set msg [string map [list %caller% [Get_caller]] $msg]
}
switch -- $matched_errorstyle {
minimal {
#return -options [list -code error -errorcode $ecode] $msg
@ -6337,7 +6369,7 @@ tcl::namespace::eval punk::args {
}
}
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
dict set parse_cache $key [dict create type "error" value $elist]
return {*}$elist
} trap {PUNKARGS} {msg erropts} {
@ -6352,6 +6384,312 @@ tcl::namespace::eval punk::args {
return $result
}
#classify a PUNKARGS VALIDATION failure class-word into the parse-status overall status
#payload = elements of the errorcode description after the class word
proc private::parse_status_classify {failureclass payload} {
switch -- $failureclass {
missingrequiredleader - missingrequiredvalue - leadermissing - optionmissing - valuemissing - missingoptionvalue {
return incomplete
}
leadingvaluecount - trailingvaluecount {
#payload: <num> min <min> max <max>
set num [lindex $payload 0]
set min [Dict_getdef [lrange $payload 1 end] min ""]
if {[string is integer -strict $num] && [string is integer -strict $min] && $num < $min} {
return incomplete
}
return invalid
}
default {
return invalid
}
}
}
#build the parse-status structure documented in the ::punk::args::parse_status definition.
#fid selects the form whose argument set the per-argument statuses cover (the form usage
#displays render). Options are unvalidated internal inputs - see parse_status/arg_error.
proc private::parse_status_build {spec_dict fid args} {
set defaults [dict create {*}{
-ok ""
-status ""
-scheme ""
-message ""
-errorcode ""
-failureclass ""
-badarg ""
-parsedargs {}
}]
set opts [dict merge $defaults $args]
set parsedargs [dict get $opts -parsedargs]
set badarg [dict get $opts -badarg]
set form_dict [dict get $spec_dict FORMS $fid]
#map each member of an -alias1|-alias2|-realname optionset to the optionset (the ARG_INFO key)
set lookup_optset [dict create]
foreach optionset [dict get $form_dict OPT_NAMES] {
foreach o [split $optionset |] {
dict set lookup_optset $o $optionset
}
}
#received argument names normalized to definition argument names.
#received is dict-like but may repeat keys for -multiple arguments; values are clause ordinals.
set receivednames [list]
set positions [dict create] ;#argname -> list of clause ordinals
foreach {r rpos} [Dict_getdef $parsedargs received {}] {
if {[string match -* $r] && [dict exists $lookup_optset $r]} {
set r [dict get $lookup_optset $r]
}
if {$r ni $receivednames} {
lappend receivednames $r
}
dict lappend positions $r $rpos
}
if {$badarg ne "" && [string match -* $badarg] && [dict exists $lookup_optset $badarg]} {
set badarg [dict get $lookup_optset $badarg]
}
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
set argstatus [dict create]
foreach {names class parsedvalues} [list {*}{
} [dict get $form_dict LEADER_NAMES] leader {*}{
} $parsed_leaders {*}{
} [dict get $form_dict OPT_NAMES] option {*}{
} $parsed_opts {*}{
} [dict get $form_dict VAL_NAMES] value {*}{
} $parsed_values {*}{
}] {
foreach arg $names {
set argpositions [Dict_getdef $positions $arg {}]
set received [llength $argpositions]
if {$arg eq $badarg} {
set status bad
set argfailclass [dict get $opts -failureclass]
} elseif {$received > 0} {
set status ok
set argfailclass ""
} else {
set status unparsed
set argfailclass ""
}
#value-in-effect (includes values the parse filled from -default).
#Direct lookup by definition argument name, matching the display renderers:
#aliased optionsets store under their canonical name and are not resolved here.
if {[dict exists $parsedvalues $arg]} {
set hasvalue 1
set value [dict get $parsedvalues $arg]
} else {
set hasvalue 0
set value ""
}
dict set argstatus $arg [dict create class $class status $status received $received positions $argpositions hasvalue $hasvalue value $value failureclass $argfailclass]
}
}
return [dict create {*}{
} ok [dict get $opts -ok] {*}{
} status [dict get $opts -status] {*}{
} scheme [dict get $opts -scheme] {*}{
} message [dict get $opts -message] {*}{
} errorcode [dict get $opts -errorcode] {*}{
} failureclass [dict get $opts -failureclass] {*}{
} badarg $badarg {*}{
} id [Dict_getdef $spec_dict id ""] {*}{
} form $fid {*}{
} receivednames $receivednames {*}{
} argstatus $argstatus {*}{
}]
}
#resolve the form name whose argument set parse-status per-argument entries cover
#(first form matching the -form selection - mirrors arg_error's display-form resolution)
proc private::parse_status_formname {spec_dict formselection} {
set formnames [dict get $spec_dict form_names]
set first [lindex $formselection 0]
if {$formselection eq "*" || $formselection eq ""} {
return [lindex $formnames 0]
}
if {[string is integer -strict $first]} {
set fid [lindex $formnames $first]
if {$fid ne ""} {
return $fid
}
return [lindex $formnames 0]
}
set fmatch [tcl::prefix::match -error "" $formnames $first]
if {$fmatch ne ""} {
return $fmatch
}
return [lindex $formnames 0]
}
lappend PUNKARGS [list {
@id -id ::punk::args::parse_status
@cmd -name punk::args::parse_status\
-summary\
"Parse-status structure from a parse attempt (validation failures reported, not raised)."\
-help\
"Run a parse attempt of arglist against a punk::args definition and
return a parse-status dict describing the outcome - for success AND
for validation failure (a validation failure is reported in the
returned structure instead of being raised as an error; other errors
such as an unknown id still raise).
This is the machine-parsable form of the information behind the
usage-display argument marking (punk::ns::cmdhelp / 'i <cmd> <args...>'):
which supplied arguments validated, which argument failed and why,
and which display scheme applies. punk::args::arg_error accepts the
structure via its -parsestatus option and derives its goodarg/badarg
row marking and choice value-in-effect highlighting from it.
Overall keys:
ok 1 parse succeeded, 0 validation failure
status valid | invalid | incomplete
invalid - a supplied word failed validation
incomplete - required arguments missing (a count or
allocation shortfall). Note a supplied word
failing its -type check can also surface as
an allocation shortfall (missingrequiredvalue)
- badarg and the per-argument statuses carry
the specifics in both situations.
scheme suggested display scheme: info (ok) | error (failure)
message empty | the validation failure message
errorcode empty | the -errorcode of the validation failure with
the bulky -argspecs payload removed
failureclass empty | first word of the errorcode description
(e.g choiceviolation, typemismatch, missingrequiredvalue)
badarg empty | name of the offending/unfillable argument
id the definition id
form form name the per-argument statuses were built for
(the first form matching -form - the form usage
displays render)
receivednames received argument names normalized to definition
argument names (opt aliases folded to their
-alias|-name optionset)
argstatus dict keyed by definition argument name, each value a
dict with keys:
class leader | option | value
status ok (received and validated) |
bad (the offending argument) |
unparsed (not received)
received count of received clauses (0 if unreceived)
positions ordinal positions of received clauses
hasvalue 1 if a value-in-effect is known
value value-in-effect - includes values the
parse filled from -default; empty when
hasvalue is 0. No values are reported
for a failed parse.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\
"Arguments to parse - supplied as a single list"
@opts -prefix 0
-form -type list -default * -help\
"Restrict parsing to the set of forms listed (see punk::args::parse).
The per-argument statuses are built for the first matching form."
-caller -type string -default "" -help\
"Caller attribution for the validation failure message
(see punk::args::parse -caller). When empty, defaults to the
definition's @cmd -name (or its id) rather than the call-frame
walk parse would perform."
@values -min 2
@form -form withid -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withid $id"
@values -max 2
withid -type literal(withid) -help\
"The literal value 'withid'"
id -type string -help\
"id of punk::args definition for a command"
@form -form withdef -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withdef $def ?$def?"
withdef -type literal(withdef) -help\
"The literal value 'withdef'"
def -type string -multiple 1 -optional 0 -help\
"Each remaining argument is a block of text
defining argument definitions."
}]
proc parse_status {args} {
if {[llength $args] < 3} {
punk::args::parse $args withid ::punk::args::parse_status
}
set opts_and_vals $args
set arglist [lpop opts_and_vals 0]
set opts [dict create {*}{
-form {*}
-caller {}
}]
while {[llength $opts_and_vals] && [string match -* [lindex $opts_and_vals 0]]} {
set k [lpop opts_and_vals 0]
switch -- $k {
-form - -caller {
if {![llength $opts_and_vals]} {
punk::args::parse $args withid ::punk::args::parse_status
}
dict set opts $k [lpop opts_and_vals 0]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
}
set tailtype [lpop opts_and_vals 0]
switch -- $tailtype {
withid {
if {[llength $opts_and_vals] != 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set id [lindex $opts_and_vals 0]
set spec_dict [get_spec $id]
if {$spec_dict eq ""} {
error "punk::args::parse_status - no such id: $id"
}
}
withdef {
if {[llength $opts_and_vals] < 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set spec_dict [resolve {*}$opts_and_vals]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
set fid [private::parse_status_formname $spec_dict [dict get $opts -form]]
set caller [dict get $opts -caller]
if {$caller eq ""} {
#the frame walk parse would do by default names parse_status's own internal
#parse call - the definition's command name is the meaningful attribution here
set caller [Dict_getdef $spec_dict cmd_info -name [Dict_getdef $spec_dict id ""]]
}
if {[catch {
punk::args::parse $arglist -form [dict get $opts -form] -errorstyle minimal -caller $caller $tailtype {*}$opts_and_vals
} r ropts]} {
set ecode [Dict_getdef $ropts -errorcode ""]
if {[lrange $ecode 0 1] ne [list PUNKARGS VALIDATION]} {
#not an argument validation failure (e.g definition error) - propagate
return -options $ropts $r
}
set classinfo [lindex $ecode 2]
set failureclass [lindex $classinfo 0]
set payload [lrange $classinfo 1 end]
set customdict [lrange $ecode 3 end]
set badarg [Dict_getdef $customdict -badarg ""]
set status [private::parse_status_classify $failureclass $payload]
#the -argspecs payload (the whole resolved spec) is display machinery - too bulky
#for a status structure; keep the classification and the small custom keys
set ecode_slim [lrange $ecode 0 2]
foreach {k v} $customdict {
if {$k ne "-argspecs"} {
lappend ecode_slim $k $v
}
}
return [private::parse_status_build $spec_dict $fid -ok 0 -status $status -scheme error -message $r -errorcode $ecode_slim -failureclass $failureclass -badarg $badarg]
}
return [private::parse_status_build $spec_dict $fid -ok 1 -status valid -scheme info -parsedargs $r]
}
#return number of values we can assign to cater for variable length clauses such as:
# {"elseif" expr "?then?" body}
#review - efficiency? each time we call this - we are looking ahead at the same info
@ -10202,7 +10540,7 @@ tcl::namespace::eval punk::args {
if {$leadername ni $leadernames_received} {
#puts stderr "private::get_dict_can_assign_value $ldridx $values $nameidx $VAL_NAMES"
set msg "Bad number of leaders for %caller%. Not enough remaining values to assign to required arguments (fail on $leadername)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -badarg $leadername -argspecs $argspecs]] $msg
} else {
puts stderr "get_dict cannot assign val:$ldr to leadername:$leadername (222)"
#return -options [list -code error -errorcode [list PUNKARGS UNCONSUMED -argspecs $argspecs]] "private::get_dict_can_assign_value consumed 0 unexpected 2?"
@ -10413,7 +10751,7 @@ tcl::namespace::eval punk::args {
if {$valname ni $valnames_received} {
#puts stderr "private::get_dict_can_assign_value $validx $values $nameidx $VAL_NAMES"
set msg "Bad number of values for %caller%. Not enough remaining values to assign to required arguments (fail on $valname)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -badarg $valname -argspecs $argspecs]] $msg
} else {
#error 444
puts stderr "get_dict cannot assign val:$val to valname:$valname (444)"
@ -13332,7 +13670,7 @@ package provide punk::args [tcl::namespace::eval punk::args {
tcl::namespace::path {::punk::args::lib ::punk::args::system}
variable pkg punk::args
variable version
set version 0.4.2
set version 0.5.0
}]
return

141
src/vfs/_vfscommon.vfs/modules/punk/ns-0.1.4.tm → src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ns-0.2.0.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application punk::ns 0.1.4
# Application punk::ns 0.2.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -5503,7 +5503,20 @@ y" {return quirkykeyscript}
usage information as this is inherently risky. (could create a file,
exit the interp etc)
"
-return -type string -default table -choices {string table tableobject}
-return -type string -default table -choices {string table tableobject dict} -choicelabels {
string "no table layout"
table "full table layout"
tableobject "table object cmd"
dict "machine-parsable resolution + parse-status"
} -help\
"Return form of the usage information.
'dict' returns a dict with keys origin, docid, cmdtype,
args_remaining and parsestatus - where parsestatus is the
parse-status structure of the supplied argument words against
the resolved definition (see punk::args::parse_status for the
documented structure; empty if the command is undocumented).
Its per-argument statuses distinguish a fully-valid, an invalid
and an incomplete argument set machine-parsably."
} {${[punk::args::resolved_def -types opts ::punk::args::arg_error -scheme]}} {
@ -5564,17 +5577,6 @@ y" {return quirkykeyscript}
set opt_grepstr [dict get $opts -grepstr]
set opt_form [dict get $opts -form]
set opt_return [dict get $opts -return]
switch -- $opt_return {
string {
set estyle "basic"
}
tableobject {
set estyle "minimal"
}
default {
set estyle "standard"
}
}
set nextopts [dict remove $opts -grepstr]
#JJJ
@ -5597,35 +5599,37 @@ y" {return quirkykeyscript}
if {[llength $testresolved] == 1} {
#only the command itself is in the args_resolved list - so we can't resolve to a deeper subcommand
ledit queryargs -1 -1 {*}$args_remaining ;#prepend
#advisory goodargs parse (marking of supplied words). With NO supplied args
#a failure only reflects missing required leaders/values - nothing to mark and
#nothing wrong with the user's (absent) input, so we show plain usage in the
#info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the
#full usage table INSIDE the raised error only for it to be discarded here -
#doubling display time for large argdocs (e.g 'i punk::args::define').
if {![llength $queryargs]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $queryargs -form $opt_form -errorstyle $advisory_estyle withid $rootdoc} parseresult]} {
if {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally
#-errorstyle minimal so no usage table is built inside a discarded error).
#-caller attributes any failure message to the queried command rather than
#an internal parse call site.
set pstatus [punk::args::parse_status $queryargs -form $opt_form -caller $querycommand withid $rootdoc]
#With NO supplied args a failure only reflects missing required
#leaders/values - nothing to mark and nothing wrong with the user's (absent)
#input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $queryargs]} {
dict set pstatus scheme info
}
} else {
return [dict create origin $rootorigin docid $rootdoc cmdtype $rootorigintype args_remaining $queryargs parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5695,36 +5699,44 @@ y" {return quirkykeyscript}
}
}
if {$origindoc ne ""} {
#important not to use "-cache 1" for this parse - need to reflect dynamically updated ensembles etc
#advisory goodargs parse (marking of supplied words). With NO supplied trailing args
#a failure only reflects missing required leaders/values (e.g 'i string is') - nothing
#to mark and nothing wrong with the user's (absent) input, so we show plain usage in
#the info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the full
#usage table INSIDE the raised error only for it to be discarded here - doubling
#display time for large argdocs (e.g 'i punk::args::define').
if {![llength $args_remaining]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $args_remaining -cache 0 -form $opt_form -errorstyle $advisory_estyle withid $origindoc} parseresult]} {
if {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
}
#caller attribution for any validation failure message: the queried command plus
#the subcommand words consumed by resolution (not the trailing argument words)
if {[llength $args_remaining]} {
set consumedwords [lrange $queryargs 0 end-[llength $args_remaining]]
} else {
set consumedwords $queryargs
}
set caller_display [string trim "$querycommand [join $consumedwords { }]"]
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally uncached and
#-errorstyle minimal so no usage table is built inside a discarded error and
#dynamically updated ensembles are reflected).
set pstatus [punk::args::parse_status $args_remaining -form $opt_form -caller $caller_display withid $origindoc]
#With NO supplied trailing args a failure only reflects missing required
#leaders/values (e.g 'i string is') - nothing to mark and nothing wrong with the
#user's (absent) input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $args_remaining]} {
dict set pstatus scheme info
}
return [dict create origin $origin docid $origindoc cmdtype $origintype args_remaining $args_remaining parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5735,6 +5747,9 @@ y" {return quirkykeyscript}
}
return $result
} else {
if {$opt_return eq "dict"} {
return [dict create origin $origin docid "" cmdtype $origintype args_remaining $args_remaining parsestatus {}]
}
return "Undocumented command $origin. Type: $origintype"
}
@ -7642,6 +7657,6 @@ namespace eval ::punk::args::register {
## Ready
package provide punk::ns [tcl::namespace::eval punk::ns {
variable version
set version 0.1.4
set version 0.2.0
}]
return

468
src/vfs/_vfscommon.vfs/modules/punk/args-0.4.2.tm → src/vfs/_vfscommon.vfs/modules/punk/args-0.5.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::args 0.4.2
# Application punk::args 0.5.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args 0 0.4.2]
#[manpage_begin punkshell_module_punk::args 0 0.5.0]
#[copyright "2024"]
#[titledesc {args parsing}] [comment {-- Name section and table of contents description --}]
#[moddesc {args to nested dict of opts and values}] [comment {-- Description at end of page heading --}]
@ -4624,8 +4624,17 @@ tcl::namespace::eval punk::args {
-badarg -type string -help\
"name of an argument to highlight"
-parsedargs -type dict -help\
"Result of successful punk::pargs::parse
(currently only looks at 'received')"
"Result of successful punk::args::parse.
Together with -badarg this is converted internally to the
parse-status structure the renderers consume (received argument
rows get the goodarg style, choice words matching an argument's
value-in-effect are highlighted)."
-parsestatus -type dict -help\
"Parse-status structure as returned by punk::args::parse_status.
The renderers derive goodarg/badarg row marking and choice
value-in-effect highlighting from it. Takes precedence over
-badarg/-parsedargs. Should be built for the same form the
display renders (see -form)."
-aserror -type boolean -help\
"If true, the usage table is raised as an error message,
otherwise it is returned as a value."
@ -4770,17 +4779,22 @@ tcl::namespace::eval punk::args {
}
#set arg_error_CLR(testsinglecolour) [a+ brightred]
upvar ::punk::args::arg_error_CLR CLR
variable arg_error_CLR
set forcereload 0 ;#no need for forcereload to be true for initial run - empty array will trigger initial load
if {[info exists CLR(testsinglecolour)]} {
if {[info exists arg_error_CLR(testsinglecolour)]} {
set terminal_colour_is_on [expr {[string length [a+ yellow]]}]
set error_colour_is_on [expr {[string length $CLR(testsinglecolour)]}]
set error_colour_is_on [expr {[string length $arg_error_CLR(testsinglecolour)]}]
if {$terminal_colour_is_on ^ $error_colour_is_on} {
#results differ
set forcereload 1
}
}
private::argerror_load_colours $forcereload
#per-render colour resolution: CLR is proc-local, seeded from the shared base array;
#a scheme's overrides are merged into this local copy only. (Merging into the shared
#arg_error_CLR leaked scheme overrides into every subsequent render until a colour
#on/off state flip forced an array reload.)
array set CLR [array get arg_error_CLR]
if {[llength $args] %2 != 0} {
set arg_error_isrunning 0
@ -4791,7 +4805,7 @@ tcl::namespace::eval punk::args {
set badarg ""
set parsedargs [dict create] ;#dict with keys: leaders,opts,values,received,solos,multis (as from punk::args::parse)
set goodargs [list]
set parsestatus "" ;#parse-status structure (see ::punk::args::parse_status) - takes precedence over -badarg/-parsedargs
#-----------------------
#todo!! make changeable from config file
#JJJ 2025-07-16
@ -4802,26 +4816,17 @@ tcl::namespace::eval punk::args {
set scheme error
set form 0
dict for {k v} $args {
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -aserror -return -scheme -form} $k]
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -parsestatus -aserror -return -scheme -form} $k]
switch -- $fullk {
-badarg {
set badarg $v
}
-parsedargs {
#todo?
dict for {set setinfo} $v {
switch -- $set {
received {
foreach {r rpos} $setinfo {
if {$r ni $goodargs} {
lappend goodargs $r
}
}
}
}
}
set parsedargs $v
}
-parsestatus {
set parsestatus $v
}
-aserror {
if {![string is boolean -strict $v]} {
set arg_error_isrunning 0
@ -4844,13 +4849,15 @@ tcl::namespace::eval punk::args {
}
default {
set arg_error_isrunning 0
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -aserror -scheme -return -form"
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -parsestatus -aserror -scheme -return -form"
}
}
}
#todo - scheme - use config and iterm toml definitions etc
switch -- $scheme {
"" - -nocolor - -nocolour {
"" - nocolor - nocolour - -nocolor - -nocolour {
#the documented choice value is 'nocolour' - dash spellings and 'nocolor'
#accepted for backward compatibility
set scheme nocolour
}
info - error {}
@ -4883,7 +4890,7 @@ tcl::namespace::eval punk::args {
#hack some basics for now.
#for coloured schemes - use bold as well as brightcolour in case colour off.
#CLR has been defaulted to arg_error_CLR for its defaults
#CLR is a proc-local copy of arg_error_CLR (see above)
#The nocolour,info,error arrays have overrides for some keys.
switch -- $scheme {
nocolour {
@ -4902,6 +4909,18 @@ tcl::namespace::eval punk::args {
}
}
#parse-status structure the renderers derive goodarg/badarg row marking and choice
#value-in-effect highlighting from (see ::punk::args::parse_status for the shape).
#An explicit -parsestatus takes precedence; otherwise build one for the displayed
#form from the -parsedargs/-badarg primitives.
if {$parsestatus ne ""} {
set PSTAT $parsestatus
} else {
set PSTAT [private::parse_status_build $spec_dict [lindex $selected_forms 0] -badarg $badarg -parsedargs $parsedargs]
}
set argstatusd [dict get $PSTAT argstatus]
set receivednames [dict get $PSTAT receivednames]
#set RST [a]
set RST "\x1b\[0m"
@ -5192,17 +5211,9 @@ tcl::namespace::eval punk::args {
#goodargs
}
}
set full_goodargs [list]
#goodargs may have simplified entries for received opts of form -alias1|-alias2|-realname
#map -realname to full argname
foreach g $goodargs {
if {[string match -* $g] && [dict exists $lookup_optset $g]} {
lappend full_goodargs [dict get $lookup_optset $g]
} else {
lappend full_goodargs $g
}
}
set goodargs $full_goodargs
#received-name normalization to optionset names (formerly done here on the
#transient goodargs local) lives in private::parse_status_build - the
#renderers below consume $argstatusd/$receivednames from the structure
if {![catch {package require punk::trie}]} {
#todo - reservelist for future options - or just to affect the prefix calculation
# (similar to -choiceprefixreservelist)
@ -5304,18 +5315,25 @@ tcl::namespace::eval punk::args {
set leading_val_names_hints {}
set trailing_val_names_display $trailing_val_names
set trailing_val_names_hints {}
#puts "--> parsedargs: $parsedargs"
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
#display options first then values
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names $parsed_leaders] [list opts $opt_names_display $opt_names_hints $opt_names $parsed_opts] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names $parsed_values]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames parsedvalues
foreach argumentclassinfo [list [list leaders $leading_val_names_display $leading_val_names_hints $leading_val_names] [list opts $opt_names_display $opt_names_hints $opt_names] [list values $trailing_val_names_display $trailing_val_names_hints $trailing_val_names]] {
lassign $argumentclassinfo argumentclass argnames_display argnames_hints argnames
set lastgroup ""
set lastgroup_parsekey ""
foreach argshow $argnames_display hint $argnames_hints arg $argnames {
set arginfo [dict get $form_dict ARG_INFO $arg]
#this argument's entry in the parse-status structure: row marking from
#its status, choice value-in-effect highlighting from hasvalue/value
if {[dict exists $argstatusd $arg]} {
set arg_status [dict get $argstatusd $arg status]
set arg_hasvalue [dict get $argstatusd $arg hasvalue]
set arg_value [dict get $argstatusd $arg value]
} else {
set arg_status unparsed
set arg_hasvalue 0
set arg_value ""
}
if {$argumentclass eq "opts"} {
set thisgroup [dict get $arginfo -group]
@ -5354,9 +5372,9 @@ tcl::namespace::eval punk::args {
}
if {$use_table} {
$t add_row [list " $thisgroup" $groupinfo "" "" $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs || $thisgroup_parsekey in $goodargs} {
} elseif {$arg_status eq "ok" || $thisgroup_parsekey in $receivednames} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5364,9 +5382,9 @@ tcl::namespace::eval punk::args {
#set arghelp "[a+ bold] $thisgroup$RST $groupinfo"
set arghelp [textblock::join -- "[a+ bold] $thisgroup$RST" " " $groupinfo]
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -5527,9 +5545,9 @@ tcl::namespace::eval punk::args {
}
append cdisplay \n $ctext
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5547,9 +5565,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5613,9 +5631,9 @@ tcl::namespace::eval punk::args {
append cdisplay \n $ctext
}
#puts "-- parsed:$parsedvalues arg:$arg c:$c"
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5640,7 +5658,7 @@ tcl::namespace::eval punk::args {
append cdisplay \n [dict get $choicelabeldict $c]
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5657,9 +5675,9 @@ tcl::namespace::eval punk::args {
} else {
set cdisplay $c
}
if {[dict exists $parsedvalues $arg] && [dict get $parsedvalues $arg] eq $c} {
if {$arg_hasvalue && $arg_value eq $c} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} elseif {[dict exists $parsedvalues $arg] && $is_multiple && $c in [dict get $parsedvalues $arg]} {
} elseif {$arg_hasvalue && $is_multiple && $c in $arg_value} {
dict lappend formattedchoices $groupname [punk::ansi::ansiwrap reverse $cdisplay]
} else {
dict lappend formattedchoices $groupname $cdisplay
@ -5809,9 +5827,9 @@ tcl::namespace::eval punk::args {
set col1 $argshow
}
$t add_row [list $col1 $typeshow $default $multiple $help]
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_BADARG
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $A_GOODARG
}
} else {
@ -5823,9 +5841,9 @@ tcl::namespace::eval punk::args {
set arghelp "[a+ bold]$argshow$RST $linetail"
}
append arghelp \n
if {$arg eq $badarg} {
if {$arg_status eq "bad"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_BADARG $arghelp]
} elseif {$arg in $goodargs} {
} elseif {$arg_status eq "ok"} {
set arghelp [punk::ansi::ansiwrap -rawansi $A_GOODARG $arghelp]
}
foreach ln [split $help \n] {
@ -6116,6 +6134,13 @@ tcl::namespace::eval punk::args {
#default to enhanced errorstyle despite slow 'catch' (unhappy path) performance
#todo - configurable per interp/namespace
-errorstyle -type string -default enhanced -choices {enhanced standard basic minimal}
-caller -type string -default "" -help\
"Caller attribution for validation error messages.
When non-empty, this string replaces the %caller% placeholder in
validation failure messages instead of the automatic call-frame
walk - use it when parsing on behalf of another command (e.g a
usage/help display naming the queried command rather than the
internal parse call site)."
-cache -type boolean -default 0 -help\
{Use sparingly.
This caches the entire parse result or formatted validation error for the
@ -6200,6 +6225,7 @@ tcl::namespace::eval punk::args {
-form {*}
-errorstyle standard
-cache 0
-caller {}
}]
#todo - load override_errorstyle from configuration
@ -6208,7 +6234,7 @@ tcl::namespace::eval punk::args {
set opts [dict merge $defaultopts $opts]
dict for {k v} $opts {
switch -- $k {
-form - -errorstyle - -cache {
-form - -errorstyle - -cache - -caller {
}
default {
#punk::args::usage $args withid ::punk::args::parse ??
@ -6246,7 +6272,7 @@ tcl::namespace::eval punk::args {
set result [punk::args::get_dict $deflist $parseargs -form [dict get $opts -form]]
} else {
variable parse_cache
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
if {[dict exists $parse_cache $key]} {
set cached [dict get $parse_cache $key]
if {[dict get $cached type] eq "result"} {
@ -6272,7 +6298,13 @@ tcl::namespace::eval punk::args {
set ecode [dict get $erroropts -errorcode]
#punk ecode is of form PUNKARGS VALIDATION {description..} -key val ...
set msg [string map [list %caller% [Get_caller]] $msg]
if {[dict get $opts -caller] ne ""} {
#explicit caller attribution supplied (e.g punk::ns::cmdhelp usage display
#naming the queried command) - frame walking would name internal call sites.
set msg [string map [list %caller% [dict get $opts -caller]] $msg]
} else {
set msg [string map [list %caller% [Get_caller]] $msg]
}
switch -- $matched_errorstyle {
minimal {
#return -options [list -code error -errorcode $ecode] $msg
@ -6337,7 +6369,7 @@ tcl::namespace::eval punk::args {
}
}
set key [list $parseargs $deflist [dict get $opts -form]]
set key [list $parseargs $deflist [dict get $opts -form] [dict get $opts -caller]]
dict set parse_cache $key [dict create type "error" value $elist]
return {*}$elist
} trap {PUNKARGS} {msg erropts} {
@ -6352,6 +6384,312 @@ tcl::namespace::eval punk::args {
return $result
}
#classify a PUNKARGS VALIDATION failure class-word into the parse-status overall status
#payload = elements of the errorcode description after the class word
proc private::parse_status_classify {failureclass payload} {
switch -- $failureclass {
missingrequiredleader - missingrequiredvalue - leadermissing - optionmissing - valuemissing - missingoptionvalue {
return incomplete
}
leadingvaluecount - trailingvaluecount {
#payload: <num> min <min> max <max>
set num [lindex $payload 0]
set min [Dict_getdef [lrange $payload 1 end] min ""]
if {[string is integer -strict $num] && [string is integer -strict $min] && $num < $min} {
return incomplete
}
return invalid
}
default {
return invalid
}
}
}
#build the parse-status structure documented in the ::punk::args::parse_status definition.
#fid selects the form whose argument set the per-argument statuses cover (the form usage
#displays render). Options are unvalidated internal inputs - see parse_status/arg_error.
proc private::parse_status_build {spec_dict fid args} {
set defaults [dict create {*}{
-ok ""
-status ""
-scheme ""
-message ""
-errorcode ""
-failureclass ""
-badarg ""
-parsedargs {}
}]
set opts [dict merge $defaults $args]
set parsedargs [dict get $opts -parsedargs]
set badarg [dict get $opts -badarg]
set form_dict [dict get $spec_dict FORMS $fid]
#map each member of an -alias1|-alias2|-realname optionset to the optionset (the ARG_INFO key)
set lookup_optset [dict create]
foreach optionset [dict get $form_dict OPT_NAMES] {
foreach o [split $optionset |] {
dict set lookup_optset $o $optionset
}
}
#received argument names normalized to definition argument names.
#received is dict-like but may repeat keys for -multiple arguments; values are clause ordinals.
set receivednames [list]
set positions [dict create] ;#argname -> list of clause ordinals
foreach {r rpos} [Dict_getdef $parsedargs received {}] {
if {[string match -* $r] && [dict exists $lookup_optset $r]} {
set r [dict get $lookup_optset $r]
}
if {$r ni $receivednames} {
lappend receivednames $r
}
dict lappend positions $r $rpos
}
if {$badarg ne "" && [string match -* $badarg] && [dict exists $lookup_optset $badarg]} {
set badarg [dict get $lookup_optset $badarg]
}
set parsed_leaders [Dict_getdef $parsedargs leaders {}]
set parsed_opts [Dict_getdef $parsedargs opts {}]
set parsed_values [Dict_getdef $parsedargs values {}]
set argstatus [dict create]
foreach {names class parsedvalues} [list {*}{
} [dict get $form_dict LEADER_NAMES] leader {*}{
} $parsed_leaders {*}{
} [dict get $form_dict OPT_NAMES] option {*}{
} $parsed_opts {*}{
} [dict get $form_dict VAL_NAMES] value {*}{
} $parsed_values {*}{
}] {
foreach arg $names {
set argpositions [Dict_getdef $positions $arg {}]
set received [llength $argpositions]
if {$arg eq $badarg} {
set status bad
set argfailclass [dict get $opts -failureclass]
} elseif {$received > 0} {
set status ok
set argfailclass ""
} else {
set status unparsed
set argfailclass ""
}
#value-in-effect (includes values the parse filled from -default).
#Direct lookup by definition argument name, matching the display renderers:
#aliased optionsets store under their canonical name and are not resolved here.
if {[dict exists $parsedvalues $arg]} {
set hasvalue 1
set value [dict get $parsedvalues $arg]
} else {
set hasvalue 0
set value ""
}
dict set argstatus $arg [dict create class $class status $status received $received positions $argpositions hasvalue $hasvalue value $value failureclass $argfailclass]
}
}
return [dict create {*}{
} ok [dict get $opts -ok] {*}{
} status [dict get $opts -status] {*}{
} scheme [dict get $opts -scheme] {*}{
} message [dict get $opts -message] {*}{
} errorcode [dict get $opts -errorcode] {*}{
} failureclass [dict get $opts -failureclass] {*}{
} badarg $badarg {*}{
} id [Dict_getdef $spec_dict id ""] {*}{
} form $fid {*}{
} receivednames $receivednames {*}{
} argstatus $argstatus {*}{
}]
}
#resolve the form name whose argument set parse-status per-argument entries cover
#(first form matching the -form selection - mirrors arg_error's display-form resolution)
proc private::parse_status_formname {spec_dict formselection} {
set formnames [dict get $spec_dict form_names]
set first [lindex $formselection 0]
if {$formselection eq "*" || $formselection eq ""} {
return [lindex $formnames 0]
}
if {[string is integer -strict $first]} {
set fid [lindex $formnames $first]
if {$fid ne ""} {
return $fid
}
return [lindex $formnames 0]
}
set fmatch [tcl::prefix::match -error "" $formnames $first]
if {$fmatch ne ""} {
return $fmatch
}
return [lindex $formnames 0]
}
lappend PUNKARGS [list {
@id -id ::punk::args::parse_status
@cmd -name punk::args::parse_status\
-summary\
"Parse-status structure from a parse attempt (validation failures reported, not raised)."\
-help\
"Run a parse attempt of arglist against a punk::args definition and
return a parse-status dict describing the outcome - for success AND
for validation failure (a validation failure is reported in the
returned structure instead of being raised as an error; other errors
such as an unknown id still raise).
This is the machine-parsable form of the information behind the
usage-display argument marking (punk::ns::cmdhelp / 'i <cmd> <args...>'):
which supplied arguments validated, which argument failed and why,
and which display scheme applies. punk::args::arg_error accepts the
structure via its -parsestatus option and derives its goodarg/badarg
row marking and choice value-in-effect highlighting from it.
Overall keys:
ok 1 parse succeeded, 0 validation failure
status valid | invalid | incomplete
invalid - a supplied word failed validation
incomplete - required arguments missing (a count or
allocation shortfall). Note a supplied word
failing its -type check can also surface as
an allocation shortfall (missingrequiredvalue)
- badarg and the per-argument statuses carry
the specifics in both situations.
scheme suggested display scheme: info (ok) | error (failure)
message empty | the validation failure message
errorcode empty | the -errorcode of the validation failure with
the bulky -argspecs payload removed
failureclass empty | first word of the errorcode description
(e.g choiceviolation, typemismatch, missingrequiredvalue)
badarg empty | name of the offending/unfillable argument
id the definition id
form form name the per-argument statuses were built for
(the first form matching -form - the form usage
displays render)
receivednames received argument names normalized to definition
argument names (opt aliases folded to their
-alias|-name optionset)
argstatus dict keyed by definition argument name, each value a
dict with keys:
class leader | option | value
status ok (received and validated) |
bad (the offending argument) |
unparsed (not received)
received count of received clauses (0 if unreceived)
positions ordinal positions of received clauses
hasvalue 1 if a value-in-effect is known
value value-in-effect - includes values the
parse filled from -default; empty when
hasvalue is 0. No values are reported
for a failed parse.
"
@form -form {withid withdef}
@leaders -min 1 -max 1
arglist -type list -optional 0 -help\
"Arguments to parse - supplied as a single list"
@opts -prefix 0
-form -type list -default * -help\
"Restrict parsing to the set of forms listed (see punk::args::parse).
The per-argument statuses are built for the first matching form."
-caller -type string -default "" -help\
"Caller attribution for the validation failure message
(see punk::args::parse -caller). When empty, defaults to the
definition's @cmd -name (or its id) rather than the call-frame
walk parse would perform."
@values -min 2
@form -form withid -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withid $id"
@values -max 2
withid -type literal(withid) -help\
"The literal value 'withid'"
id -type string -help\
"id of punk::args definition for a command"
@form -form withdef -synopsis "parse_status arglist ?-form {int|<formname>...}? ?-caller <string>? withdef $def ?$def?"
withdef -type literal(withdef) -help\
"The literal value 'withdef'"
def -type string -multiple 1 -optional 0 -help\
"Each remaining argument is a block of text
defining argument definitions."
}]
proc parse_status {args} {
if {[llength $args] < 3} {
punk::args::parse $args withid ::punk::args::parse_status
}
set opts_and_vals $args
set arglist [lpop opts_and_vals 0]
set opts [dict create {*}{
-form {*}
-caller {}
}]
while {[llength $opts_and_vals] && [string match -* [lindex $opts_and_vals 0]]} {
set k [lpop opts_and_vals 0]
switch -- $k {
-form - -caller {
if {![llength $opts_and_vals]} {
punk::args::parse $args withid ::punk::args::parse_status
}
dict set opts $k [lpop opts_and_vals 0]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
}
set tailtype [lpop opts_and_vals 0]
switch -- $tailtype {
withid {
if {[llength $opts_and_vals] != 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set id [lindex $opts_and_vals 0]
set spec_dict [get_spec $id]
if {$spec_dict eq ""} {
error "punk::args::parse_status - no such id: $id"
}
}
withdef {
if {[llength $opts_and_vals] < 1} {
punk::args::parse $args withid ::punk::args::parse_status
}
set spec_dict [resolve {*}$opts_and_vals]
}
default {
punk::args::parse $args withid ::punk::args::parse_status
}
}
set fid [private::parse_status_formname $spec_dict [dict get $opts -form]]
set caller [dict get $opts -caller]
if {$caller eq ""} {
#the frame walk parse would do by default names parse_status's own internal
#parse call - the definition's command name is the meaningful attribution here
set caller [Dict_getdef $spec_dict cmd_info -name [Dict_getdef $spec_dict id ""]]
}
if {[catch {
punk::args::parse $arglist -form [dict get $opts -form] -errorstyle minimal -caller $caller $tailtype {*}$opts_and_vals
} r ropts]} {
set ecode [Dict_getdef $ropts -errorcode ""]
if {[lrange $ecode 0 1] ne [list PUNKARGS VALIDATION]} {
#not an argument validation failure (e.g definition error) - propagate
return -options $ropts $r
}
set classinfo [lindex $ecode 2]
set failureclass [lindex $classinfo 0]
set payload [lrange $classinfo 1 end]
set customdict [lrange $ecode 3 end]
set badarg [Dict_getdef $customdict -badarg ""]
set status [private::parse_status_classify $failureclass $payload]
#the -argspecs payload (the whole resolved spec) is display machinery - too bulky
#for a status structure; keep the classification and the small custom keys
set ecode_slim [lrange $ecode 0 2]
foreach {k v} $customdict {
if {$k ne "-argspecs"} {
lappend ecode_slim $k $v
}
}
return [private::parse_status_build $spec_dict $fid -ok 0 -status $status -scheme error -message $r -errorcode $ecode_slim -failureclass $failureclass -badarg $badarg]
}
return [private::parse_status_build $spec_dict $fid -ok 1 -status valid -scheme info -parsedargs $r]
}
#return number of values we can assign to cater for variable length clauses such as:
# {"elseif" expr "?then?" body}
#review - efficiency? each time we call this - we are looking ahead at the same info
@ -10202,7 +10540,7 @@ tcl::namespace::eval punk::args {
if {$leadername ni $leadernames_received} {
#puts stderr "private::get_dict_can_assign_value $ldridx $values $nameidx $VAL_NAMES"
set msg "Bad number of leaders for %caller%. Not enough remaining values to assign to required arguments (fail on $leadername)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -badarg $leadername -argspecs $argspecs]] $msg
} else {
puts stderr "get_dict cannot assign val:$ldr to leadername:$leadername (222)"
#return -options [list -code error -errorcode [list PUNKARGS UNCONSUMED -argspecs $argspecs]] "private::get_dict_can_assign_value consumed 0 unexpected 2?"
@ -10413,7 +10751,7 @@ tcl::namespace::eval punk::args {
if {$valname ni $valnames_received} {
#puts stderr "private::get_dict_can_assign_value $validx $values $nameidx $VAL_NAMES"
set msg "Bad number of values for %caller%. Not enough remaining values to assign to required arguments (fail on $valname)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -argspecs $argspecs]] $msg
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -badarg $valname -argspecs $argspecs]] $msg
} else {
#error 444
puts stderr "get_dict cannot assign val:$val to valname:$valname (444)"
@ -13332,7 +13670,7 @@ package provide punk::args [tcl::namespace::eval punk::args {
tcl::namespace::path {::punk::args::lib ::punk::args::system}
variable pkg punk::args
variable version
set version 0.4.2
set version 0.5.0
}]
return

141
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ns-0.1.4.tm → src/vfs/_vfscommon.vfs/modules/punk/ns-0.2.0.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application punk::ns 0.1.4
# Application punk::ns 0.2.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -5503,7 +5503,20 @@ y" {return quirkykeyscript}
usage information as this is inherently risky. (could create a file,
exit the interp etc)
"
-return -type string -default table -choices {string table tableobject}
-return -type string -default table -choices {string table tableobject dict} -choicelabels {
string "no table layout"
table "full table layout"
tableobject "table object cmd"
dict "machine-parsable resolution + parse-status"
} -help\
"Return form of the usage information.
'dict' returns a dict with keys origin, docid, cmdtype,
args_remaining and parsestatus - where parsestatus is the
parse-status structure of the supplied argument words against
the resolved definition (see punk::args::parse_status for the
documented structure; empty if the command is undocumented).
Its per-argument statuses distinguish a fully-valid, an invalid
and an incomplete argument set machine-parsably."
} {${[punk::args::resolved_def -types opts ::punk::args::arg_error -scheme]}} {
@ -5564,17 +5577,6 @@ y" {return quirkykeyscript}
set opt_grepstr [dict get $opts -grepstr]
set opt_form [dict get $opts -form]
set opt_return [dict get $opts -return]
switch -- $opt_return {
string {
set estyle "basic"
}
tableobject {
set estyle "minimal"
}
default {
set estyle "standard"
}
}
set nextopts [dict remove $opts -grepstr]
#JJJ
@ -5597,35 +5599,37 @@ y" {return quirkykeyscript}
if {[llength $testresolved] == 1} {
#only the command itself is in the args_resolved list - so we can't resolve to a deeper subcommand
ledit queryargs -1 -1 {*}$args_remaining ;#prepend
#advisory goodargs parse (marking of supplied words). With NO supplied args
#a failure only reflects missing required leaders/values - nothing to mark and
#nothing wrong with the user's (absent) input, so we show plain usage in the
#info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the
#full usage table INSIDE the raised error only for it to be discarded here -
#doubling display time for large argdocs (e.g 'i punk::args::define').
if {![llength $queryargs]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $queryargs -form $opt_form -errorstyle $advisory_estyle withid $rootdoc} parseresult]} {
if {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally
#-errorstyle minimal so no usage table is built inside a discarded error).
#-caller attributes any failure message to the queried command rather than
#an internal parse call site.
set pstatus [punk::args::parse_status $queryargs -form $opt_form -caller $querycommand withid $rootdoc]
#With NO supplied args a failure only reflects missing required
#leaders/values - nothing to mark and nothing wrong with the user's (absent)
#input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $queryargs]} {
dict set pstatus scheme info
}
} else {
return [dict create origin $rootorigin docid $rootdoc cmdtype $rootorigintype args_remaining $queryargs parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $queryargs]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $rootdoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5695,36 +5699,44 @@ y" {return quirkykeyscript}
}
}
if {$origindoc ne ""} {
#important not to use "-cache 1" for this parse - need to reflect dynamically updated ensembles etc
#advisory goodargs parse (marking of supplied words). With NO supplied trailing args
#a failure only reflects missing required leaders/values (e.g 'i string is') - nothing
#to mark and nothing wrong with the user's (absent) input, so we show plain usage in
#the info scheme instead of the internal-looking parse error (G-046 item 5).
#Use -errorstyle minimal for that case: the $estyle rendering would build the full
#usage table INSIDE the raised error only for it to be discarded here - doubling
#display time for large argdocs (e.g 'i punk::args::define').
if {![llength $args_remaining]} {
set advisory_estyle minimal
} else {
set advisory_estyle $estyle
}
if {[catch {punk::args::parse $args_remaining -cache 0 -form $opt_form -errorstyle $advisory_estyle withid $origindoc} parseresult]} {
if {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} elseif {$opt_return eq "tableobject"} {
set result [punk::args::arg_error "$parseresult" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result $parseresult
}
#caller attribution for any validation failure message: the queried command plus
#the subcommand words consumed by resolution (not the trailing argument words)
if {[llength $args_remaining]} {
set consumedwords [lrange $queryargs 0 end-[llength $args_remaining]]
} else {
set consumedwords $queryargs
}
set caller_display [string trim "$querycommand [join $consumedwords { }]"]
#advisory goodargs parse of the supplied words via parse_status (validation
#failures are reported in the structure, not raised; internally uncached and
#-errorstyle minimal so no usage table is built inside a discarded error and
#dynamically updated ensembles are reflected).
set pstatus [punk::args::parse_status $args_remaining -form $opt_form -caller $caller_display withid $origindoc]
#With NO supplied trailing args a failure only reflects missing required
#leaders/values (e.g 'i string is') - nothing to mark and nothing wrong with the
#user's (absent) input, so we show plain usage in the info scheme instead of the
#internal-looking parse error (G-046 item 5).
if {$opt_return eq "dict"} {
if {$scheme_received} {
dict set pstatus scheme [dict get $opts -scheme]
} elseif {![dict get $pstatus ok] && ![llength $args_remaining]} {
dict set pstatus scheme info
}
return [dict create origin $origin docid $origindoc cmdtype $origintype args_remaining $args_remaining parsestatus $pstatus]
}
if {[dict get $pstatus ok]} {
#show usage - with goodargs marked
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsedargs $parseresult]
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
} elseif {![llength $args_remaining]} {
if {!$scheme_received} {
dict set nextopts -scheme info
}
set result [punk::args::arg_error "" [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0]
} else {
set result [punk::args::arg_error [dict get $pstatus message] [punk::args::get_spec $origindoc] {*}$nextopts -aserror 0 -parsestatus $pstatus]
}
if {$opt_grepstr ne ""} {
if {[llength $opt_grepstr] == 1} {
@ -5735,6 +5747,9 @@ y" {return quirkykeyscript}
}
return $result
} else {
if {$opt_return eq "dict"} {
return [dict create origin $origin docid "" cmdtype $origintype args_remaining $args_remaining parsestatus {}]
}
return "Undocumented command $origin. Type: $origintype"
}
@ -7642,6 +7657,6 @@ namespace eval ::punk::args::register {
## Ready
package provide punk::ns [tcl::namespace::eval punk::ns {
variable version
set version 0.1.4
set version 0.2.0
}]
return
Loading…
Cancel
Save