Tcl Template Parser - http://www.magma.com.ni/sw/ttp/
Started from the need to create lots of files from a templates and some variable definitions.
Now it is several things:
Look at the (hopefully soon) comprehensive documentation if you want to use the program.
On this page I want to present some concepts used when coding TTP.
Command-line parsing with active tokens and recursion
The initial command-line parser I used was a simple while loop; it makes use of the "shift" proc, which pops off an element of a list.
set params {} set cl $argv while {[llength $cl]} { set t [pop cl] switch -- $t { -h {usage; exit 0} -d {set ::loglevel [pop cl]} ... default {lappend params $t}}}
This is a very simple-minded parser that requires all options and parameters to be separated by whitespace, consumes all arguments of the command line, and pushes everything that is not defined as option - arguments, hopefully - on a list for later usage. 'pop' is made in such a way that it always pops off an empty list from an empty list.
Since 'while is bad' I wanted to do better and recurred to the intrinsic list processing feature of 'proc':
Here comes the whole parser:
proc options args {if [llength $args] {eval $args}}
It is started with:
eval options $argv
Very self-describing, isn't it? It has no syntax though, so let's make one:
proc -h args {usage; exit 0} proc -d loglevel {set ::loglevel $loglevel; eval options $args} ...
If we 'eval options -d 4 -h' the following will happen:
But how about parameters? They would be 'eval'ed and trigger an error: invalid command name "..."\nwhile evaluating '...'.
This is the default behaviour of the unknown function and not what we want, so let's redefine it. 'unknown' is called with the (unknown) commandname and the (variable) argumentlist. The commandname is our parameter from the command line:
proc unknown {param args} { lappend params $param; eval options $args}
So Tcl handles
lets put it together:
set params {} proc -h args {usage; exit 0} proc -d {loglevel args} {set ::loglevel $loglevel; eval options $args} # more options here ... proc options args {if [llength $args] {eval $args}} proc unknown {param args} { lappend ::params $param; eval options $args} eval options $argv
While this is not really shorter than the while loop, it has some error handling built in, e.g., if you specify '-d' without a following argument, Tcl personally rants: 'wrong # args: should be "-d loglevel args"'
If you specify 'set ::loglevel 8' on the command line, you get it set to 8, because the 'parameter' set is not unknown. So if you want to avoid Tcl code 'injection' from the command line you better run the parser inside a slave interpreter with only the -option commands.
The command-line parser in TTP is much more elaborate, to account for some of such pitfalls. It also makes use of code introspection to generate the 'usage' message automatically. How this? In the two above examples of -option procedures you see that the argument list (minus 'args') shows as directly the number (and connotation) of the arguments of a command-line option. We can get a list of options with 'info procs -*' and the argument list of a specific option with e.g. 'info args -d'.
Tcl does not supported procedure comments like, e.g., in some lisp dialects; however you could cope with this by defining your options this way:
proc -h args {# give usage message usage; exit 0}
Then you can get to the option comment with 'lindex [split [info body -h] \n] 0'; of course you need to strip off the '# ' in front of the comment. From all this you can create lines like:
-h .. give usage message -d loglevel .. set loglevlel
automatically. If you add an option, you automatically get it added to the usage message.