📄 optparse.tcl
字号:
} return $val } # Description format error helper proc OptOptUsage {item {what ""}} { return -code error "invalid description format$what: $item\n\ should be a list of {varname|-flagname ?-type? ?defaultvalue?\ ?helpstring?}"; } # Generate a canonical form single instruction proc OptNewInst {state varname type typeArgs help} { list $state $varname [list 0 {}] $type $typeArgs $help; # ^ ^ # | | # hasBeenSet=+ +=currentValue } # Translate one item to canonical form proc OptNormalizeOne {item} { set lg [Lassign $item varname arg1 arg2 arg3];# puts "called optnormalizeone '$item' v=($varname), lg=$lg"; set isflag [OptIsFlag $varname]; set isopt [OptIsOpt $varname]; if {$isflag} { set state "flags"; } elseif {$isopt} { set state "optValue"; } elseif {[string compare $varname "args"]} { set state "value"; } else { set state "args"; } # apply 'smart' 'fuzzy' logic to try to make # description writer's life easy, and our's difficult : # let's guess the missing arguments :-) switch $lg { 1 { if {$isflag} { return [OptNewInst $state $varname boolflag false ""]; } else { return [OptNewInst $state $varname any "" ""]; } } 2 { # varname default # varname help set type [OptGuessType $arg1] if {[string compare $type "string"] == 0} { if {$isflag} { set type boolflag set def false } else { set type any set def "" } set help $arg1 } else { set help "" set def $arg1 } return [OptNewInst $state $varname $type $def $help]; } 3 { # varname type value # varname value comment if {[regexp {^-(.+)$} $arg1 x type]} { # flags/optValue as they are optional, need a "value", # on the contrary, for a variable (non optional), # default value is pointless, 'cept for choices : if {$isflag || $isopt || ($type == "choice")} { return [OptNewInst $state $varname $type $arg2 ""]; } else { return [OptNewInst $state $varname $type "" $arg2]; } } else { return [OptNewInst $state $varname\ [OptGuessType $arg1] $arg1 $arg2] } } 4 { if {[regexp {^-(.+)$} $arg1 x type]} { return [OptNewInst $state $varname $type $arg2 $arg3]; } else { return -code error [OptOptUsage $item]; } } default { return -code error [OptOptUsage $item]; } } } # Auto magic lasy type determination proc OptGuessType {arg} { if {[regexp -nocase {^(true|false)$} $arg]} { return boolean } if {[regexp {^(-+)?[0-9]+$} $arg]} { return int } if {![catch {expr {double($arg)}}]} { return float } return string } # Error messages front ends proc OptAmbigous {desc arg} { OptError "ambigous option \"$arg\", choose from:" [OptSelection $desc] } proc OptFlagUsage {desc arg} { OptError "bad flag \"$arg\", must be one of" $desc; } proc OptTooManyArgs {desc arguments} { OptError "too many arguments (unexpected argument(s): $arguments),\ usage:"\ $desc 1 } proc OptParamType {item} { if {[OptIsFlag $item]} { return "flag"; } else { return "parameter"; } } proc OptBadValue {item arg {err {}}} {# puts "bad val err = \"$err\""; OptError "bad value \"$arg\" for [OptParamType $item]"\ [list $item] } proc OptMissingValue {descriptions} {# set item [OptCurDescFinal $descriptions]; set item [OptCurDesc $descriptions]; OptError "no value given for [OptParamType $item] \"[OptName $item]\"\ (use -help for full usage) :"\ [list $item] }proc ::tcl::OptKeyError {prefix descKey {header 0}} { OptError $prefix [OptKeyGetDesc $descKey] $header;} # determine string length for nice tabulated output proc OptLengths {desc nlName tlName dlName} { upvar $nlName nl; upvar $tlName tl; upvar $dlName dl; foreach item $desc { if {[OptIsCounter $item]} continue; if {[OptIsPrg $item]} { OptLengths $item nl tl dl } else { SetMax nl [string length [OptName $item]] SetMax tl [string length [OptType $item]] set dv [OptTypeArgs $item]; if {[OptState $item] != "header"} { set dv "($dv)"; } set l [string length $dv]; # limit the space allocated to potentially big "choices" if {([OptType $item] != "choice") || ($l<=12)} { SetMax dl $l } else { if {![info exists dl]} { set dl 0 } } } } } # output the tree proc OptTree {desc nl tl dl} { set res ""; foreach item $desc { if {[OptIsCounter $item]} continue; if {[OptIsPrg $item]} { append res [OptTree $item $nl $tl $dl]; } else { set dv [OptTypeArgs $item]; if {[OptState $item] != "header"} { set dv "($dv)"; } append res [format "\n %-*s %-*s %-*s %s" \ $nl [OptName $item] $tl [OptType $item] \ $dl $dv [OptHelp $item]] } } return $res; }# Give nice usage stringproc ::tcl::OptError {prefix desc {header 0}} { # determine length if {$header} { # add faked instruction set h [list [OptNewInst header Var/FlagName Type Value Help]]; lappend h [OptNewInst header ------------ ---- ----- ----]; lappend h [OptNewInst header {( -help} "" "" {gives this help )}] set desc [concat $h $desc] } OptLengths $desc nl tl dl # actually output return "$prefix[OptTree $desc $nl $tl $dl]"}################ General Utility functions ######################### List utility functions# Naming convention:# "Lvarxxx" take the list VARiable name as argument# "Lxxxx" take the list value as argument# (which is not costly with Tcl8 objects system# as it's still a reference and not a copy of the values)## Is that list empty ?proc ::tcl::Lempty {list} { expr {[llength $list]==0}}# Gets the value of one leaf of a lists treeproc ::tcl::Lget {list indexLst} { if {[llength $indexLst] <= 1} { return [lindex $list $indexLst]; } Lget [lindex $list [Lfirst $indexLst]] [Lrest $indexLst];}# Sets the value of one leaf of a lists tree# (we use the version that does not create the elements because# it would be even slower... needs to be written in C !)# (nb: there is a non trivial recursive problem with indexes 0,# which appear because there is no difference between a list# of 1 element and 1 element alone : [list "a"] == "a" while # it should be {a} and [listp a] should be 0 while [listp {a b}] would be 1# and [listp "a b"] maybe 0. listp does not exist either...)proc ::tcl::Lvarset {listName indexLst newValue} { upvar $listName list; if {[llength $indexLst] <= 1} { Lvarset1nc list $indexLst $newValue; } else { set idx [Lfirst $indexLst]; set targetList [lindex $list $idx]; # reduce refcount on targetList (not really usefull now, # could be with optimizing compiler)# Lvarset1 list $idx {}; # recursively replace in targetList Lvarset targetList [Lrest $indexLst] $newValue; # put updated sub list back in the tree Lvarset1nc list $idx $targetList; }}# Set one cell to a value, eventually create all the needed elements# (on level-1 of lists)variable emptyList {}proc ::tcl::Lvarset1 {listName index newValue} { upvar $listName list; if {$index < 0} {return -code error "invalid negative index"} set lg [llength $list]; if {$index >= $lg} { variable emptyList; for {set i $lg} {$i<$index} {incr i} { lappend list $emptyList; } lappend list $newValue; } else { set list [lreplace $list $index $index $newValue]; }}# same as Lvarset1 but no bound checking / creationproc ::tcl::Lvarset1nc {listName index newValue} { upvar $listName list; set list [lreplace $list $index $index $newValue];}# Increments the value of one leaf of a lists tree# (which must exists)proc ::tcl::Lvarincr {listName indexLst {howMuch 1}} { upvar $listName list; if {[llength $indexLst] <= 1} { Lvarincr1 list $indexLst $howMuch; } else { set idx [Lfirst $indexLst]; set targetList [lindex $list $idx]; # reduce refcount on targetList Lvarset1nc list $idx {}; # recursively replace in targetList Lvarincr targetList [Lrest $indexLst] $howMuch; # put updated sub list back in the tree Lvarset1nc list $idx $targetList; }}# Increments the value of one cell of a listproc ::tcl::Lvarincr1 {listName index {howMuch 1}} { upvar $listName list; set newValue [expr {[lindex $list $index]+$howMuch}]; set list [lreplace $list $index $index $newValue]; return $newValue;}# Returns the first element of a listproc ::tcl::Lfirst {list} { lindex $list 0}# Returns the rest of the list minus first elementproc ::tcl::Lrest {list} { lrange $list 1 end}# Removes the first element of a listproc ::tcl::Lvarpop {listName} { upvar $listName list; set list [lrange $list 1 end];}# Same but returns the removed elementproc ::tcl::Lvarpop2 {listName} { upvar $listName list; set el [Lfirst $list]; set list [lrange $list 1 end]; return $el;}# Assign list elements to variables and return the length of the listproc ::tcl::Lassign {list args} { # faster than direct blown foreach (which does not byte compile) set i 0; set lg [llength $list]; foreach vname $args { if {$i>=$lg} break uplevel [list set $vname [lindex $list $i]]; incr i; } return $lg;}# Misc utilities# Set the varname to value if value is greater than varname's current value# or if varname is undefinedproc ::tcl::SetMax {varname value} { upvar 1 $varname var if {![info exists var] || $value > $var} { set var $value }}# Set the varname to value if value is smaller than varname's current value# or if varname is undefinedproc ::tcl::SetMin {varname value} { upvar 1 $varname var if {![info exists var] || $value < $var} { set var $value }} # everything loaded fine, lets create the test proc: OptCreateTestProc # Don't need the create temp proc anymore: rename OptCreateTestProc {}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -