Posted to tcl by aspect at Fri Oct 10 06:24:10 GMT 2014view pretty
# dependency, adapted from the wiki proc pipe {args} { set anonvar ~ set args [lassign $args body] foreach cmd $args { if {[string first $anonvar $cmd] >= 0} { set body [string map [list $anonvar "\[$body\]"] $cmd] } else { set body "$cmd \[$body\]" } } set body } # example: # puts [pipe {open /etc/passwd r} \ {read} \ {string trim} \ {split ~ \n} \ {lindex ~ end} \ {split ~ :} \ {lindex ~ 4} \ {puts}] # generate arg parsing code to go at the start of a proc # supports TIP#288 with some extensions, see BNF below proc ArgParser {argspec} { set name "" if {$name ne ""} { set name {[lindex [info level -1] 0]} } #foreach {name argspec} [list $argspec $name] {} # parse argspec into segments: # argspec ::= required* | optional* | "args"? | optional* | required* # required ::= name # optional ::= {name default} set req_l {} set opt_l {} set args {} set opt_r {} set req_r {} for {set i 0} {$i < [llength $argspec]} {incr i} { set a [lindex $argspec $i] if {!($a ne "args" && [llength $a] == 1)} { break } lappend req_l $a } for {} {$i < [llength $argspec]} {incr i} { set a [lindex $argspec $i] if {[llength $a] == 1} { break } lappend opt_l $a } for {} {$i < [llength $argspec]} {incr i} { set a [lindex $argspec $i] if {$a ne "args"} { break } lappend args $a } for {} {$i < [llength $argspec]} {incr i} { set a [lindex $argspec $i] if {[llength $a] == 1} { break } lappend opt_r $a } for {} {$i < [llength $argspec]} {incr i} { set a [lindex $argspec $i] if {[llength $a] != 1} { break } lappend req_r $a } set opt_r [lreverse $opt_r] set req_r [lreverse $req_r] if {[llength $args] > 1} {error "args can only occur once!"} if {$i != [llength $argspec]} {error "didn't consume whole argspec!"} set min_argc [expr {[llength [concat $req_l $req_r]]}] debug show {[list $req_l $opt_l $args $opt_r $req_r]} set parser [GenArgParser $req_l $opt_l $args $opt_r $req_r] # emit code for parsing args: set badArgMsg [format {wrong # args: should be "%s"} [concat $name [FormatArgspec $argspec]]] return "try {$parser}\ trap {TCL WRONGARGS} {} {throw {TCL WRONGARGS} [list $badArgMsg]}" # sl { # try $parser # trap {TCL WRONGARGS} {} { # throw {TCL WRONGARGS} $badArgMsg # } # } } ;# rl - required left ;# ol - optional left ;# as - args ;# or - optional right ;# rr - required right proc GenArgParser {rl {ol ""} {as ""} {or ""} {rr ""}} { set olnames [lmap x $ol {lindex $x 0}] set ollist [lmap x $olnames {string cat \$ [list $x]}] set ollist [join $ollist \ ] set ornames [lmap x $or {lindex $x 0}] set orlist [lmap x $ornames {string cat \$ [list $x]}] set orlist [join $orlist \ ] set arglen [llength [concat {*}$rl {*}$rr]] if {$ol eq "" && $or eq "" && $as eq ""} { set op != } else { set op < } set precheck [subst -noc {if {[llength \$args] $op $arglen} {throw {TCL WRONGARGS} ""}}] set script {} lappend script [subst {set args} ] if {$rl ne ""} { lappend script [subst {lassign ~ $rl} ] } if {$rr ne ""} { lappend script [subst {lreverse} ] lappend script [subst {lassign ~ $rr} ] lappend script [subst {lreverse} ] } if {$ol ne ""} { if {$or eq "" && $as eq ""} { lappend script [subst {apply {{$ol} {list $ollist}} {*}~} ] } else { lappend script [subst {apply {{$ol args} {list $ollist {*}\$args}} {*}~} ] } lappend script [subst {lassign ~ $olnames} ] } if {$or ne ""} { lappend script [subst {lreverse} ] if {$as eq ""} { lappend script [subst {apply {{$or} {list $orlist}} {*}~} ] } else { lappend script [subst {apply {{$or args} {list $orlist {*}\$args}} {*}~} ] } lappend script [subst {lassign ~ $ornames} ] lappend script [subst {lreverse} ] } if {$as ne ""} { lappend script [subst {set args} ] } else { lappend script [subst {if {~ ne ""} {throw {TCL WRONGARGS} ""};unset args}] } debug log {script is: $script} return "$precheck;[pipe {*}$script]" }