if false {
wdb on page tcl9var, we discussed unification of namespaces of commands and variables. Main argument against it was broken compatibility to current variables.
Here a try to use it with source-compatibility to Tcl 8.x:
} package require Tcl 8.5 # because of dict namespace eval tcl9var { variable tcl9val {} namespace export tcl9var } proc tcl9var::handleVar {name {cmd get} args} { variable tcl9val if {$cmd in {set unset}} then { dict $cmd tcl9val $name {*}$args } else { dict $cmd $tcl9val $name {*}$args } } proc tcl9var::tcl9var {name args} { variable tcl9val set ns [uplevel namespace current] if {$ns eq "::"} then { set n ::$name } else { set n ${ns}::$name } dict set tcl9val $n {*}$args uplevel 1 [list proc $name args { ::tcl9var::handleVar [ namespace origin [lindex [info level 0] 0] ] {*}$args }] $name } namespace import tcl9var::tcl9var if false {
As an example, we use a simple variable:
% tcl9var a apple apple % puts "my [a] is red" my apple is red % a set [a]tree ::a appletree % puts "my [a] is high" my appletree is high %
In one way we can build a variable as a dictionary:
% tcl9var fruit a apple a apple % tcl9var fruit o orange a apple o orange % fruit a apple o orange % fruit get a apple o orange % fruit get a apple % fruit get o orange % fruit set o mango ::a appletree ::fruit {a apple o mango} % fruit get o mango
Care must be taken if the "raw value" of a variable has an odd number of elements -- then dict usage fails.
With usage as dict, we can introduce a special variable $ internally built as dictionary as follows:
% tcl9var $ start 0 start 0 % tcl9var $ end 10 start 0 end 10 % $ get start 0 % $ get end 10 %
Now, the syntax $abc shall shall be expanded by proc expand$ to the appropriate call [$ get abc].
With this expansion of cmd lines, you can make things like $hello, or $hello(world), or $hello(world)(of_tcl_programming), i.e. true multi-dimensional arrays.
} proc expand$ str { if {![regexp {[$]([[:alnum:]_]+)((?:[(].*[)])*)} $str m v b]} then { return $str } set matchStart [string first $m $str] set matchEnd $matchStart incr matchEnd [string length $m] incr matchStart -1 append result [string range $str 0 $matchStart]\ "\[$ get $v" foreach x [split [string trim [string map {( ""} $b] )] )] { append result " [list $x]" } append result \]\ [expand$ [string range $str $matchEnd end]] } if false {
Example:
% expand$ {This $a stems from $your(garden)} This [$ get a] stems from [$ get your garden] % % expand$ {test call: $a(b)(c)(d)} test call: [$ get a b c d] %
An appropriate procedure set can be defined as needed by backwards compatibility. See tcl9set below.
} proc tcl9set {name args} { regexp {^([^(]+)((?:[(].*[)])*)} $name - names parens foreach {- sub} [regexp -inline -all {[(]([^)]+)[)]} $parens] { append names " [list $sub]" } if {[llength $args] == 0} then { uplevel "$ get $names" } elseif {[llength $args] == 1} then { if {[uplevel info command $] eq ""} then { uplevel "tcl9var $ [list $name] {}" } uplevel "$ set $names [list {*}$args]" uplevel "$ get $names" } else { return -code error [list tcl9set requires 1 or 2 arguments!] } } if false {
Example:
% tcl9set fruit(a) apple apple % tcl9set fruit(l) lemon lemon % tcl9set fruit(l) lemon % tcl9set fruit a apple l lemon %
Iterator foreach shall make use of $ as well as command dict with as well as argument assignment in procedures. So no unwanted collision if a variable name equals command name.
Consequences: syntax with $ works as usual.
Semantics of dict provided by variable methods. Shorter source code.
Unsolved problems: as $ is a procedure here, it cannot (yet) be local to a proc or apply closure. Perhaps subject for a TIP? (I'm not sure.)
}