tcl9var compatibility issues

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.

Compatibility issues

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.)

}