# run.tcl
# Copyright 2001 by Larry Smith
# Wild Open Source, Inc
# For license terms GPL
#
# Replacement for "source" but expands macros
# and allows a preprocessing pass for commands
# delimited by <<< and >>>.
# It even provides for "real" comments #...<eol>
# that are removed before processing.
proc run { filename { macrolist "" } } {
if { "$macrolist" != "" } {
upvar $macrolist macros
}
if [catch { set f [ open $filename r ] } err ] { return -code $err }
set src [ read $f ]
foreach key [array names macros] {
regsub -all -linestop $key $src $macros($key) src
}
set exp ""
while 1 {
if [regexp "(.*)(<<<.*>>>)(.*)" $src -> head exp tail] {
regsub <<< $exp "" exp
regsub >>> $exp "" exp
set result [ uplevel eval $exp ]
set src "$head$result$tail"
} else {
break
}
}
uplevel eval $src
}
if 0 {
here's an example preprocessor:
}
# source run.tcl
proc compute { args } {
set exp ""
set id ""
regsub "''" [ string trim $args ] "@@@" args
while 1 {
regexp "(\[^a-zA-Z_'\]*)(\[a-zA-Z0-9_'\]*)(.*)" $args -> head id tail
if ![ string length $id ] {
set exp "$exp$head"
break
}
set dollar ""
if ![ string equal [ string index $id 0 ] "'" ] {
if ![ string equal [info commands $id] "" ] {
set id "\[ $id"
regexp {[^\(]*\(([^\)]*)\)(.*)} $tail -> params tail
set tail " $params \]$tail"
} else { set dollar "\$" }
}
append exp "$head$dollar$id"
set args $tail
}
regsub -all "'" $exp "\"" exp
set map "@@@ ' and && or || not ! <> != true 1 false 0 on 1 off 0 yes 1 no 0 pi 3.1415926535"
foreach { from to } $map {
regsub $from $exp $to exp
}
set exp [ uplevel subst -novariable \{$exp\} ]
return "\{ $exp \}"
}
set xlate(IF) "if <<< compute "
set xlate(THEN) ">>> \{"
set xlate(ELSE) "\} else \{"
set xlate(ELSIF) "\} elseif \[ compute "
set xlate(END) "\}"
set xlate(WHILE) "while \{ \[ compute "
set xlate(DO) "\] \} \{"
set xlate(#.*\\n) "\\n"
if 0 {
Now to invoke a file using the new syntax just use:
}
run foo.tcl xlate
if 0 {
Here's an example foo.tcl:
}
# This is a real comment
set x 1
IF x <> 1 THEN
puts "x is NOT 1"
ELSE
puts "x IS 1"
END
if 0 {
This results in:
}
x IS 1
AMG: Nifty.Just a note... it's not strictly necessary to \-quote the braces inside your xlate(*) strings; they're already quoted by being between double quotes. Left bracket, on the other hand, requires a backslash when not quoted by braces.Lars H: OTOH, it's typically still a good idea to \-escape braces, especially when they as here are heavily unbalanced. It's quite similar to the why can I not place unmatched braces in Tcl comments issue.An alternative approach for macro processing of Tcl code is to replace proc and process each body separately. That is what Sugar does.

