TMG is a line based macro processor implemented in 22 SLOC. The first character of the line determines how to expand it, in particular:
- @ - its just plain code, like a foreach loop, e.g. @foreach i {1 2 3} {
- $ - just expand any variables using, e.g. $ and I is $i
- ! - expand both variables and commands, e.g. ! \expr sin($i)\
- Anything else is just a literal line
The implementation is:
proc tmg {s} {
set prog {}
set ::_out {}
foreach line [split $s \n] {
if {[regexp {^[@](.*)} $line -> rest]} {
append prog $rest \n
} elseif {[regexp {^[$](.*)} $line -> rest]} {
append prog "append _out \[subst -nocommands -nobackslashes [list $rest]\] \\n" \n
} elseif {[regexp {^[!](.*)} $line -> rest]} {
append prog "append _out \[subst [list $rest]\] \\n" \n
} else {
append prog "append _out [list $line] \\n" \n
}
}
if {[catch [list uplevel #0 $prog] r]} {
puts "$prog failed $r $::errorInfo"
return ""
} else {
return $r
}
}
SafeTCL lets us run code with a limited set of commands so if we combine it TMG we can build code and data in a moderately secure way.
proc safetcl {code {name {}}} {
set si [interp create -safe]
if {$name != ""} {
interp eval $si [list set name $name]
}
foreach c [info commands {[%*=?!]*}] {
interp alias $si $c {} $c
}
foreach c {puts verbose check check_never exit} {
interp alias $si $c {} $c
}
set r [interp eval $si $code]
interp delete $si
return $r
}