Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch amg-argparse-validation Excluding Merge-Ins
This is equivalent to a diff from eccc25a0c4 to b8b70870c9
2019-11-24
| ||
21:25 | Merge amg-argparse Leaf check-in: b8b70870c9 user: andy tags: amg-argparse-validation | |
21:24 | Merge trunk Leaf check-in: eccc25a0c4 user: andy tags: amg-argparse | |
2019-11-23
| ||
05:02 | 1. Integrated markdown work. Updated local documentation. 2. Reworked the collection and post processing of per-test(suite) timings added in commit [6b2f59f4e4] for faster sorting. Further fixed an issue with the collection of the per-test timings in the face of variable-field data. 3. Switching the 8.6 series from 8.6.9 to 8.6.10 for testing caused failures (hook, string::token::shell) due to changes in command name reporting (FQN in a few places where plain names were reported before). This actually looks to be a bug fix, restoring 8.5 behaviour. These testsuite issues were fixed by extending the test code used to select the expected result by core version. Added a new test utility command `byConstraint` to help with that, allowing for easy multi-way selection. check-in: b99647b031 user: aku tags: trunk | |
2019-04-26
| ||
17:52 | Beginnings of effort to rework validation to allow validators to see all results, not just the current value check-in: 36387739e6 user: andy tags: amg-argparse-validation | |
17:51 | Update to-do list check-in: 9064b9d02f user: andy tags: amg-argparse | |
Changes to modules/argparse/argparse.tcl.
︙ | ︙ | |||
229 230 231 232 233 234 235 | # # After switch processing, parameter allocation determines how many arguments to # assign to each parameter. Arguments assigned to switches are not used in # parameter processing. First, arguments are allocated to required parameters; # second, to optional, non-catchall parameters; and last to catchall parameters. # Finally, each parameter is assigned the allocated number of arguments. proc ::argparse {args} { | | | | | < | < < < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | # # After switch processing, parameter allocation determines how many arguments to # assign to each parameter. Arguments assigned to switches are not used in # parameter processing. First, arguments are allocated to required parameters; # second, to optional, non-catchall parameters; and last to catchall parameters. # Finally, each parameter is assigned the allocated number of arguments. proc ::argparse {args} { # Validation and enumeration processing helper routine. set enumHelper {apply {{name opt args} { if {[dict exists $opt enum]} { set command [list tcl::prefix match -message "$name value"\ {*}[if {[uplevel 1 {info exists exact}]} {list -exact}]\ [dict get $opt enum]] lmap arg $args {{*}$command $arg} } else { return $args } }}} # Process arguments. set level 1 set enum {} set validate {} for {set i 0} {$i < [llength $args]} {incr i} { |
︙ | ︙ | |||
329 330 331 332 333 334 335 336 337 338 339 340 341 342 | # Parse element definition list. set def {} set aliases {} set order {} set switches {} set upvars {} set omitted {} foreach elem $definition { # Read element definition switches. set opt {} for {set i 1} {$i < [llength $elem]} {incr i} { if {[set switch [regsub {^-} [tcl::prefix match { -alias -argument -boolean -catchall -default -enum -forbid -ignore -imply -keep -key -level -optional -parameter -pass | > | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | # Parse element definition list. set def {} set aliases {} set order {} set switches {} set upvars {} set omitted {} set validations {} foreach elem $definition { # Read element definition switches. set opt {} for {set i 1} {$i < [llength $elem]} {incr i} { if {[set switch [regsub {^-} [tcl::prefix match { -alias -argument -boolean -catchall -default -enum -forbid -ignore -imply -keep -key -level -optional -parameter -pass |
︙ | ︙ | |||
426 427 428 429 430 431 432 | foreach {switch others} { parameter {alias boolean value argument imply} ignore {key pass} required {boolean default} argument {boolean value} upvar {boolean inline catchall} boolean {default value} | < | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | foreach {switch others} { parameter {alias boolean value argument imply} ignore {key pass} required {boolean default} argument {boolean value} upvar {boolean inline catchall} boolean {default value} } { if {[dict exists $opt $switch]} { foreach other $others { if {[dict exists $opt $other]} { return -code error "-$switch and -$other conflict" } } |
︙ | ︙ | |||
510 511 512 513 514 515 516 | return -code error "element alias collision: [dict get $opt alias]" } else { # Build list of switches (with aliases), and link switch aliases. dict set aliases [dict get $opt alias] $name lappend switches -[dict get $opt alias]|$name } | | < < < < | > > > | > | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | return -code error "element alias collision: [dict get $opt alias]" } else { # Build list of switches (with aliases), and link switch aliases. dict set aliases [dict get $opt alias] $name lappend switches -[dict get $opt alias]|$name } # Map from upvar keys back to element names. if {[dict exists $opt upvar] && [dict exists $opt key]} { dict set upvars [dict get $opt key] $name } # Resolve named enumeration lists. if {[dict exists $opt enum] && [dict exists $enum [dict get $opt enum]]} { dict set opt enum [dict get $enum [dict get $opt enum]] } # Resolve validation expressions. if {[dict exists $opt validate]} { if {[dict exists $validate [dict get $opt validate]]} { dict set opt validateMsg "[dict get $opt validate] validation" dict set opt validate [dict get $validate\ [dict get $opt validate]] } else { dict set opt validateMsg "validation: [dict get $opt validate]" } dict set validations $name {} } # Save element definition. dict set def $name $opt # Prepare to identify omitted elements. dict set omitted $name {} |
︙ | ︙ | |||
570 571 572 573 574 575 576 | # Perform shared key logic. if {[dict exists $opt key]} { dict for {otherName otherOpt} $def { if {$name ne $otherName && [dict exists $otherOpt key] && [dict get $otherOpt key] eq [dict get $opt key]} { # Limit when shared keys may be used. | > | < < < | | < < > > | | | | | > | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 | # Perform shared key logic. if {[dict exists $opt key]} { dict for {otherName otherOpt} $def { if {$name ne $otherName && [dict exists $otherOpt key] && [dict get $otherOpt key] eq [dict get $opt key]} { # Limit when shared keys may be used. foreach key {parameter argument catchall} { if {[dict exists $opt $key]} { return -code error "$name cannot use -$key because\ it shares a key with $otherName" } } foreach key {default validate upvar} { if {[dict exists $opt $key] && [dict exists $otherOpt $key]} { return -code error "$name and $otherName cannot both\ use -$key because they share a key" } } # Create forbid constraints on shared keys. if {![dict exists $otherOpt forbid] || $name ni [dict get $otherOpt forbid]} { dict update def $otherName otherOpt { dict lappend otherOpt forbid $name |
︙ | ︙ | |||
620 621 622 623 624 625 626 | } } } set force [lreplace $argv 0 $end] set argv [lrange $argv 0 $end] # Perform switch logic. | < | 615 616 617 618 619 620 621 622 623 624 625 626 627 628 | } } } set force [lreplace $argv 0 $end] set argv [lrange $argv 0 $end] # Perform switch logic. set missing {} if {$switches ne {}} { # Build regular expression to match switches. set re ^- if {[info exists long]} { append re -? } |
︙ | ︙ | |||
728 729 730 731 732 733 734 | set $var [dict get $def $name $var] } } # Keep track of which switches have been seen. dict unset omitted $name | | < | | | | | | | | | | | | | | | | | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | set $var [dict get $def $name $var] } } # Keep track of which switches have been seen. dict unset omitted $name # Validate switch arguments and store values into the result array. if {[dict exists $def $name catchall]} { # The switch is catchall, so store all remaining arguments. set argv [{*}$enumHelper $normal [dict get $def $name] {*}$argv] if {[info exists key]} { set result($key) $argv } if {[info exists pass]} { if {[info exists normalize]} { lappend result($pass) $normal {*}$argv } else { lappend result($pass) $arg {*}$argv } } break } elseif {![dict exists $def $name argument]} { # The switch expects no arguments. if {$equal eq "="} { return -code error "$normal doesn't allow an argument" } if {[info exists key]} { if {[dict exists $def $name value]} { set result($key) [dict get $def $name value] } else { set result($key) {} } } if {[info exists pass]} { if {[info exists normalize]} { lappend result($pass) $normal } else { lappend result($pass) $arg } } } elseif {$argv ne {}} { # The switch was given the expected argument. set argv0 [lindex [{*}$enumHelper $normal\ [dict get $def $name] [lindex $argv 0]] 0] if {[info exists key]} { if {[dict exists $def $name optional]} { set result($key) [list {} $argv0] } else { set result($key) $argv0 } } if {[info exists pass]} { if {[info exists normalize]} { lappend result($pass) $normal $argv0 } elseif {$equal eq "="} { lappend result($pass) $arg } else { lappend result($pass) $arg [lindex $argv 0] } } set argv [lrange $argv 1 end] } else { # The switch was not given the expected argument. if {![dict exists $def $name optional]} { return -code error "$normal requires an argument" } if {[info exists key]} { set result($key) {} } if {[info exists pass]} { if {[info exists normalize]} { lappend result($pass) $normal } else { lappend result($pass) $arg } } } # Insert this switch's implied arguments into the argument list. if {[dict exists $def $name imply]} { set argv [concat [dict get $def $name imply] $argv] |
︙ | ︙ | |||
926 927 928 929 930 931 932 | # all omitted switches that have a pass-through key, accept an argument, and # have a default value. if {[info exists normalize]} { dict for {name opt} $def { if {[dict exists $opt switch] && [dict exists $opt pass] && [dict exists $opt argument] && [dict exists $opt default] && [dict exists $omitted $name]} { | | | | | | | | | | | | | | | > > > | | | | | | | | < | | | | | 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 | # all omitted switches that have a pass-through key, accept an argument, and # have a default value. if {[info exists normalize]} { dict for {name opt} $def { if {[dict exists $opt switch] && [dict exists $opt pass] && [dict exists $opt argument] && [dict exists $opt default] && [dict exists $omitted $name]} { lappend result([dict get $opt pass])\ -$name [dict get $opt default] } } } # Apply enumeration logic and store parameters in result array. set i 0 foreach name $order { set opt [dict get $def $name] if {[dict exists $alloc $name]} { if {![dict exists $opt catchall] && $name ne {}} { set val [lindex [{*}$enumHelper $name\ $opt [lindex $params $i]] 0] if {[dict exists $opt pass]} { if {[string index $val 0] eq "-" && ![info exists result([dict get $opt pass])]} { lappend result([dict get $opt pass]) -- } lappend result([dict get $opt pass]) $val } incr i } else { set step [dict get $alloc $name] set val [lrange $params $i [expr {$i + $step - 1}]] if {$name ne {}} { set val [{*}$enumHelper $name $opt {*}$val] } if {[dict exists $opt pass]} { if {[string index [lindex $val 0] 0] eq "-" && ![info exists result([dict get $opt pass])]} { lappend result([dict get $opt pass]) -- } lappend result([dict get $opt pass]) {*}$val } incr i $step } if {[dict exists $opt key]} { set result([dict get $opt key]) $val } } elseif {[info exists normalize] && [dict exists $opt default] && [dict exists $opt pass]} { # If normalization is enabled and this omitted parameter has both a # default value and a pass-through key, explicitly store the default # value in the pass-through key, located in the correct position so # that it can be recognized again later. if {[string index [dict get $opt default] 0] eq "-" && ![info exists result([dict get $opt pass])]} { lappend result([dict get $opt pass]) -- } lappend result([dict get $opt pass]) [dict get $opt default] } } # Create default values for missing elements. # TODO: Build list of defaults that were added. Skip validation for these # keys because defaults may well be intended to be outside the allowable # range for explicitly specified values. dict for {name opt} $def { if {[dict exists $opt key] && ![info exists result([dict get $opt key])]} { if {[dict exists $opt default]} { set result([dict get $opt key]) [dict get $opt default] } elseif {[dict exists $opt catchall]} { set result([dict get $opt key]) {} } } if {[dict exists $opt pass] && ![info exists result([dict get $opt pass])]} { set result([dict get $opt pass]) {} } } if {[info exists inline]} { # When -inline is used, return result array, converted to be a dict. array get result } else { # Unless -keep was used, unset caller variables for omitted elements. if {![info exists keep]} { dict for {name opt} $def { if {[dict exists $opt key] && ![dict exists $opt keep] && ![info exists result([dict get $opt key])]} { uplevel 1 [list ::unset -nocomplain [dict get $opt key]] } } } # Update caller variables to store results. foreach {key val} [array get result] { if {[dict exists $upvars $key]} { # If this element uses -upvar, link to the named variable. uplevel 1 [list ::upvar\ [dict get $def [dict get $upvars $key] level] $val $key] } else { # Store result into caller variables. uplevel 1 [list ::set $key $val] } } } } # vim: set sts=4 sw=4 tw=80 et ft=tcl: |