Well, instead of using bash or python, I suggest using Tcl itself to do that. I assume that you have Tcl 8.5 or higher.
The basic idea is to use an interpreter which ignores most commands except proc definitions.
set ip [interp create]
interp hide $ip source
interp eval $ip {
rename ::tcl::info::frame tcl:info:frame
}
interp hide $ip tcl:info:frame
interp eval $ip {
namespace delete ::
}
proc NOP args {}
interp alias $ip unknown {} NOP
Now we need to handle some commands in a special way:
proc qualify-cmd {name} {
return $name
}
proc proc-alias {name arg body} {
define-cmd [qualify-cmd $name]
}
interp alias $ip proc {} proc-alias
proc define-cmd {name} {
global procs
set frameinfo [interp invokehidden $::ip tcl:info:frame 1]
if {[dict get $frameinfo type] eq "source"} {
set file [dict get $frameinfo file]
set line [dict get $frameinfo line]
} else {
set file "<unknown>"
set line 0
}
if {[dict exists $procs $name]} {
set ofile [dict get $procs $name file]
set oline [dict get $procs $name line]
return -code error "proc redefiniton of $name in $file line $line (original $ofile line $oline)"
} else {
dict set procs $name file $file
dict set procs $name line $line
}
}
# Load the old definitions
set procs {}
# Now we have to source changed file in the slave interp
proc checkfile {file} {
global procs
dict for {name val} $procs {
if {[dict get $val file] eq $file} {
dict unset procs $name
}
}
interp invokehidden $ip source foobar.tcl
}
The rest is the hardest work, but I'm not sure if it is needed for you:
- namespaces. You have to handle the
namespace
command and modify qualify-cmd
.
- branches. Someone could define a command inside
if
or while
etc.
- variables. This solution is not bulletproof. If someone uses variables outside of an proc, this will fail. You could try and catch the error, set the variable and try again. (And that's why I want
unknownvar
)