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
mangoCare 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 {((?:[(].*[)])*)} $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.)
}