CMcC 4Jun2010: An attempt to derive a weakly-Tcl like language using unknown to exploit Tcl's incompletely recursive definition to interpret the 0th (command position) of each command as either a 'section' or a 'variable'-within-a-section, permitting an .ini like configuration structure with almost-full Tcl syntax.
This example uses three passes of evaluation: subst-for-commands, subst-for-vars and eval-of resultant form. It does so because there is currently no other way to distinguish command-application from the 0th position, and from substitution within word (per Dodekalogue 2.)
This three-pass interpretation, as well as being less efficient than a single pass of eval where unknown was able to make the distinction through introspection, also precludes a variable from referring to other variables within its section (although access to previously defined sections is permitted using namespace-form.)
# Config.tcl - support tcl-like config files
if {[info exists argv0] && ($argv0 eq [info script])} {
lappend auto_path ~/Desktop/Work/Wub/ [file dirname [info script]]
}
if {[catch {package require Debug}]} {
#proc Debug.config {args} {}
proc Debug.config {args} {puts stderr HTTP@[uplevel subst $args]}
} else {
Debug define config 10
}
#package require OO
#package require Dict
package provide Config 1.0
oo::class create Config {
method dict {} {
set result [configI eval ::_O::toDict]
}
method eval {config} {
configI eval ::_O::namespace unknown ::_O::sunknown
configI eval ::_O::namespace eval ::_C [list $config]
return [my dict]
}
constructor {args} {
Debug.config {Creating Config [self] $args}
if {[llength $args]%2} {
set cf [lindex $args end]
set args [lrange $args 0 end-1]
dict set args config $cf
}
variable config ""
variable {*}$args
catch {set args [dict merge [Site var? Config] $args]} ;# allow .ini file to modify defaults
next? {*}$args
variable interp [interp create configI]
configI eval {
namespace eval ::_O {}
proc ::get {section var} {
return [set ::_C::${section}::$var]
}
proc ::toDict {} {
set result {}
foreach ns [namespace children ::_C] {
dict set result [namespace tail $ns] {}
foreach v [info vars ::${ns}::*] {
dict set result [namespace tail $ns] [namespace tail $v] [set $v]
}
}
return $result
}
# construct the variable unknown
proc ::vunknown {ns args} {
lassign $args var val
set ::_C::${ns}::$var $val
}
# construct the section unknown
proc ::sunknown {args} {
set args [lassign $args ns script]
namespace eval ::_C::$ns {} ;# create the section namespace
namespace eval ::_C [list ::_O::namespace unknown [list ::_O::vunknown $ns]]
set script [namespace eval ::_C::$ns [list ::_O::subst -nocommands $script]]
set script [subst -novariables $script] ;# first run the script in ::_O for []
namespace eval ::_C $script
namespace eval ::_C [list ::_O::namespace unknown [list ::_O::sunknown]]
}
foreach cmd [info commands ::*] {
if {$cmd ni {::rename ::if}} {
::rename $cmd ::_O::$cmd
}
}
::rename ::if ::_O::if
::rename ::rename ::_O::rename
}
my eval $config
}
}
if {[info exists argv0] && ($argv0 eq [info script])} {
Debug on config 10
set cf [Config new {
section {
var val ;# this is a variable
var1 val1
v1 2
v2 [expr {$v1 ^ 2}]
}
sect1 {
v1 [expr {[get section v1] ^ 10}]
}
}]
puts stderr "RESULT: [$cf dict]"
}