Browse Source

misc fixups, punk::args usage table in error msg

master
Julian Noble 3 months ago
parent
commit
1b85cb9a37
  1. 26
      src/modules/argparsingtest-999999.0a1.0.tm
  2. 445
      src/modules/flagfilter-0.3.tm
  3. 6
      src/modules/natsort-0.1.1.6.tm
  4. 3
      src/modules/punk-0.1.tm
  5. 96
      src/modules/punk/args-999999.0a1.0.tm
  6. 4
      src/modules/punk/char-999999.0a1.0.tm
  7. 8
      src/modules/punk/console-999999.0a1.0.tm
  8. 2
      src/modules/punk/du-999999.0a1.0.tm
  9. 13
      src/modules/punk/fileline-999999.0a1.0.tm
  10. 159
      src/modules/punk/lib-999999.0a1.0.tm
  11. 29
      src/modules/punk/mix/commandset/module-999999.0a1.0.tm
  12. 2
      src/modules/punk/mix/templates-999999.0a1.0.tm

26
src/modules/argparsingtest-999999.0a1.0.tm

@ -115,7 +115,7 @@ namespace eval argparsingtest {
-frametype \uFFEF\ -frametype \uFFEF\
-show_edge \uFFEF\ -show_edge \uFFEF\
-show_seps \uFFEF\ -show_seps \uFFEF\
-x a\ -x ""\
-y b\ -y b\
-z c\ -z c\
-1 1\ -1 1\
@ -139,7 +139,7 @@ namespace eval argparsingtest {
-frametype \uFFEF\ -frametype \uFFEF\
-show_edge \uFFEF\ -show_edge \uFFEF\
-show_seps \uFFEF\ -show_seps \uFFEF\
-x a\ -x ""\
-y b\ -y b\
-z c\ -z c\
-1 1\ -1 1\
@ -164,7 +164,7 @@ namespace eval argparsingtest {
-frametype \uFFEF\ -frametype \uFFEF\
-show_edge \uFFEF\ -show_edge \uFFEF\
-show_seps \uFFEF\ -show_seps \uFFEF\
-x a\ -x ""\
-y b\ -y b\
-z c\ -z c\
-1 1\ -1 1\
@ -191,7 +191,7 @@ namespace eval argparsingtest {
-frametype \uFFEF\ -frametype \uFFEF\
-show_edge \uFFEF\ -show_edge \uFFEF\
-show_seps \uFFEF\ -show_seps \uFFEF\
-x a\ -x ""\
-y b\ -y b\
-z c\ -z c\
-1 1\ -1 1\
@ -209,7 +209,7 @@ namespace eval argparsingtest {
-frametype \uFFEF\ -frametype \uFFEF\
-show_edge \uFFEF\ -show_edge \uFFEF\
-show_seps \uFFEF\ -show_seps \uFFEF\
-x a\ -x ""\
-y b\ -y b\
-z c\ -z c\
-1 1\ -1 1\
@ -235,7 +235,7 @@ namespace eval argparsingtest {
-show_edge -default \uFFEF -type string -show_edge -default \uFFEF -type string
-show_seps -default \uFFEF -type string -show_seps -default \uFFEF -type string
-join -type none -multiple 1 -join -type none -multiple 1
-x -default a -type string -x -default "" -type string
-y -default b -type string -y -default b -type string
-z -default c -type string -z -default c -type string
-1 -default 1 -type boolean -1 -default 1 -type boolean
@ -249,12 +249,12 @@ namespace eval argparsingtest {
set argd [punk::args::get_dict { set argd [punk::args::get_dict {
*proc -name argtest4 -description "test of punk::args::get_dict comparative performance" *proc -name argtest4 -description "test of punk::args::get_dict comparative performance"
*opts -anyopts 0 *opts -anyopts 0
-return -default string -type string -return -default string -type string -choices {string object} -help "return type"
-frametype -default \uFFEF -type string -frametype -default \uFFEF -type string
-show_edge -default \uFFEF -type string -show_edge -default \uFFEF -type string
-show_seps -default \uFFEF -type string -show_seps -default \uFFEF -type string
-join -type none -multiple 1 -join -type none -multiple 1
-x -default a -type string -x -default "" -type string
-y -default b -type string -y -default b -type string
-z -default c -type string -z -default c -type string
-1 -default 1 -type boolean -validate_without_ansi true -1 -default 1 -type boolean -validate_without_ansi true
@ -273,7 +273,7 @@ namespace eval argparsingtest {
{-show_edge \uFFEF "show table outer borders"} {-show_edge \uFFEF "show table outer borders"}
{-show_seps \uFFEF "show separators"} {-show_seps \uFFEF "show separators"}
{-join "solo option"} {-join "solo option"}
{-x a "x val"} {-x "" "x val"}
{-y b "y val"} {-y b "y val"}
{-z c "z val"} {-z c "z val"}
{-1 1 "1val"} {-1 1 "1val"}
@ -296,7 +296,7 @@ namespace eval argparsingtest {
{show_edge.arg \uFFEF "show table borders"} {show_edge.arg \uFFEF "show table borders"}
{show_seps.arg \uFFEF "show table seps"} {show_seps.arg \uFFEF "show table seps"}
{join "join the things"} {join "join the things"}
{x.arg a "arg x"} {x.arg "" "arg x"}
{y.arg b "arg y"} {y.arg b "arg y"}
{z.arg c "arg z"} {z.arg c "arg z"}
{1.arg 1 "arg 1"} {1.arg 1 "arg 1"}
@ -314,7 +314,7 @@ namespace eval argparsingtest {
{show_edge.arg \uFFEF "show table borders"} {show_edge.arg \uFFEF "show table borders"}
{show_seps.arg \uFFEF "show table seps"} {show_seps.arg \uFFEF "show table seps"}
{join "join the things"} {join "join the things"}
{x.arg a "arg x"} {x.arg "" "arg x"}
{y.arg b "arg y"} {y.arg b "arg y"}
{z.arg c "arg z"} {z.arg c "arg z"}
{1.boolean 1 "arg 1"} {1.boolean 1 "arg 1"}
@ -333,7 +333,7 @@ namespace eval argparsingtest {
{ -frametype string \uFFEF } { -frametype string \uFFEF }
{ -show_edge string \uFFEF } { -show_edge string \uFFEF }
{ -show_seps string \uFFEF } { -show_seps string \uFFEF }
{ -x string a } { -x string "" }
{ -y string b } { -y string b }
{ -z string c } { -z string c }
{ -1 boolean 1 } { -1 boolean 1 }
@ -354,7 +354,7 @@ namespace eval argparsingtest {
{-show_edge -type string -default \uFFEF} {-show_edge -type string -default \uFFEF}
{-show_seps -type string -default \uFFEF} {-show_seps -type string -default \uFFEF}
{-join -type none -multiple} {-join -type none -multiple}
{-x -type string -default a} {-x -type string -default ""}
{-y -type string -default b} {-y -type string -default b}
{-z -type string -default c} {-z -type string -default c}
{-1 -type boolean -default 1} {-1 -type boolean -default 1}

445
src/modules/flagfilter-0.3.tm

@ -101,7 +101,7 @@ namespace eval flagfilter {
if {$a eq "--"} { if {$a eq "--"} {
break break
} }
if {$a in [dict keys $solodict]} { if {[dict exists $solodict $a]} {
set last_was_flag 0 set last_was_flag 0
if {[dict exists $solo_accumulator $a]} { if {[dict exists $solo_accumulator $a]} {
set soloval [concat [dict get $solo_accumulator $a] [dict get $solodict $a]] set soloval [concat [dict get $solo_accumulator $a] [dict get $solodict $a]]
@ -228,23 +228,23 @@ namespace eval flagfilter {
dictformat_rec $dict "" " " dictformat_rec $dict "" " "
} }
proc dictformat_rec {dict indent indentstring} { proc dictformat_rec {dict indent indentstring} {
# unpack this dimension # unpack this dimension
set is_empty 1 set is_empty 1
dict for {key value} $dict { dict for {key value} $dict {
set is_empty 0 set is_empty 0
if {[isdict $value]} { if {[isdict $value]} {
append result "$indent[list $key]\n$indent\{\n" append result "$indent[list $key]\n$indent\{\n"
append result "[dictformat_rec $value "$indentstring$indent" $indentstring]\n" append result "[dictformat_rec $value "$indentstring$indent" $indentstring]\n"
append result "$indent\}\n" append result "$indent\}\n"
} else { } else {
append result "$indent[list $key] [list $value]\n" append result "$indent[list $key] [list $value]\n"
} }
} }
if {$is_empty} { if {$is_empty} {
#experimental.. #experimental..
append result "$indent\n" append result "$indent\n"
#append result "" #append result ""
} }
return $result return $result
} }
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
@ -252,96 +252,96 @@ namespace eval flagfilter {
#solo 'category' includes longopts with value #solo 'category' includes longopts with value
#solo flags include the general list of -soloflags, and those specific to the current -commandprocessors spec (mashopts and singleopts) #solo flags include the general list of -soloflags, and those specific to the current -commandprocessors spec (mashopts and singleopts)
proc is_this_flag_solo {f solos objp} { proc is_this_flag_solo {f solos objp} {
if {![string match -* $f]} { if {![string match -* $f]} {
#not even flaglike #not even flaglike
return 0 return 0
} }
if {$f in $solos} { if {$f in $solos} {
#review! - global -soloflags shouldn't override the requirements of a commandprocessor! #review! - global -soloflags shouldn't override the requirements of a commandprocessor!
#but.. each commandprocessor needs to understand global solos occuring before our match so that we classify correctly.. #but.. each commandprocessor needs to understand global solos occuring before our match so that we classify correctly..
#todo - this may need to reference v_map and current position in scanlist to do properly #todo - this may need to reference v_map and current position in scanlist to do properly
return 1 return 1
} }
if {$f eq "-"} { if {$f eq "-"} {
#unless the caller declared it as a solo - treat this as a non flag element. (likely use is as a command match) #unless the caller declared it as a solo - treat this as a non flag element. (likely use is as a command match)
return 0 return 0
} }
if {$f eq "--"} { if {$f eq "--"} {
#this is it's own type endofoptions #this is it's own type endofoptions
return 0 return 0
} }
set p_opts [$objp get_combined_opts] set p_opts [$objp get_combined_opts]
set mashopts [dict get $p_opts mashopts] set mashopts [dict get $p_opts mashopts]
set singleopts [dict get $p_opts singleopts] set singleopts [dict get $p_opts singleopts]
set pairopts [dict get $p_opts pairopts] set pairopts [dict get $p_opts pairopts]
set longopts [dict get $p_opts longopts] set longopts [dict get $p_opts longopts]
if {$f in $singleopts} { if {$f in $singleopts} {
return 1 return 1
} }
#"any" keywords used by processors to consume anything - where we're not too worried about classifying a flagvalue vs an operand #"any" keywords used by processors to consume anything - where we're not too worried about classifying a flagvalue vs an operand
#examine these last so that an explicit configuration of flags as pairopts,mashopts etc can still be classified correctly #examine these last so that an explicit configuration of flags as pairopts,mashopts etc can still be classified correctly
if {"any" in $singleopts} { if {"any" in $singleopts} {
return 1 return 1
} }
if {[string first "=" $f] >=1} { if {[string first "=" $f] >=1} {
if {"any" in $longopts} { if {"any" in $longopts} {
return 1 return 1
} }
#todo foreach longopt - split on = and search #todo foreach longopt - split on = and search
} }
#Flag could still be part of a solo if it is in mashopts *and* has a value following it as part of the mash - but if it's a pairopt, but not mashable - we can rule it out now #Flag could still be part of a solo if it is in mashopts *and* has a value following it as part of the mash - but if it's a pairopt, but not mashable - we can rule it out now
if {($f in $pairopts) && ($f ni $mashopts)} { if {($f in $pairopts) && ($f ni $mashopts)} {
return 0 return 0
} }
#todo - suport mashes where one of the mashed flags takes an arg - review: only valid if it's last in the mash? #todo - suport mashes where one of the mashed flags takes an arg - review: only valid if it's last in the mash?
#(presumably so - unless we there was some other value delimiter such as isnumeric or capitalised flags vs lowercase values - but that seems a step too far - would require some sort of mashspec/mash-strategy config) #(presumably so - unless we there was some other value delimiter such as isnumeric or capitalised flags vs lowercase values - but that seems a step too far - would require some sort of mashspec/mash-strategy config)
#last part of mash may actually be the value too. which complicates things #last part of mash may actually be the value too. which complicates things
#linux ls seems to do this for example: #linux ls seems to do this for example:
# ls -w 0 # ls -w 0
# ls -lw 0 # ls -lw 0
# ls -lw0 # ls -lw0
# also man.. e.g # also man.. e.g
# man -Tdvi # man -Tdvi
# man -Hlynx # man -Hlynx
# man -H # man -H
# - note this last one. '-H lynx' doesn't work - so it's a mashable opt that can take a value, but is not in pairopts! (-H with no value uses env value for browser) # - note this last one. '-H lynx' doesn't work - so it's a mashable opt that can take a value, but is not in pairopts! (-H with no value uses env value for browser)
# see also comments in is_this_flag_mash # see also comments in is_this_flag_mash
# #
set flagletters [split [string range $f 1 end] ""] set flagletters [split [string range $f 1 end] ""]
set posn 1 set posn 1
set is_solo 1 ;#default assumption to disprove set is_solo 1 ;#default assumption to disprove
#trailing letters may legitimately not be in mashopts if they are part of a mashed value #trailing letters may legitimately not be in mashopts if they are part of a mashed value
#we can return 0 if we hit a non-mash flag first.. but at each mashflag we need to test if we can classify as definitely solo or not, or else keep processing #we can return 0 if we hit a non-mash flag first.. but at each mashflag we need to test if we can classify as definitely solo or not, or else keep processing
foreach l $flagletters { foreach l $flagletters {
if {"-$l" ni $mashopts} { if {"-$l" ni $mashopts} {
#presumably an ordinary flag not-known to us #presumably an ordinary flag not-known to us
return 0 return 0
} else { } else {
if {"-$l" in $pairopts} { if {"-$l" in $pairopts} {
if {$posn == [llength $flagletters]} { if {$posn == [llength $flagletters]} {
#in pairopts and mash - but no value for it in the mash - thefore not a solo #in pairopts and mash - but no value for it in the mash - thefore not a solo
return 0 return 0
} else {
#entire tail is the value - this letter is effectively solo
return 1
}
} elseif {"-$l" in $singleopts} {
#not allowed to take a value - keep processing letters
} else { } else {
#entire tail is the value - this letter is effectively solo #can take a value! but not if at very end of mash. Either way This is a solo
return 1 return 1
} }
} elseif {"-$l" in $singleopts} {
#not allowed to take a value - keep processing letters
} else {
#can take a value! but not if at very end of mash. Either way This is a solo
return 1
} }
} }
} return $is_solo
return $is_solo
} }
#todo? support global (non-processor specific) mash list? -mashflags ? #todo? support global (non-processor specific) mash list? -mashflags ?
proc is_this_flag_mash {f objp} { proc is_this_flag_mash {f objp} {
@ -373,7 +373,7 @@ namespace eval flagfilter {
# mashopt cannot be in both singleopts and pairopts. (NAND) # mashopt cannot be in both singleopts and pairopts. (NAND)
foreach l $flagletters { foreach l $flagletters {
if {-$l in $pairopts} { if {-$l in $pairopts} {
if {"$-l" in $mashopts} { if {"-$l" in $mashopts} {
#need to consider any remainder in the mash as this value .. if no remainder - then this is a mash, but not 'solo' because this flag needs to consume the following arg. #need to consider any remainder in the mash as this value .. if no remainder - then this is a mash, but not 'solo' because this flag needs to consume the following arg.
# We are only concerned with mashness here so just stop processing mash elements when we hit the first one that is a pairopt # We are only concerned with mashness here so just stop processing mash elements when we hit the first one that is a pairopt
break break
@ -449,43 +449,43 @@ namespace eval flagfilter {
proc add_dispatch_raw {recordvar parentname v} { proc add_dispatch_raw {recordvar parentname v} {
upvar $recordvar drecord upvar $recordvar drecord
if {[dict exists $drecord $parentname]} { if {[dict exists $drecord $parentname]} {
set dispatchinfo [dict get $drecord $parentname raw] set dispatchinfo [dict get $drecord $parentname raw]
lappend dispatchinfo $v lappend dispatchinfo $v
dict set drecord $parentname raw $dispatchinfo dict set drecord $parentname raw $dispatchinfo
} }
} }
proc add_dispatch_argument {recordvar parentname k v} { proc add_dispatch_argument {recordvar parentname k v} {
upvar $recordvar drecord upvar $recordvar drecord
if {[dict exists $drecord $parentname]} { if {[dict exists $drecord $parentname]} {
set dispatchinfo [dict get $drecord $parentname arguments] set dispatchinfo [dict get $drecord $parentname arguments]
lappend dispatchinfo $k $v ;#e.g -opt 1 lappend dispatchinfo $k $v ;#e.g -opt 1
dict set drecord $parentname arguments $dispatchinfo dict set drecord $parentname arguments $dispatchinfo
} }
} }
proc lsearch-all-stride-2 {l search} { proc lsearch-all-stride-2 {l search} {
set posns [lmap i [lsearch -all $l $search] {expr {($i % 2) == 0 ? $i : [list x]}}] set posns [lmap i [lsearch -all $l $search] {expr {($i % 2) == 0 ? $i : [list x]}}]
return [lsearch -all -inline -not $posns x] return [lsearch -all -inline -not $posns x]
} }
proc update_dispatch_argument {recordvar parentname k v} { proc update_dispatch_argument {recordvar parentname k v} {
upvar $recordvar drecord upvar $recordvar drecord
if {[dict exists $drecord $parentname]} { if {[dict exists $drecord $parentname]} {
set dispatchinfo [dict get $drecord $parentname arguments] set dispatchinfo [dict get $drecord $parentname arguments]
#can't assume there aren't repeat values e.g -v -v #can't assume there aren't repeat values e.g -v -v
#dict set dispatchinfo $k $v #dict set dispatchinfo $k $v
if {[package vcompare [info tclversion] 8.7a5] >= 0} { if {[package vcompare [info tclversion] 8.7a5] >= 0} {
set posns [lsearch -all -stride 2 $dispatchinfo $k] set posns [lsearch -all -stride 2 $dispatchinfo $k]
} else { } else {
set posns [lsearch-all-stride-2 $dispatchinfo $k] set posns [lsearch-all-stride-2 $dispatchinfo $k]
} }
set lastitem [lindex $posns end] set lastitem [lindex $posns end]
if {[string length $lastitem]} { if {[string length $lastitem]} {
set val_idx [expr {$lastitem + 1}] set val_idx [expr {$lastitem + 1}]
set dispatchinfo [lreplace $dispatchinfo[set dispatchinfo {}] $val_idx $val_idx $v] ;# inlineK set dispatchinfo [lreplace $dispatchinfo[set dispatchinfo {}] $val_idx $val_idx $v] ;# inlineK
dict set drecord $parentname arguments $dispatchinfo dict set drecord $parentname arguments $dispatchinfo
} else { } else {
error "Unable to update dispatch argument $k with value $v in dispatch record for $parentname" error "Unable to update dispatch argument $k with value $v in dispatch record for $parentname"
} }
#dict set drecord $parentname $dispatchinfo #dict set drecord $parentname $dispatchinfo
} }
} }
@ -561,7 +561,7 @@ namespace eval flagfilter {
if {$f in $solos} { if {$f in $solos} {
return 0 return 0
} }
if {$f in [list "-" "--"]} { if {$f in {- --}} {
return 0 return 0
} }
#longopts (--x=blah) and alternative --x blah #longopts (--x=blah) and alternative --x blah
@ -617,17 +617,29 @@ namespace eval flagfilter {
variable o_codemap variable o_codemap
variable o_flagcategory variable o_flagcategory
constructor {values} { constructor {values} {
set o_codemap [list \ set o_codemap [dict create \
operand op \ operand op \
flagvalue fv \ flagvalue fv \
soloflag so \ soloflag so \
flag fl \ flag fl \
unallocated un \ unallocated un \
endofoptions eo \ endofoptions eo \
] ]
set o_flagcategory [list "flag" "flagvalue" "soloflag"] set o_flagcategory [list "flag" "flagvalue" "soloflag"]
set o_values $values set o_values $values
set o_remaining [lsearch -all $values *] ;#create a list of indices e.g 0 1 2 3 4 5 6 #set o_remaining [lsearch -all $values *] ;#create a list of indices e.g 0 1 2 3 4 5 6
#lsearch -all <list> * is fast for very small lists - but lseq wins from size around 30+
if {[llength $values]} {
if {[llength $values] < 30} {
#common case is short lists - but we don't want to penalize large lists
set o_remaining [lsearch -all $values *]
} else {
#punk::lib::range wraps lseq if available
set o_remaining [punk::lib::range 0 [llength $values]-1]
}
} else {
set o_remaining [list]
}
set o_allocated [list] set o_allocated [list]
set o_map [list] set o_map [list]
foreach posn $o_remaining { foreach posn $o_remaining {
@ -703,8 +715,11 @@ namespace eval flagfilter {
dict for {k vinfo} $o_map { dict for {k vinfo} $o_map {
lassign $vinfo class type val lassign $vinfo class type val
if {[string match $classmatch $class]} { if {[string match $classmatch $class]} {
if {$type ni [list flag flagvalue soloflag]} { switch -- $type {
lappend resultlist $val flag - flagvalue - soloflag {}
default {
lappend resultlist $val
}
} }
} }
} }
@ -716,8 +731,10 @@ namespace eval flagfilter {
dict for {k vinfo} $o_map { dict for {k vinfo} $o_map {
lassign $vinfo class type val lassign $vinfo class type val
if {[string match $classmatch $class]} { if {[string match $classmatch $class]} {
if {$type in [list flag flagvalue soloflag]} { switch -- $type {
lappend list_flagged $val flag - flagvalue - soloflag {
lappend list_flagged $val
}
} }
} }
} }
@ -775,15 +792,19 @@ namespace eval flagfilter {
return $all_flagged return $all_flagged
} }
method typedrange_class_type_from_arg {argclass argtype} { method typedrange_class_type_from_arg {argclass argtype} {
#set o_flagcategory [list "flag" "flagvalue" "soloflag"]
if {$argclass eq "unallocated"} { if {$argclass eq "unallocated"} {
if {$argtype in $o_flagcategory} { switch -- $argtype {
return [list unallocated flagtype] flag - flagvalue - soloflag {
} else { return [list unallocated flagtype]
if {![string length $argtype]} { }
#should only happen if something wrong with the tail_processor - rather than error out, for now at least make it stand out in the . default {
set argtype UNKNOWN if {![string length $argtype]} {
#should only happen if something wrong with the tail_processor - rather than error out, for now at least make it stand out in the .
set argtype UNKNOWN
}
return [list unallocated ${argtype}type] ;#e.g unallocated_operand, unallocated_endofoptions
} }
return [list unallocated ${argtype}type] ;#e.g unallocated_operand, unallocated_endofoptions
} }
} else { } else {
return [list $argclass argtype] ;# e.g command something return [list $argclass argtype] ;# e.g command something
@ -916,17 +937,20 @@ namespace eval flagfilter {
append remline [overtype::left $col "."] append remline [overtype::left $col "."]
} else { } else {
set tp [lindex [dict get $o_map $vidx] 1] set tp [lindex [dict get $o_map $vidx] 1]
set tp [string map $o_codemap $tp] #set tp [string map $o_codemap $tp]
if {[dict exists $o_codemap $tp]} {
set tp [dict get $o_codemap $tp]
}
append remline [overtype::left $col $tp] append remline [overtype::left $col $tp]
} }
} }
set cmdlist [list] set cmdlist [list]
dict for {vidx info} $o_map { dict for {vidx info} $o_map {
if {[lindex $info 0] ne "unallocated"} { if {[lindex $info 0] ne "unallocated"} {
set c [lindex [split [lindex $info 0] .] 0] set c [lindex [split [lindex $info 0] .] 0]
if {$c ni $cmdlist} { if {$c ni $cmdlist} {
lappend cmdlist $c lappend cmdlist $c
} }
} }
} }
set clinelist [list] set clinelist [list]
@ -935,7 +959,10 @@ namespace eval flagfilter {
dict for {vidx info} $o_map { dict for {vidx info} $o_map {
lassign $info class type v lassign $info class type v
if {($c eq $class) || [string equal -length [string length "$c."] "$c." $class]} { if {($c eq $class) || [string equal -length [string length "$c."] "$c." $class]} {
set tp [string map $o_codemap $type] #set tp [string map $o_codemap $type]
if {[dict exists $o_codemap $type]} {
set tp [dict get $o_codemap $type]
}
append cline [overtype::left $col $tp] append cline [overtype::left $col $tp]
} else { } else {
append cline [overtype::left $col "."] append cline [overtype::left $col "."]
@ -951,7 +978,10 @@ namespace eval flagfilter {
append aline [overtype::left $col "."] append aline [overtype::left $col "."]
} else { } else {
set tp [lindex [dict get $o_map $vidx] 1] set tp [lindex [dict get $o_map $vidx] 1]
set tp [string map $o_codemap $tp] #set tp [string map $o_codemap $tp]
if {[dict exists $o_codemap $tp]} {
set tp [dict get $o_codemap $tp]
}
append aline [overtype::left $col $tp] append aline [overtype::left $col $tp]
} }
} }
@ -1389,27 +1419,27 @@ namespace eval flagfilter {
set unflagged_list_remaining [$VMAP get_list_unflagged_by_class "unallocated"] set unflagged_list_remaining [$VMAP get_list_unflagged_by_class "unallocated"]
return [list \ return [dict create \
listremaining $unconsumed_flags_and_values \ listremaining $unconsumed_flags_and_values \
parseerrors $argerrors \ parseerrors $argerrors \
parsestatus $parsestatus \ parsestatus $parsestatus \
flagged $all_flagged_plus \ flagged $all_flagged_plus \
flaggedlist $all_flagged_list \ flaggedlist $all_flagged_list \
flaggedremaining $remaining_flagged \ flaggedremaining $remaining_flagged \
flaggedlistremaining $remaining_flagged_list \ flaggedlistremaining $remaining_flagged_list \
unflagged $unflagged \ unflagged $unflagged \
unflaggedlist $unflagged_list \ unflaggedlist $unflagged_list \
unflaggedremaining $remaining_unflagged \ unflaggedremaining $remaining_unflagged \
unflaggedlistremaining $unflagged_list_remaining \ unflaggedlistremaining $unflagged_list_remaining \
flaggednew $extra_flags_from_positionals \ flaggednew $extra_flags_from_positionals \
arglist [concat $unflagged_list_in_processing_order $all_flagged] \ arglist [concat $unflagged_list_in_processing_order $all_flagged] \
arglistremaining [concat $unflagged_list_remaining $remaining_flagged] \ arglistremaining [concat $unflagged_list_remaining $remaining_flagged] \
impliedflagged $implied_flagged \ impliedflagged $implied_flagged \
impliedunflagged $implied_unflagged \ impliedunflagged $implied_unflagged \
dispatch $dispatch \ dispatch $dispatch \
classifications [$VMAP get_map] \ classifications [$VMAP get_map] \
gridstring "\n[$VMAP grid]" \ gridstring "\n[$VMAP grid]" \
vmapobject "flagfilter::VMAP_$runid" \ vmapobject "flagfilter::VMAP_$runid" \
] ]
} }
@ -1463,7 +1493,7 @@ namespace eval flagfilter {
#not even flaglike #not even flaglike
return 1 return 1
} }
if {$f in [list "-" "--"]} { if {$f in {- --}} {
return 1 return 1
} }
} }
@ -1557,24 +1587,16 @@ namespace eval flagfilter {
set o_pairopts [list] set o_pairopts [list]
set o_longopts [list] set o_longopts [list]
if {[dict exists $o_pinfo mashopts]} { if {[dict exists $o_pinfo mashopts]} {
foreach m [dict get $o_pinfo mashopts] { lappend o_mashopts {*}[dict get $o_pinfo mashopts]
lappend o_mashopts $m
}
} }
if {[dict exists $o_pinfo singleopts]} { if {[dict exists $o_pinfo singleopts]} {
foreach s [dict get $o_pinfo singleopts] { lappend o_singleopts {*}[dict get $o_pinfo singleopts]
lappend o_singleopts $s
}
} }
if {[dict exists $o_pinfo pairopts]} { if {[dict exists $o_pinfo pairopts]} {
foreach po [dict get $o_pinfo pairopts] { lappend o_pairopts {*}[dict get $o_pinfo pairopts]
lappend o_pairopts $po
}
} }
if {[dict exists $o_pinfo longopts]} { if {[dict exists $o_pinfo longopts]} {
foreach l [dict get $o_pinfo longopts] { lappend o_longopts {*}[dict get $o_pinfo longopts]
lappend o_longopts $l
}
} }
} }
@ -1701,16 +1723,17 @@ namespace eval flagfilter {
if {[my can_match $a]} { if {[my can_match $a]} {
return 0 return 0
} }
if {$a in [list "-" "--"]} { if {$a in {- --}} {
#specials not defined as solos #specials not defined as solos
return 0 return 0
} }
if {$o_name eq "global"} { if {$o_name eq "global"} {
} } elseif {$o_name eq "tail_processor"} {
if {$o_name eq "tail_processor"} {
} }
if {$a in $o_singleopts} { if {$a in $o_singleopts} {
return 1 return 1
} }
@ -1782,6 +1805,7 @@ namespace eval flagfilter {
if {[my is_sub]} { if {[my is_sub]} {
#this spec is a sub #this spec is a sub
set subopts [my get_opts] set subopts [my get_opts]
#does order matter? could use struct::set union ?
foreach m [dict get $subopts mashopts] { foreach m [dict get $subopts mashopts] {
if {$m ni $mashopts} { if {$m ni $mashopts} {
lappend mashopts $m lappend mashopts $m
@ -1821,12 +1845,10 @@ namespace eval flagfilter {
proc get_command_info {cmdname cspecs} { proc get_command_info {cmdname cspecs} {
foreach item $cspecs { foreach item $cspecs {
lassign $item cmd specinfo lassign $item cmd specinfo
if {$cmd eq $cmdname} { if {$cmd eq $cmdname && [dict exists $specinfo dispatch]} {
if {[dict exists $specinfo dispatch]} { return $specinfo
return $specinfo
} }
}
} }
return [list] return [list]
} }
@ -2103,10 +2125,8 @@ namespace eval flagfilter {
#puts stderr " check_flags - temporary disable of checking for invalid flags" #puts stderr " check_flags - temporary disable of checking for invalid flags"
set pairflagged $flagged_list set pairflagged $flagged_list
foreach {f v} $pairflagged { foreach {f v} $pairflagged {
if {$f ni $acceptextra} { if {$f ni $acceptextra && $f ni $known_flags} {
if {$f ni $known_flags} { lappend invalid_flags $f
lappend invalid_flags $f
}
} }
} }
} }
@ -2176,7 +2196,7 @@ namespace eval flagfilter {
do_debug 2 $debugc "commandinfo for $parentname: $commandinfo" do_debug 2 $debugc "commandinfo for $parentname: $commandinfo"
set command [dict get $dispatchrecord command] set command [dict get $dispatchrecord command]
#support for %x% placeholders in dispatchrecord command #support for %x% placeholders in dispatchrecord command
set command [string map [list %match% %matched%] $command] ;#alias set command [string map {%match% %matched%} $command] ;#alias
set command [string map [list %matched% [dict get $dispatchrecord matched]] $command] set command [string map [list %matched% [dict get $dispatchrecord matched]] $command]
set argnum_indices [regexp -indices -all -inline $re_argnum $command] set argnum_indices [regexp -indices -all -inline $re_argnum $command]
@ -2222,11 +2242,12 @@ namespace eval flagfilter {
set matched_opts [list] set matched_opts [list]
set matched_in_order [list] set matched_in_order [list]
set prefix "${parentname}." set prefix "${parentname}."
set prefixlen [string length $prefix]
foreach {k v} $argvals { foreach {k v} $argvals {
#puts "$$$$ $k" #puts "$$$$ $k"
if {[string equal -length [string length $prefix] $prefix $k]} { if {[string equal -length $prefixlen $prefix $k]} {
#key is prefixed with "commandname." #key is prefixed with "commandname."
set k [string replace $k 0 [string length $prefix]-1] set k [string replace $k 0 $prefixlen-1]
} }
#todo - -- ? #todo - -- ?
if {[string match -* $k]} { if {[string match -* $k]} {
@ -2548,9 +2569,10 @@ namespace eval flagfilter {
} else { } else {
set tail_unallocated [list] set tail_unallocated [list]
} }
set extraflags [list] #set extraflags [list]
#set extraflags [punk::lib::dict_merge_ordered $defaults $tail_unallocated] set extraflags [punk::lib::dict_merge_ordered $defaults $tail_unallocated]
#dict merge based operation can't work if there are solo_flags #dict merge based operation can't work if there are solo_flags?
#review
if {[llength $tail_unallocated]} { if {[llength $tail_unallocated]} {
for {set i $a} {$i <=$b} {incr i} { for {set i $a} {$i <=$b} {incr i} {
set arginfo [dict get $classifications $i] set arginfo [dict get $classifications $i]
@ -2597,20 +2619,19 @@ namespace eval flagfilter {
for {set i $a} {$i <=$b} {incr i} { for {set i $a} {$i <=$b} {incr i} {
set arginfo [dict get $classifications $i] set arginfo [dict get $classifications $i]
lassign $arginfo class ftype v lassign $arginfo class ftype v
if {$ftype eq "flag"} { switch -- $ftype {
lappend extraflags $v flag - flagvalue {
} lappend extraflags $v
if {$ftype eq "soloflag"} {
lappend extraflags $v
if {[dict exists $defaults $v]} {
lappend extraflags [dict get $defaults $v]
} else {
lappend extraflags 1
} }
} soloflag {
if {$ftype eq "flagvalue"} { lappend extraflags $v
lappend extraflags $v if {[dict exists $defaults $v]} {
} lappend extraflags [dict get $defaults $v]
} else {
lappend extraflags 1
}
}
}
} }
foreach {k v} [dict get $defaults] { foreach {k v} [dict get $defaults] {
if {$k ni $extraflags} { if {$k ni $extraflags} {

6
src/modules/natsort-0.1.1.6.tm

@ -1712,9 +1712,9 @@ namespace eval natsort {
set debug [dict get $args -debug] set debug [dict get $args -debug]
set collate [dict get $args -collate] set collate [dict get $args -collate]
set db [dict get $args -db] set db [dict get $args -db]
set winlike [dict get $args -winlike] set winlike [dict get $args -winlike]
set topchars [dict get $args -topchars] set topchars [dict get $args -topchars]

3
src/modules/punk-0.1.tm

@ -4603,7 +4603,8 @@ namespace eval punk {
know {[expr $args] || 1} {tailcall expr $args} know {[expr $args] || 1} {tailcall expr $args}
#it is significantly faster to call a proc such as punk::lib::range like this than to inline it in the unknown proc #it is significantly faster to call a proc such as punk::lib::range like this than to inline it in the unknown proc
know {[regexp {^([0-9]+)\.\.([0-9]+)$} [lindex $args 0 0] -> from to]} {punk::lib:range $from $to} #punk::lib::range is defined as a wrapper to lseq if it is available (8.7+)
know {[regexp {^([+-]*[0-9_]+)\.\.([+-]*[0-9_]+)$} [lindex $args 0 0] -> from to]} {punk::lib::range $from $to}
#NOTE: #NOTE:

96
src/modules/punk/args-999999.0a1.0.tm

@ -427,7 +427,7 @@ tcl::namespace::eval punk::args {
dict - dictionary { dict - dictionary {
set v dict set v dict
} }
any - ansistring { none - any - ansistring {
} }
default { default {
@ -566,6 +566,7 @@ tcl::namespace::eval punk::args {
} }
} }
-default - -solo - -range - -choices - -choicelabels - -minlen - -maxlen - -nocase - -optional - -multiple - -validate_without_ansi - -allow_ansi - -strip_ansi - -help - -ARGTYPE { -default - -solo - -range - -choices - -choicelabels - -minlen - -maxlen - -nocase - -optional - -multiple - -validate_without_ansi - -allow_ansi - -strip_ansi - -help - -ARGTYPE {
#review -solo 1 vs -type none ?
tcl::dict::set spec_merged $spec $specval tcl::dict::set spec_merged $spec $specval
} }
default { default {
@ -663,8 +664,51 @@ tcl::namespace::eval punk::args {
return $caller return $caller
} }
proc err {msg args} { proc arg_error {msg spec_dict {badarg ""}} {
set errmsg $msg
if {![catch {package require textblock}]} {
append errmsg \n
set t [textblock::class::table new [a+ web-yellow]Usage[a]]
$t add_column -headers {Arg}
$t add_column -headers {Type}
$t add_column -headers {Default}
set c_default [a+ web-white Web-limegreen]
set c_badarg [a+ web-crimson]
foreach arg [dict get $spec_dict opt_names] {
set arginfo [dict get $spec_dict arg_info $arg]
if {[dict exists $arginfo -default]} {
#set default $c_default[dict get $arginfo -default]
set default [dict get $arginfo -default]
} else {
set default ""
}
$t add_row [list $arg [dict get $arginfo -type] $default]
if {$arg eq $badarg} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $c_badarg
}
}
foreach arg [dict get $spec_dict val_names] {
set arginfo [dict get $spec_dict arg_info $arg]
if {[dict exists $arginfo -default]} {
set default [dict get $arginfo -default]
} else {
set default ""
}
$t add_row [list $arg [dict get $arginfo -type] $default]
if {$arg eq $badarg} {
$t configure_row [expr {[$t row_count]-1}] -ansibase $c_badarg
}
}
$t configure -show_hseps 0 -show_header 1 -ansibase_body [a+ web-white] -ansibase_header [a+ brightyellow] -ansiborder_header [a+ brightyellow] -ansiborder_body [a+ brightyellow]
append errmsg [$t print]
} else {
#todo - something boring
}
error $errmsg
} }
#generally we expect values to contain leading dashes only if -- specified. Otherwise no reliable way determine difference between bad flags and values #generally we expect values to contain leading dashes only if -- specified. Otherwise no reliable way determine difference between bad flags and values
@ -761,12 +805,12 @@ tcl::namespace::eval punk::args {
set a [lindex $arglist $i] set a [lindex $arglist $i]
if {![tcl::string::match -* $a]} { if {![tcl::string::match -* $a]} {
#we can't treat as first positional arg - as it comes before the eopt indicator -- #we can't treat as first positional arg - as it comes before the eopt indicator --
error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs" arg_error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs" $argspecs
} }
#TODO! #TODO!
if {[tcl::dict::get $arg_info $a -type] ne "none"} { if {[tcl::dict::get $arg_info $a -type] ne "none"} {
if {[incr i] > $maxidx} { if {[incr i] > $maxidx} {
error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $a which is not marked with -solo 1" arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $a which is not marked with -solo 1" $argspecs $a
} }
} }
lappend flagsreceived $a ;#dups ok lappend flagsreceived $a ;#dups ok
@ -798,7 +842,7 @@ tcl::namespace::eval punk::args {
} }
#incr i to skip flagval #incr i to skip flagval
if {[incr i] > $maxidx} { if {[incr i] > $maxidx} {
error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $fullopt at index [expr {$i-1}] which is not marked with -type none" $argspecs $fullopt
} }
} else { } else {
#type none (solo-flag) #type none (solo-flag)
@ -828,7 +872,7 @@ tcl::namespace::eval punk::args {
} }
lappend flagsreceived $a ;#adhoc flag as supplied lappend flagsreceived $a ;#adhoc flag as supplied
if {[incr i] > $maxidx} { if {[incr i] > $maxidx} {
error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" arg_error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last adhoc option $a at index [expr {$i-1}] which is not marked with -type none" $argspecs $a
} }
} else { } else {
#review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none #review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none
@ -845,7 +889,7 @@ tcl::namespace::eval punk::args {
} else { } else {
#delay Get_caller so only called in the unhappy path #delay Get_caller so only called in the unhappy path
set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt] set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt]
error $errmsg arg_error $errmsg $argspecs $fullopt
} }
} }
} }
@ -892,14 +936,14 @@ tcl::namespace::eval punk::args {
if {$val_max == -1} { if {$val_max == -1} {
#only check min #only check min
if {$num_values < $val_min} { if {$num_values < $val_min} {
error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected at least $val_min" arg_error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected at least $val_min" $argspecs
} }
} else { } else {
if {$num_values < $val_min || $num_values > $val_max} { if {$num_values < $val_min || $num_values > $val_max} {
if {$val_min == $val_max} { if {$val_min == $val_max} {
error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected exactly $val_min" arg_error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected exactly $val_min" $argspecs
} else { } else {
error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected between $val_min and $val_max inclusive" arg_error "bad number of trailing values for [Get_caller]. Got $num_values values. Expected between $val_min and $val_max inclusive" $argspecs
} }
} }
} }
@ -926,10 +970,10 @@ tcl::namespace::eval punk::args {
#} #}
#for now (2024-06) punk::lib::ldiff is a better compromise across normal/safe interps e.g 0.7/0.8us #for now (2024-06) punk::lib::ldiff is a better compromise across normal/safe interps e.g 0.7/0.8us
if {[llength [set missing [punk::lib::ldiff $opt_required $flagsreceived]]]} { if {[llength [set missing [punk::lib::ldiff $opt_required $flagsreceived]]]} {
error "Required option missing for [Get_caller]. missing flags $missing are marked with -optional false - so must be present in full-length form" arg_error "Required option missing for [Get_caller]. missing flags $missing are marked with -optional false - so must be present in full-length form" $argspecs
} }
if {[llength [set missing [punk::lib::ldiff $val_required $valnames_received]]]} { if {[llength [set missing [punk::lib::ldiff $val_required $valnames_received]]]} {
error "Required value missing for [Get_caller]. missing values $missing marked with -optional false - so must be present" arg_error "Required value missing for [Get_caller]. missing values $missing marked with -optional false - so must be present" $argspecs
} }
@ -1009,13 +1053,13 @@ tcl::namespace::eval punk::args {
-minlen { -minlen {
# -1 for disable is as good as zero # -1 for disable is as good as zero
if {[tcl::string::length $e_check] < $checkval} { if {[tcl::string::length $e_check] < $checkval} {
error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'" arg_error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'" $argspecs $argname
} }
} }
-maxlen { -maxlen {
if {$checkval ne "-1"} { if {$checkval ne "-1"} {
if {[tcl::string::length $e_check] > $checkval} { if {[tcl::string::length $e_check] > $checkval} {
error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'" arg_error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'" $argspecs $argname
} }
} }
} }
@ -1032,16 +1076,16 @@ tcl::namespace::eval punk::args {
lassign [tcl::dict::get $thisarg -range] low high lassign [tcl::dict::get $thisarg -range] low high
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {![tcl::string::is integer -strict $e_check]} { if {![tcl::string::is integer -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e'" arg_error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e'" $argspecs $argname
} }
if {$e_check < $low || $e_check > $high} { if {$e_check < $low || $e_check > $high} {
error "Option $argname for [Get_caller] must be integer between $low and $high. Received: '$e'" arg_error "Option $argname for [Get_caller] must be integer between $low and $high. Received: '$e'" $argspecs $argname
} }
} }
} else { } else {
foreach e_check $vlist_check { foreach e_check $vlist_check {
if {![tcl::string::is integer -strict $e_check]} { if {![tcl::string::is integer -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e_check'" arg_error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e_check'" $argspecs $argname
} }
} }
} }
@ -1060,7 +1104,7 @@ tcl::namespace::eval punk::args {
#todo - small-value double comparisons with error-margin? review #todo - small-value double comparisons with error-margin? review
lassign $checkval low high lassign $checkval low high
if {$e_check < $low || $e_check > $high} { if {$e_check < $low || $e_check > $high} {
error "Option $argname for [Get_caller] must be between $low and $high. Received: '$e'" arg_error "Option $argname for [Get_caller] must be between $low and $high. Received: '$e'" $argspecs $argname
} }
} }
} }
@ -1071,14 +1115,14 @@ tcl::namespace::eval punk::args {
bool { bool {
foreach e_check $vlist_check { foreach e_check $vlist_check {
if {![tcl::string::is boolean -strict $e_check]} { if {![tcl::string::is boolean -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'boolean'. Received: '$e_check'" arg_error "Option $argname for [Get_caller] requires type 'boolean'. Received: '$e_check'" $argspecs $argname
} }
} }
} }
dict { dict {
foreach e_check $vlist_check { foreach e_check $vlist_check {
if {[llength $e_check] %2 != 0} { if {[llength $e_check] %2 != 0} {
error "Option $argname for [Get_caller] requires type 'dict' - must be key value pairs. Received: '$e_check'" arg_error "Option $argname for [Get_caller] requires type 'dict' - must be key value pairs. Received: '$e_check'" $argspecs $argname
} }
} }
} }
@ -1097,7 +1141,7 @@ tcl::namespace::eval punk::args {
xdigit { xdigit {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {![tcl::string::is $type $e_check]} { if {![tcl::string::is $type $e_check]} {
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'" arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'" $argspecs $argname
} }
} }
} }
@ -1108,19 +1152,19 @@ tcl::namespace::eval punk::args {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {!([tcl::string::length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} { if {!([tcl::string::length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} {
#what about special file names e.g on windows NUL ? #what about special file names e.g on windows NUL ?
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which doesn't look like it could be a file or directory" arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which doesn't look like it could be a file or directory" $argspecs $argname
} }
} }
if {$type eq "existingfile"} { if {$type eq "existingfile"} {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {![file exists $e_check]} { if {![file exists $e_check]} {
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing file" arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing file" $argspecs $argname
} }
} }
} elseif {$type eq "existingdirectory"} { } elseif {$type eq "existingdirectory"} {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {![file isdirectory $e_check]} { if {![file isdirectory $e_check]} {
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing directory" arg_error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which is not an existing directory" $argspecs $argname
} }
} }
} }
@ -1128,7 +1172,7 @@ tcl::namespace::eval punk::args {
char { char {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {[tcl::string::length $e_check] != 1} { if {[tcl::string::length $e_check] != 1} {
error "Option $argname for [Get_caller] requires type 'character'. Received: '$e' which is not a single character" arg_error "Option $argname for [Get_caller] requires type 'character'. Received: '$e' which is not a single character" $argspecs $argname
} }
} }
} }
@ -1148,7 +1192,7 @@ tcl::namespace::eval punk::args {
set choices_test $choices set choices_test $choices
} }
if {$v_test ni $choices_test} { if {$v_test ni $choices_test} {
error "Option $argname for [Get_caller] must be one of the listed values $choices $casemsg. Received: '$e'" arg_error "Option $argname for [Get_caller] must be one of the listed values $choices $casemsg. Received: '$e'" $argspecs $argname
} }
} }
} }

4
src/modules/punk/char-999999.0a1.0.tm

@ -1990,7 +1990,9 @@ tcl::namespace::eval punk::char {
#\uFFEFBOM/ ZWNBSP and others that should be zero width #\uFFEFBOM/ ZWNBSP and others that should be zero width
#todo - work out proper way to mark/group zero width. #todo - work out proper way to mark/group zero width.
set text [tcl::string::map [list \u200b "" \u200c "" \u200d "" \uFFEF ""] $text] #set text [tcl::string::map [list \u200b "" \u200c "" \u200d "" \uFFEF ""] $text]
set text [tcl::string::map [list \u200b "" \u200c "" \u200d ""] $text]
#\uFFEF tends to print as 1 length replacement char - REVIEW
# -- --- --- --- --- --- --- # -- --- --- --- --- --- ---
#we should only map control sequences to nothing after processing ones with length effects, such as \b (\x07f) or DEL \x1f #we should only map control sequences to nothing after processing ones with length effects, such as \b (\x07f) or DEL \x1f

8
src/modules/punk/console-999999.0a1.0.tm

@ -20,10 +20,10 @@
package require punk::ansi package require punk::ansi
if {"windows" eq $::tcl_platform(platform)} { #if {"windows" eq $::tcl_platform(platform)} {
#package require zzzload # #package require zzzload
#zzzload::pkg_require twapi # #zzzload::pkg_require twapi
} #}
#see https://learn.microsoft.com/en-us/windows/console/classic-vs-vt #see https://learn.microsoft.com/en-us/windows/console/classic-vs-vt
#https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session #https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session

2
src/modules/punk/du-999999.0a1.0.tm

@ -1024,7 +1024,7 @@ namespace eval punk::du {
dict set effective_opts -with_times $timed_types dict set effective_opts -with_times $timed_types
dict set effective_opts -with_sizes $sized_types dict set effective_opts -with_sizes $sized_types
return [list dirs $dirs vfsmounts $vfsmounts links $links files $files filesizes [dict get $mdata_lists fsizes] sizes [dict get $mdata_lists allsizes] times $alltimes flaggedhidden {} flaggedsystem {} flaggedreadonly {} altname {} opts $effective_opts errors $errors] return [list dirs $dirs vfsmounts $vfsmounts links $links files $files filesizes [dict get $mdata_lists fsizes] sizes [dict get $mdata_lists allsizes] times [dict get $mdata_lists alltimes] flaggedhidden {} flaggedsystem {} flaggedreadonly {} altname {} opts $effective_opts errors $errors]
} }
#we can halve the number of round trips on unix-like systems, where 'hidden' always corresponds to dotted files #we can halve the number of round trips on unix-like systems, where 'hidden' always corresponds to dotted files

13
src/modules/punk/fileline-999999.0a1.0.tm

@ -1650,16 +1650,19 @@ namespace eval punk::fileline::system {
#gets very slow (comparitively) with large resultsets #gets very slow (comparitively) with large resultsets
proc _range_spans_chunk_boundaries_tcl {start end chunksize args} { proc _range_spans_chunk_boundaries_tcl {start end chunksize args} {
if {$chunksize < 1} {error "chunksize must be > 0"} ;#sanitycheck in case called directly if {$chunksize < 1} {error "chunksize must be > 0"} ;#sanitycheck in case called directly
set defaults [dict create\ set opts [dict create\
-offset 0\ -offset 0\
] ]
set known_opts [dict keys $defaults]
foreach {k v} $args { foreach {k v} $args {
if {$k ni $known_opts} { switch -- $k {
error "unknown option '$k'. Known options: $known_opts" -offset {
dict set opts $k $v
}
default {
error "unknown option '$k'. Known options: [dict keys $opts]"
}
} }
} }
set opts [dict merge $defaults $args]
# -- --- --- --- # -- --- --- ---
set opt_offset [dict get $opts -offset] set opt_offset [dict get $opts -offset]
# -- --- --- --- # -- --- --- ---

159
src/modules/punk/lib-999999.0a1.0.tm

@ -335,15 +335,66 @@ namespace eval punk::lib {
#[para] Core API functions for punk::lib #[para] Core API functions for punk::lib
#[list_begin definitions] #[list_begin definitions]
proc range {from to args} { if {[info commands lseq] ne ""} {
if {[info commands lseq] ne ""} { #tcl 8.7+ lseq significantly faster, especially for larger ranges
#tcl 8.7+ lseq significantly faster for larger ranges #support minimal set from to
return [lseq $from $to] proc range {from to} {
lseq $from $to
}
} else {
#lseq accepts basic expressions e.g 4-2 for both arguments
#e.g we can do lseq 0 [llength $list]-1
#if range is to be consistent with the lseq version above - it should support that, even though we don't support most lseq functionality in either wrapper.
proc range {from to} {
set to [offset_expr $to]
set from [offset_expr $from]
if {$to > $from} {
set count [expr {($to -$from) + 1}]
if {$from == 0} {
return [lsearch -all [lrepeat $count 0] *]
} else {
incr from -1
return [lmap v [lrepeat $count 0] {incr from}]
}
#slower methods.
#2)
#set i -1
#set L [lrepeat $count 0]
#lmap v $L {lset L [incr i] [incr from];lindex {}}
#return $L
#3)
#set L {}
#for {set i 0} {$i < $count} {incr i} {
# lappend L [incr from]
#}
#return $L
} elseif {$from > $to} {
set count [expr {$from - $to} + 1]
#1)
if {$to == 0} {
return [lreverse [lsearch -all [lrepeat $count 0] *]]
} else {
incr from
return [lmap v [lrepeat $count 0] {incr from -1}]
}
#2)
#set i -1
#set L [lrepeat $count 0]
#lmap v $L {lset L [incr i] [incr from -1];lindex {}}
#return $L
#3)
#set L {}
#for {set i 0} {$i < $count} {incr i} {
# lappend L [incr from -1]
#}
#return $L
} else {
return [list $from]
}
} }
set count [expr {($to -$from) + 1}]
incr from -1
return [lmap v [lrepeat $count 0] {incr from}]
} }
proc is_list_all_in_list {small large} { proc is_list_all_in_list {small large} {
package require struct::list package require struct::list
package require struct::set package require struct::set
@ -358,14 +409,53 @@ namespace eval punk::lib {
#somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist #somewhat like struct::set difference - but order preserving, and doesn't treat as a 'set' so preserves dupes in fromlist
#struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on, #struct::set difference may happen to preserve ordering when items are integers, but order can't be relied on,
# especially as struct::list has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other. # especially as struct::set has 2 differing implementations (tcl vs critcl) which return results with different ordering to each other and different deduping behaviour in some cases (e.g empty 2nd arg)
proc ldiff {fromlist removeitems} { proc ldiff {fromlist removeitems} {
if {[llength $removeitems] == 0} {return $fromlist}
set result {}
foreach item $fromlist {
if {$item ni $removeitems} {
lappend result $item
}
}
return $result
}
proc ldiff2 {fromlist removeitems} {
set doomed [list] set doomed [list]
foreach item $removeitems { foreach item $removeitems {
lappend doomed {*}[lsearch -all -exact $fromlist $item] lappend doomed {*}[lsearch -all -exact $fromlist $item]
} }
lremove $fromlist {*}$doomed lremove $fromlist {*}$doomed
} }
#non-dupe preserving - for consistency in dedupe behaviour we don't shortcircuit empty B
#consistent dedupe and order-maintenance of remaining items in A differentiate this from struct::set difference
#also struct::set difference with critcl is faster
proc setdiff {A B} {
if {[llength $A] == 0} {return {}}
set d [dict create]
foreach x $A {dict set d $x {}}
foreach x $B {dict unset d $x}
return [dict keys $d]
}
#bulk dict remove is slower than a foreach with dict unset
#proc setdiff2 {fromlist removeitems} {
# #if {[llength $fromlist] == 0} {return {}}
# set d [dict create]
# foreach x $fromlist {
# dict set d $x {}
# }
# return [dict keys [dict remove $d {*}$removeitems]]
#}
#array is about 15% faster than dict - but unordered. (which is ok for sets - but we have struct::set for that)
proc setdiff_unordered {A B} {
if {[llength $A] == 0} {return {}}
array set tmp {}
foreach x $A {::set tmp($x) .}
foreach x $B {catch {unset tmp($x)}}
return [array names tmp]
}
package require struct::set package require struct::set
if {[struct::set equal [struct::set union {a a} {}] {a}]} { if {[struct::set equal [struct::set union {a a} {}] {a}]} {
proc lunique_unordered {list} { proc lunique_unordered {list} {
@ -373,12 +463,22 @@ namespace eval punk::lib {
} }
} else { } else {
puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!" puts stderr "WARNING: struct::set union <list> <emptylist> no longer dedupes!"
#we could also test a sequence of: struct::set add
proc lunique_unordered {list} { proc lunique_unordered {list} {
tailcall lunique $list tailcall lunique $list
} }
} }
#order-preserving #order-preserving
proc lunique {list} { proc lunique {list} {
set new {}
foreach item $list {
if {$item ni $new} {
lappend new $item
}
}
return $new
}
proc lunique2 {list} {
set doomed [list] set doomed [list]
#expr 'in' probably faster than using a dict - for lists approx < 20,000 items. (wiki wisdom - url?) #expr 'in' probably faster than using a dict - for lists approx < 20,000 items. (wiki wisdom - url?)
for {set i 0} {$i < [llength $list]} {} { for {set i 0} {$i < [llength $list]} {} {
@ -388,30 +488,6 @@ namespace eval punk::lib {
} }
lremove $list {*}$doomed lremove $list {*}$doomed
} }
proc lunique1 {list} {
set doomed [list]
#expr 'in' probably faster than using a dict - for lists approx < 20,000 items. (wiki wisdom - url?)
set i 0
foreach item $list {
if {$i in $doomed} {
incr i
continue
}
lappend doomed {*}[lrange [lsearch -all -exact -start $i $list $item] 1 end]
incr i
}
puts --->doomed:$doomed
lremove $list {*}$doomed
}
proc lunique2 {list} {
set new {}
foreach item $list {
if {$item ni $new} {
lappend new $item
}
}
return $new
}
#The closure-like behaviour is *very* slow especially when called from a context such as the global namespace with lots of vars and large arrays such as ::env #The closure-like behaviour is *very* slow especially when called from a context such as the global namespace with lots of vars and large arrays such as ::env
proc lmapflat_closure {varnames list script} { proc lmapflat_closure {varnames list script} {
set result [list] set result [list]
@ -537,6 +613,23 @@ namespace eval punk::lib {
# return "ok" # return "ok"
#} #}
#supports *safe* ultra basic offset expressions as used by lindex etc, but without the 'end' features
#safe in that we don't evaluate the expression as a string.
proc offset_expr {expression} {
set expression [tcl::string::map {_ {}} $expression]
if {[tcl::string::is integer -strict $expression]} {
return [expr {$expression}]
}
if {[regexp {(.*)([+-])(.*)} $expression _match a op b] && [tcl::string::is integer -strict $a] && [tcl::string::is integer -strict $b]} {
if {$op eq "-"} {
return [expr {$a - $b}]
} else {
return [expr {$a + $b}]
}
} else {
error "bad expression '$expression': must be integer?\[+-\]integer?"
}
}
proc lindex_resolve {list index} { proc lindex_resolve {list index} {
#*** !doctools #*** !doctools
@ -554,7 +647,7 @@ namespace eval punk::lib {
if {![llength $list]} { if {![llength $list]} {
return -1 return -1
} }
set index [string map [list _ ""] $index] ;#forward compatibility with integers such as 1_000 set index [tcl::string::map {_ {}} $index] ;#forward compatibility with integers such as 1_000
if {[string is integer -strict $index]} { if {[string is integer -strict $index]} {
#can match +i -i #can match +i -i
if {$index < 0} { if {$index < 0} {

29
src/modules/punk/mix/commandset/module-999999.0a1.0.tm

@ -129,18 +129,25 @@ namespace eval punk::mix::commandset::module {
put stderr "get_template_basefolders WARNING - no handler available for the 'punk.templates' capability - template providers will be unable to provide template locations" put stderr "get_template_basefolders WARNING - no handler available for the 'punk.templates' capability - template providers will be unable to provide template locations"
} }
} }
proc new {module args} { proc new {args} {
set year [clock format [clock seconds] -format %Y] set year [clock format [clock seconds] -format %Y]
set defaults [list\ set moduletypes [punk::mix::cli::lib::module_types]
-project \uFFFF\ set argspecs [subst {
-version \uFFFF\ -project -default \uFFFF
-license <unspecified>\ -version -default \uFFFF
-template punk.module\ -license -default <unspecified>
-type \uFFFF\ -template -default punk.module
-force 0\ -type -default \uFFFF -choices {$moduletypes}
-quiet 0\ -force -default 0 -type boolean
] -quiet -default 0 -type boolean
set opts [dict merge $defaults $args] *values -min 1 -max 1
module -type string
}]
set argd [punk::args::get_dict $argspecs $args]
lassign [dict values $argd] opts values
set module [dict get $values module]
#set opts [dict merge $defaults $args]
#todo - review compatibility between -template and -type #todo - review compatibility between -template and -type
#-type is the wrapping technology e.g 'plain' for none or tarjar/zipkit etc (consider also snappy/snappy-tcl) #-type is the wrapping technology e.g 'plain' for none or tarjar/zipkit etc (consider also snappy/snappy-tcl)

2
src/modules/punk/mix/templates-999999.0a1.0.tm

@ -59,7 +59,7 @@ namespace eval punk::mix::templates {
oo::objdefine provider { oo::objdefine provider {
method register {{capabilityname_glob *}} { method register {{capabilityname_glob *}} {
#puts registering punk::mix::templates $capabilityname #puts registering punk::mix::templates $capabilityname
next next $capabilityname_glob
} }
method capabilities {} { method capabilities {} {
next next

Loading…
Cancel
Save