From 54e3cf4daf3c1a9f5853be55834f3510ed9dbcca Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Tue, 3 Oct 2023 22:53:32 +1100 Subject: [PATCH] refactor and move ns commands to punk::ns - namespace path-glob search improvements --- src/bootsupport/modules/punk/mix-0.2.tm | 7 +- src/modules/punk-0.1.tm | 1394 +---------------- .../mix/commandset/loadedlib-999999.0a1.0.tm | 8 +- src/modules/punk/ns-999999.0a1.0.tm | 1307 ++++++++++++++++ src/modules/punk/ns-buildversion.txt | 3 + src/modules/punk/repl-0.1.tm | 13 +- 6 files changed, 1342 insertions(+), 1390 deletions(-) create mode 100644 src/modules/punk/ns-999999.0a1.0.tm create mode 100644 src/modules/punk/ns-buildversion.txt diff --git a/src/bootsupport/modules/punk/mix-0.2.tm b/src/bootsupport/modules/punk/mix-0.2.tm index 837f8690..10d9ca20 100644 --- a/src/bootsupport/modules/punk/mix-0.2.tm +++ b/src/bootsupport/modules/punk/mix-0.2.tm @@ -5,6 +5,7 @@ package provide punk::mix [namespace eval punk::mix { }] package require punk::repo +package rqeuire punk::ns namespace eval punk::mix::cli { namespace ensemble create @@ -509,14 +510,14 @@ namespace eval punk::mix::cli { return false } - set moduleprefix [punk::nsprefix $libfound] + set moduleprefix [punk::ns::nsprefix $libfound] if {[string length $moduleprefix]} { - set moduleprefix_parts [punk::nsparts $moduleprefix] + set moduleprefix_parts [punk::ns::nsparts $moduleprefix] set relative_path [file join {*}$moduleprefix_parts] } else { set relative_path "" } - set pkgtail [punk::nstail $libfound] + set pkgtail [punk::ns::nstail $libfound] set target_path [file join $modulefolder_path $relative_path ${pkgtail}-${ver}.tm] if {$opt_askme} { diff --git a/src/modules/punk-0.1.tm b/src/modules/punk-0.1.tm index 0d7cba23..425a30ca 100644 --- a/src/modules/punk-0.1.tm +++ b/src/modules/punk-0.1.tm @@ -73,6 +73,7 @@ namespace eval ::repl { variable running 0 } package require punk::config +package require punk::ns package require punk::winpath ;# for windows paths - but has functions that can be called on unix systems package require punk::repo package require punk::du @@ -124,7 +125,6 @@ namespace eval punk { variable last_run_display [list] variable colour_disabled 0 - variable ns_current "::" #variable re_headvar1 {([a-zA-Z:@.(),]+?)(?![^(]*\))(,.*)*$} @@ -4686,6 +4686,7 @@ namespace eval punk { } return [list stdout [dict get $running_config color_stdout] stderr [dict get $running_config color_stderr]] } + #useful for aliases e.g treemore -> xmore tree proc xmore {args} { if {[llength $args]} { @@ -5024,7 +5025,7 @@ namespace eval punk { } #------------------------------------------------------------------- - namespace export help aliases alias nsjoin nsprefix dirfiles dirfiles_dict exitcode % pipedata pipecase pipeline pipematch pipeswitch pipeswitchc pipecase linelist linesort inspect list_as_lines is_list_all_in_list is_list_all_ni_list val treemore + namespace export help aliases alias dirfiles dirfiles_dict exitcode % pipedata pipecase pipeline pipematch pipeswitch pipeswitchc pipecase linelist linesort inspect list_as_lines is_list_all_in_list is_list_all_ni_list val treemore #namespace ensemble create @@ -5034,185 +5035,6 @@ namespace eval punk { } - #todo - package up as navns - proc corp {path} { - #thanks to Richard Suchenwirth for the original - wiki.tcl-lang.org/page/corp - #Note: modified here to support aliases and relative/absolute name (with respect to namespace .ie ::name vs name) - if {[info exists ::auto_index($path)]} { - set body "# $::auto_index($path)\n" - } else { - set body "" - } - - if {[string match ::* $path]} { - set targetns [nsprefix $path] - set name [nstail $path] - } else { - set thispath [uplevel 1 [list ::nsthis $path]] - set targetns [nsprefix $thispath] - set name [nstail $thispath] - } - #puts stderr "corp upns:$upns" - - #set name [string trim $name :] - #set origin [namespace origin ${upns}::$name] - set origin [punk::nseval $targetns [list ::namespace origin $name]] - - #An renamed alias may exist that is the same name as a proc that was created later.. so we must check for the proc before looking into aliases! - if {$origin ni [info procs $origin]} { - - #It seems an interp alias of "::x"" behaves the same as "x" - #But we can't create both at the same time - and they have to be queried by the exact name. - #So we query for alias with and without leading :: - set alias_qualified [interp alias {} [string trim $origin :]] - set alias_unqualified [interp alias {} $origin] - if {[string length $alias_qualified] && [string length $alias_unqualified]} { - #our assumptions are wrong.. change in tcl version? - puts stderr "corp: Found alias for unqualified name:'[string trim $origin :]' and qualified name: '$origin' - unexpected (assumed impossible as at Tcl 8.6)" - if {$alias_qualified ne $alias_unqalified} { - - } else { - set alias $alias_unqualified - } - } else { - set alias ${alias_qualified}${alias_unqualified} ;#concatenate - as at least one should be empty - } - - if {[string length $alias]} { - #todo - consider following alias-chain to ultimate proc? - #it can always be manually done with: - #.= corp $name |/1> corp |/1> corp .. - #depending on number of aliases in the chain - return [list alias {*}$alias] - } - } - if {[nsprefix $origin] ne [nsprefix [nsjoin ${targetns} $name]]} { - append body "# namespace origin $origin" \n - } - - append body [info body $origin] - set argl {} - foreach a [info args $origin] { - if {[info default $origin $a def]} { - lappend a $def - } - lappend argl $a - } - list proc [nsjoin ${targetns} $name] $argl $body - } - - proc nsjoin {prefix name} { - if {[string match ::* $name]} { - if {"$prefix" ne ""} { - error "nsjoin: won't join non-empty prefix to absolute namespace path '$name'" - } - return $name - } - if {"$prefix" eq "::"} { - return ::$name - } - #if {"$name" eq ""} { - # return $prefix - #} - #nsjoin ::x::y "" should return ::x::y:: - this is the correct fully qualified form used to call a command that is the empty string - return ${prefix}::$name - } - proc nsjoinall {prefix args} { - #if {![llength $args]} { - # error "usage: nsjoinall prefix relativens \[relativens ...\]" - #} - set segments [list $prefix] - foreach sub $args { - if {[string match ::* $sub]} { - if {[string length [concat {*}$segments]]} { - error "nsjoin: won't join non-empty namespace prefix to absolute namespace path '$sub'" - } - } - lappend segments $sub - } - set nonempty_segments [list] - foreach s $segments { - if {[string length $s]} { - lappend nonempty_segments $s - } - } - if {$prefix eq "::"} { - return ::[join [lrange $nonempty_segments 1 end] ::] - } - return [join $nonempty_segments ::] - } - proc nsprefix {{nspath ""}} { - #normalize the common case of :::: - set nspath [string map [list :::: ::] $nspath] - set rawprefix [string range $nspath 0 end-[string length [punk::nstail $nspath]]] - if {$rawprefix eq "::"} { - return $rawprefix - } else { - if {[string match *:: $rawprefix]} { - return [string range $rawprefix 0 end-2] - } else { - return $rawprefix - } - #return [string trimright $rawprefix :] - } - } - - #namespace tail which handles :::cmd ::x:::y ::x:::/y etc - #todo - raise error for unexpected sequences such as :::: or more than 2 colons together. - proc nstail {nspath args} { - #normalize the common case of :::: - set nspath [string map [list :::: ::] $nspath] - set mapped [string map [list :: \u0FFF] $nspath] - set parts [split $mapped \u0FFF] - - set defaults [list -strict 0] - set opts [dict merge $defaults $args] - set strict [dict get $opts -strict] - - if {$strict} { - foreach p $parts { - if {[string match :* $p]} { - error "nstail unpaired colon ':' in $nspath" - } - } - } - - #e.g ::x::y:::z should return ":z" - return [lindex $parts end] - } - - #return a list of namespace segments - always with leading empty string for fully qualified namespace (ie for ::x) - #'supports' to some extent unreasonable namespaces /commands such as x: ::x: ::x:::y - #Can be used to either support use of such namespaces/commands - or as part of validation to disallow them - #as opposed to silent behaviour of Tcl namespace commands which don't handle them consistently (for tcl 8.x anyway Review tcl 9) - #Note that for ::x:: the trailing :: cannot represent a trailing namespace part being an empty string - #This is because Tcl's 'namespace eval "" ""' reports 'only global namespace can have empty name' - #NOTE tcl allows creating ambiguous namespaces. e.g ::punk:::etc:::blah - # is this :: punk :etc :blah or :: punk :etc: blah - #clearly leading/trailing colons in namespaces and commands are just a bad idea. - #nsparts will prefer leading colon (ie greedy on ::) - #This is important to support leading colon commands such as :/ - # ie ::punk:::jjj:::etc -> :: punk :jjj :etc - proc nsparts {nspath} { - set nspath [string map [list :::: ::] $nspath] - set mapped [string map [list :: \u0FFF] $nspath] - set parts [split $mapped \u0FFF] - if {[lindex $parts end] eq ""} { - - } - return $parts - } - - #review ??? - proc ns_relative_to_location {name} { - if {[string match ::* $name]} { - error "ns_relative_to_location accepts a relative namespace name only ie one without leading ::" - } - - } - proc ns_absolute_to_location {name} { - - } #tilde #These aliases work fine for interactive use - but the result is always a string int-rep @@ -5232,964 +5054,6 @@ namespace eval punk { interp alias {} ~ {} punk::~ - - interp alias {} nsjoin {} punk::nsjoin - interp alias {} nsprefix {} punk::nsprefix - interp alias {} nstail {} punk::nstail - interp alias {} nsparts {} punk::nsparts - interp alias {} nstree {} punk::nstree - - #tcl 8.x has creative writing var weirdness.. tcl 9 is likely to differ - proc nsvars {{nsglob "*"}} { - set ns_absolute [uplevel 1 [list punk::nspath_here_absolute $nsglob]] - #set commandns [uplevel 1 [list namespace current]] - - set searchns [nsprefix $ns_absolute] ;#the ns within which we want to searchns - set searchall [nsjoin $searchns *] ;#will correctly form ::* or ::childns::* - - set rawresult [info vars $searchall] ;#will not find vars in ::x:::y etc. - seems to be impossible in tcl 8.x - set matched_fullpath [list] - foreach r $rawresult { - lappend matched_fullpath [nstail $r] - } - - set location [nsprefix $ns_absolute] - set tailmatch [nstail $ns_absolute] - set raw_matched_in_ns [punk::nseval $location [list ::info vars $tailmatch]] - #NOTE: tcl <9 will read vars from global namespace - so we are only checking the intersection here - #(this is due to info vars ::etc:::blah failing to handle additional colon) - set matched_in_ns [list] - set result [list] - foreach r $raw_matched_in_ns { - set m [nstail $r] - lappend matched_in_ns $m - if {$m in $matched_fullpath} { - lappend result $m - } - } - - - return [list_as_lines [lsort $result]] - #.= lsort $result |> list_as_lines - } - interp alias {} nsvars {} punk::nsvars - interp alias {} nsorigin {} apply {ns {namespace origin [uplevel 1 nsthis $ns]} ::} - - #todo - walk up each ns - testing for possibly weirdly named namespaces - proc nsexists {nspath} { - - } - - #create possibly nested namespace structure - but only if not already existant - proc n/new {args} { - variable ns_current - if {![llength $args]} { - error "usage: :/new \[ ...\]" - } - set a1 [lindex $args 0] - set is_absolute [string match ::* $a1] - if {$is_absolute} { - set nspath [nsjoinall {*}$args] - } else { - if {[string match :* $a1]} { - puts stderr "n/new WARNING namespace with leading colon '$a1' is likely to have unexpected results" - } - set nspath [nsjoinall $ns_current {*}$args] - } - - set ns_exists [punk::nseval [punk::nsprefix $nspath] [list ::namespace exists [punk::nstail $nspath] ]] - - if {$ns_exists} { - error "Namespace $nspath already exists" - } - #namespace eval [nsprefix $nspath] [list namespace eval [nstail $nspath] {}] - punk::nseval [nsprefix $nspath] [list ::namespace eval [nstail $nspath] {}] - n/ $nspath - } - - - #nn/ ::/ nsup/ - back up one namespace level - proc nsup/ {v args} { - variable ns_current - if {$ns_current eq "::"} { - puts stderr "Already at global namespace '::'" - } else { - set out "" - set nsq [nsprefix $ns_current] - if {$v eq "/"} { - set out [punk::get_nslist -match [nsjoin $nsq *] -types [list children]] - } else { - set out [punk::get_nslist -match [nsjoin $nsq *] -types [list all]] - } - #set out [punk::nslist [nsjoin $nsq *]] - set ns_current $nsq - append out "\n$ns_current" - return $out - } - } - - - - #experimental - #is there ever any difference to {namespace current}? - #interp alias {} nsthis {} .= .= namespace code {namespace current} |> .=* <0/#| - #interp alias {} nsthis {} namespace current - - interp alias {} nsthis {} punk::nspath_here_absolute - proc nspath_here_absolute {{nspath "\uFFFF"}} { - set path_is_absolute [expr {[string match ::* $nspath]}] - if {$path_is_absolute} { - return $nspath - } - set ns_caller [uplevel 1 {::namespace current}] ;#must qualify. Needs to be called from arbitrary namespaces where 'namespace' command may exist (e.g tdom::schema) - if {$nspath eq "\uFFFF"} { - return $ns_caller - } - #nsjoin will join nscaller with empty nspath to form nscaller:: - which is correct way to represent command named with empty string - return [punk::nsjoin $ns_caller $nspath] - } - - proc nspath_to_absolute {nspath base} { - set path_is_absolute [expr {[string match ::* $nspath]}] - if {$path_is_absolute} { - return $nspath - } - if {![string length $nspath]} { - return $base - } - return [punk::nsjoin $base $nspath] - } - - #cli command - impure - relies on caller/ns_current - proc nslist_dict {{glob "*"}} { - set ns_absolute [uplevel 1 [list punk::nspath_here_absolute $glob]] - return [get_ns_dicts $ns_absolute] - } - proc nslist_dict1 {{glob "*"}} { - variable ns_current ;#keep fully qualified ie :: or ::etc - set ns_caller [uplevel 1 {namespace current}] - puts "nslist_dict ns_caller: $ns_caller (ns_current: $ns_current)" - - set glob_is_absolute [expr {[string match ::* $glob]}] - set globquals [namespace qualifiers $glob] - if {[string length $globquals]} { - if {$glob_is_absolute} { - set fqpath $globquals - } else { - set fqpath ${ns_caller}::${globquals} - } - } else { - if {$glob_is_absolute} { - set fqpath :: - } else { - set fqpath $ns_caller - } - } - #puts stderr ">>fqpath $fqpath" - set globtail [nstail $glob] - if {[hasglobs $globtail]} { - set location $fqpath - set glob $globtail - } else { - - if {$fqpath eq "::"} { - set location ::${globtail} - } else { - if {[string length $globtail]} { - set location ${fqpath}::${globtail} - } else { - set location ${fqpath} - } - } - set glob * - } - return [get_nslist_dict ${location}::$glob] - } - - #recursive nseval - for introspection of weird namespace trees - #approx 10x slower than normal namespace eval - but still only a few microseconds.. fine for repl introspection - proc nseval_script {location} { - set parts [nsparts $location] - if {[lindex $parts 0] eq ""} { - lset parts 0 :: - } - if {[lindex $parts end] eq ""} { - set parts [lrange $parts 0 end-1] - } - - set body "" - set i 0 - set tails [lrepeat [llength $parts] ""] - foreach ns $parts { - set cmdlist [list ::namespace eval $ns] - set t "" - if {$i > 0} { - append body " " - } - append body $cmdlist - if {$i == ([llength $parts] -1)} { - append body "