set person {
name "John Smith"
age 35
address {
address1 "30 New Road"
address2 "Old Estate"
town "Reading"
county "Berkshire"
}
children {
{
{
name Janet
age 3
}
{
name Joe
age 5
}
}
}
}to be created instead like this: set person [dict config {
set name "John Smith"
set age 35
set address [dict config {
set address1 "30 New Road"
set address2 "Old Estate"
set town "Reading"
set county "Berkshire"
}]
set children [list]
lappend children [dict config {
set name Janet
set age 3
}] [dict config {
set name Joe
set age 5
}]
}]Within the dict config code block, a key/value is only added to the dictionary by setting variables in the code block using set (not ::set).Variables within the dict config code block can't be seen outside of that code block. Also, dict config either creates a new dictionary, or modifies an existing one, as shown in the test code below. The dictionary is updated at the end of the code block.Here's the code:
namespace eval ::DictExtension {
namespace export dict
proc dict {args} {
if {[lindex $args 0] eq "config"} {
if {[llength $args] < 2} {
error "dict config error"
}
::set block [format {
::dict for {configVar configVal} $configVars {
set $configVar $configVal
}
%s
foreach configVar [::dict keys $configVars] {
::dict set configVars $configVar [set $configVar]
}
return $configVars
} [lindex $args end]]
if {[llength $args] == 2} {
return [uplevel [list apply [list {configVars} $block \
::DictExtension] ""]]
} else {
if {![uplevel info exists [lindex $args 1]]} {
uplevel set [lindex $args 1] \{\}
}
if {[llength $args] == 3 || [uplevel ::dict exists \
\$[lrange $args 1 end-1]]} {
::set vars [uplevel ::dict get \$[lrange $args 1 end-1]]
} else {
::set vars ""
}
::set vars [uplevel [list apply [list {configVars} $block \
::DictExtension] \ $vars]]
::dict for {var val} $vars {
uplevel ::dict set [lrange $args 1 end-1] \{$var\} \{$val\}
}
}
} else {
return [uplevel ::dict $args]
}
}
proc set {args} {
if {[llength $args] == 2} {
upvar configVars vars
::dict set vars [lindex $args 0] [lindex $args 1]
}
return [uplevel ::set $args]
}
}
namespace eval test {
namespace import ::DictExtension::*
set person [dict config {
set name "John Smith"
set age 35
set address [dict config {
set address1 "30 New Road"
set address2 "Old Estate"
set town "Reading"
set county "Berkshire"
}]
set children [list]
lappend children [dict config {
set name Janet
set age 3
}] [dict config {
set name Joe
set age 5
}]
}]
puts "initial:\n$person"
puts [dict get $person name]
puts [dict get $person address address1]
dict config person address {
set address1 "New House"
set address2 "Another Estate"
set town "Milton Keynes"
set county "Buckinghamshire"
}
puts "\nfirst update:\n$person"
puts [dict get $person name]
puts [dict get $person address address1]
dict config person {
incr age
lappend children [dict config {
set name Jack
set age 0
}]
}
puts "\nsecond update:\n$person"
puts [dict get $person name]
puts [dict get $person age]
}And here's the output:
initial:
name {John Smith} age 35 address {address1 {30 New Road} address2 {Old Estate} town Reading county Berkshire} children {{name Janet age 3} {name Joe age 5}}
John Smith
30 New Road
first update:
name {John Smith} age 35 address {address1 {New House} address2 {Another Estate} town {Milton Keynes} county Buckinghamshire} children {{name Janet age 3} {name Joe age 5}}
John Smith
New House
second update:
name {John Smith} age 36 address {address1 {New House} address2 {Another Estate} town {Milton Keynes} county Buckinghamshire} children {{name Janet age 3} {name Joe age 5} {name Jack age 0}}
John Smith
36As dict config variable scope is limited to the code block, use uplevel to access variables external to the code block. For example, below uses uplevel 1 to get access to name, and uplevel 2 to get access to location.
set name "John Smith"
set location Berkshire
set person [dict config {
set name [uplevel 1 {set name}]
set age 35
set address [dict config {
set address1 "30 New Road"
set address2 "Old Estate"
set town "Reading"
set county [uplevel 2 {set location}]
}]
...