Browse Source

safe interp fixes

master
Julian Noble 5 months ago
parent
commit
ac5a496f92
  1. 7
      src/modules/argparsingtest-999999.0a1.0.tm
  2. 5
      src/modules/canaryspace-999999.0a1.0.tm
  3. 35
      src/modules/natsort-0.1.1.6.tm
  4. 47
      src/modules/punk-0.1.tm
  5. 1539
      src/modules/punk/ansi-999999.0a1.0.tm
  6. 297
      src/modules/punk/args-999999.0a1.0.tm
  7. 118
      src/modules/punk/assertion-999999.0a1.0.tm
  8. 11
      src/modules/punk/cap/handlers/templates-999999.0a1.0.tm
  9. 994
      src/modules/punk/char-999999.0a1.0.tm
  10. 63
      src/modules/punk/config-0.1.tm
  11. 501
      src/modules/punk/experiment-999999.0a1.0.tm
  12. 3
      src/modules/punk/experiment-buildversion.txt
  13. 163
      src/modules/punk/lib-999999.0a1.0.tm
  14. 12
      src/modules/punk/ns-999999.0a1.0.tm
  15. 38
      src/modules/punk/overlay-0.1.tm
  16. 46
      src/modules/punk/repl-0.1.tm
  17. 2
      src/modules/punkcheck-0.1.0.tm
  18. 48
      src/modules/shellfilter-0.1.9.tm
  19. 113
      src/modules/textblock-999999.0a1.0.tm

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

@ -46,8 +46,11 @@
#[list_begin itemized] #[list_begin itemized]
package require Tcl 8.6- package require Tcl 8.6-
package require punk::args
package require struct::set
#*** !doctools #*** !doctools
#[item] [package {Tcl 8.6}] #[item] [package {Tcl 8.6}]
#[item] [package {punk::args}]
# #package require frobz # #package require frobz
# #*** !doctools # #*** !doctools
@ -240,7 +243,7 @@ namespace eval argparsingtest {
-3 -default 3 -type integer -3 -default 3 -type integer
*values *values
} $args] } $args]
return [dict get $argd opts] return [tcl::dict::get $argd opts]
} }
proc test1_punkargs_validate_without_ansi {args} { proc test1_punkargs_validate_without_ansi {args} {
set argd [punk::args::get_dict { set argd [punk::args::get_dict {
@ -259,7 +262,7 @@ namespace eval argparsingtest {
-3 -default 3 -type integer -validate_without_ansi true -3 -default 3 -type integer -validate_without_ansi true
*values *values
} $args] } $args]
return [dict get $argd opts] return [tcl::dict::get $argd opts]
} }
package require opt package require opt

5
src/modules/canaryspace-999999.0a1.0.tm

@ -23,7 +23,10 @@
# Meta description and so may need to be comprised mainly of fully qualified commands. # Meta description and so may need to be comprised mainly of fully qualified commands.
# @@ Meta End # @@ Meta End
#usage example
#% use canaryspace
# using the repl, enter commands that may use the calling context and ensure there are no unexpected canaryspace emissions on stderr.
# (expect only a single CANARYSPACE output for entered command if it is at global level.)
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements ## Requirements

35
src/modules/natsort-0.1.1.6.tm

@ -1428,23 +1428,26 @@ namespace eval natsort {
} }
} }
set is_namematch [called_directly_namematch] if {![interp issafe]} {
set is_inodematch [called_directly_inodematch] set is_namematch [called_directly_namematch]
#### set is_inodematch [called_directly_inodematch]
#review - reliability of mechanisms to determine direct calls ####
# we don't want application being called when being used as a library, but we need it to run if called directly or from symlinks etc #review - reliability of mechanisms to determine direct calls
#-- choose a policy and leave the others commented. # we don't want application being called when being used as a library, but we need it to run if called directly or from symlinks etc
#set is_called_directly $is_namematch #-- choose a policy and leave the others commented.
#set is_called_directly $is_inodematch #set is_called_directly $is_namematch
set is_called_directly [expr {$is_namematch || $is_inodematch}] #set is_called_directly $is_inodematch
#set is_called_directly [expr {$is_namematch && $is_inodematch}] set is_called_directly [expr {$is_namematch || $is_inodematch}]
### #set is_called_directly [expr {$is_namematch && $is_inodematch}]
###
#puts stdout "called_directly_name: [called_directly_namematch] called_directly_inode: [called_directly_inodematch]"
#puts stdout "called_directly_name: [called_directly_namematch] called_directly_inode: [called_directly_inodematch]"
} else {
#safe interp
set is_called_directly 0
}
#
proc test_pass_fail_message {pass {additional ""}} { proc test_pass_fail_message {pass {additional ""}} {

47
src/modules/punk-0.1.tm

@ -160,13 +160,6 @@ namespace eval punk {
#variable re_headvar1 {([a-zA-Z:@.(),]+?)(?![^(]*\))(,.*)*$} #variable re_headvar1 {([a-zA-Z:@.(),]+?)(?![^(]*\))(,.*)*$}
proc get_repl_runid {} {
if {[tsv::exists repl runid]} {
return [tsv::get repl runid]
} else {
return 0
}
}
#ordinary emission of chunklist when no repl #ordinary emission of chunklist when no repl
proc emit_chunklist {chunklist} { proc emit_chunklist {chunklist} {
@ -1009,7 +1002,7 @@ namespace eval punk {
} }
#set assigned [dict values $leveldata] #set assigned [dict values $leveldata]
set pairs [list] set pairs [list]
dict for {k v} $leveldata {lappend pairs [list $k $v]} tcl::dict::for {k v} $leveldata {lappend pairs [list $k $v]}
set assigned [lindex [list $pairs [unset pairs]] 0] set assigned [lindex [list $pairs [unset pairs]] 0]
} elseif {[string is integer -strict $index]} { } elseif {[string is integer -strict $index]} {
if {[catch {llength $leveldata} len]} { if {[catch {llength $leveldata} len]} {
@ -1696,7 +1689,7 @@ namespace eval punk {
set action ?mismatch-not-a-dict set action ?mismatch-not-a-dict
} else { } else {
set pairs [list] set pairs [list]
dict for {k v} $leveldata {lappend pairs [list $k $v]} tcl::dict::for {k v} $leveldata {lappend pairs [list $k $v]}
set assigned [lindex [list $pairs [unset pairs]] 0] set assigned [lindex [list $pairs [unset pairs]] 0]
} }
} }
@ -4541,6 +4534,37 @@ namespace eval punk {
} }
} }
# ---------------------------
# commands that should be aliased in safe interps that need to use punk repl
#
proc get_repl_runid {} {
if {[interp issafe]} {
if {[info commands ::tsv::exists] eq ""} {
puts stderr "punk::get_repl_runid cannot operate directly in safe interp - install the appropriate punk aliases"
error "punk::get_repl_runid punk repl aliases not installed"
}
#if safe interp got here - there must presumably be a direct set of aliases on tsv::* commands
}
if {[tsv::exists repl runid]} {
return [tsv::get repl runid]
} else {
return 0
}
}
#ensure we don't get into loop in unknown when in safe interp - which won't have tsv
proc set_repl_last_unknown {args} {
if {[interp issafe]} {
if {[info commands ::tsv::set] eq ""} {
puts stderr "punk::set_repl_last_unknown cannot operate directly in safe interp - install an alias to tsv::set repl last_unknown"
return
}
#tsv::* somehow working - possibly custom aliases for tsv functionality ? review
}
tsv::set repl last_unknown {*}$args
}
# ---------------------------
proc configure_unknown {} { proc configure_unknown {} {
#----------------------------- #-----------------------------
#these are critical e.g core behaviour or important for repl displaying output correctly #these are critical e.g core behaviour or important for repl displaying output correctly
@ -4557,7 +4581,8 @@ namespace eval punk {
package require base64 package require base64
#set ::punk::last_run_display [list] #set ::punk::last_run_display [list]
#set ::repl::last_unknown [lindex $args 0] ;#jn #set ::repl::last_unknown [lindex $args 0] ;#jn
tsv::set repl last_unknown [lindex $args 0] ;#REVIEW #tsv::set repl last_unknown [lindex $args 0] ;#REVIEW
punk::set_repl_last_unknown [lindex $args 0]
}][info body ::unknown] }][info body ::unknown]
@ -6229,7 +6254,7 @@ namespace eval punk {
set script_extensions [list] set script_extensions [list]
set extension_lookup [dict create] set extension_lookup [dict create]
dict for {lang langinfo} $scriptconfig { tcl::dict::for {lang langinfo} $scriptconfig {
set extensions [dict get $langinfo extensions] set extensions [dict get $langinfo extensions]
lappend script_extensions {*}$extensions lappend script_extensions {*}$extensions
foreach e $extensions { foreach e $extensions {

1539
src/modules/punk/ansi-999999.0a1.0.tm

File diff suppressed because it is too large Load Diff

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

@ -186,11 +186,19 @@
## Requirements ## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#All ensemble commands are slower in a safe interp as they aren't compiled the same way
#https://core.tcl-lang.org/tcl/tktview/1095bf7f75
#as this is needed in safe interps too, and the long forms tcl::dict::for tcl::string::map are no slower in a normal interp - we use the long form here.
#(As at 2024-06 There are many tcl8.6/8.7 interps in use which are affected by this and it's unknown when/whether it will be fixed)
#ensembles: array binary chan clock dict encoding info namespace string
#possibly file too, although that is generally hidden/modified in a safe interp
#*** !doctools #*** !doctools
#[subsection dependencies] #[subsection dependencies]
#[para] packages used by punk::args #[para] packages used by punk::args
#[list_begin itemized] #[list_begin itemized]
package require Tcl 8.6- package require Tcl 8.6-
#*** !doctools #*** !doctools
#[item] [package {Tcl 8.6-}] #[item] [package {Tcl 8.6-}]
@ -210,11 +218,11 @@ package require Tcl 8.6-
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace # oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args::class { tcl::namespace::eval punk::args::class {
#*** !doctools #*** !doctools
#[subsection {Namespace punk::args::class}] #[subsection {Namespace punk::args::class}]
#[para] class definitions #[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} { if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools #*** !doctools
#[list_begin enumerated] #[list_begin enumerated]
@ -243,13 +251,13 @@ namespace eval punk::args::class {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace # Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args { tcl::namespace::eval punk::args {
namespace export {[a-z]*} tcl::namespace::export {[a-z]*}
variable argspec_cache variable argspec_cache
variable argspecs variable argspecs
variable id_counter variable id_counter
set argspec_cache [dict create] set argspec_cache [tcl::dict::create]
set argspecs [dict create] set argspecs [tcl::dict::create]
set id_counter 0 set id_counter 0
#*** !doctools #*** !doctools
@ -265,12 +273,12 @@ namespace eval punk::args {
#review - check if there is a built-into-tcl way to do this quickly #review - check if there is a built-into-tcl way to do this quickly
#for now we will just key using the whole string #for now we will just key using the whole string
set cache_key $optionspecs set cache_key $optionspecs
if {[dict exists $argspec_cache $cache_key]} { if {[tcl::dict::exists $argspec_cache $cache_key]} {
return [dict get $argspec_cache $cache_key] return [tcl::dict::get $argspec_cache $cache_key]
} }
set optionspecs [string map [list \r\n \n] $optionspecs] set optionspecs [tcl::string::map [list \r\n \n] $optionspecs]
set optspec_defaults [dict create\ set optspec_defaults [tcl::dict::create\
-type string\ -type string\
-optional 1\ -optional 1\
-allow_ansi 1\ -allow_ansi 1\
@ -279,7 +287,7 @@ namespace eval punk::args {
-nocase 0\ -nocase 0\
-multiple 0\ -multiple 0\
] ]
set valspec_defaults [dict create\ set valspec_defaults [tcl::dict::create\
-type string\ -type string\
-optional 0\ -optional 0\
-allow_ansi 1\ -allow_ansi 1\
@ -295,10 +303,10 @@ namespace eval punk::args {
#todo - detect if anything in the spec uses -allow_ansi 0, -validate_without_ansi 1 or -strip_ansi 1 and set a flag indicating if punk::ansi::ta::detect should be run on the argslist #todo - detect if anything in the spec uses -allow_ansi 0, -validate_without_ansi 1 or -strip_ansi 1 and set a flag indicating if punk::ansi::ta::detect should be run on the argslist
set opt_required [list] set opt_required [list]
set val_required [list] set val_required [list]
set arg_info [dict create] set arg_info [tcl::dict::create]
set opt_defaults [dict create] set opt_defaults [tcl::dict::create]
set opt_names [list] ;#defined opts set opt_names [list] ;#defined opts
set val_defaults [dict create] set val_defaults [tcl::dict::create]
set opt_solos [list] set opt_solos [list]
#first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end #first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end
set val_names [list] set val_names [list]
@ -309,21 +317,21 @@ namespace eval punk::args {
set linelist [split $optionspecs \n] set linelist [split $optionspecs \n]
set lastindent "" set lastindent ""
foreach ln $linelist { foreach ln $linelist {
if {[string trim $ln] eq ""} {continue} if {[tcl::string::trim $ln] eq ""} {continue}
regexp {(\s*).*} $ln _all lastindent regexp {(\s*).*} $ln _all lastindent
break ;#break at first non-empty break ;#break at first non-empty
} }
#puts "indent1:[ansistring VIEW $lastindent]" #puts "indent1:[ansistring VIEW $lastindent]"
set in_record 0 set in_record 0
foreach rawline $linelist { foreach rawline $linelist {
set recordsofar [string cat $linebuild $rawline] set recordsofar [tcl::string::cat $linebuild $rawline]
if {![info complete $recordsofar]} { if {![tcl::info::complete $recordsofar]} {
#append linebuild [string trimleft $rawline] \n #append linebuild [string trimleft $rawline] \n
if {$in_record} { if {$in_record} {
if {[string length $lastindent]} { if {[tcl::string::length $lastindent]} {
#trim only the whitespace corresponding to last indent - not all whitespace on left #trim only the whitespace corresponding to last indent - not all whitespace on left
if {[string first $lastindent $rawline] == 0} { if {[tcl::string::first $lastindent $rawline] == 0} {
set trimmedline [string range $rawline [string length $lastindent] end] set trimmedline [tcl::string::range $rawline [tcl::string::length $lastindent] end]
append linebuild $trimmedline \n append linebuild $trimmedline \n
} else { } else {
append linebuild $rawline \n append linebuild $rawline \n
@ -340,10 +348,10 @@ namespace eval punk::args {
} }
} else { } else {
set in_record 0 set in_record 0
if {[string length $lastindent]} { if {[tcl::string::length $lastindent]} {
#trim only the whitespace corresponding to last indent - not all whitespace on left #trim only the whitespace corresponding to last indent - not all whitespace on left
if {[string first $lastindent $rawline] == 0} { if {[tcl::string::first $lastindent $rawline] == 0} {
set trimmedline [string range $rawline [string length $lastindent] end] set trimmedline [tcl::string::range $rawline [tcl::string::length $lastindent] end]
append linebuild $trimmedline append linebuild $trimmedline
} else { } else {
append linebuild $rawline append linebuild $rawline
@ -361,19 +369,19 @@ namespace eval punk::args {
set val_max -1 ;#-1 for no limit set val_max -1 ;#-1 for no limit
set spec_id "" set spec_id ""
foreach ln $records { foreach ln $records {
set trimln [string trim $ln] set trimln [tcl::string::trim $ln]
switch -- [string index $trimln 0] { switch -- [tcl::string::index $trimln 0] {
"" - # {continue} "" - # {continue}
} }
set linespecs [lassign $trimln argname] set linespecs [lassign $trimln argname]
if {$argname ne "*id" && [llength $linespecs] %2 != 0} { if {$argname ne "*id" && [llength $linespecs] %2 != 0} {
error "punk::args::get_dict - bad optionspecs line for record '$argname' Remaining items on line must be in paired option-value format - received '$linespecs'" error "punk::args::get_dict - bad optionspecs line for record '$argname' Remaining items on line must be in paired option-value format - received '$linespecs'"
} }
set firstchar [string index $argname 0] set firstchar [tcl::string::index $argname 0]
set secondchar [string index $argname 1] set secondchar [tcl::string::index $argname 1]
if {$firstchar eq "*" && $secondchar ne "*"} { if {$firstchar eq "*" && $secondchar ne "*"} {
set starspecs $linespecs set starspecs $linespecs
switch -- [string range $argname 1 end] { switch -- [tcl::string::range $argname 1 end] {
id { id {
#id line must have single entry - a unique id assigned by the user - an id will be allocated if no id line present or the value is "auto" #id line must have single entry - a unique id assigned by the user - an id will be allocated if no id line present or the value is "auto"
if {[llength $starspecs] != 1} { if {[llength $starspecs] != 1} {
@ -398,10 +406,10 @@ namespace eval punk::args {
} }
-minlen - -maxlen - -range - -choices - -choicelabels { -minlen - -maxlen - -range - -choices - -choicelabels {
#review - only apply to certain types? #review - only apply to certain types?
dict set optspec_defaults $k $v tcl::dict::set optspec_defaults $k $v
} }
-nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels { -nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels {
dict unset optspec_defaults $k tcl::dict::unset optspec_defaults $k
} }
-type - -type -
-optional - -optional -
@ -410,7 +418,7 @@ namespace eval punk::args {
-strip_ansi - -strip_ansi -
-multiple { -multiple {
#allow overriding of defaults for options that occur later #allow overriding of defaults for options that occur later
dict set optspec_defaults $k $v tcl::dict::set optspec_defaults $k $v
} }
default { default {
error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts" error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts"
@ -431,17 +439,17 @@ namespace eval punk::args {
} }
-minlen - -maxlen - -range - -choices - -choicelabels { -minlen - -maxlen - -range - -choices - -choicelabels {
#review - only apply to certain types? #review - only apply to certain types?
dict set valspec_defaults $k $v tcl::dict::set valspec_defaults $k $v
} }
-nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels { -nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels {
dict unset valspec_defaults $k tcl::dict::unset valspec_defaults $k
} }
-type - -type -
-allow_ansi - -allow_ansi -
-validate_without_ansi - -validate_without_ansi -
-strip_ansi - -strip_ansi -
-multiple { -multiple {
dict set valspec_defaults $k $v tcl::dict::set valspec_defaults $k $v
} }
default { default {
error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts" error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts"
@ -457,16 +465,16 @@ namespace eval punk::args {
continue continue
} elseif {$firstchar eq "-"} { } elseif {$firstchar eq "-"} {
set argspecs $linespecs set argspecs $linespecs
dict set argspecs -ARGTYPE option tcl::dict::set argspecs -ARGTYPE option
lappend opt_names $argname lappend opt_names $argname
set is_opt 1 set is_opt 1
} else { } else {
if {$firstchar eq "*"} { if {$firstchar eq "*"} {
#allow basic ** escaping for literal argname that begins with * #allow basic ** escaping for literal argname that begins with *
set argname [string range $argname 1 end] set argname [tcl::string::range $argname 1 end]
} }
set argspecs $linespecs set argspecs $linespecs
dict set argspecs -ARGTYPE value tcl::dict::set argspecs -ARGTYPE value
lappend val_names $argname lappend val_names $argname
set is_opt 0 set is_opt 0
} }
@ -478,20 +486,20 @@ namespace eval punk::args {
switch -- $spec { switch -- $spec {
-type { -type {
#normalize here so we don't have to test during actual args parsing in main function #normalize here so we don't have to test during actual args parsing in main function
switch -- [string tolower $specval] { switch -- [tcl::string::tolower $specval] {
int - integer { int - integer {
dict set merged -type int tcl::dict::set merged -type int
} }
bool - boolean { bool - boolean {
dict set merged -type bool tcl::dict::set merged -type bool
} }
char - character { char - character {
dict set merged -type char tcl::dict::set merged -type char
} }
"" - none { "" - none {
if {$is_opt} { if {$is_opt} {
dict set merged -type none tcl::dict::set merged -type none
dict set merged -default 0 ;#-default 0 can still be overridden if -default appears after -type - we'll allow it. tcl::dict::set merged -default 0 ;#-default 0 can still be overridden if -default appears after -type - we'll allow it.
lappend opt_solos $argname lappend opt_solos $argname
} else { } else {
#-solo only valid for flags #-solo only valid for flags
@ -499,12 +507,12 @@ namespace eval punk::args {
} }
} }
default { default {
dict set merged -type [string tolower $specval] tcl::dict::set merged -type [tcl::string::tolower $specval]
} }
} }
} }
-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 {
dict set merged $spec $specval tcl::dict::set merged $spec $specval
} }
default { default {
set known_argspecs [list -default -type -range -choices -nocase -optional -multiple -validate_without_ansi -allow_ansi -strip_ansi -help] set known_argspecs [list -default -type -range -choices -nocase -optional -multiple -validate_without_ansi -allow_ansi -strip_ansi -help]
@ -514,40 +522,40 @@ namespace eval punk::args {
} }
set argspecs $merged set argspecs $merged
#if {$is_opt} { #if {$is_opt} {
set argchecks [dict remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen set argchecks [tcl::dict::remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen
#} else { #} else {
# set argchecks [dict remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen # set argchecks [tcl::dict::remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen
#} #}
dict set arg_info $argname $argspecs tcl::dict::set arg_info $argname $argspecs
dict set arg_checks $argname $argchecks tcl::dict::set arg_checks $argname $argchecks
if {![dict get $argspecs -optional]} { if {![tcl::dict::get $argspecs -optional]} {
if {$is_opt} { if {$is_opt} {
lappend opt_required $argname lappend opt_required $argname
} else { } else {
lappend val_required $argname lappend val_required $argname
} }
} }
if {[dict exists $argspecs -default]} { if {[tcl::dict::exists $argspecs -default]} {
if {$is_opt} { if {$is_opt} {
dict set opt_defaults $argname [dict get $argspecs -default] tcl::dict::set opt_defaults $argname [tcl::dict::get $argspecs -default]
} else { } else {
dict set val_defaults $argname [dict get $argspecs -default] tcl::dict::set val_defaults $argname [tcl::dict::get $argspecs -default]
} }
} }
} }
#confirm any valnames before last don't have -multiple key #confirm any valnames before last don't have -multiple key
foreach valname [lrange $val_names 0 end-1] { foreach valname [lrange $val_names 0 end-1] {
if {[dict get $arg_info $valname -multiple]} { if {[tcl::dict::get $arg_info $valname -multiple]} {
error "bad key -multiple on argument spec for '$valname'. Only the last value argument specification can be marked -multiple" error "bad key -multiple on argument spec for '$valname'. Only the last value argument specification can be marked -multiple"
} }
} }
if {$spec_id eq "" || [string tolower $spec_id] eq "auto"} { if {$spec_id eq "" || [tcl::string::tolower $spec_id] eq "auto"} {
variable id_counter variable id_counter
set spec_id "autoid_[incr id_counter]" set spec_id "autoid_[incr id_counter]"
} }
set result [dict create\ set result [tcl::dict::create\
id $spec_id\ id $spec_id\
arg_info $arg_info\ arg_info $arg_info\
arg_checks $arg_checks\ arg_checks $arg_checks\
@ -566,28 +574,28 @@ namespace eval punk::args {
valspec_defaults $valspec_defaults\ valspec_defaults $valspec_defaults\
proc_info $proc_info\ proc_info $proc_info\
] ]
dict set argspec_cache $cache_key $result tcl::dict::set argspec_cache $cache_key $result
dict set argspecs $spec_id $optionspecs tcl::dict::set argspecs $spec_id $optionspecs
return $result return $result
} }
proc get_spec {id} { proc get_spec {id} {
variable argspecs variable argspecs
if {[dict exists $argspecs $id]} { if {[tcl::dict::exists $argspecs $id]} {
return [dict get $argspecs $id] return [tcl::dict::get $argspecs $id]
} }
return return
} }
proc get_spec_ids {{match *}} { proc get_spec_ids {{match *}} {
variable argspecs variable argspecs
return [dict keys $argspecs $match] return [tcl::dict::keys $argspecs $match]
} }
#for use within get_dict only #for use within get_dict only
#This mechanism gets less-than-useful results for oo methods #This mechanism gets less-than-useful results for oo methods
#e.g {$obj} #e.g {$obj}
proc Get_caller {} { proc Get_caller {} {
set cmdinfo [dict get [info frame -3] cmd] set cmdinfo [tcl::dict::get [tcl::info::frame -3] cmd]
#puts "-->$cmdinfo" #puts "-->$cmdinfo"
set caller [regexp -inline {\S+} $cmdinfo] set caller [regexp -inline {\S+} $cmdinfo]
if {$caller eq "namespace"} { if {$caller eq "namespace"} {
@ -681,7 +689,7 @@ namespace eval punk::args {
set argspecs [Get_argspecs $optionspecs] set argspecs [Get_argspecs $optionspecs]
dict with argspecs {} ;#turn keys into vars tcl::dict::with argspecs {} ;#turn keys into vars
#puts "-arg_info->$arg_info" #puts "-arg_info->$arg_info"
set flagsreceived [list] set flagsreceived [list]
@ -692,12 +700,12 @@ namespace eval punk::args {
set maxidx [expr {[llength $arglist]-1}] set maxidx [expr {[llength $arglist]-1}]
for {set i 0} {$i <= $maxidx} {incr i} { for {set i 0} {$i <= $maxidx} {incr i} {
set a [lindex $arglist $i] set a [lindex $arglist $i]
if {![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" error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs"
} }
#TODO! #TODO!
if {[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" error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $a which is not marked with -solo 1"
} }
@ -714,20 +722,20 @@ namespace eval punk::args {
set maxidx [expr {[llength $rawargs]-1}] set maxidx [expr {[llength $rawargs]-1}]
for {set i 0} {$i <= $maxidx} {incr i} { for {set i 0} {$i <= $maxidx} {incr i} {
set a [lindex $rawargs $i] set a [lindex $rawargs $i]
if {![string match -* $a]} { if {![tcl::string::match -* $a]} {
#assume beginning of positional args #assume beginning of positional args
incr i -1 incr i -1
break break
} }
if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} { if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} {
if {[dict get $arg_info $fullopt -type] ne "none"} { if {[tcl::dict::get $arg_info $fullopt -type] ne "none"} {
#non-solo #non-solo
set flagval [lindex $rawargs $i+1] set flagval [lindex $rawargs $i+1]
if {[dict get $arg_info $fullopt -multiple]} { if {[dict get $arg_info $fullopt -multiple]} {
dict lappend opts $fullopt $flagval tcl::dict::lappend opts $fullopt $flagval
} else { } else {
dict set opts $fullopt $flagval tcl::dict::set opts $fullopt $flagval
} }
#incr i to skip flagval #incr i to skip flagval
if {[incr i] > $maxidx} { if {[incr i] > $maxidx} {
@ -735,15 +743,15 @@ namespace eval punk::args {
} }
} else { } else {
#type none (solo-flag) #type none (solo-flag)
if {[dict get $arg_info $fullopt -multiple]} { if {[tcl::dict::get $arg_info $fullopt -multiple]} {
if {[dict get $opts $fullopt] == 0} { if {[tcl::dict::get $opts $fullopt] == 0} {
#review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified #review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified
dict set opts $fullopt 1 tcl::dict::set opts $fullopt 1
} else { } else {
dict lappend opts $fullopt 1 tcl::dict::lappend opts $fullopt 1
} }
} else { } else {
dict set opts $fullopt 1 tcl::dict::set opts $fullopt 1
} }
} }
lappend flagsreceived $fullopt ;#dups ok lappend flagsreceived $fullopt ;#dups ok
@ -751,12 +759,12 @@ namespace eval punk::args {
if {$opt_any} { if {$opt_any} {
set newval [lindex $rawargs $i+1] set newval [lindex $rawargs $i+1]
#opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option #opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option
dict set arg_info $a $optspec_defaults ;#use default settings for unspecified opt tcl::dict::set arg_info $a $optspec_defaults ;#use default settings for unspecified opt
if {[dict get $arg_info $a -type] ne "none"} { if {[tcl::dict::get $arg_info $a -type] ne "none"} {
if {[dict get $arg_info $a -multiple]} { if {[tcl::dict::get $arg_info $a -multiple]} {
dict lappend opts $a $newval tcl::dict::lappend opts $a $newval
} else { } else {
dict set opts $a $newval tcl::dict::set opts $a $newval
} }
lappend flagsreceived $a ;#adhoc flag as supplied lappend flagsreceived $a ;#adhoc flag as supplied
if {[incr i] > $maxidx} { if {[incr i] > $maxidx} {
@ -764,19 +772,19 @@ namespace eval punk::args {
} }
} 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
if {[dict get $arg_info $a -multiple]} { if {[tcl::dict::get $arg_info $a -multiple]} {
if {![dict exists $opts $a]} { if {![tcl::dict::exists $opts $a]} {
dict set opts $a 1 tcl::dict::set opts $a 1
} else { } else {
dict lappend opts $a 1 tcl::dict::lappend opts $a 1
} }
} else { } else {
dict set opts $a 1 tcl::dict::set opts $a 1
} }
} }
} else { } else {
#delay Get_caller so only called in the unhappy path #delay Get_caller so only called in the unhappy path
set errmsg [string map [list %caller% [Get_caller]] $fullopt] set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt]
error $errmsg error $errmsg
} }
} }
@ -800,20 +808,20 @@ namespace eval punk::args {
break break
} }
if {$valname ne ""} { if {$valname ne ""} {
if {[dict get $arg_info $valname -multiple]} { if {[tcl::dict::get $arg_info $valname -multiple]} {
dict lappend values_dict $valname $val tcl::dict::lappend values_dict $valname $val
set in_multiple $valname set in_multiple $valname
} else { } else {
dict set values_dict $valname $val tcl::dict::set values_dict $valname $val
} }
lappend valnames_received $valname lappend valnames_received $valname
} else { } else {
if {$in_multiple ne ""} { if {$in_multiple ne ""} {
dict lappend values_dict $in_multiple $val tcl::dict::lappend values_dict $in_multiple $val
#name already seen #name already seen
} else { } else {
dict set values_dict $validx $val tcl::dict::set values_dict $validx $val
dict set arg_info $validx $valspec_defaults tcl::dict::set arg_info $validx $valspec_defaults
lappend valnames_received $validx lappend valnames_received $validx
} }
} }
@ -844,6 +852,11 @@ namespace eval punk::args {
#The aim is to allow a wrapper function to specify a default value for an option (and/or other changes/restrictions to a particular option or two) - without having to re-specify all the options for the underlying function. #The aim is to allow a wrapper function to specify a default value for an option (and/or other changes/restrictions to a particular option or two) - without having to re-specify all the options for the underlying function.
#without full respecification in the wrapper - we can't know that a supplied prefix is unambiguous at the next level #without full respecification in the wrapper - we can't know that a supplied prefix is unambiguous at the next level
#For this reason we need to pass on full-length opts for any defined options in the wrapper even if anyopts is true #For this reason we need to pass on full-length opts for any defined options in the wrapper even if anyopts is true
#safe interp note - cannot avoid struct::set difference ensemble as it could be c or tcl implementation and we don't have an option to call directly?
#example timing difference:
#struct::set difference {x} {a b}
#normal interp 0.18 u2 vs save interp 9.4us
if {[llength [set missing [struct::set difference $opt_required $flagsreceived]]]} { if {[llength [set missing [struct::set difference $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" error "Required option missing for [Get_caller]. missing flags $missing are marked with -optional false - so must be present in full-length form"
} }
@ -853,24 +866,24 @@ namespace eval punk::args {
#todo - allow defaults outside of choices/ranges #todo - allow defaults outside of choices/ranges
#check types,ranges,choices #check types,ranges,choices
set opts_and_values [dict merge $opts $values_dict] set opts_and_values [tcl::dict::merge $opts $values_dict]
#set combined_defaults [dict merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash #set combined_defaults [tcl::dict::merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash
#puts "---opts_and_values:$opts_and_values" #puts "---opts_and_values:$opts_and_values"
#puts "---arg_info:$arg_info" #puts "---arg_info:$arg_info"
dict for {argname v} $opts_and_values { tcl::dict::for {argname v} $opts_and_values {
set thisarg [dict get $arg_info $argname] set thisarg [tcl::dict::get $arg_info $argname]
#set thisarg_keys [dict keys $thisarg] #set thisarg_keys [tcl::dict::keys $thisarg]
set thisarg_checks [dict get $arg_checks $argname] set thisarg_checks [tcl::dict::get $arg_checks $argname]
set is_multiple [dict get $thisarg -multiple] set is_multiple [tcl::dict::get $thisarg -multiple]
set is_allow_ansi [dict get $thisarg -allow_ansi] set is_allow_ansi [tcl::dict::get $thisarg -allow_ansi]
set is_validate_without_ansi [dict get $thisarg -validate_without_ansi] set is_validate_without_ansi [tcl::dict::get $thisarg -validate_without_ansi]
set is_strip_ansi [dict get $thisarg -strip_ansi] set is_strip_ansi [tcl::dict::get $thisarg -strip_ansi]
set has_default [dict exists $thisarg -default] set has_default [tcl::dict::exists $thisarg -default]
if {$has_default} { if {$has_default} {
set defaultval [dict get $thisarg -default] set defaultval [tcl::dict::get $thisarg -default]
} }
set type [dict get $thisarg -type] set type [tcl::dict::get $thisarg -type]
set has_choices [dict exists $thisarg -choices] set has_choices [tcl::dict::exists $thisarg -choices]
if {$is_multiple} { if {$is_multiple} {
set vlist $v set vlist $v
@ -916,20 +929,22 @@ namespace eval punk::args {
switch -- $type { switch -- $type {
any {} any {}
string { string {
if {[dict size $thisarg_checks]} { if {[tcl::dict::size $thisarg_checks]} {
foreach e_check $vlist_check { foreach e_check $vlist_check {
dict for {checkopt checkval} $thisarg_checks { #safe jumptable test
#dict for {checkopt checkval} $thisarg_checks {}
tcl::dict::for {checkopt checkval} $thisarg_checks {
switch -- $checkopt { switch -- $checkopt {
-minlen { -minlen {
# -1 for disable is as good as zero # -1 for disable is as good as zero
if {[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:[string length $e_check] value:'$e_check'" error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'"
} }
} }
-maxlen { -maxlen {
if {$checkval ne "-1"} { if {$checkval ne "-1"} {
if {[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:[string length $e_check] value:'$e_check'" error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'"
} }
} }
} }
@ -942,10 +957,10 @@ namespace eval punk::args {
package require ansi package require ansi
} }
int { int {
if {[dict exists $thisarg -range]} { if {[tcl::dict::exists $thisarg -range]} {
lassign [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 {![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'" error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e'"
} }
if {$e_check < $low || $e_check > $high} { if {$e_check < $low || $e_check > $high} {
@ -954,7 +969,7 @@ namespace eval punk::args {
} }
} else { } else {
foreach e_check $vlist_check { foreach e_check $vlist_check {
if {![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'" error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e_check'"
} }
} }
@ -962,11 +977,13 @@ namespace eval punk::args {
} }
double { double {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {![string is double -strict $e_check]} { if {![tcl::string::is double -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'double'. Received: '$e'" error "Option $argname for [Get_caller] requires type 'double'. Received: '$e'"
} }
if {[dict size $thisarg_checks]} { if {[tcl::dict::size $thisarg_checks]} {
dict for {checkopt checkval} $thisarg_checks { #safe jumptable test
#dict for {checkopt checkval} $thisarg_checks {}
tcl::dict::for {checkopt checkval} $thisarg_checks {
switch -- $checkopt { switch -- $checkopt {
-range { -range {
#todo - small-value double comparisons with error-margin? review #todo - small-value double comparisons with error-margin? review
@ -982,7 +999,7 @@ namespace eval punk::args {
} }
bool { bool {
foreach e_check $vlist_check { foreach e_check $vlist_check {
if {![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'" error "Option $argname for [Get_caller] requires type 'boolean'. Received: '$e_check'"
} }
} }
@ -1001,7 +1018,7 @@ namespace eval punk::args {
wordchar - wordchar -
xdigit { xdigit {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {![string is $type $e_check]} { if {![tcl::string::is $type $e_check]} {
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'" error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'"
} }
} }
@ -1011,7 +1028,7 @@ namespace eval punk::args {
existingfile - existingfile -
existingdirectory { existingdirectory {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {!([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" error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which doesn't look like it could be a file or directory"
} }
@ -1032,7 +1049,7 @@ namespace eval punk::args {
} }
char { char {
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {[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" error "Option $argname for [Get_caller] requires type 'character'. Received: '$e' which is not a single character"
} }
} }
@ -1040,13 +1057,13 @@ namespace eval punk::args {
} }
if {$has_choices} { if {$has_choices} {
#todo -choicelabels #todo -choicelabels
set choices [dict get $thisarg -choices] set choices [tcl::dict::get $thisarg -choices]
set nocase [dict get $thisarg -nocase] set nocase [tcl::dict::get $thisarg -nocase]
foreach e $vlist e_check $vlist_check { foreach e $vlist e_check $vlist_check {
if {$nocase} { if {$nocase} {
set casemsg "(case insensitive)" set casemsg "(case insensitive)"
set choices_test [string tolower $choices] set choices_test [tcl::string::tolower $choices]
set v_test [string tolower $e_check] set v_test [tcl::string::tolower $e_check]
} else { } else {
set casemsg "(case sensitive)" set casemsg "(case sensitive)"
set v_test $e_check set v_test $e_check
@ -1060,24 +1077,24 @@ namespace eval punk::args {
} }
if {$is_strip_ansi} { if {$is_strip_ansi} {
set stripped_list [lmap e $vlist {punk::ansi::stripansi $e}] ;#no faster or slower, but more concise than foreach set stripped_list [lmap e $vlist {punk::ansi::stripansi $e}] ;#no faster or slower, but more concise than foreach
if {[dict get $thisarg -multiple]} { if {[tcl::dict::get $thisarg -multiple]} {
if {[dict get $thisarg -ARGTYPE] eq "option"} { if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} {
dict set opts $argname $stripped_list tcl::dict::set opts $argname $stripped_list
} else { } else {
dict set values_dict $argname $stripped_list tcl::dict::set values_dict $argname $stripped_list
} }
} else { } else {
if {[dict get $thisarg -ARGTYPE] eq "option"} { if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} {
dict set opts $argname [lindex $stripped_list 0] tcl::dict::set opts $argname [lindex $stripped_list 0]
} else { } else {
dict set values_dict [lindex $stripped_list 0] tcl::dict::set values_dict [lindex $stripped_list 0]
} }
} }
} }
} }
#maintain order of opts $opts values $values as caller may use lassign. #maintain order of opts $opts values $values as caller may use lassign.
return [dict create opts $opts values $values_dict] return [tcl::dict::create opts $opts values $values_dict]
} }
#proc sample1 {p1 args} { #proc sample1 {p1 args} {
@ -1099,9 +1116,9 @@ namespace eval punk::args {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace # Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args::lib { tcl::namespace::eval punk::args::lib {
namespace export * tcl::namespace::export *
namespace path [namespace parent] tcl::namespace::path [tcl::namespace::parent]
#*** !doctools #*** !doctools
#[subsection {Namespace punk::args::lib}] #[subsection {Namespace punk::args::lib}]
#[para] Secondary functions that are part of the API #[para] Secondary functions that are part of the API
@ -1126,7 +1143,7 @@ namespace eval punk::args::lib {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools #*** !doctools
#[section Internal] #[section Internal]
namespace eval punk::args::system { tcl::namespace::eval punk::args::system {
#*** !doctools #*** !doctools
#[subsection {Namespace punk::args::system}] #[subsection {Namespace punk::args::system}]
#[para] Internal functions that are not part of the API #[para] Internal functions that are not part of the API
@ -1136,7 +1153,7 @@ namespace eval punk::args::system {
} }
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready ## Ready
package provide punk::args [namespace eval punk::args { package provide punk::args [tcl::namespace::eval punk::args {
variable pkg punk::args variable pkg punk::args
variable version variable version
set version 999999.0a1.0 set version 999999.0a1.0

118
src/modules/punk/assertion-999999.0a1.0.tm

@ -69,11 +69,11 @@ package require Tcl 8.6-
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace # oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::assertion::class { tcl::namespace::eval punk::assertion::class {
#*** !doctools #*** !doctools
#[subsection {Namespace punk::assertion::class}] #[subsection {Namespace punk::assertion::class}]
#[para] class definitions #[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} { if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools #*** !doctools
#[list_begin enumerated] #[list_begin enumerated]
@ -100,16 +100,16 @@ namespace eval punk::assertion::class {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin #keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin
namespace eval punk::assertion::primary { tcl::namespace::eval punk::assertion::primary {
#tcl::namespace::export {[a-z]*}
namespace export * tcl::namespace::export assertActive assertInactive
proc assertActive {expr args} { proc assertActive {expr args} {
set code [catch {uplevel 1 [list expr $expr]} res] set code [catch {uplevel 1 [list expr $expr]} res]
if {$code} { if {$code} {
return -code $code $res return -code $code $res
} }
if {![string is boolean -strict $res]} { if {![tcl::string::is boolean -strict $res]} {
return -code error "invalid boolean expression: $expr" return -code error "invalid boolean expression: $expr"
} }
@ -124,28 +124,40 @@ namespace eval punk::assertion::primary {
upvar ::punk::assertion::CallbackCmd CallbackCmd upvar ::punk::assertion::CallbackCmd CallbackCmd
# Might want to catch this # Might want to catch this
namespace eval :: $CallbackCmd [list $msg] tcl::namespace::eval :: $CallbackCmd [list $msg]
} }
proc assertInactive args {} proc assertInactive args {}
} }
namespace eval punk::assertion::secondary { tcl::namespace::eval punk::assertion::secondary {
namespace export * tcl::namespace::export *
#we need to actually define these procs here, (not import then re-export) - or namespace origin will report the original source namespace - which isn't what we want. #we need to actually define these procs here, (not import then re-export) - or namespace origin will report the original source namespace - which isn't what we want.
proc assertActive {expr args} [info body ::punk::assertion::primary::assertActive] proc assertActive {expr args} [tcl::info::body ::punk::assertion::primary::assertActive]
proc assertInactive args {} proc assertInactive args {}
} }
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace # Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::assertion { tcl::namespace::eval punk::assertion {
variable CallbackCmd [list return -code error] variable CallbackCmd [list return -code error]
namespace import ::punk::assertion::primary::assertActive
#puts --------AAA
#*very* slow in safe interp - why?
#tcl::namespace::import ::punk::assertion::primary::assertActive
proc do_ns_import {} {
uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive]
}
do_ns_import
#puts --------BBB
rename assertActive assert rename assertActive assert
namespace export * }
tcl::namespace::eval punk::assertion {
tcl::namespace::export *
#variable xyz #variable xyz
#*** !doctools #*** !doctools
@ -177,7 +189,7 @@ namespace eval punk::assertion {
set n [llength $args] set n [llength $args]
if {$n > 1} { if {$n > 1} {
return -code error "wrong # args: should be\ return -code error "wrong # args: should be\
\"[lindex [info level 0] 0] ?command?\"" \"[lindex [tcl::info::level 0] 0] ?command?\""
} }
if {$n} { if {$n} {
set cb [lindex $args 0] set cb [lindex $args 0]
@ -187,41 +199,41 @@ namespace eval punk::assertion {
} }
proc active {{on_off ""}} { proc active {{on_off ""}} {
set nscaller [uplevel 1 [list namespace current]] set nscaller [uplevel 1 [list tcl::namespace::current]]
set which_assert [namespace eval $nscaller {namespace which assert}] set which_assert [tcl::namespace::eval $nscaller {tcl::namespace::which assert}]
#puts "nscaller:'$nscaller'" #puts "nscaller:'$nscaller'"
#puts "which_assert: $which_assert" #puts "which_assert: $which_assert"
if {$on_off eq ""} { if {$on_off eq ""} {
if {$which_assert eq ""} {return 0} if {$which_assert eq ""} {return 0}
set assertorigin [namespace origin $which_assert] set assertorigin [tcl::namespace::origin $which_assert]
#puts "ns which assert: $which_assert" #puts "ns which assert: $which_assert"
#puts "ns origin assert: $assertorigin" #puts "ns origin assert: $assertorigin"
return [expr {"assertActive" eq [namespace tail $assertorigin]}] return [expr {"assertActive" eq [tcl::namespace::tail $assertorigin]}]
} }
if {![string is boolean -strict $on_off]} { if {![tcl::string::is boolean -strict $on_off]} {
error "invalid boolean value : $on_off" error "invalid boolean value : $on_off"
} else { } else {
set info_command [namespace eval $nscaller {info commands assert}] set info_command [tcl::namespace::eval $nscaller {tcl::info::commands assert}]
if {$on_off} { if {$on_off} {
#Enable it in calling namespace #Enable it in calling namespace
if {"assert" eq $info_command} { if {"assert" eq $info_command} {
#There is an assert command reachable - due to namespace path etc, it could be in another namespace entirely - (not necessarily in an ancestor namespace of the namespace's tree structure) #There is an assert command reachable - due to namespace path etc, it could be in another namespace entirely - (not necessarily in an ancestor namespace of the namespace's tree structure)
if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} { if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} {
namespace eval $nscaller { tcl::namespace::eval $nscaller {
set assertorigin [namespace origin assert] set assertorigin [tcl::namespace::origin assert]
set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin] set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin]
switch -- $assertorigin_ns { switch -- $assertorigin_ns {
::punk::assertion { ::punk::assertion {
#original import - switch to primary origin #original import - switch to primary origin
rename assert {} rename assert {}
namespace import ::punk::assertion::primary::assertActive tcl::namespace::import ::punk::assertion::primary::assertActive
rename assertActive assert rename assertActive assert
} }
::punk::assertion::primary - ::punk::assertion::secondary { ::punk::assertion::primary - ::punk::assertion::secondary {
#keep using from same origin ns #keep using from same origin ns
rename assert {} rename assert {}
namespace import ${assertorigin_ns}::assertActive tcl::namespace::import ${assertorigin_ns}::assertActive
rename assertActive assert rename assertActive assert
} }
default { default {
@ -232,10 +244,10 @@ namespace eval punk::assertion {
return 1 return 1
} else { } else {
#assert is available, but isn't in the calling namespace - we should enable it in a way that is distinguishable from case where assert was explicitly imported to this namespace #assert is available, but isn't in the calling namespace - we should enable it in a way that is distinguishable from case where assert was explicitly imported to this namespace
namespace eval $nscaller { tcl::namespace::eval $nscaller {
set assertorigin [namespace origin assert] set assertorigin [tcl::namespace::origin assert]
if {[string match ::punk::assertion::* $assertorigin]} { if {[tcl::string::match ::punk::assertion::* $assertorigin]} {
namespace import ::punk::assertion::secondary::assertActive tcl::namespace::import ::punk::assertion::secondary::assertActive
rename assertActive assert rename assertActive assert
} else { } else {
error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin" error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin"
@ -254,20 +266,20 @@ namespace eval punk::assertion {
if {"assert" eq $info_command} { if {"assert" eq $info_command} {
if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} { if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} {
#assert is present in callers NS #assert is present in callers NS
namespace eval $nscaller { tcl::namespace::eval $nscaller {
set assertorigin [namespace origin assert] set assertorigin [tcl::namespace::origin assert]
set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin] set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin]
switch -glob -- $assertorigin_ns { switch -glob -- $assertorigin_ns {
::punk::assertion { ::punk::assertion {
#original import #original import
rename assert {} rename assert {}
namespace import punk::assertion::primary::assertInactive tcl::namespace::import punk::assertion::primary::assertInactive
rename assertInactive assert rename assertInactive assert
} }
::punk::assertion::primary - ::punk::assertion::secondary { ::punk::assertion::primary - ::punk::assertion::secondary {
#keep using from same origin ns #keep using from same origin ns
rename assert {} rename assert {}
namespace import ${assertorigin_ns}::assertInactive tcl::namespace::import ${assertorigin_ns}::assertInactive
rename assertInactive assert rename assertInactive assert
} }
default { default {
@ -278,11 +290,11 @@ namespace eval punk::assertion {
return 0 return 0
} else { } else {
#assert not present in callers NS - first install of secondary (if assert is from punk::assertion::*) #assert not present in callers NS - first install of secondary (if assert is from punk::assertion::*)
namespace eval $nscaller { tcl::namespace::eval $nscaller {
set assertorigin [namespace origin assert] set assertorigin [tcl::namespace::origin assert]
set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin] set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin]
if {[string match ::punk::assertion::* $assertorigin]} { if {[tcl::string::match ::punk::assertion::* $assertorigin]} {
namespace import ::punk::assertion::secondary::assertInactive tcl::namespace::import ::punk::assertion::secondary::assertInactive
rename assertInactive assert rename assertInactive assert
} else { } else {
error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin" error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin"
@ -310,9 +322,9 @@ namespace eval punk::assertion {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace # Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::assertion::lib { tcl::namespace::eval punk::assertion::lib {
namespace export * tcl::namespace::export *
namespace path [namespace parent] tcl::namespace::path [tcl::namespace::parent]
#*** !doctools #*** !doctools
#[subsection {Namespace punk::assertion::lib}] #[subsection {Namespace punk::assertion::lib}]
#[para] Secondary functions that are part of the API #[para] Secondary functions that are part of the API
@ -337,7 +349,7 @@ namespace eval punk::assertion::lib {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools #*** !doctools
#[section Internal] #[section Internal]
namespace eval punk::assertion::system { tcl::namespace::eval punk::assertion::system {
#*** !doctools #*** !doctools
#[subsection {Namespace punk::assertion::system}] #[subsection {Namespace punk::assertion::system}]
#[para] Internal functions that are not part of the API #[para] Internal functions that are not part of the API
@ -346,33 +358,33 @@ namespace eval punk::assertion::system {
#nsprefix/nstail are string functions - they do not concern themselves with what namespaces are present in the system #nsprefix/nstail are string functions - they do not concern themselves with what namespaces are present in the system
proc nsprefix {{nspath {}}} { proc nsprefix {{nspath {}}} {
#normalize the common case of :::: #normalize the common case of ::::
set nspath [string map [list :::: ::] $nspath] set nspath [tcl::string::map [list :::: ::] $nspath]
set rawprefix [string range $nspath 0 end-[string length [nstail $nspath]]] set rawprefix [tcl::string::range $nspath 0 end-[tcl::string::length [nstail $nspath]]]
if {$rawprefix eq "::"} { if {$rawprefix eq "::"} {
return $rawprefix return $rawprefix
} else { } else {
if {[string match *:: $rawprefix]} { if {[tcl::string::match *:: $rawprefix]} {
return [string range $rawprefix 0 end-2] return [tcl::string::range $rawprefix 0 end-2]
} else { } else {
return $rawprefix return $rawprefix
} }
#return [string trimright $rawprefix :] #return [tcl::string::trimright $rawprefix :]
} }
} }
#see also punk::ns - keep in sync #see also punk::ns - keep in sync
proc nstail {nspath args} { proc nstail {nspath args} {
#normalize the common case of :::: #normalize the common case of ::::
set nspath [string map [list :::: ::] $nspath] set nspath [tcl::string::map [list :::: ::] $nspath]
set mapped [string map [list :: \u0FFF] $nspath] set mapped [tcl::string::map [list :: \u0FFF] $nspath]
set parts [split $mapped \u0FFF] set parts [split $mapped \u0FFF]
set defaults [list -strict 0] set defaults [list -strict 0]
set opts [dict merge $defaults $args] set opts [tcl::dict::merge $defaults $args]
set strict [dict get $opts -strict] set strict [tcl::dict::get $opts -strict]
if {$strict} { if {$strict} {
foreach p $parts { foreach p $parts {
if {[string match :* $p]} { if {[tcl::string::match :* $p]} {
error "nstail unpaired colon ':' in $nspath" error "nstail unpaired colon ':' in $nspath"
} }
} }
@ -381,7 +393,7 @@ namespace eval punk::assertion::system {
return [lindex $parts end] return [lindex $parts end]
} }
proc nsjoin {prefix name} { proc nsjoin {prefix name} {
if {[string match ::* $name]} { if {[tcl::string::match ::* $name]} {
if {"$prefix" ne ""} { if {"$prefix" ne ""} {
error "nsjoin: won't join non-empty prefix to absolute namespace path '$name'" error "nsjoin: won't join non-empty prefix to absolute namespace path '$name'"
} }
@ -400,7 +412,7 @@ namespace eval punk::assertion::system {
} }
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready ## Ready
package provide punk::assertion [namespace eval punk::assertion { package provide punk::assertion [tcl::namespace::eval punk::assertion {
variable pkg punk::assertion variable pkg punk::assertion
variable version variable version
set version 999999.0a1.0 set version 999999.0a1.0

11
src/modules/punk/cap/handlers/templates-999999.0a1.0.tm

@ -85,8 +85,19 @@ namespace eval punk::cap::handlers::templates {
module { module {
set provide_statement [package ifneeded $pkg [package require $pkg]] set provide_statement [package ifneeded $pkg [package require $pkg]]
set tmfile [lindex $provide_statement end] set tmfile [lindex $provide_statement end]
if {[interp issafe]} {
#default safe interp can't use file exists/normalize etc.. but safe interp may have a policy/alias set allowing file access to certain paths - so test if file exists is usable
if {[catch {file exists $tmfile} tm_exists]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING (expected in most safe interps) - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
flush stderr
return 0
}
} else {
set tm_exists [file exists $tmfile]
}
if {![file exists $tmfile]} { if {![file exists $tmfile]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability" puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
flush stderr
return 0 return 0
} }

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

File diff suppressed because it is too large Load Diff

63
src/modules/punk/config-0.1.tm

@ -1,10 +1,5 @@
package provide punk::config [namespace eval punk::config {
variable version
set version 0.1
}]
namespace eval punk::config { tcl::namespace::eval punk::config {
variable loaded variable loaded
variable startup ;#include env overrides variable startup ;#include env overrides
variable running variable running
@ -33,38 +28,42 @@ namespace eval punk::config {
#defaults #defaults
dict set startup configset .punkshell tcl::dict::set startup configset .punkshell
dict set startup exec_unknown true ;#whether to use exec instead of experimental shellfilter::run tcl::dict::set startup exec_unknown true ;#whether to use exec instead of experimental shellfilter::run
#dict set startup color_stdout [list cyan bold] ;#not a good idea to default #tcl::dict::set startup color_stdout [list cyan bold] ;#not a good idea to default
dict set startup color_stdout [list] tcl::dict::set startup color_stdout [list]
#This wraps the stderr stream as it comes in with Ansi - probably best to default to empty.. but it's useful. #This wraps the stderr stream as it comes in with Ansi - probably best to default to empty.. but it's useful.
dict set startup color_stderr [list red bold] tcl::dict::set startup color_stderr [list red bold]
dict set startup syslog_stdout "127.0.0.1:514" tcl::dict::set startup syslog_stdout "127.0.0.1:514"
dict set startup syslog_stderr "127.0.0.1:514" tcl::dict::set startup syslog_stderr "127.0.0.1:514"
dict set startup syslog_active 0 tcl::dict::set startup syslog_active 0
#default file logs to logs folder at same location as exe if writable, or empty string #default file logs to logs folder at same location as exe if writable, or empty string
dict set startup logfile_stdout "" tcl::dict::set startup logfile_stdout ""
dict set startup logfile_stderr "" tcl::dict::set startup logfile_stderr ""
set exename ""
set exename [info nameofexecutable] catch {
#catch for safe interps
#safe base will return empty string, ordinary safe interp will raise error
set exename [tcl::info::nameofexecutable]
}
if {$exename ne ""} { if {$exename ne ""} {
set exefolder [file dirname [info nameofexecutable]] set exefolder [file dirname $exename]
set log_folder $exefolder/logs set log_folder $exefolder/logs
dict set startup scriptlib $exefolder/scriptlib tcl::dict::set startup scriptlib $exefolder/scriptlib
dict set startup apps $exefolder/../../punkapps tcl::dict::set startup apps $exefolder/../../punkapps
if {[file exists $log_folder]} { if {[file exists $log_folder]} {
if {[file isdirectory $log_folder] && [file writable $log_folder]} { if {[file isdirectory $log_folder] && [file writable $log_folder]} {
dict set startup logfile_stdout $log_folder/repl-exec-stdout.txt tcl::dict::set startup logfile_stdout $log_folder/repl-exec-stdout.txt
dict set startup logfile_stderr $log_folder/repl-exec-stderr.txt tcl::dict::set startup logfile_stderr $log_folder/repl-exec-stderr.txt
} }
} }
} else { } else {
#probably a safe interp - which cannot access info nameofexecutable even if access given to the location via punk::island #probably a safe interp - which cannot access info nameofexecutable even if access given to the location via punk::island
#review - todo? #review - todo?
dict set startup scriptlib "" tcl::dict::set startup scriptlib ""
dict set startup apps "" tcl::dict::set startup apps ""
} }
@ -95,16 +94,20 @@ namespace eval punk::config {
set f [set ::env($evar)] set f [set ::env($evar)]
if {$f ne "default"} { if {$f ne "default"} {
#e.g PUNK_SCRIPTLIB -> scriptlib #e.g PUNK_SCRIPTLIB -> scriptlib
set varname [string tolower [string range $evar 5 end]] set varname [tcl::string::tolower [tcl::string::range $evar 5 end]]
dict set startup $varname $f tcl::dict::set startup $varname $f
} }
} }
} }
unset -nocomplain evar unset -nocomplain evar
unset -nocomplain vars unset -nocomplain vars
set running [dict create] set running [tcl::dict::create]
set running [dict merge $running $startup] set running [tcl::dict::merge $running $startup]
}
package provide punk::config [tcl::namespace::eval punk::config {
variable version
set version 0.1
} }]

501
src/modules/punk/experiment-999999.0a1.0.tm

@ -0,0 +1,501 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'pmix make' or bin/punkmake to update from <pkg>-buildversion.txt
# module template: shellspy/src/decktemplates/vendor/punk/modules/template_module-0.0.2.tm
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2024
#
# @@ Meta Begin
# Application punk::experiment 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin shellspy_module_punk::experiment 0 999999.0a1.0]
#[copyright "2024"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require punk::experiment]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of punk::experiment
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by punk::experiment
#[list_begin itemized]
package require Tcl 8.6-
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::experiment::class {
#*** !doctools
#[subsection {Namespace punk::experiment::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::experiment {
namespace export {[a-z]*} ;# Convention: export all lowercase
#variable xyz
#*** !doctools
#[subsection {Namespace punk::experiment}]
#[para] Core API functions for punk::experiment
#[list_begin definitions]
#proc sample1 {p1 n args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [arg n] [opt {option value...}]]
# #[para]Description of sample1
# #[para] Arguments:
# # [list_begin arguments]
# # [arg_def tring p1] A description of string argument p1.
# # [arg_def integer n] A description of integer argument n.
# # [list_end]
# return "ok"
#}
variable o_opts_table [dict create\
]
variable o_opts_table_defaults [dict create\
-test 1\
-test2 etc\
-test3 333\
-test4 444\
]
set topt_keys [dict keys $o_opts_table_defaults]
set topt_switchkeys [list -test - -test2 - -test3 - -test4]
proc configure args [string map [list %topt_keys% $topt_keys %topt_switchkeys% $topt_switchkeys] {
variable o_opts_table
variable o_opts_table_defaults
if {![llength $args]} {
return $o_opts_table
}
if {[llength $args] == 1} {
if {[lindex $args 0] in [list %topt_keys%]} {
#query single option
set k [lindex $args 0]
set val [dict get $o_opts_table $k]
set returndict [dict create option $k value $val ansireset "\x1b\[m"]
set infodict [dict create]
switch -- $k {
-ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder_body - -ansiborder_footer {
dict set infodict debug [ansistring VIEW $val]
}
-framemap_body - -framemap_header - -framelimits_body - -framelimits_header {
dict set returndict effective [dict get $o_opts_table_effective $k]
}
}
dict set returndict info $infodict
return $returndict
#return [dict create option $k value $val ansireset "\x1b\[m" info $infodict]
} else {
error "textblock::table configure - unrecognised option '[lindex $args 0]'. Known values [dict keys $o_opts_table_defaults]"
}
}
if {[llength $args] %2 != 0} {
error "[namespace current]::table configure - unexpected argument count. Require name value pairs"
}
foreach {k v} $args {
switch -- $k {
%topt_switchkeys% {}
default {
error "[namespace current]::table configure - unrecognised option '$k'. Known values [dict keys $o_opts_table_defaults]"
}
}
#if {$k ni [dict keys $o_opts_table_defaults]} {
# error "[namespace current]::table configure - unrecognised option '$k'. Known values [dict keys $o_opts_table_defaults]"
#}
}
set checked_opts [list]
foreach {k v} $args {
switch -- $k {
-ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder-body - -ansiborder_footer {
set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]"
set ansi_codes [list] ;
foreach {pt code} $parts {
if {$pt ne ""} {
#we don't expect plaintext in an ansibase
error "Unable to interpret $k value as ansi SGR codes. Plaintext detected. Consider using for example: '\[punk::ansi::a+ green]' (or alias '\[a+ green]') to build ansi. debug view: [punk::ansi::ansistring VIEW $v]"
}
if {$code ne ""} {
lappend ansi_codes $code
}
}
set ansival [punk::ansi::codetype::sgr_merge_singles $ansi_codes]
lappend checked_opts $k $ansival
}
-frametype - -frametype_header - -frametype_body {
#frametype will raise an error if v is not a valid custom dict or one of the known predefined types such as light,heavy,double etc
lassign [textblock::frametype $v] _cat category _type ftype
lappend checked_opts $k $v
}
-framemap_body - -framemap_header {
#upvar ::textblock::class::opts_table_defaults tdefaults
#set default_bmap [dict get $tdefaults -framemap_body]
#todo - check keys and map
if {[llength $v] == 1} {
if {$v eq "default"} {
upvar ::textblock::class::opts_table_defaults tdefaults
set default_map [dict get $tdefaults $k]
lappend checked_opts $k $default_map
} else {
error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}"
}
} else {
foreach {subk subv} $v {
switch -- $subk {
topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {}
default {
error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}"
}
}
dict for {seg subst} $subv {
switch -- $seg {
hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {}
default {
error "textblock::table::configure invalid $subk value $seg. Known values {hl hlt hlb vl vll vlr trc tlc blc brc}"
}
}
}
}
lappend checked_opts $k $v
}
}
-framelimits_body - -framelimits_header {
set specific_framelimits [list]
foreach fl $v {
switch -- $fl {
"default" {
lappend specific_framelimits trc hlt tlc vll blc hlb brc vlr
}
hl {
lappend specific_framelimits hlt hlb
}
vl {
lappend specific_framelimits vll vlr
}
hlt - hlb - vll - vlr - trc - tlc - blc - brc {
lappend specific_framelimits $fl
}
default {
error "textblock::table::configure invalid $k '$fl'. Known values {hl hlb hlt vl vll vlr trc tlc blc brc} (or default for all)"
}
}
}
lappend checked_opts $k $specific_framelimits
}
-ansireset {
if {$v eq "\uFFEF"} {
set RST "\x1b\[m" ;#[a]
lappend checked_opts $k $RST
} else {
error "textblock::table::configure -ansireset is read-only. It is present only to prevent unwanted colourised output in configure commands"
}
}
-show_hseps {
if {![string is boolean $v]} {
error "textblock::table::configure invalid $k '$v'. Must be a boolean or empty string"
}
lappend checked_opts $k $v
#these don't affect column width calculations
}
-show_edge {
if {![string is boolean $v]} {
error "textblock::table::configure invalid $k '$v'. Must be a boolean or empty string"
}
lappend checked_opts $k $v
#these don't affect column width calculations - except if table -minwidth/-maxwidth come into play
set o_calculated_column_widths [list] ;#invalidate cached column widths - a recalc will be forced when needed
}
-show_vseps {
#we allow empty string - so don't use -strict boolean check
if {![string is boolean $v]} {
error "textblock::table::configure invalid $k '$v'. Must be a boolean or empty string"
}
#affects width calculations
set o_calculated_column_widths [list]
lappend checked_opts $k $v
}
-minwidth - -maxwidth {
set o_calculated_column_widths [list]
lappend checked_opts $k $v
}
default {
lappend checked_opts $k $v
}
}
}
#all options checked - ok to update o_opts_table and o_opts_table_effective
#set o_opts_table [dict merge $o_opts_table $checked_opts]
foreach {k v} $args {
#yes in safe
switch -- $k {
-framemap_header - -framemap_body {
#framemaps don't require setting every key to update.
#e.g configure -framemaps {topleft <map>}
#needs to merge with existing unspecified keys such as topright middleleft etc.
if {$v eq "default"} {
dict set o_opts_table $k default
} else {
if {[dict get $o_opts_table $k] eq "default"} {
dict set o_opts_table $k $v
} else {
dict set o_opts_table $k [dict merge [dict get $o_opts_table $k] $v]
}
}
}
default {
dict set o_opts_table $k $v
}
}
}
#use values from checked_opts for the effective opts
dict for {k v} $checked_opts {
switch -- $k {
-framemap_body - -framemap_header {
set existing [dict get $o_opts_table_effective $k]
#set updated $existing
#dict for {subk subv} $v {
# dict set updated $subk $subv
#}
#dict set o_opts_table_effective $k $updated
dict set o_opts_table_effective $k [dict merge $existing $v]
}
-framelimits_body - -framelimits_header {
#my Set_effective_framelimits
dict set o_opts_table_effective $k $v
}
default {
dict set o_opts_table_effective $k $v
}
}
}
#ansireset exception
dict set o_opts_table -ansireset [dict get $o_opts_table_effective -ansireset]
return $o_opts_table
}]
proc test1 {args} {
set result [list]
dict for {k v} $args {
switch -- $k {
-a - -b - -c - -d - -e - -f - -g - -h - -i - -j - -k - -l - -m - -n - -o - -p - -q - -r - -s - -t - -u - -v - -w - -x - -y - -z {
switch -- $k {
-a - -b - -c {
lappend result "dfor-switcharm1-switcharm1-$k"
}
default {
lappend result "dfor-switcharm1-switchdefault-$k"
}
}
}
default {
switch -- $k {
-1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 {
lappend result "dfor-switchdefault-switcharm1-$k"
}
default {
lappend result "dfor-switchdefault-switchdefault-$k"
}
}
}
}
}
return $result
}
proc test2 {args} {
set result [list]
foreach {k v} $args {
switch -- $k {
-a - -b - -c - -d - -e - -f - -g - -h - -i - -j - -k - -l - -m - -n - -o - -p - -q - -r - -s - -t - -u - -v - -w - -x - -y - -z {
switch -- $k {
-a - -b - -c {
lappend result "dfor-switcharm1-switcharm1-$k"
}
default {
lappend result "dfor-switcharm1-switchdefault-$k"
}
}
}
default {
switch -- $k {
-1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 {
lappend result "dfor-switchdefault-switcharm1-$k"
}
default {
lappend result "dfor-switchdefault-switchdefault-$k"
}
}
}
}
}
return $result
}
proc test3 {args} {
set result [list]
for {set i 0} {$i < [llength $args]} {incr i} {
set k [lindex $args $i]
switch -- $k {
-a - -b - -c - -d - -e - -f - -g - -h - -i - -j - -k - -l - -m - -n - -o - -p - -q - -r - -s - -t - -u - -v - -w - -x - -y - -z {
switch -- $k {
-a - -b - -c {
lappend result "dfor-switcharm1-switcharm1-$k"
}
default {
lappend result "dfor-switcharm1-switchdefault-$k"
}
}
}
default {
switch -- $k {
-1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 {
lappend result "dfor-switchdefault-switcharm1-$k"
}
default {
lappend result "dfor-switchdefault-switchdefault-$k"
}
}
}
}
}
return $result
}
oo::class create c1 {
method test1 args [info body ::punk::experiment::test1]
method test2 args [info body ::punk::experiment::test2]
method test3 args [info body ::punk::experiment::test2]
}
c1 create obj1
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::experiment ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::experiment::lib {
namespace export {[a-z]*} ;# Convention: export all lowercase
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace punk::experiment::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::experiment::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval punk::experiment::system {
#*** !doctools
#[subsection {Namespace punk::experiment::system}]
#[para] Internal functions that are not part of the API
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::experiment [namespace eval punk::experiment {
variable pkg punk::experiment
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

3
src/modules/punk/experiment-buildversion.txt

@ -0,0 +1,3 @@
0.1.0
#First line must be a semantic version number
#all other lines are ignored.

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

@ -66,11 +66,11 @@ package require Tcl 8.6-
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace # oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::lib::class { tcl::namespace::eval punk::lib::class {
#*** !doctools #*** !doctools
#[subsection {Namespace punk::lib::class}] #[subsection {Namespace punk::lib::class}]
#[para] class definitions #[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} { if {[info commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools #*** !doctools
#[list_begin enumerated] #[list_begin enumerated]
@ -96,46 +96,46 @@ namespace eval punk::lib::class {
} }
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::lib::ensemble { tcl::namespace::eval punk::lib::ensemble {
#wiki.tcl-lang.org/page/ensemble+extend #wiki.tcl-lang.org/page/ensemble+extend
# extend an ensemble-like routine with the routines in some namespace # extend an ensemble-like routine with the routines in some namespace
proc extend {routine extension} { proc extend {routine extension} {
if {![string match ::* $routine]} { if {![string match ::* $routine]} {
set resolved [uplevel 1 [list ::namespace which $routine]] set resolved [uplevel 1 [list ::tcl::namespace::which $routine]]
if {$resolved eq {}} { if {$resolved eq {}} {
error [list {no such routine} $routine] error [list {no such routine} $routine]
} }
set routine $resolved set routine $resolved
} }
set routinens [namespace qualifiers $routine] set routinens [tcl::namespace::qualifiers $routine]
if {$routinens eq {::}} { if {$routinens eq {::}} {
set routinens {} set routinens {}
} }
set routinetail [namespace tail $routine] set routinetail [tcl::namespace::tail $routine]
if {![string match ::* $extension]} { if {![string match ::* $extension]} {
set extension [uplevel 1 [ set extension [uplevel 1 [
list [namespace which namespace] current]]::$extension list [tcl::namespace::which namespace] current]]::$extension
} }
if {![namespace exists $extension]} { if {![tcl::namespace::exists $extension]} {
error [list {no such namespace} $extension] error [list {no such namespace} $extension]
} }
set extension [namespace eval $extension [ set extension [tcl::namespace::eval $extension [
list [namespace which namespace] current]] list [tcl::namespace::which namespace] current]]
namespace eval $extension [ tcl::namespace::eval $extension [
list [namespace which namespace] export *] list [tcl::namespace::which namespace] export *]
while 1 { while 1 {
set renamed ${routinens}::${routinetail}_[info cmdcount] set renamed ${routinens}::${routinetail}_[info cmdcount]
if {[namespace which $renamed] eq {}} break if {[tcl::namespace::which $renamed] eq {}} break
} }
rename $routine $renamed rename $routine $renamed
namespace eval $extension [ tcl::namespace::eval $extension [
list namespace ensemble create -command $routine -unknown [ list namespace ensemble create -command $routine -unknown [
list apply {{renamed ensemble routine args} { list apply {{renamed ensemble routine args} {
list $renamed $routine list $renamed $routine
@ -147,7 +147,7 @@ namespace eval punk::lib::ensemble {
} }
} }
namespace eval punk::lib::compat { tcl::namespace::eval punk::lib::compat {
#*** !doctools #*** !doctools
#[subsection {Namespace punk::lib::compat}] #[subsection {Namespace punk::lib::compat}]
#[para] compatibility functions for features that may not be available in earlier Tcl versions #[para] compatibility functions for features that may not be available in earlier Tcl versions
@ -315,8 +315,8 @@ namespace eval punk::lib::compat {
} }
# Bind [string insert] to [::tcl::string::insert]. # Bind [string insert] to [::tcl::string::insert].
namespace ensemble configure string -map [dict replace\ tcl::namespace::ensemble configure string -map [tcl::dict::replace\
[namespace ensemble configure string -map]\ [tcl::namespace::ensemble configure string -map]\
insert ::tcl::string::insert] insert ::tcl::string::insert]
} }
#*** !doctools #*** !doctools
@ -327,7 +327,7 @@ namespace eval punk::lib::compat {
# Base namespace # Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::lib { namespace eval punk::lib {
namespace export * tcl::namespace::export *
#variable xyz #variable xyz
#*** !doctools #*** !doctools
@ -368,29 +368,29 @@ namespace eval punk::lib {
#capture - use uplevel 1 or namespace eval depending on context #capture - use uplevel 1 or namespace eval depending on context
set capture [uplevel 1 { set capture [uplevel 1 {
apply { varnames { apply { varnames {
set capturevars [dict create] set capturevars [tcl::dict::create]
set capturearrs [dict create] set capturearrs [tcl::dict::create]
foreach fullv $varnames { foreach fullv $varnames {
set v [namespace tail $fullv] set v [tcl::namespace::tail $fullv]
upvar 1 $v var upvar 1 $v var
if {[info exists var]} { if {[info exists var]} {
if {(![array exists var])} { if {(![array exists var])} {
dict set capturevars $v $var tcl::dict::set capturevars $v $var
} else { } else {
dict set capturearrs capturedarray_$v [array get var] tcl::dict::set capturearrs capturedarray_$v [array get var]
} }
} else { } else {
#A variable can show in the results for 'info vars' but still not 'exist'. e.g a 'variable x' declaration in the namespace where the variable has never been set #A variable can show in the results for 'info vars' but still not 'exist'. e.g a 'variable x' declaration in the namespace where the variable has never been set
} }
} }
return [dict create vars $capturevars arrs $capturearrs] return [tcl::dict::create vars $capturevars arrs $capturearrs]
} } [info vars] } } [info vars]
} ] } ]
# -- --- --- # -- --- ---
set cvars [dict get $capture vars] set cvars [tcl::dict::get $capture vars]
set carrs [dict get $capture arrs] set carrs [tcl::dict::get $capture arrs]
set apply_script "" set apply_script ""
foreach arrayalias [dict keys $carrs] { foreach arrayalias [tcl::dict::keys $carrs] {
set realname [string range $arrayalias [string first _ $arrayalias]+1 end] set realname [string range $arrayalias [string first _ $arrayalias]+1 end]
append apply_script [string map [list %realname% $realname %arrayalias% $arrayalias] { append apply_script [string map [list %realname% $realname %arrayalias% $arrayalias] {
array set %realname% [set %arrayalias%][unset %arrayalias%] array set %realname% [set %arrayalias%][unset %arrayalias%]
@ -409,9 +409,9 @@ namespace eval punk::lib {
foreach $varnames $list { foreach $varnames $list {
lappend result {*}[apply\ lappend result {*}[apply\
[list\ [list\
[concat $varnames [dict keys $cvars] [dict keys $carrs] ]\ [concat $varnames [tcl::dict::keys $cvars] [tcl::dict::keys $carrs] ]\
$apply_script\ $apply_script\
] {*}[subst $values] {*}[dict values $cvars] {*}[dict values $carrs] ] ] {*}[subst $values] {*}[tcl::dict::values $cvars] {*}[tcl::dict::values $carrs] ]
} }
return $result return $result
} }
@ -456,8 +456,8 @@ namespace eval punk::lib {
error {wrong # args: should be "dict_getdef dictValue ?key ...? key default"} error {wrong # args: should be "dict_getdef dictValue ?key ...? key default"}
} }
set keys [lrange $args -1 end-1] set keys [lrange $args -1 end-1]
if {[dict exists $dictValue {*}$keys]} { if {[tcl::dict::exists $dictValue {*}$keys]} {
return [dict get $dictValue {*}$keys] return [tcl::dict::get $dictValue {*}$keys]
} else { } else {
return [lindex $args end] return [lindex $args end]
} }
@ -566,7 +566,7 @@ namespace eval punk::lib {
} else { } else {
#we still don't know the actual integer index for an index such as end-x or int-int without parsing and evaluating ourself. #we still don't know the actual integer index for an index such as end-x or int-int without parsing and evaluating ourself.
#we can return the value - but only in a way that won't collide with our -1 out-of-range indicator #we can return the value - but only in a way that won't collide with our -1 out-of-range indicator
return [dict create value [lindex $resultlist 0]] return [tcl::dict::create value [lindex $resultlist 0]]
} }
} }
@ -661,17 +661,17 @@ namespace eval punk::lib {
if {[llength $argopts]%2 !=0} { if {[llength $argopts]%2 !=0} {
error "[namespace current]::hex2dec arguments prior to list_largeHex must be option/value pairs - received '$argopts'" error "[namespace current]::hex2dec arguments prior to list_largeHex must be option/value pairs - received '$argopts'"
} }
set opts [dict create\ set opts [tcl::dict::create\
-validate 1\ -validate 1\
-empty_as_hex "INVALID set -empty_as_hex to a hex string e.g FF if empty values should be replaced"\ -empty_as_hex "INVALID set -empty_as_hex to a hex string e.g FF if empty values should be replaced"\
] ]
set known_opts [dict keys $opts] set known_opts [tcl::dict::keys $opts]
foreach {k v} $argopts { foreach {k v} $argopts {
dict set opts [tcl::prefix match -message "options for hex2dec. Unexpected option" $known_opts $k] $v tcl::dict::set opts [tcl::prefix match -message "options for hex2dec. Unexpected option" $known_opts $k] $v
} }
# -- --- --- --- # -- --- --- ---
set opt_validate [dict get $opts -validate] set opt_validate [tcl::dict::get $opts -validate]
set opt_empty [dict get $opts -empty_as_hex] set opt_empty [tcl::dict::get $opts -empty_as_hex]
# -- --- --- --- # -- --- --- ---
set list_largeHex [lmap h $list_largeHex[unset list_largeHex] {string map [list _ ""] [string trim $h]}] set list_largeHex [lmap h $list_largeHex[unset list_largeHex] {string map [list _ ""] [string trim $h]}]
@ -710,21 +710,21 @@ namespace eval punk::lib {
if {[llength $argopts]%2 !=0} { if {[llength $argopts]%2 !=0} {
error "[namespace current]::dec2hex arguments prior to list_decimals must be option/value pairs - received '$argopts'" error "[namespace current]::dec2hex arguments prior to list_decimals must be option/value pairs - received '$argopts'"
} }
set defaults [dict create\ set defaults [tcl::dict::create\
-width 1\ -width 1\
-case upper\ -case upper\
-empty_as_decimal "INVALID set -empty_as_decimal to a number if empty values should be replaced"\ -empty_as_decimal "INVALID set -empty_as_decimal to a number if empty values should be replaced"\
] ]
set known_opts [dict keys $defaults] set known_opts [tcl::dict::keys $defaults]
set fullopts [dict create] set fullopts [tcl::dict::create]
foreach {k v} $argopts { foreach {k v} $argopts {
dict set fullopts [tcl::prefix match -message "options for [namespace current]::dec2hex. Unexpected option" $known_opts $k] $v tcl::dict::set fullopts [tcl::prefix match -message "options for [tcl::namespace::current]::dec2hex. Unexpected option" $known_opts $k] $v
} }
set opts [dict merge $defaults $fullopts] set opts [tcl::dict::merge $defaults $fullopts]
# -- --- --- --- # -- --- --- ---
set opt_width [dict get $opts -width] set opt_width [tcl::dict::get $opts -width]
set opt_case [dict get $opts -case] set opt_case [tcl::dict::get $opts -case]
set opt_empty [dict get $opts -empty_as_decimal] set opt_empty [tcl::dict::get $opts -empty_as_decimal]
# -- --- --- --- # -- --- --- ---
@ -933,35 +933,35 @@ namespace eval punk::lib {
proc sieve n { proc sieve n {
set primes [list] set primes [list]
if {$n < 2} {return $primes} if {$n < 2} {return $primes}
set nums [dict create] set nums [tcl::dict::create]
for {set i 2} {$i <= $n} {incr i} { for {set i 2} {$i <= $n} {incr i} {
dict set nums $i "" tcl::dict::set nums $i ""
} }
set next 2 set next 2
set limit [expr {sqrt($n)}] set limit [expr {sqrt($n)}]
while {$next <= $limit} { while {$next <= $limit} {
for {set i $next} {$i <= $n} {incr i $next} {dict unset nums $i} for {set i $next} {$i <= $n} {incr i $next} {tcl::dict::unset nums $i}
lappend primes $next lappend primes $next
dict for {next -} $nums break tcl::dict::for {next -} $nums break
} }
return [concat $primes [dict keys $nums]] return [concat $primes [tcl::dict::keys $nums]]
} }
proc sieve2 n { proc sieve2 n {
set primes [list] set primes [list]
if {$n < 2} {return $primes} if {$n < 2} {return $primes}
set nums [dict create] set nums [tcl::dict::create]
for {set i 2} {$i <= $n} {incr i} { for {set i 2} {$i <= $n} {incr i} {
dict set nums $i "" tcl::dict::set nums $i ""
} }
set next 2 set next 2
set limit [expr {sqrt($n)}] set limit [expr {sqrt($n)}]
while {$next <= $limit} { while {$next <= $limit} {
for {set i $next} {$i <= $n} {incr i $next} {dict unset nums $i} for {set i $next} {$i <= $n} {incr i $next} {tcl::dict::unset nums $i}
lappend primes $next lappend primes $next
#dict for {next -} $nums break #dict for {next -} $nums break
set next [lindex $nums 0] set next [lindex $nums 0]
} }
return [concat $primes [dict keys $nums]] return [concat $primes [tcl::dict::keys $nums]]
} }
proc hasglobs {str} { proc hasglobs {str} {
@ -1002,7 +1002,7 @@ namespace eval punk::lib {
#[para]This function merges the two dicts whilst maintaining the key order of main followed by defaults. #[para]This function merges the two dicts whilst maintaining the key order of main followed by defaults.
#1st merge (inner merge) with wrong values taking precedence - but right key-order - then (outer merge) restore values #1st merge (inner merge) with wrong values taking precedence - but right key-order - then (outer merge) restore values
return [dict merge [dict merge $main $defaults] $main] return [tcl::dict::merge [tcl::dict::merge $main $defaults] $main]
} }
proc askuser {question} { proc askuser {question} {
@ -1044,7 +1044,7 @@ namespace eval punk::lib {
set answer [gets stdin] set answer [gets stdin]
} }
} finally { } finally {
fconfigure stdin -blocking [dict get $stdin_state -blocking] fconfigure stdin -blocking [tcl::dict::get $stdin_state -blocking]
} }
return $answer return $answer
} }
@ -1162,13 +1162,13 @@ namespace eval punk::lib {
} }
proc list_as_lines2 {args} { proc list_as_lines2 {args} {
#eat or own dogfood version - shows the implementation is simpler - but unfortunately not suitable for a simple function like this which should be as fast as possible? #eat or own dogfood version - shows the implementation is simpler - but unfortunately not suitable for a simple function like this which should be as fast as possible?
lassign [dict values [punk::args::get_dict { lassign [tcl::dict::values [punk::args::get_dict {
-joinchar -default \n -joinchar -default \n
*values -min 1 -max 1 *values -min 1 -max 1
} $args]] opts values } $args]] opts values
puts "opts:$opts" puts "opts:$opts"
puts "values:$values" puts "values:$values"
return [join [dict get $values 0] [dict get $opts -joinchar]] return [join [tcl::dict::get $values 0] [tcl::dict::get $opts -joinchar]]
} }
proc lines_as_list {args} { proc lines_as_list {args} {
@ -1189,7 +1189,7 @@ namespace eval punk::lib {
} else { } else {
set opts [lrange $args 0 end-1] set opts [lrange $args 0 end-1]
} }
#set opts [dict merge {-block {}} $opts] #set opts [tcl::dict::merge {-block {}} $opts]
set bposn [lsearch $opts -block] set bposn [lsearch $opts -block]
if {$bposn < 0} { if {$bposn < 0} {
lappend opts -block {} lappend opts -block {}
@ -1203,11 +1203,11 @@ namespace eval punk::lib {
#-anyopts 1 avoids having to know what to say if odd numbers of options passed etc #-anyopts 1 avoids having to know what to say if odd numbers of options passed etc
#we don't have to decide what is an opt vs a value #we don't have to decide what is an opt vs a value
#even if the caller provides the argument -block without a value the next function's validation will report a reasonable error because there is now nothing in $values (consumed by -block) #even if the caller provides the argument -block without a value the next function's validation will report a reasonable error because there is now nothing in $values (consumed by -block)
lassign [dict values [punk::args::get_dict { lassign [tcl::dict::values [punk::args::get_dict {
*opts -any 1 *opts -any 1
-block -default {} -block -default {}
} $args]] opts valuedict } $args]] opts valuedict
tailcall linelist {*}$opts {*}[dict values $valuedict] tailcall linelist {*}$opts {*}[tcl::dict::values $valuedict]
} }
# important for pipeline & match_assign # important for pipeline & match_assign
@ -1222,7 +1222,7 @@ namespace eval punk::lib {
set text [string map [list \r\n \n] $text] ;#review - option? set text [string map [list \r\n \n] $text] ;#review - option?
set arglist [lrange $args 0 end-1] set arglist [lrange $args 0 end-1]
set opts [dict create\ set opts [tcl::dict::create\
-block {trimhead1 trimtail1}\ -block {trimhead1 trimtail1}\
-line {}\ -line {}\
-commandprefix ""\ -commandprefix ""\
@ -1232,7 +1232,7 @@ namespace eval punk::lib {
foreach {o v} $arglist { foreach {o v} $arglist {
switch -- $o { switch -- $o {
-block - -line - -commandprefix - -ansiresets - -ansireplays { -block - -line - -commandprefix - -ansiresets - -ansireplays {
dict set opts $o $v tcl::dict::set opts $o $v
} }
default { default {
error "linelist: Unrecognized option '$o' usage:$usage" error "linelist: Unrecognized option '$o' usage:$usage"
@ -1240,7 +1240,7 @@ namespace eval punk::lib {
} }
} }
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_block [dict get $opts -block] set opt_block [tcl::dict::get $opts -block]
if {[llength $opt_block]} { if {[llength $opt_block]} {
foreach bo $opt_block { foreach bo $opt_block {
switch -- $bo { switch -- $bo {
@ -1272,7 +1272,7 @@ namespace eval punk::lib {
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_line [dict get $opts -line] set opt_line [tcl::dict::get $opts -line]
set tl_left 0 set tl_left 0
set tl_right 0 set tl_right 0
set tl_both 0 set tl_both 0
@ -1299,11 +1299,11 @@ namespace eval punk::lib {
set tl_both 1 set tl_both 1
} }
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_commandprefix [dict get $opts -commandprefix] set opt_commandprefix [tcl::dict::get $opts -commandprefix]
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_ansiresets [dict get $opts -ansiresets] set opt_ansiresets [tcl::dict::get $opts -ansiresets]
# -- --- --- --- --- --- # -- --- --- --- --- ---
set opt_ansireplays [dict get $opts -ansireplays] set opt_ansireplays [tcl::dict::get $opts -ansireplays]
if {$opt_ansireplays} { if {$opt_ansireplays} {
if {$opt_ansiresets eq "auto"} { if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 1 set opt_ansiresets 1
@ -1604,8 +1604,29 @@ namespace eval punk::lib {
} }
#we are interested in seeing jumpTable line and following lines up until next line starting with "Command" or bracketed number e.g (164) #we are interested in seeing jumpTable line and following lines up until next line starting with "Command" or bracketed number e.g (164)
proc show_jump_tables {procname} { proc show_jump_tables {args} {
set data [tcl::unsupported::disassemble proc $procname] #avoiding use of 'info cmdtype' as unavaliable in safe interps as at 2024-06.
if {[llength $args] == 1} {
set data [tcl::unsupported::disassemble proc [lindex $args 0]]
} elseif {[llength $args] == 2} {
#review - this looks for direct methods on the supplied object/class, and then tries to disassemble method on the supplied class or class of supplied object if it isn't a class itself.
#not sure if this handles more complex hierarchies or mixins etc.
lassign $args obj method
if {![info object isa object $obj]} {
error "show_jump_tables unable to examine '$args'. $obj is not an oo object"
}
#classes are objects too and can have direct methods
if {$method in [info object methods $obj]} {
set data [tcl::unsupported::disassemble objmethod $obj $method]
} else {
if {![info object isa class $obj]} {
set obj [info object class $obj]
}
set data [tcl::unsupported::disassemble method $obj $method]
}
} else {
error "show_jump_tables expected a procname or a class/object and method"
}
set result "" set result ""
set in_jt 0 set in_jt 0
foreach ln [split $data \n] { foreach ln [split $data \n] {
@ -1888,7 +1909,7 @@ namespace eval punk::lib::system {
} }
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready ## Ready
package provide punk::lib [namespace eval punk::lib { package provide punk::lib [tcl::namespace::eval punk::lib {
variable pkg punk::lib variable pkg punk::lib
variable version variable version
set version 999999.0a1.0 set version 999999.0a1.0

12
src/modules/punk/ns-999999.0a1.0.tm

@ -994,7 +994,19 @@ namespace eval punk::ns {
} }
#info cmdtype available in 8.7+ #info cmdtype available in 8.7+
#safe interps also seem to have it disabled for some reason
proc cmdtype {cmd} { proc cmdtype {cmd} {
if {[interp issafe]} {
if {[catch {::tcl::info::cmdtype $cmd} result]} {
if {[info commands ::cmdtype] ne ""} {
#hack - look for an alias that may have been specifically enabled to bring this back
tailcall ::cmdtype $cmd
}
return na
} else {
return $result
}
}
if {[info commands ::tcl::info::cmdtype] ne ""} { if {[info commands ::tcl::info::cmdtype] ne ""} {
tailcall info cmdtype $cmd tailcall info cmdtype $cmd
} }

38
src/modules/punk/overlay-0.1.tm

@ -41,41 +41,41 @@ namespace eval ::punk::overlay {
#} #}
namespace eval $routine [ namespace eval $routine [
list namespace ensemble configure $routine -unknown [ ::list namespace ensemble configure $routine -unknown [
list apply {{base ensemble subcommand args} { ::list ::apply {{base ensemble subcommand args} {
list ${base}::_redirected $ensemble $subcommand ::list ${base}::_redirected $ensemble $subcommand
}} $base }} $base
] ]
] ]
punk::mix::util::namespace_import_pattern_to_namespace_noclobber ::punk::mix::util::* ${routine}::util punk::mix::util::namespace_import_pattern_to_namespace_noclobber ::punk::mix::util::* ${routine}::util
#namespace eval ${routine}::util { #namespace eval ${routine}::util {
#namespace import ::punk::mix::util::* #::namespace import ::punk::mix::util::*
#} #}
punk::mix::util::namespace_import_pattern_to_namespace_noclobber ${base}::lib::* ${routine}::lib punk::mix::util::namespace_import_pattern_to_namespace_noclobber ${base}::lib::* ${routine}::lib
#namespace eval ${routine}::lib [string map [list <base> $base] { #namespace eval ${routine}::lib [string map [list <base> $base] {
# namespace import <base>::lib::* # ::namespace import <base>::lib::*
#}] #}]
namespace eval ${routine}::lib [string map [list <base> $base <routine> $routine] { namespace eval ${routine}::lib [string map [list <base> $base <routine> $routine] {
if {[namespace exists <base>::lib]} { if {[::namespace exists <base>::lib]} {
set current_paths [namespace path] ::set current_paths [namespace path]
if {"<routine>" ni $current_paths} { if {"<routine>" ni $current_paths} {
lappend current_paths <routine> ::lappend current_paths <routine>
} }
namespace path $current_paths ::namespace path $current_paths
} }
}] }]
namespace eval $routine { namespace eval $routine {
set exportlist [list] ::set exportlist [::list]
foreach cmd [info commands [namespace current]::*] { ::foreach cmd [::info commands [::namespace current]::*] {
set c [namespace tail $cmd] ::set c [::namespace tail $cmd]
if {![string match _* $c]} { if {![::string match _* $c]} {
lappend exportlist $c ::lappend exportlist $c
} }
} }
namespace export {*}$exportlist ::namespace export {*}$exportlist
} }
return $routine return $routine
@ -122,18 +122,18 @@ namespace eval ::punk::overlay {
#let child namespace 'lib' resolve parent namespace and thus util::xxx #let child namespace 'lib' resolve parent namespace and thus util::xxx
namespace eval ${cmdnamespace}::lib [string map [list <cmdns> $cmdnamespace] { namespace eval ${cmdnamespace}::lib [string map [list <cmdns> $cmdnamespace] {
set nspaths [namespace path] ::set nspaths [::namespace path]
if {"<cmdns>" ni $nspaths} { if {"<cmdns>" ni $nspaths} {
lappend nspaths <cmdns> ::lappend nspaths <cmdns>
} }
namespace path $nspaths ::namespace path $nspaths
}] }]
set imported_commands [list] set imported_commands [list]
set nscaller [uplevel 1 [list namespace current]] set nscaller [uplevel 1 [list namespace current]]
if {[catch { if {[catch {
#review - noclobber? #review - noclobber?
namespace eval ${nscaller}::temp_import [list namespace import ${cmdnamespace}::*] namespace eval ${nscaller}::temp_import [list ::namespace import ${cmdnamespace}::*]
foreach cmd [info commands ${nscaller}::temp_import::*] { foreach cmd [info commands ${nscaller}::temp_import::*] {
set cmdtail [namespace tail $cmd] set cmdtail [namespace tail $cmd]
if {$cmdtail eq "_default"} { if {$cmdtail eq "_default"} {

46
src/modules/punk/repl-0.1.tm

@ -41,6 +41,7 @@ package require textblock
if {![info exists ::env(SHELL)]} { if {![info exists ::env(SHELL)]} {
set ::env(SHELL) punk86 set ::env(SHELL) punk86
} }
@ -1278,6 +1279,8 @@ proc repl::repl_handler {inputchan prompt_config} {
#if it's been set to raw - assume it is deliberately done this way as the user could have alternatively called punk::mode raw or punk::console::enableVirtualTerminal #if it's been set to raw - assume it is deliberately done this way as the user could have alternatively called punk::mode raw or punk::console::enableVirtualTerminal
#by not doing this automatically - we assume the caller has a reason. #by not doing this automatically - we assume the caller has a reason.
} else { } else {
#JMN FIX!
#this returns 0 in rawmode on 8.6 after repl thread changes
set rawmode [set ::punk::console::is_raw] set rawmode [set ::punk::console::is_raw]
} }
@ -1332,9 +1335,10 @@ proc repl::repl_handler {inputchan prompt_config} {
set cols 3 set cols 3
if {[string is integer -strict $rows]} { if {[string is integer -strict $rows]} {
set RED [punk::ansi::a+ red bold]; set RST [punk::ansi::a] set RED [punk::ansi::a+ red bold]; set RST [punk::ansi::a]
set msg "${RED}line-length Tcl windows channel bug? Hit enter to continue$RST" set msg "${RED}fblocked $inputchan is true. (line-length Tcl windows channel bug?) Hit enter to continue$RST"
set msglen [ansistring length $msg] set msglen [ansistring length $msg]
punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg #punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg
puts stderr $msg
} }
after 100 after 100
} }
@ -2494,6 +2498,12 @@ namespace eval repl {
#package require shellrun #package require shellrun
package require textblock package require textblock
#md5 uses open so can't be directly called in a safe interp
#it will need to delegate to a call here in the main interp of the codethread using an installed alias
set md5version [package require md5]
#we also need to 'package provide md5 $md5version' in the safe interp itself so that it won't override
#punk::configure_unknown ;#must be called because we hacked the tcl 'unknown' proc #punk::configure_unknown ;#must be called because we hacked the tcl 'unknown' proc
#child codethread (outside of code interp) needs to know details of the calling repl #child codethread (outside of code interp) needs to know details of the calling repl
@ -2534,6 +2544,28 @@ namespace eval repl {
thread::send %replthread% [list punk::console::colour {*}$args] thread::send %replthread% [list punk::console::colour {*}$args]
interp eval code [list punk::console::colour {*}$args] interp eval code [list punk::console::colour {*}$args]
} }
proc mode args {
thread::send %replthread% [list punk::console::mode {*}$args]
interp eval code [list ::punk::console::mode {*}$args]
}
proc cmdtype cmd {
code invokehidden tcl:info:cmdtype $cmd
}
#punk repl tsv wrappers
proc set_repl_last_unknown args {
tsv::set repl last_unknown {*}$args
}
proc get_repl_runid args {
if {[tsv::exists repl runid]} {
return [tsv::get repl runid]
} else {
return 0
}
}
proc md5 args {
::md5::md5 {*}$args
}
} }
namespace eval ::repl::interpextras { namespace eval ::repl::interpextras {
#install using safe::setLogCmd #install using safe::setLogCmd
@ -2633,6 +2665,7 @@ namespace eval repl {
interp share {} [shellfilter::stack::item_tophandle stderr] code interp share {} [shellfilter::stack::item_tophandle stderr] code
} }
code alias ::md5::md5 ::repl::interphelpers::md5
code alias exit ::repl::interphelpers::quit code alias exit ::repl::interphelpers::quit
} elseif {$safe == 2} { } elseif {$safe == 2} {
safe::interpCreate code -nested 1 safe::interpCreate code -nested 1
@ -2687,6 +2720,9 @@ namespace eval repl {
#review - exit should do something slightly different #review - exit should do something slightly different
# see ::safe::interpDelete # see ::safe::interpDelete
code alias exit ::repl::interphelpers::quit code alias exit ::repl::interphelpers::quit
code alias ::md5::md5 ::repl::interphelpers::md5
interp eval code [list package provide md5 $md5version]
} else { } else {
interp create code interp create code
interp eval code { interp eval code {
@ -2706,8 +2742,14 @@ namespace eval repl {
code alias quit ::repl::interphelpers::quit code alias quit ::repl::interphelpers::quit
code alias editbuf ::repl::interphelpers::editbuf code alias editbuf ::repl::interphelpers::editbuf
code alias colour ::repl::interphelpers::colour code alias colour ::repl::interphelpers::colour
code alias mode ::repl::interphelpers::mode
#code alias after ::repl::interphelpers::do_after #code alias after ::repl::interphelpers::do_after
code alias ::punk::set_repl_last_unknown ::repl::interphelpers::set_repl_last_unknown
code alias ::punk::get_repl_runid ::repl::interphelpers::get_repl_runid
code alias cmdtype ::repl::interphelpers::cmdtype
#temporary debug aliases - deliberate violation of safety provided by safe interp #temporary debug aliases - deliberate violation of safety provided by safe interp
code alias escapeeval ::repl::interphelpers::escapeeval code alias escapeeval ::repl::interphelpers::escapeeval

2
src/modules/punkcheck-0.1.0.tm

@ -1294,7 +1294,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal dict unset config -call-depth-internal
dict unset config -max_depth dict unset config -max_depth
dict unset config -subdirlist dict unset config -subdirlist
dict for {k v} $config { tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} { if {$v eq "\uFFFF"} {
dict unset config $k dict unset config $k
} }

48
src/modules/shellfilter-0.1.9.tm

@ -329,11 +329,14 @@ namespace eval shellfilter::chan {
} }
} }
method initialize {ch mode} { method initialize {ch mode} {
return [list initialize finalize write] return [list initialize finalize write flush clear]
} }
method finalize {ch} { method finalize {ch} {
my destroy my destroy
} }
method clear {ch} {
return
}
method watch {ch events} { method watch {ch events} {
# must be present but we ignore it because we do not # must be present but we ignore it because we do not
# post any events # post any events
@ -341,6 +344,9 @@ namespace eval shellfilter::chan {
#method read {ch count} { #method read {ch count} {
# return ? # return ?
#} #}
method flush {ch} {
return ""
}
method write {ch bytes} { method write {ch bytes} {
set stringdata [encoding convertfrom $o_enc $bytes] set stringdata [encoding convertfrom $o_enc $bytes]
foreach v $o_datavars { foreach v $o_datavars {
@ -374,7 +380,7 @@ namespace eval shellfilter::chan {
} }
} }
method initialize {transform_handle mode} { method initialize {transform_handle mode} {
return [list initialize read write finalize] return [list initialize read drain write flush clear finalize]
} }
method finalize {transform_handle} { method finalize {transform_handle} {
::shellfilter::log::close $o_logsource ::shellfilter::log::close $o_logsource
@ -384,12 +390,21 @@ namespace eval shellfilter::chan {
# must be present but we ignore it because we do not # must be present but we ignore it because we do not
# post any events # post any events
} }
method clear {transform_handle} {
return
}
method drain {transform_handle} {
return ""
}
method read {transform_handle bytes} { method read {transform_handle bytes} {
set logdata [encoding convertfrom $o_enc $bytes] set logdata [encoding convertfrom $o_enc $bytes]
#::shellfilter::log::write $o_logsource $logdata #::shellfilter::log::write $o_logsource $logdata
puts -nonewline $o_localchan $logdata puts -nonewline $o_localchan $logdata
return $bytes return $bytes
} }
method flush {transform_handle} {
return ""
}
method write {transform_handle bytes} { method write {transform_handle bytes} {
set logdata [encoding convertfrom $o_enc $bytes] set logdata [encoding convertfrom $o_enc $bytes]
#::shellfilter::log::write $o_logsource $logdata #::shellfilter::log::write $o_logsource $logdata
@ -524,18 +539,27 @@ namespace eval shellfilter::chan {
} }
} }
method initialize {transform_handle mode} { method initialize {transform_handle mode} {
return [list initialize read write finalize] return [list initialize read write clear flush drain finalize]
} }
method finalize {transform_handle} { method finalize {transform_handle} {
my destroy my destroy
} }
method clear {transform_handle} {
return
}
method watch {transform_handle events} { method watch {transform_handle events} {
} }
method drain {transform_handle} {
return ""
}
method read {transform_handle bytes} { method read {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes] set instring [encoding convertfrom $o_enc $bytes]
set outstring [punk::ansi::stripansi $instring] set outstring [punk::ansi::stripansi $instring]
return [encoding convertto $o_enc $outstring] return [encoding convertto $o_enc $outstring]
} }
method flush {transform_handle} {
return ""
}
method write {transform_handle bytes} { method write {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes] set instring [encoding convertfrom $o_enc $bytes]
set outstring [punk::ansi::stripansi $instring] set outstring [punk::ansi::stripansi $instring]
@ -614,19 +638,33 @@ namespace eval shellfilter::chan {
} }
} }
method initialize {transform_handle mode} { method initialize {transform_handle mode} {
return [list initialize write finalize] return [list initialize write flush read drain clear finalize]
} }
method finalize {transform_handle} { method finalize {transform_handle} {
my destroy my destroy
} }
method watch {transform_handle events} { method watch {transform_handle events} {
} }
method clear {transform_handle} {
return
}
method flush {transform_handle} {
return ""
}
method write {transform_handle bytes} { method write {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes] set instring [encoding convertfrom $o_enc $bytes]
set outstring "$o_do_colour$instring$o_do_normal" set outstring "$o_do_colour$instring$o_do_normal"
#set outstring ">>>$instring" #set outstring ">>>$instring"
return [encoding convertto $o_enc $outstring] return [encoding convertto $o_enc $outstring]
} }
method drain {transform_handle} {
return ""
}
method read {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes]
set outstring "$o_do_colour$instring$o_do_normal"
return [encoding convertto $o_enc $outstring]
}
method meta_is_redirection {} { method meta_is_redirection {} {
return $o_is_junction return $o_is_junction
} }
@ -2109,7 +2147,7 @@ namespace eval shellfilter {
error "Trailing arguments after any positional arguments must be in pairs of the form -argname argvalue. Valid flags are:'$valid_flags'" error "Trailing arguments after any positional arguments must be in pairs of the form -argname argvalue. Valid flags are:'$valid_flags'"
} }
set invalid_flags [list] set invalid_flags [list]
dict for {k -} $args { foreach {k -} $args {
switch -- $k { switch -- $k {
-timeout - -timeout -
-outprefix - -outprefix -

113
src/modules/textblock-999999.0a1.0.tm

@ -184,13 +184,17 @@ namespace eval textblock {
onlysolo [list]\ onlysolo [list]\
] ]
#ensembles seem to be not compiled in safe interp
#https://core.tcl-lang.org/tcl/tktview/1095bf7f75
#as we want textblock to be usable in safe interps - use tcl::dict::for as a partial workaround
#This at least means the script argument, especially switch statements can get compiled.
#It does however affect all ensemble commands - so even 'dict get/set' etc won't be as efficient in a safe interp.
#e.g $t configure -framemap_body [table_edge_map " "] #e.g $t configure -framemap_body [table_edge_map " "]
proc table_edge_map {char} { proc table_edge_map {char} {
variable table_edge_parts variable table_edge_parts
set map [list] set map [list]
dict for {celltype parts} $table_edge_parts { tcl::dict::for {celltype parts} $table_edge_parts {
set tmap [list] set tmap [list]
foreach p $parts { foreach p $parts {
dict set tmap $p $char dict set tmap $p $char
@ -202,7 +206,7 @@ namespace eval textblock {
proc table_sep_map {char} { proc table_sep_map {char} {
variable table_hseps variable table_hseps
set map [list] set map [list]
dict for {celltype parts} $table_hseps { tcl::dict::for {celltype parts} $table_hseps {
set tmap [list] set tmap [list]
foreach p $parts { foreach p $parts {
dict set tmap $p $char dict set tmap $p $char
@ -214,7 +218,7 @@ namespace eval textblock {
proc header_edge_map {char} { proc header_edge_map {char} {
variable header_edge_parts variable header_edge_parts
set map [list] set map [list]
dict for {celltype parts} $header_edge_parts { tcl::dict::for {celltype parts} $header_edge_parts {
set tmap [list] set tmap [list]
foreach p $parts { foreach p $parts {
dict set tmap $p $char dict set tmap $p $char
@ -522,14 +526,18 @@ namespace eval textblock {
error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}" error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}"
} }
} else { } else {
dict for {subk subv} $v { #safe jumptable test
#dict for {subk subv} $v {}
foreach {subk subv} $v {
switch -- $subk { switch -- $subk {
topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {} topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {}
default { default {
error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}" error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}"
} }
} }
dict for {seg subst} $subv { #safe jumptable test
#dict for {seg subst} $subv {}
foreach {seg subst} $subv {
switch -- $seg { switch -- $seg {
hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {} hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {}
default { default {
@ -632,7 +640,10 @@ namespace eval textblock {
} }
} }
#use values from checked_opts for the effective opts #use values from checked_opts for the effective opts
dict for {k v} $checked_opts { #safe jumptable test
#dict for {k v} $checked_opts {}
#foreach {k v} $checked_opts {}
tcl::dict::for {k v} $checked_opts {
switch -- $k { switch -- $k {
-framemap_body - -framemap_header { -framemap_body - -framemap_header {
set existing [dict get $o_opts_table_effective $k] set existing [dict get $o_opts_table_effective $k]
@ -687,7 +698,7 @@ namespace eval textblock {
} }
$m add columns [dict size $o_columndata] $m add columns [dict size $o_columndata]
$m add rows [dict size $o_rowdefs] $m add rows [dict size $o_rowdefs]
dict for {k v} $o_columndata { tcl::dict::for {k v} $o_columndata {
$m set column $k $v $m set column $k $v
} }
return $m return $m
@ -950,7 +961,7 @@ namespace eval textblock {
if {$args_got_headers} { if {$args_got_headers} {
#if the headerlist length for this column has shrunk,and it was the longest - we may now have excess entries in o_headerstates #if the headerlist length for this column has shrunk,and it was the longest - we may now have excess entries in o_headerstates
set zero_heights [list] set zero_heights [list]
dict for {hidx _v} $o_headerstates { tcl::dict::for {hidx _v} $o_headerstates {
#pass empty string for exclude_column so we don't exclude our own column #pass empty string for exclude_column so we don't exclude our own column
if {[my header_height_calc $hidx ""] == 0} { if {[my header_height_calc $hidx ""] == 0} {
lappend zero_heights $hidx lappend zero_heights $hidx
@ -974,7 +985,7 @@ namespace eval textblock {
} }
method header_count_calc {} { method header_count_calc {} {
set max_headers 0 set max_headers 0
dict for {k cdef} $o_columndefs { tcl::dict::for {k cdef} $o_columndefs {
set num_headers [llength [dict get $cdef -headers]] set num_headers [llength [dict get $cdef -headers]]
set max_headers [expr {max($max_headers,$num_headers)}] set max_headers [expr {max($max_headers,$num_headers)}]
} }
@ -994,7 +1005,7 @@ namespace eval textblock {
} else { } else {
set exclude_colidx [lindex [dict keys $o_columndefs] $exclude_column] set exclude_colidx [lindex [dict keys $o_columndefs] $exclude_column]
} }
dict for {cidx cdef} $o_columndefs { tcl::dict::for {cidx cdef} $o_columndefs {
if {$exclude_colidx == $cidx} { if {$exclude_colidx == $cidx} {
continue continue
} }
@ -1014,7 +1025,7 @@ namespace eval textblock {
method header_colspans {} { method header_colspans {} {
set num_headers [my header_count_calc] set num_headers [my header_count_calc]
set colspans_by_header [dict create] set colspans_by_header [dict create]
dict for {cidx cdef} $o_columndefs { tcl::dict::for {cidx cdef} $o_columndefs {
set headerlist [dict get $cdef -headers] set headerlist [dict get $cdef -headers]
set colspans_for_column [dict get $cdef -header_colspans] set colspans_for_column [dict get $cdef -header_colspans]
for {set h 0} {$h < $num_headers} {incr h} { for {set h 0} {$h < $num_headers} {incr h} {
@ -1076,7 +1087,7 @@ namespace eval textblock {
set result [dict create] set result [dict create]
dict set result -colspans [dict get $colspans_by_header $hidx] dict set result -colspans [dict get $colspans_by_header $hidx]
set header_row_items [list] set header_row_items [list]
dict for {cidx cdef} $o_columndefs { tcl::dict::for {cidx cdef} $o_columndefs {
set colheaders [dict get $cdef -headers] set colheaders [dict get $cdef -headers]
set relevant_header [lindex $colheaders $hidx] set relevant_header [lindex $colheaders $hidx]
#The -headers element of the column def is allowed to have less elements than the total, even be empty. Number of headers is defined by the longest -header list in the set of columns #The -headers element of the column def is allowed to have less elements than the total, even be empty. Number of headers is defined by the longest -header list in the set of columns
@ -1095,7 +1106,7 @@ namespace eval textblock {
switch -- $k { switch -- $k {
-values { -values {
set header_row_items [list] set header_row_items [list]
dict for {cidx cdef} $o_columndefs { tcl::dict::for {cidx cdef} $o_columndefs {
set colheaders [dict get $cdef -headers] set colheaders [dict get $cdef -headers]
set relevant_header [lindex $colheaders $hidx] set relevant_header [lindex $colheaders $hidx]
#The -headers element of the column def is allowed to have less elements than the total, even be empty. Number of headers is defined by the longest -header list in the set of columns #The -headers element of the column def is allowed to have less elements than the total, even be empty. Number of headers is defined by the longest -header list in the set of columns
@ -1127,14 +1138,16 @@ namespace eval textblock {
if {[llength $args] %2 != 0} { if {[llength $args] %2 != 0} {
error "textblock::table configure_header incorrect number of options following index_expression. Require name value pairs. Known options: [dict keys $o_opts_header_defaults]" error "textblock::table configure_header incorrect number of options following index_expression. Require name value pairs. Known options: [dict keys $o_opts_header_defaults]"
} }
dict for {k v} $args { foreach {k v} $args {
if {$k ni [dict keys $o_opts_header_defaults]} { if {$k ni [dict keys $o_opts_header_defaults]} {
error "[namespace current]::table configure_row unknown option '$k'. Known options: [dict keys $o_opts_header_defaults]" error "[namespace current]::table configure_row unknown option '$k'. Known options: [dict keys $o_opts_header_defaults]"
} }
} }
set checked_opts [list] set checked_opts [list]
dict for {k v} $args { #safe jumptable test
#dict for {k v} $args {}
foreach {k v} $args {
switch -- $k { switch -- $k {
-ansibase { -ansibase {
set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]" set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]"
@ -1248,8 +1261,10 @@ namespace eval textblock {
} }
#configured opts all good #configured opts all good
#safe jumptable test
dict for {k v} $checked_opts { #dict for {k v} $checked_opts {}
#foreach {k v} $checked_opts {}
tcl::dict::for {k v} $checked_opts {
switch -- $k { switch -- $k {
-values { -values {
set c 0 set c 0
@ -1342,7 +1357,9 @@ namespace eval textblock {
if {[llength $args] %2 !=0} { if {[llength $args] %2 !=0} {
error "[namespace current]::table::add_row unexpected argument count. Require name value pairs. Known options: [dict keys $defaults]" error "[namespace current]::table::add_row unexpected argument count. Require name value pairs. Known options: [dict keys $defaults]"
} }
dict for {k v} $args { #safe jumptable test
#dict for {k v} $args {}
foreach {k v} $args {
switch -- $k { switch -- $k {
-minheight - -maxheight - -ansibase - -ansireset {} -minheight - -maxheight - -ansibase - -ansireset {}
default { default {
@ -1363,7 +1380,7 @@ namespace eval textblock {
} }
} else { } else {
if {![llength $valuelist]} { if {![llength $valuelist]} {
dict for {k coldef} $o_columndefs { tcl::dict::for {k coldef} $o_columndefs {
lappend valuelist [dict get $coldef -defaultvalue] lappend valuelist [dict get $coldef -defaultvalue]
} }
} }
@ -1451,13 +1468,13 @@ namespace eval textblock {
if {[llength $args] %2 != 0} { if {[llength $args] %2 != 0} {
error "textblock::table configure_row incorrect number of options following index_expression. Require name value pairs. Known options: [dict keys $o_opts_row_defaults]" error "textblock::table configure_row incorrect number of options following index_expression. Require name value pairs. Known options: [dict keys $o_opts_row_defaults]"
} }
dict for {k v} $args { foreach {k v} $args {
if {$k ni [dict keys $o_opts_row_defaults]} { if {$k ni [dict keys $o_opts_row_defaults]} {
error "[namespace current]::table configure_row unknown option '$k'. Known options: [dict keys $o_opts_row_defaults]" error "[namespace current]::table configure_row unknown option '$k'. Known options: [dict keys $o_opts_row_defaults]"
} }
} }
set checked_opts [list] set checked_opts [list]
dict for {k v} $args { foreach {k v} $args {
switch -- $k { switch -- $k {
-ansibase { -ansibase {
set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]" set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]"
@ -1514,7 +1531,7 @@ namespace eval textblock {
set o_rowdefs [dict create] set o_rowdefs [dict create]
set o_rowstates [dict create] set o_rowstates [dict create]
#The data values are stored by column regardless of whether added row by row #The data values are stored by column regardless of whether added row by row
dict for {cidx records} $o_columndata { tcl::dict::for {cidx records} $o_columndata {
dict set o_columndata $cidx [list] dict set o_columndata $cidx [list]
#reset only the body fields in o_columnstates #reset only the body fields in o_columnstates
dict set o_columnstates $cidx minwidthbodyseen 0 dict set o_columnstates $cidx minwidthbodyseen 0
@ -1587,7 +1604,7 @@ namespace eval textblock {
-position "inner"\ -position "inner"\
-return "string"\ -return "string"\
] ]
dict for {k v} $args { foreach {k v} $args {
switch -- $k { switch -- $k {
-position - -return { -position - -return {
dict set opts $k $v dict set opts $k $v
@ -2387,7 +2404,7 @@ namespace eval textblock {
set defaults [dict create\ set defaults [dict create\
-usetables 1\ -usetables 1\
] ]
dict for {k v} $args { foreach {k v} $args {
switch -- $k { switch -- $k {
-usetables {} -usetables {}
default { default {
@ -2403,13 +2420,13 @@ namespace eval textblock {
#puts stdout "columndefs: $o_columndefs" #puts stdout "columndefs: $o_columndefs"
puts stdout "columndefs:" puts stdout "columndefs:"
if {!$opt_usetables} { if {!$opt_usetables} {
dict for {k v} $o_columndefs { tcl::dict::for {k v} $o_columndefs {
puts " $k $v" puts " $k $v"
} }
} else { } else {
set t [textblock::class::table new] set t [textblock::class::table new]
$t add_column -headers "Col" $t add_column -headers "Col"
dict for {col coldef} $o_columndefs { tcl::dict::for {col coldef} $o_columndefs {
foreach property [dict keys $coldef] { foreach property [dict keys $coldef] {
if {$property eq "-ansireset"} { if {$property eq "-ansireset"} {
continue continue
@ -2422,7 +2439,7 @@ namespace eval textblock {
#build our inner tables first so we can sync widths #build our inner tables first so we can sync widths
set col_header_tables [dict create] set col_header_tables [dict create]
set max_widths [dict create 0 0 1 0 2 0 3 0] ;#max inner table column widths set max_widths [dict create 0 0 1 0 2 0 3 0] ;#max inner table column widths
dict for {col coldef} $o_columndefs { tcl::dict::for {col coldef} $o_columndefs {
set row [list $col] set row [list $col]
set colheaders [dict get $coldef -headers] set colheaders [dict get $coldef -headers]
#inner table probably overkill here ..but just as easy #inner table probably overkill here ..but just as easy
@ -2451,14 +2468,18 @@ namespace eval textblock {
} }
} }
dict for {col coldef} $o_columndefs { #safe jumptable test
#dict for {col coldef} $o_columndefs {}
tcl::dict::for {col coldef} $o_columndefs {
set row [list $col] set row [list $col]
dict for {property val} $coldef { #safe jumptable test
#dict for {property val} $coldef {}
tcl::dict::for {property val} $coldef {
switch -- $property { switch -- $property {
-ansireset {continue} -ansireset {continue}
-headers { -headers {
set htable [dict get $col_header_tables $col] set htable [dict get $col_header_tables $col]
dict for {innercol maxw} $max_widths { tcl::dict::for {innercol maxw} $max_widths {
$htable configure_column $innercol -minwidth $maxw -blockalign left $htable configure_column $innercol -minwidth $maxw -blockalign left
} }
lappend row [$htable print] lappend row [$htable print]
@ -2481,7 +2502,7 @@ namespace eval textblock {
} }
puts stdout "columnstates: $o_columnstates" puts stdout "columnstates: $o_columnstates"
puts stdout "headerstates: $o_headerstates" puts stdout "headerstates: $o_headerstates"
dict for {k coldef} $o_columndefs { tcl::dict::for {k coldef} $o_columndefs {
if {[dict exists $o_columndata $k]} { if {[dict exists $o_columndata $k]} {
set headerlist [dict get $coldef -headers] set headerlist [dict get $coldef -headers]
set coldata [dict get $o_columndata $k] set coldata [dict get $o_columndata $k]
@ -2732,7 +2753,7 @@ namespace eval textblock {
-cached 1\ -cached 1\
] ]
#-colspan is relevant to header/footer data only #-colspan is relevant to header/footer data only
dict for {k v} $args { foreach {k v} $args {
switch -- $k { switch -- $k {
-headers - -footers - -colspan - -data - -cached { -headers - -footers - -colspan - -data - -cached {
dict set opts $k $v dict set opts $k $v
@ -2761,7 +2782,7 @@ namespace eval textblock {
set colheaders [dict get $o_columndefs $cidx -headers] set colheaders [dict get $o_columndefs $cidx -headers]
set all_colspans_by_header [my header_colspans] set all_colspans_by_header [my header_colspans]
set hlist [list] set hlist [list]
dict for {hrow cspans} $all_colspans_by_header { tcl::dict::for {hrow cspans} $all_colspans_by_header {
set s [lindex $cspans $cidx] set s [lindex $cspans $cidx]
#todo - map 'all' entries to a number? #todo - map 'all' entries to a number?
#we should build a version of header_colspans that does this #we should build a version of header_colspans that does this
@ -2862,7 +2883,7 @@ namespace eval textblock {
set colspace_added [dict create] set colspace_added [dict create]
set ordered_spans [dict create] set ordered_spans [dict create]
dict for {col spandata} [my spangroups] { tcl::dict::for {col spandata} [my spangroups] {
set dwidth [my column_datawidth $col -data 1 -headers 0 -footers 0 -cached 1] set dwidth [my column_datawidth $col -data 1 -headers 0 -footers 0 -cached 1]
set minwidth [dict get $o_columndefs $col -minwidth] set minwidth [dict get $o_columndefs $col -minwidth]
set maxwidth [dict get $o_columndefs $col -maxwidth] set maxwidth [dict get $o_columndefs $col -maxwidth]
@ -2893,7 +2914,9 @@ namespace eval textblock {
} }
} }
dict for {spanid spandata} $ordered_spans { #safe jumptable test
#dict for {spanid spandata} $ordered_spans {}
tcl::dict::for {spanid spandata} $ordered_spans {
lassign [split $spanid ,] startcol hrow lassign [split $spanid ,] startcol hrow
set memcols [dict get $spandata membercols] ;#dict with col and initial width - we ignore initial width, it's there in case we want to allocate space based on initial data width ratios set memcols [dict get $spandata membercols] ;#dict with col and initial width - we ignore initial width, it's there in case we want to allocate space based on initial data width ratios
set colids [dict keys $memcols] set colids [dict keys $memcols]
@ -3045,7 +3068,7 @@ namespace eval textblock {
set spaninfo [list] set spaninfo [list]
set numcols [dict size $o_columndefs] set numcols [dict size $o_columndefs]
#note that 'all' can occur in positions other than column 0 - meaning all remaining #note that 'all' can occur in positions other than column 0 - meaning all remaining
dict for {hrow rawspans} $spans_by_header { tcl::dict::for {hrow rawspans} $spans_by_header {
set thiscol_spanval [lindex $rawspans $cidx] set thiscol_spanval [lindex $rawspans $cidx]
if {$thiscol_spanval eq "all" || $thiscol_spanval > 0} { if {$thiscol_spanval eq "all" || $thiscol_spanval > 0} {
set spanstartcol $cidx ;#own column set spanstartcol $cidx ;#own column
@ -3080,7 +3103,7 @@ namespace eval textblock {
set opts [dict create\ set opts [dict create\
-algorithm $o_column_width_algorithm\ -algorithm $o_column_width_algorithm\
] ]
dict for {k v} $args { foreach {k v} $args {
switch -- $k { switch -- $k {
-algorithm { -algorithm {
dict set opts $k $v dict set opts $k $v
@ -3317,7 +3340,7 @@ namespace eval textblock {
-compact 1\ -compact 1\
-forcecolour 0\ -forcecolour 0\
] ]
dict for {k v} $args { foreach {k v} $args {
switch -- $k { switch -- $k {
-return - -compact - -forcecolour { -return - -compact - -forcecolour {
dict set opts $k $v dict set opts $k $v
@ -4131,7 +4154,7 @@ namespace eval textblock {
#} #}
#2 - the more useful one? #2 - the more useful one?
dict for {b bdict} $blockinfo { tcl::dict::for {b bdict} $blockinfo {
lappend r0 [dict get $blockinfo $b left0] [dict get $blockinfo $b right0] lappend r0 [dict get $blockinfo $b left0] [dict get $blockinfo $b right0]
lappend r1 [dict get $blockinfo $b left1] [dict get $blockinfo $b right1] lappend r1 [dict get $blockinfo $b left1] [dict get $blockinfo $b right1]
lappend r2 [dict get $blockinfo $b left2] [dict get $blockinfo $b right2] lappend r2 [dict get $blockinfo $b left2] [dict get $blockinfo $b right2]
@ -4145,7 +4168,7 @@ namespace eval textblock {
set t [textblock::list_as_table [expr {1 + (2 * [dict size $blockinfo])}] $rows -return object] set t [textblock::list_as_table [expr {1 + (2 * [dict size $blockinfo])}] $rows -return object]
$t configure_column 0 -headers [list [dict get $opts -description] "within_ansi"] -ansibase $column_ansi $t configure_column 0 -headers [list [dict get $opts -description] "within_ansi"] -ansibase $column_ansi
set col 1 set col 1
dict for {b bdict} $blockinfo { tcl::dict::for {b bdict} $blockinfo {
if {[dict exists $bheaders $b]} { if {[dict exists $bheaders $b]} {
set hdr [dict get $bheaders $b] set hdr [dict get $bheaders $b]
} else { } else {
@ -5651,7 +5674,7 @@ namespace eval textblock {
set termwidth 80 set termwidth 80
} }
dict for {k v} $frame_cache { tcl::dict::for {k v} $frame_cache {
lassign $v _f frame _used used lassign $v _f frame _used used
#set fwidth [textblock::widthtopline $frame] #set fwidth [textblock::widthtopline $frame]
#review - are cached frames uniform width lines? #review - are cached frames uniform width lines?
@ -5819,7 +5842,9 @@ namespace eval textblock {
error "frame option -joins '$opt_joins' must contain only values from the set: left,right,up,down with targets heavy,light,ascii,altg,arc,double,block,block1,custom e.g down-light" error "frame option -joins '$opt_joins' must contain only values from the set: left,right,up,down with targets heavy,light,ascii,altg,arc,double,block,block1,custom e.g down-light"
} }
set is_boxmap_ok 1 set is_boxmap_ok 1
dict for {boxelement subst} $opt_boxmap { #safe jumptable test
#dict for {boxelement subst} $opt_boxmap {}
tcl::dict::for {boxelement subst} $opt_boxmap {
switch -- $boxelement { switch -- $boxelement {
hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {} hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {}
default { default {
@ -6013,7 +6038,7 @@ namespace eval textblock {
#puts "---> $opt_boxmap" #puts "---> $opt_boxmap"
#review - we handle double-wide in custom frames - what about for boxmaps? #review - we handle double-wide in custom frames - what about for boxmaps?
dict for {boxelement sub} $opt_boxmap { tcl::dict::for {boxelement sub} $opt_boxmap {
if {$boxelement eq "vl"} { if {$boxelement eq "vl"} {
set vll $sub set vll $sub
set vlr $sub set vlr $sub

Loading…
Cancel
Save