Richard Suchenwirth 2006-04-26 - A question on comp.lang.tcl brought me to experiment with code that rewrites itself. The idea is that settings are not saved in a separate resource file, but in the script itself, as setting a global variable with the desired value. So the next time the script is run, the previously saved setting applies. Here's what I did:
proc save {filename varlist} { set f [open $filename] set data [read $f] close $f set lines {} foreach line [split $data \n] { foreach var $varlist { if [regexp "^ *set $var " $line] { set line [list set $var [set ::$var]] } } lappend lines $line } set f [open $filename w] puts $f [join $lines \n] close $f }
#-- Now for a testing demo:
package require Tk
#-- We will test with this variable:
set foo hello label .1 -text foo: entry .2 -textvar foo button .3 -text Save -command [list save [info script] foo] eval pack [winfo children .] -side left
#-- Invaluable little helper for rapid restart:
bind . <Escape> {exec wish $argv0 &; exit}
lexfiend 2006-04-27 - Here's a modification that places all the state variables in a single section, so that save doesn't inadvertantly overwrite other sets of the state variables and thereby change the program logic. However, note that this forces you to pre-initialize any namespaces on which the state variables depend.
# Create empty namespaces first namespace eval testns {} ### VARIABLE SAVE SECTION set test1 "I am here" set testns::test2 "Here as well" ### VARIABLE SAVE SECTION proc save {filename varlist} { set f [open $filename] set data [read $f] close $f set lines {} set inSaveSection 0 foreach line [split $data \n] { if [regexp "^ *#+ *VARIABLE SAVE SECTION" $line] { if {!$inSaveSection} { # Start of save section lappend lines "### VARIABLE SAVE SECTION" foreach var $varlist { lappend lines [list set $var [set ::$var]] } set inSaveSection 1 } else { # End of save section lappend lines "### VARIABLE SAVE SECTION" set inSaveSection 0 } } elseif {$inSaveSection} { # Somewhere in save section - do nothing } else { lappend lines $line } } set f [open $filename w] puts $f [join $lines \n] close $f } set testns::test2 "Gone forever" set test1 "Not here now" save [info script] {test1 testns::test2}
EMJ - 2006-04-26 - Aaaaargh! Self-modifying code, the slippery slope to certain unmaintainability! That of course is a remark from the distant past, when clever people wrote very fast but generally incomprehensible self-modifying assembly language. The warning is still valid, however - be careful!
For the stated purpose, it's not actually too bad, but I think I would use an alias or something rather than just set, in order to make the regex more discriminating. I guess it's simply the reverse of what tkbiff does, writing most of its code to its config file!
IL - This kind of stuff always makes me think of DNA, the assembly of the organic life. I suppose that makes tRNA the compiler and proteins the executable... heh.