Index: config.tcl ================================================================== --- config.tcl +++ config.tcl @@ -1085,11 +1085,12 @@ # Now map the fully expanded option name to its handler and # let it deal with the remaining things, including retrieval # of the option argument (if any), validation, etc. - [dict get $myoption [lindex $options 0]] process $option $mypq + set full [lindex $options 0] + [dict get $myoption $full] process $full $mypq return } method tooMany {} { debug.cmdr/config {} Index: doc/parts/dsl_para_naming.inc ================================================================== --- doc/parts/dsl_para_naming.inc +++ doc/parts/dsl_para_naming.inc @@ -45,10 +45,24 @@ the specification of any number additional flags to be recognized. [para] Note however that the framework automatically recognizes not only the specified flag(s), but also all their unique prefixes, obviating the need for this command in many cases. + +[comment {- - -- --- ----- -------- -------------}] +[call [cmd neg-alias] [arg name]] +[call [cmd !alias] [arg name]] + +This command applies only to [term boolean] [term option]s. For them it allows +the specification of any number additional flags to be recognized, which are +aliases of its [emph inverted] form, i.e. of [const --no-FOO] for an option [const FOO]. + +[para] This in contrast to [cmd alias]es, which are for the regular form of the option. + +[para] Note further that the framework automatically recognizes not +only the specified flag(s), but also all their unique prefixes, +obviating the need for this command in many cases. [list_end] Index: embedded/man/files/cmdr_dsl_parameter.n ================================================================== --- embedded/man/files/cmdr_dsl_parameter.n +++ embedded/man/files/cmdr_dsl_parameter.n @@ -277,10 +277,14 @@ .SH SYNOPSIS \fBlabel\fR \fItext\fR .sp \fBalias\fR \fIname\fR .sp +\fBneg-alias\fR \fIname\fR +.sp +\fB!alias\fR \fIname\fR +.sp \fBno-promotion\fR .sp \fBoptional\fR .sp \fBtest\fR @@ -389,10 +393,23 @@ the specification of any number additional flags to be recognized\&. .sp Note however that the framework automatically recognizes not only the specified flag(s), but also all their unique prefixes, obviating the need for this command in many cases\&. +.TP +\fBneg-alias\fR \fIname\fR +.TP +\fB!alias\fR \fIname\fR +This command applies only to \fIboolean\fR \fIoption\fRs\&. For them it allows +the specification of any number additional flags to be recognized, which are +aliases of its \fIinverted\fR form, i\&.e\&. of \fB--no-FOO\fR for an option \fBFOO\fR\&. +.sp +This in contrast to \fBalias\fRes, which are for the regular form of the option\&. +.sp +Note further that the framework automatically recognizes not +only the specified flag(s), but also all their unique prefixes, +obviating the need for this command in many cases\&. .PP .PP .SS "GENERAL CONTROL" .PP The general handling of a \fIparameter\fR is influenced by Index: embedded/www/doc/files/cmdr_dsl_parameter.html ================================================================== --- embedded/www/doc/files/cmdr_dsl_parameter.html +++ embedded/www/doc/files/cmdr_dsl_parameter.html @@ -133,24 +133,26 @@
Welcome to the Cmdr project, written by Andreas Kupries.
@@ -217,42 +219,52 @@ If that is not enough for a specific option this command allows the specification of any number additional flags to be recognized.Note however that the framework automatically recognizes not only the specified flag(s), but also all their unique prefixes, obviating the need for this command in many cases.
+This command applies only to boolean options. For them it allows +the specification of any number additional flags to be recognized, which are +aliases of its inverted form, i.e. of --no-FOO for an option FOO.
+This in contrast to aliases, which are for the regular form of the option.
+Note further that the framework automatically recognizes not +only the specified flag(s), but also all their unique prefixes, +obviating the need for this command in many cases.
The general handling of a parameter is influenced by three commands:
When the framework encounters an unknown flag during the Parsing phase it will not unconditionally throw an error about it, but first check if the next available input parameter (if any) could accept the flag string as its value, per that input's validation type, and if yes, does so.
This command causes the rejection of such flag strings by this parameter on general principle, without having to validate it.
Note that it is only useful for and applicable to input parameters.
This command marks the parameter as optional, i.e. as something the user may skip on the command line, with the application supplying sensible defaults (See section Representations). This causes the framework to expend some effort in the Parsing phase to determine whether an argument word should be assigned to the parameter, or not.
This setting is only applicable to inputs, as options are optional by definition, and state is hidden.
This command is related to the above, switching the runtime from the standard regime for acceptance (based on counting and thresholds) to a different regime based on validation.
More details are explained in section Parsing of Cmdr - Runtime Processing Flow.
This command excludes the parameter from the generated help.
Its main use case is the hiding of options giving an application developer access to the internals of their application, something a regular user has no need of, and doesn't have to know about.
This command specifies a constant default value for the internal representation.
This command specifies a callback to compute the default internal representation at runtime. This is useful if the default is something which cannot be captured as a fixed value. An example would be a handle to some resource, or a dynamically created object.
The command prefix is invoked with a single argument, the @@ -297,11 +309,11 @@
Use the empty string for a list parameter.
Use the default value supplied by the chosen validation type (See section Validation).
This command actually does not specify an internal representation, but activates another method for the user to specify the string representation of the parameter, outside of the command line. As such it has priority over either default and generate, @@ -326,11 +338,11 @@ generate callback. As internal representations the resulting values are not run through the chosen validation type.
This command marks the parameter as a list. In other words, its string and internal representation is actually a list of such, instead of a single value. By default all parameters are scalar.
This affects the handling of the parameter by the @@ -349,15 +361,15 @@
The last two specification commands dealing with the representations control when the internal representation is created.
This command marks a parameter as defered, causing its internal representation to be computed on first access to its value. This is the default for state parameters.
This command marks a parameter as immediate, causing its internal representation to be computed in the Completion phase. This is the default for input and option parameters.
This command specifies a validation type for the parameter, in the form of a command prefix (or the name of one of the builtin types, see package cmdr::validate). The set of methods this callback has to support, their signatures, etc. are all explained in Cmdr - Writing custom validation types. This document @@ -399,11 +411,11 @@
Use identity if no default is specified and the parameter is an input.
Use boolean if the specified default value is a Tcl boolean.
Use integer if the specified default value is a Tcl integer.
Use identity as fallback of last resort.
This command is best discussed as part of the wider area of boolean options, i.e. options with the standard validation type boolean assigned to them. These have associated special behaviours, both in the handling of the specification, and in the Parsing phase.
@@ -444,19 +456,19 @@The two callbacks not yet described are the state-change callbacks through which the framework can actively drive parts of the application while processing the command line, whereas normally the application drives access to parameters through their methods.
This command declares the state-change callback to invoke when the internal representation of the parameter is generated from the string representation, or the various ways of getting a default.
The callback is invoked with two arguments, the cmdr::parameter instance of the changed parameter, and its internal representation, in this order.
This command declares the state-change callback to invoke when the string representation of the parameter is set during command line parsing.
The callback is invoked with two arguments, the cmdr::parameter instance of the changed parameter, Index: parameter.tcl ================================================================== --- parameter.tcl +++ parameter.tcl @@ -84,10 +84,11 @@ set mystopinteraction no ;# specified interaction is not suppressed. set myislist no ;# scalar vs list parameter set myisdocumented yes set myonlypresence no ;# options only, no argument when true. + set myhasinverted no ;# options only, presence of negative aliases. set myhasdefault no ;# flag for default existence set mydefault {} ;# default value - raw set mygenerate {} ;# generator command prefix set myinteractive no ;# no interactive query of value set myprompt "Enter ${name}: " ;# standard prompt for interaction @@ -175,10 +176,14 @@ inverted { return "Complementary alias of [my Option $myname]." } } } return $mydescription } + + method flag-type {detail} { + return [dict get $myflags $detail] + } method primary {option} { return [expr {[dict get $myflags $option] eq "primary"}] } @@ -276,10 +281,12 @@ set myflags {} # Import the DSL commands to translate the specification. link \ {alias Alias} \ + {!alias NegAlias} \ + {neg-alias NegAlias} \ {default Default} \ {defered Defered} \ {generate Generate} \ {immediate Immediate} \ {interact Interact} \ @@ -315,10 +322,12 @@ my C5_StateHasAlternateInput my C6_RequiredArgumentForbiddenDefault my C6_RequiredArgumentForbiddenGenerator my C6_RequiredArgumentForbiddenInteract my C7_DefaultGeneratorConflict + my C10_ForbiddenInvertedAlias + my C11_ForbiddenInvertedAlias return } # # ## ### ##### ######## ############# @@ -380,10 +389,17 @@ method Alias {name} { my Alias_Option dict set myflags [my Option $name] alias return } + + method NegAlias {name} { + my Alias_Option + dict set myflags [my Option $name] inverted + set myhasinverted yes + return + } method Optional {} { # Arguments only. Options are already optional, and state # parameters must not be. my Optional_State ; # Order of tests is important, enabling us @@ -532,23 +548,37 @@ my Assert {$myiscmdline && !$myisordered} \ {Non-option parameter "@" cannot have presence-only} forward C9_ForbiddenPresence \ my Assert {(!$myhasdefault && ![llength $mygenerate] && ![llength $myvalidate]) || !$myonlypresence} \ - {Customized option cannot be presence-only} + {Customized option "@" cannot be presence-only} forward C9_PresenceDefaultConflict \ my Assert {!$myonlypresence} \ - {Presence-only option cannot have custom default value} + {Presence-only option "@" cannot have custom default value} forward C9_PresenceGeneratorConflict \ my Assert {!$myonlypresence} \ - {Presence-only option cannot have custom generator command} + {Presence-only option "@" cannot have custom generator command} forward C9_PresenceValidateConflict \ my Assert {!$myonlypresence} \ - {Presence-only option cannot have custom validation type} + {Presence-only option "@" cannot have custom validation type} + + forward C10_ForbiddenInvertedAlias \ + my Assert { + !$myiscmdline || $myisordered || + ($myvalidate eq "::cmdr::validate::boolean") || + !$myhasinverted + } {Non-boolean option "@" cannot have negated alias} + + forward C11_ForbiddenInvertedAlias \ + my Assert { + !$myiscmdline || $myisordered || + !$myonlypresence || + !$myhasinverted + } {Presence option "@" cannot have negated alias} # # ## ### ##### ######## ############# ## Internal: DSL support. Syntax constraints. forward Alias_Option \ @@ -671,10 +701,11 @@ # The primary option is not inverted, make an alias which is. set alternate no-$myname } dict set myflags [my Option $alternate] inverted + set myhasinverted yes return } method Option {name} { # Short options (single character) get a single-dash '-'. @@ -861,11 +892,11 @@ # Insert implied boolean flag value. # # --foo non-boolean-value ==> --foo YES non-boolean-value # --no-foo non-boolean-value ==> --foo NO non-boolean-value - # Invert meaning of option. + # Invert meaning of option (inverted aliases, std, and user). # --no-foo YES ==> --foo NO # --no-foo NO ==> --foo YES # Take implied or explicit value. if {![$queue size] || ![string is boolean -strict [$queue peek]]} { @@ -874,11 +905,11 @@ # queue size && boolean set value [$queue get] } # Invert meaning, if so requested. - if {[string match --no-* $flag]} { + if {[dict get $myflags $flag] eq "inverted"} { set value [expr {!$value}] } } else { # Everything else has no special forms. The option's value # is required here. @@ -1259,13 +1290,13 @@ myinteractive myprompt mydefault myhasdefault \ mywhencomplete mywhenset mygenerate myvalidate \ myflags mythreshold myhasstring mystring \ myhasvalue myvalue mylocker mystopinteraction \ myisdocumented myonlypresence myisdefered \ - myisundefined mynopromote + myisundefined mynopromote myhasinverted # # ## ### ##### ######## ############# } # # ## ### ##### ######## ############# ##################### ## Ready package provide cmdr::parameter 1.4 Index: tests/parameter.tests ================================================================== --- tests/parameter.tests +++ tests/parameter.tests @@ -46,10 +46,13 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: 'no' flags [--no-A -A -X] + --no-A = inverted + -A = primary + -X = alias ge () va (::cmdr::validate::boolean) wd () } } @@ -77,16 +80,58 @@ para (no-A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: 'no' flags [--no-A -A -X] + --no-A = primary + -A = inverted + -X = alias + ge () + va (::cmdr::validate::boolean) + wd () + } + } +} + +test cmdr-parameter-1.6 {parameter DSL, option, negative alias} -body { + NiceParamSpec option { neg-alias X } +} -result { + foo bar = { + description: '' + option (--no-A) = A + option (-A) = A + option (-X) = A + map --n --> (--no-A) + map --no --> (--no-A) + map --no- --> (--no-A) + map --no-A --> (--no-A) + map -A --> (-A) + map -X --> (-X) + para (A) { + description: '-' + unordered, cmdline, single, optional, silent, immediate + default: 'no' + flags [--no-A -A -X] + --no-A = inverted + -A = primary + -X = inverted ge () va (::cmdr::validate::boolean) wd () } } } + +test cmdr-parameter-1.7 {parameter DSL, option, non-boolean, negative alias} -body { + BadParamSpec option { default 2 ; neg-alias X } +} -returnCodes error \ + -result {Non-boolean option "A" cannot have negated alias} + +test cmdr-parameter-1.8 {parameter DSL, option, presence, negative alias} -body { + BadParamSpec option { presence ; neg-alias X } +} -returnCodes error \ + -result {Presence option "A" cannot have negated alias} # # ## ### ##### ######## ############# ##################### ## Parameter DSL: 'default' across parameters (input, option, state) test cmdr-parameter-2.0 {parameter DSL, default, wrong num args, not enough} -body { @@ -176,10 +221,12 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: '0' flags [--no-A -A] + --no-A = inverted + -A = primary ge () va (::cmdr::validate::boolean) wd () } } @@ -195,10 +242,11 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: '2' flags [-A] + -A = primary ge () va (::cmdr::validate::integer) wd () } } @@ -214,10 +262,11 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: 'X' flags [-A] + -A = primary ge () va (::cmdr::validate::identity) wd () } } @@ -336,10 +385,11 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate no default flags [-A] + -A = primary ge (G) va (::cmdr::validate::identity) wd () } } @@ -446,10 +496,12 @@ description: '-' unordered, cmdline, single, optional, interact, immediate default: 'no' prompt: 'Enter A: ' flags [--no-A -A] + --no-A = inverted + -A = primary ge () va (::cmdr::validate::boolean) wd () } } @@ -528,10 +580,12 @@ para (A) { description: '-' unordered, cmdline, splat, optional, silent, immediate default: '' flags [--no-A -A] + --no-A = inverted + -A = primary ge () va (::cmdr::validate::boolean) wd () } } @@ -692,10 +746,11 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: '0' flags [-A] + -A = primary ge () va (::cmdr::validate::integer) wd () } } @@ -711,10 +766,11 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: '' flags [-A] + -A = primary ge () va (::cmdr::validate::identity) wd () } } @@ -800,10 +856,12 @@ para (A) { description: '-' unordered, cmdline, single, optional, silent, immediate default: 'no' flags [--no-A -A] + --no-A = inverted + -A = primary ge () va (::cmdr::validate::boolean) wd (X) } } Index: tests/private.tests ================================================================== --- tests/private.tests +++ tests/private.tests @@ -54,10 +54,12 @@ para (OPTION) { description: 'OPTION-DESC' unordered, cmdline, single, optional, silent, immediate default: 'no' flags [--no-OPTION --OPTION] + --no-OPTION = inverted + --OPTION = primary ge () va (::cmdr::validate::boolean) wd () } para (STATE) { @@ -238,10 +240,12 @@ para (A) { description: 'D' unordered, cmdline, single, optional, silent, immediate default: 'no' flags [--no-A -A] + --no-A = inverted + -A = primary ge () va (::cmdr::validate::boolean) wd () } } Index: tests/runtime.tests ================================================================== --- tests/runtime.tests +++ tests/runtime.tests @@ -620,9 +620,52 @@ input A - { validate integer } input B - { validate integer } } 1 X } -returnCodes error \ -result {Expected an integer for input "B", got "X"} + +# # ## ### ##### ######## ############# ##################### +## Options, negative aliases + +test cmdr-runtime-8.0 {options, simple, boolean, special forms II} -body { + Parse { + option A - + option B - + option C - + option D - + option E - + option F - + option G - { validate identity } + } -A -G=X --no-B -G=X -C=1 -D=0 --no-E=1 --no-F=0 +} -result { + A = 'yes' v'1' + B = '0' v'0' + C = '1' v'1' + D = '0' v'0' + E = '0' v'0' + F = '1' v'1' + G = 'X' v'X' +} + +test cmdr-runtime-8.1 {options, boolean, aliases} -body { + Parse { + option A - { alias X } + option B - { neg-alias Y } + } -X -Y +} -result { + A = 'yes' v'1' + B = '0' v'0' +} + +test cmdr-runtime-8.2 {options, boolean, aliases, special forms} -body { + Parse { + option A - { alias X } + option B - { neg-alias Y } + } -X=0 -Y=0 +} -result { + A = '0' v'0' + B = '1' v'1' +} # # ## ### ##### ######## ############# ##################### return Index: tests/support.tcl ================================================================== --- tests/support.tcl +++ tests/support.tcl @@ -250,10 +250,13 @@ } else { lappend result " mode=peek+test" } } lappend result " flags \[[$c options]\]" + foreach oname [$c options] { + lappend result " $oname = [$c flag-type $oname]" + } lappend result " ge ([$c generator])" lappend result " va ([$c validator])" lappend result " wd ([$c when-complete])" lappend result " \}" }