# subcommands.tcl # Copyright April 13, 2005 - Pierre Coueffin proc subcommand {cmd arglname commands} { set code [list switch -exact -- $cmd] set switches {} foreach {c a b} $commands { set b "nameArgs [list $a] \$[set arglname]\n$b" lappend switches $c $b } lappend code $switches uplevel 1 $code } proc nameArgs {prototype argl} { set mandatory {} set optional {} set args [lindex $prototype end] if {! [string match $args args]} { set args {} upvar args v set v {} } else { set prototype [lrange $prototype 0 end-1] } foreach proto $prototype { switch [llength $proto] { 0 { error "You can't have an argument with no name." } 1 { if {[llength $optional] > 0} { error "You can't have a mandatory argument after an optional one." } lappend mandatory $proto } 2 { foreach part $proto { lappend optional $part } } default { error "too many fields in argument specifier \"$proto\"" } } } if {[llength $argl] < [llength $mandatory]} { set errmsg "wrong # args: should be \"$mandatory" foreach {opt default} $optional { append errmsg " ?$opt?" } append errmsg " $args\"" error $errmsg } foreach name $mandatory arg [lrange $argl 0 [llength $mandatory]] { upvar $name v set v $arg } set argl [lrange $argl [llength $mandatory] end] if {[llength $argl] > [expr [llength $optional] / 2]} { if {$args == {}} { set errmsg "wrong # args: should be \"$mandatory" foreach {opt default} $optional { append errmsg " ?$opt?" } append errmsg "\"" error $errmsg } upvar args v set len [expr [llength $optional] / 2] set v [lrange $argl $len end] set argl [lrange $argl 0 $len] } set i 0 foreach {opt default} $optional arg $argl { upvar $opt v if {[llength $argl] <= $i} { set v $default } else { set v $arg } incr i } return }
LV What would be an example of this in action?
[Pierre Coueffin] subcommand is used in The serial iterator (It's the only example, since I just wrote it...)namedArgs is useful for breaking up argument lists. I intend to replace a bunch of places where I do:
foreach {a b c} $args breakwith:
namedArgs {a b {c defaultValueForC} {d defaultValueForD} args} $argsthe idea was to be more compatible with the way proc handles arguments. That's why it has so many attempts at sanity checks and verbose error messages...It's not perfect yet, but I want to get some feedback in case anyone else spots the glaring design flaws that I'm sure are lurking in it.