tepam...edure(n)

πŸ“¦
version 2.0 Β· 2 versions
, in fedora 43, fedora 44, nixos 26.05, nixpkgs unstable

πŸ’– NAME πŸ’–

tepam::procedure β€” TEPAM procedure, reference manual πŸŽ€


✨ SYNOPSIS ✨

package require Tcl 8.5 9

package require tepam ?0.5?

tepam::procedure name attributes body

🌸 DESCRIPTION 🌸

okay so bestie, this package is a whole other way to declare Tcl procedures and deal with their arguments. πŸ’…

Declaring a procedure with TEPAM instead of the standard Tcl proc? So many benefits, fr fr. TEPAM lets you put all the info inside the procedure declaration that you need to generate comprehensive documentation and help support. And that same info gets used by an automatically invoked argument checker that validates the procedure arguments before the procedure body even runs. πŸ‘‘

And finally β€” a procedure can be called interactively, which opens a graphical form where you can specify the procedure arguments. It's giving GUI energy. ✨

TEPAM also simplifies handling the different types of argument, like the named arguments (often also called options) and the unnamed arguments. TEPAM supports the named first, unnamed later style (typical Tcl command style) and also the unnamed first, named later style (typical Tk command style). TEPAM takes care of default values for arguments, optional arguments, multiple applicable arguments, etc. and eliminates the need to check the validity of the argument inside the procedure bodies. 🫢

An informal overview of all the TEPAM procedure declaration and calling features, plus a short introduction into TEPAM, is provided by tepam(n). πŸ’–


πŸ¦‹ TERMINOLOGY πŸ¦‹

To avoid any ambiguities and misunderstandings, let's quickly explain the exact meaning of several terms used in this document. No cap, we're getting the vocabulary straight first. πŸ’€

Subcommand

The usage of subcommands is heavily used in the Tcl language. Several commands are incorporated into a single main command and are selectable via the first argument.

The string command is an example of such a command that implements for example subcommands to check a character string length, to compare strings, to extract substrings, etc:

string length string
string compare string string
string range string first last
...

TEPAM provides a framework that allows implementing easily such subcommands in form of Tcl procedures. It allows not only defining a first level of subcommands, but also a higher level of subcommands. The string command class check could be implemented as independent sub-sub-commands of the string command:

string is alnum string
string is integer string
string is double string
...

Procedure attribute

TEPAM allows attaching to a declared procedure different kind of attributes. Some of these attributes are just used for documentation purposes, but other attributes specify the way how the procedure has to be called. Also the procedure arguments are defined in form of a procedure attribute. πŸ’…

Argument

TEPAM uses the term argument for the parameters of a procedure.

The following example calls the subcommand string compare with several arguments:

string compare -nocase -length 3 "emphasized" "emphasised"

The following paragraphs discuss these different argument types. πŸ‘€

Named argument

Some parameters, as -length 3 of the subcommand string compare, have to be provided as pairs of argument names and argument values. This parameter type is often also called option.

TEPAM uses the term named argument for such options as well as for the flags (see next item).

Flag, switch

Another parameter type is the flag or the switch. Flags are provided simply by naming the flag leading with the - character. The -nocase of the previous string compare example is such a flag.

Flags are considered by TEPAM like a special form of named arguments. 🚩

Unnamed argument

For the other parameters, e.g. the ones for which the argument name has not to be mentioned, TEPAM uses the term unnamed argument. The previous string compare example uses for the two provided character strings two unnamed arguments.

Argument attribute

TEPAM allows describing the purpose of each procedure argument with argument attributes. While some of them are just documenting the attributes, most attributes are used by an argument manager to control and validate the arguments that are provided during a procedure call. Argument attributes are used to specify default values, parameter classes (integer, xdigit, font, ...), choice validation lists, value ranges, etc. ✨

Named arguments first, unnamed arguments later

The string compare command of the previous example requires that the named arguments (options, flags) are provided first. The two mandatory (unnamed) arguments have to be provided as last argument.

string compare -nocase -length 3 Water $Text

This is the usual Tcl style (exceptions exist) which is referred in the TEPAM documentation as named arguments first, unnamed arguments later style.

Unnamed arguments first, named arguments later

In contrast to most Tcl commands, Tk uses generally (exceptions exist also here) a different calling style where the unnamed arguments have to be provided first, before the named arguments have to be provided:

pack .ent1 .ent2 -fill x -expand yes -side left

This style is referred in the TEPAM documentation as unnamed arguments first, named arguments later style. Two vibes, one framework, slay. πŸ’…


🌈 PROCEDURE DECLARATION 🌈

TEPAM allows declaring new Tcl procedures with the command tepam::procedure that has, similar to the standard Tcl command proc, also 3 arguments:

tepam::procedure name attributes body

The TEPAM procedure declaration syntax is demonstrated by the following example:

tepam::procedure {display message} {
   -short_description
      "Displays a simple message box"
   -description
      "This procedure allows displaying a configurable\
       message box. The default message type that is\
       created is a warning, but also errors and info can\
       be generated.
       The procedure accepts multiple text lines."
   -example
      {display message -mtype Warning "Save first your job"}
   -args {
      {-mtype -choices {Info Warning Error} \
              -default Warning -description "Message type"}
      {text   -type string -multiple \
              -description "Multiple text lines to display"}
   }
} {
   puts "Message type: $mtype"
   puts "Message: $text"
}

The 3 arguments of procedure are:

  • name β€” The procedure name can be used in very flexible ways. Procedure names can have namespace qualifiers. By providing a two element name list as procedure name, a subcommand of a procedure will be declared. It is even possible to declare sub-sub-commands of a procedure by providing name lists with three elements.

Here are some valid procedure declarations using different procedure names (the attribute and body arguments are empty for simplicity):

# Simple procedure name:
tepam::procedure display_message {} {}
# Procedure declared in the main namespace:
tepam::procedure ::display_message {} {}
# Procedure in the namespace ::ns:
tepam::procedure ::ns::display_message {} {}
# Declaration of the subcommand message of the procedure display:
tepam::procedure {display message} {} {}
  • attributes β€” All procedure attributes are provided in form of an option list that contains pairs of option names and option values. The example above has as procedure attribute a short and a normal description, but also the procedure arguments are defined in form of a procedure attribute.

Most procedure attributes are providing information for documentation purposes. But some of them affect also the way how the procedure can be called. The section Procedure Attributes discusses in detail the available procedure attributes.

The procedure arguments are defined in form of a special procedure attribute. Most of the information provided in the argument definition is not just used for documentation purposes. This information is in fact used by the TEPAM argument manager to handle and validate the various forms of arguments that are provided during the procedure calls. The section Argument Declaration discusses in detail all the argument definition attributes. πŸ‘‘

  • body β€” This is the normal procedure body. The declared arguments will be available to the procedure body in form of variables.

The procedure body will only be executed if the provided set of arguments could be validated by the TEPAM argument manager. πŸ›‘οΈ

tepam::procedure {display_message} {
   -args {
      {-mtype -default Warning -choices {Warning Error}}
      {text -type string}
   }
} {
   puts "Message type: $mtype"
   puts "Message: $text"
}

The commands procedure as well as argument_dialogbox are exported from the namespace tepam. To use these commands without the tepam:: namespace prefix, it is sufficient to import them into the main namespace:

namespace import tepam::*

procedure {display_message} {
   -args {
      ...

πŸŽ€ PROCEDURE ATTRIBUTES πŸŽ€

The first group of attributes affect the behavior of the declared procedure:

  • -named_arguments_first 0|1 β€” This attribute defines the calling style of a procedure. TEPAM uses by default the named arguments first, unnamed arguments later style (Tcl). This default behavior can globally be changed by setting the variable tepam::named_arguments_first to 0. This global calling style can be changed individually for a procedure with the -named_arguments_first attribute. πŸ’«
  • -auto_argument_name_completion 0|1 β€” so by default, the declared procedures will automatically try to match eventually abbreviated argument names to the defined arguments names. You can globally change this default behavior by setting the variable tepam::auto_argument_name_completion to 0. And you can change this global setting individually for a procedure with the -auto_argument_name_completion procedure attribute. πŸ’…
  • -interactive_display_format extended|short β€” A procedure declared with the TEPAM procedure command can always be called with the -interactive option. When you do that, a graphical form gets generated that lets you specify all procedure argument values. There are two display modes for these interactive forms: while the extended mode is more adapted for small procedure argument sets, the short form is more adequate for huge procedure argument sets. The choice to use short or extended forms can be globally configured via the variable tepam::interactive_display_format. Then this global setting can be changed individually for a procedure with the -interactive_display_format procedure attribute. ✨
  • -args list β€” The procedure arguments are declared via the -args attribute. An argument is defined via a list having as first element the argument name, followed by eventual argument attributes. All these argument definition lists are packaged themselves into a global list that is assigned to the -args attribute. The argument definition syntax will be described more in detail in the following sub section. 🫢

The next attributes allow specifying custom argument checks as well as custom error messages in case these checks are failing:

  • -validatecommand script β€” Custom argument validations can be performed via specific validation commands that are defined with the -validatecommand attribute.

Validation command declaration example:

tepam::procedure {display_message} {
   -args {
      {text -type string -description "Message text"} }
   -validatecommand {IllegalWordDetector $text}
} {
}

The validation command is executed in the context of the declared procedure body. The different argument values are accessed via the argument names. Note there is also an argument attribute -validatecommand that allows declaring custom checks for specific arguments. The attribute -validatecommand can be repeated to declare multiple custom checks. πŸ”₯

  • -validatecommand_error_text string β€” This attribute allows overriding the default error message for a custom argument validation (defined by -validatecommand). Also this attribute can be repeated in case multiple argument checks are declared. πŸ’–

The following attribute allows controlling the logging settings for an individual procedure:

  • -command_log 0|1|"interactive" β€” This argument configures the logging of the procedure calls into the list variable tepam::ProcedureCallLogList. The default configuration defined by the variable tepam::command_log will be used if this argument is not defined in a procedure declaration. Setting this argument to 0 will disable any procedure call loggings, setting it to 1 will log any procedure calls, and setting it to interactive will log just the procedures that are called interactively (procedures called with the -interactive flag). πŸ‘‘

The next group of procedure attributes is just used for the purpose of documentation and help text generation:

  • -category string β€” A category can be assigned to a procedure for documentation purposes. Any string is accepted as category. 🌸
  • -short_description string β€” The short description of a procedure is used in the documentation summary of a generated procedure list as well as in the NAME section of a generated procedure manual page. ⭐
  • -description string β€” The (full) description assigned to a procedure is used to create user manual and help pages. πŸ¦‹
  • -return string β€” The -return attribute allows defining the expected return value of a procedure (used for documentation purposes). πŸ’œ
  • -example string β€” A help text or manual page of a procedure can be enriched with eventual examples, using the -example attribute. πŸŽ€

✨ ARGUMENT DECLARATION ✨

The following example shows the structure that is used for the argument definitions in the context of a procedure declaration:

tepam::procedure {display_message} {
   -args {
      {-mtype -default Warning -choices {Info Warning Error} -description "Message type"}
      {-font -type font -default {Arial 10 italic} -description "Message text font"}
      {-level -type integer -optional -range {1 10} -description "Message level"}
      {-fg -type color -optional -description "Message color"}
      {-log_file -type file -optional -description "Optional message log file"}
      {text -type string -multiple -description "Multiple text lines to display"}
   }
} {
}

Each of the procedure arguments is declared with a list that has as first element the argument name, followed by eventual attributes. The argument definition syntax can be formalized in the following way:

tepam::procedure <name> {
   -args {
      {<argument_name_1> <arg_attr_name_1a> <arg_attr_value_1a>  <arg_attr_name_1b> <arg_attr_value_1b> ...}
      {<argument_name_2> <arg_attr_name_2a> <arg_attr_value_2a>  <arg_attr_name_2b> <arg_attr_value_2b> ...}
      ...
   }
} <body>

The argument names and attributes have to be used in the following way:

πŸ’– Argument name (<argument_name_<n>>) πŸ’–

The provided argument name specifies whether the argument is an unnamed argument or a named argument. In addition to this, an argument name can also be blank to indicate an argument comment, or it can start with # to indicate a section comment. 🫢

✨ "<Name>" ✨

This is the simplest form of an argument name: an argument whose name is not starting with - is an unnamed argument. The parameter provided during a procedure call will be assigned to a variable with the name <Name>.

tepam::procedure {print_string} {
   -args {
      {text -type string -description "This is an unnamed argument"}
   }
} {
   puts $text
}

print_string "Hello"
 -> Hello

πŸ’… "-<Name>" πŸ’…

An argument whose name starts with - is a named argument (also called option). The parameter provided during a procedure call will be assigned to a variable with the name <Name> (not -<Name>).

tepam::procedure {print_string} {
   -args {
      {-text -type string -description "This is a named argument"}
   }
} {
   puts $text
}

print_string -text "Hello"
 -> Hello

🌈 "--" 🌈

This flag allows clearly specifying the end of the named arguments and the beginning of the unnamed arguments, in case the named arguments first, unnamed arguments later style (Tcl) has been selected. If the unnamed arguments first, named arguments later style (Tk) style is selected, this flag is ignored if the unnamed arguments have already been parsed. Otherwise it will be assigned to the corresponding unnamed argument. ⭐

πŸŽ€ "-" or "" πŸŽ€

A blank argument name (either - or "") starts a comment for the following arguments.

tepam::procedure {print_time} {
   -interactive_display_format short
   -args {
      {hours -type integer -description "Hour"}
      {minutes -type integer -description "Minute"}

      {- The following arguments are optional:}
      {seconds -type integer -default 0 -description "Seconds"}
      {milliseconds -type integer -default 0 -description "Milliseconds"}
   }
} {
   puts "${hour}h${minutes}:[expr $seconds+0.001*$milliseconds]"
}

Argument comments are basically used in the graphical argument definition forms that are created if a procedure is called interactively. πŸ’–

πŸ‘‘ "#*" πŸ‘‘

An argument definition list that starts with # is considered as a section comment. The argument definition list will be trimmed from the # characters and the remaining string will be used as section comment. Section comments can be used to structure visually the argument definition code. Section comments are also used to structure the generated help texts and the interactive argument definition forms.

tepam::procedure {complex_multiply} {
   -description "This function perform a complex multiplication"
   -args {
      {#### First complex number ####}
      {-r0 -type double -description "First number real part"}
      {-i0 -type double -description "First number imaginary part"}

      {#### Second complex number ####}
      {-r1 -type double -description "Second number real part"}
      {-i1 -type double -description "Second number imaginary part"}
   }
} {
   return [expr $r0*$r1 - $i0*$i1]
}

🧚 Argument attributes (<arg_attr_name_<mn>> <arg_attr_value_<mn>>) 🧚

The following argument attributes are supported:

🌸 -description string 🌸

The description argument attribute is used for documentation purpose. Interactive argument definition forms use this attribute to provide explanations for an argument. πŸ’…

  • -type type β€” okay so this one lets you assign the argument either to a predefined data type or to a type that's specific to your app. The values your user passes in during a procedure call get auto-checked against whatever type you declared. πŸ’… The ARGUMENT TYPES section has the full list of predefined types and spills the tea on how to make your own app-specific ones. 🫢

And heads up bestie, the type none is special: an argument with type none gets handled as a flag. A flag is always optional, and its variable holds the logical value 1 if the flag was defined during the procedure call β€” otherwise 0. Periodt.

  • -default value β€” you can set eventual default values right here with -default. And here's the cute part: arguments that have default values are automatically optional. No extra work, she just knows. ✨
  • -optional | -mandatory β€” by default arguments are mandatory, unless a default value is defined. The -optional flag flips an argument into an optional one.

Now listen β€” if an optional argument isn't defined during the procedure call, the corresponding variable will not be defined at all. Not empty, not zero, just… not there. πŸ«₯ And -mandatory is the opposite of -optional. Honestly? It exists only for completion reasons, since an argument is mandatory by default anyway. It's giving "I'm here to be symmetric" energy. 😭

  • -multiple β€” arguments with the -multiple attribute can be defined multiple times during a procedure call. All the values provided for that argument get stored in a list variable β€” and yes, that's true even if the argument is only defined once during the call. One value, still a list. That's the vibe. πŸ’–

-multiple can go on unnamed arguments and named arguments. For a named argument, the pair of argument name/argument value has to be repeated for each value you provide. And if the -multiple argument is an unnamed one? Then it has to be the absolute last one of all the unnamed arguments. No exceptions, she's the finale. 🌟

  • -choices list β€” you can hand an argument a possible set of valid values with the -choices attribute. Whatever value gets provided during a procedure call will be checked against those choice values. Vibe check, but make it data validation. βœ…
  • -choicelabels list β€” and you can give each choice option an eventual short description with -choicelabels. Those descriptions show up in the generated help texts and act as the radio and check box labels for interactive calls. πŸŽ€

-choicelabels is optional, but if you define it, its list needs to have the identical size as the -choices argument list. They're a matched set, iykyk. πŸ‘‘

  • -range {double double} β€” another constraint you can drop on an argument. The valid range is a list with the minimum valid value and a maximum valid value. And bestie, -range should only be used for numerical arguments, like integers and doubles. Don't try it on your strings, she will not cooperate. πŸ”₯
  • -validatecommand script β€” custom argument value validations, via specific validation commands, defined with -validatecommand. The validation command you provide can be a complete script, and inside it the pattern %P gets replaced by the argument value that has to be validated. 🧚

Validation command declaration example:

tepam::procedure {display_message} {
   -args {
      {text -type string -description "Message text" \
            -validatecommand {IllegalWordDetector %P}}
} {
}

So while this custom attribute's whole purpose is validating one specific argument, there's also a global -validatecommand attribute that lets you do validation involving multiple arguments. Both exist, both are iconic. πŸ’œ

  • -validatecommand_error_text string β€” this lets you override the default error message for a custom argument validation (the one defined by -validatecommand). Sometimes you just want your own words, and honestly? Fair. 🫢
  • -widget string β€” the widgets that let you define the different arguments in an interactive procedure call are normally picked automatically, based on the argument type. But -widget lets you explicitly say "no, use this widget type for this argument." Main character energy, you choose. ✨
  • -auxargs list β€” when a procedure gets called interactively, you can feed additional argument attributes to the interactive argument definition form via -auxargs, which is itself a list of attribute name/attribute value pairs:
-auxargs {-<arg_attr_name_1a> <arg_attr_value_1a> \
          -<arg_attr_name_1b> <arg_attr_value_1b>
          ...
}

For example, if a procedure takes a file name as an argument, it might be beneficial to specify the required file type for the interactive argument definition form. That info gets handed over via -auxargs:

tepam::procedure LoadPicture {
   -args {
      {FileName -type existingfile -description "Picture file" \
                 -auxargs {-filetypes {{"GIF" {*.gif}} {"JPG" {*.jpg}} }}}
   }
} {
}
  • -auxargs_commands script β€” if those auxiliary argument attributes aren't static and have to be dynamically adaptable, -auxargs_commands lets you define them via commands that get executed during a procedure call. You provide a list of pairs of auxiliary attribute names and commands. The provided commands are executed in the context of the calling procedure. Big "she adapts in real time" energy. 🌈
-auxargs_commands {-<arg_attr_name_1a> <arg_attr_command_1a> \
                   -<arg_attr_name_1b> <arg_attr_command_1b>
                   ...
}

πŸ’– VARIABLES πŸ’–

Several variables defined inside the ::tepam namespace impact the mode of operation of the procedures that have been declared with the TEPAM procedure command. Yes bestie, global levers. Let's go. 🌸

named_arguments_first

This variable defines the general calling style of the procedures. By default it's set to 1, which selects the named arguments first, unnamed arguments later style (Tcl style).

By setting this variable to 0, the named arguments first, unnamed arguments later style is globally selected (Tk style):

set tepam::named_arguments_first 0

And while this variable defines the general calling style, the procedure attribute -named_arguments_first can adapt this style individually for each declared procedure. Global vibes, local override. πŸ’…

auto_argument_name_completion

This variable controls the general automatic argument name matching mode. By default it's set to 1, meaning the called procedures try to match eventually abbreviated argument names with the declared argument names. Type half a name, she figures it out. πŸ”

By setting this variable to 0, the automatic argument name matching mode is disabled:

set tepam::auto_argument_name_completion 0

And while this variable defines the general matching mode, the procedure attribute -auto_argument_name_completion can adapt this mode individually for each declared procedure. πŸŽ€

interactive_display_format

A procedure declared via the TEPAM procedure command can always be called with the -interactive switch. Do that and a graphical form gets generated that lets you enter all procedure arguments interactively. Cute, right?

There are two display modes for these interactive forms. The extended mode β€” which is the default β€” is more adapted for small procedure argument sets. The short form is more adequate for huge procedure argument sets:

set tepam::interactive_display_format "short"

The choice between short and extended forms can be globally configured via the variable interactive_display_format. And this global setting can be changed individually for a procedure with the procedure attribute -interactive_display_format. ✨

help_line_length

The maximum line length used by the procedure help text generator can be specified with this variable. The default length is set to 80 (characters), and it can easily be adapted to the need of an application:

set tepam::help_line_length 120

Since this variable is applied directly during the help text generation, its value can continuously be adapted to the current need. Change it mid-flow, she keeps up. πŸ¦‹

command_log

Procedure calls can be logged inside the list variable tepam::ProcedureCallLogList. The variable tepam::command_log controls the default logging settings for any procedures. The following configurations are supported:

  • 0: Disables any procedure call loggings
  • 1: Enables any procedure call loggings
  • "interactive": Will log any procedures called interactively (e.g. procedures called with the -interactive flag). This is the default configuration.

This default logging configuration can be changed individually for each procedure with the -command_log attribute. πŸ“

🌸 ARGUMENT TYPES 🌸

okay bestie, TEPAM comes with a whole comprehensive set of procedure argument types, like a full closet. and if you need something custom, you can absolutely add your own application specific types. no gatekeeping here πŸ’–


πŸ’– PREDEFINED ARGUMENT TYPES πŸ’–

quick reminder: you can assign a type to each specified procedure argument. it's giving ✨organized✨

tepam::procedure {warning} {
   -args {
      {-font -type font -default {Arial 10 italic}}
      {-severity_level -type integer -optional -range {1 10}}
      {-fg -type color -optional -description "Message color"}
      {text -type string -multiple -description "Multiple text lines to display"}
   }
} {
   ...
}

so there's a lil first category of types that are special purpose. here they are, no cap:

  • none β€” a flag, aka a switch, is a named argument with the type none. flags are always optional, and the default value of the assigned variable is set to 0. and here's the iconic part: unlike normal named arguments, you don't have to hand over any argument value to a flag. periodt.
tepam::procedure flag_test {
   -args {
      {-flag -type none -description "This is a flag"}
   }
} {
   puts $flag
}

flag_test
-> 0

flag_test -flag
-> 1

since no argument value has to be provided to a flag, no data check is performed for this argument type either. it's just vibes, honestly πŸ’…

  • string β€” the generic argument data type. any data string can be provided and no data type checks are performed, so it's very much come-as-you-are. the string type lets you define single line strings during the interactive procedure calls.
  • text β€” literally identical to string, the only difference being that it allows entering multi line strings during interactive procedure calls. that's it, that's the glow-up πŸ¦‹
  • {} β€” a blank argument type means an undefined argument type. this is the default argument type used when you didn't explicitly specify one. an argument with a blank type behaves identically to one with a string type, e.g. no argument data checks are performed. the only difference? the data type string gets mentioned in the generated help documentation, and for the blank type it does not. sneaky little detail, iykyk.

several numerical types are defined by TEPAM. their type validation procedures use the string is <type> -strict commands to check whether the provided arguments are valid, which assures that no empty strings are accepted as argument value. here's the type validation expression for the numerical types, and the argument types it applies to:

string is <type_to_check> -strict <argument_value>
  • boolean
  • integer
  • double

empty strings are accepted as argument value for all the alpha numeric argument types 🫢. here's the category and the validation expression used for them:

string is <type_to_check> <argument_value>
  • alnum
  • alpha
  • ascii
  • control
  • digit
  • graph
  • lower
  • print
  • punct
  • space
  • upper
  • wordchar
  • xdigit

and on top of the types checked with the string is <type> commands, TEPAM specifies some other useful data types. obsessed with the range honestly πŸ”₯

  • char β€” any string with a length of 1 character meets the character type. the check is made with this expression:
expr [string length <argument_value>]==1
  • color β€” any character strings that Tk accepts as a color count as a valid color argument. heads up bestie: the Tk package has to be loaded to use the type color. TEPAM uses the following command to validate the color type:
expr ![catch {winfo rgb . <argument_value>}]
  • font β€” any character strings that Tk accepts as a font count as a valid font argument. again, the Tk package has to be loaded to use the type font. TEPAM is using the following command to validate the color type:
expr ![catch {font measure <argument_value> ""}]
  • file β€” any strings that do not contain one of the following characters are considered valid file names: * ? " < >. it is not necessary that the file and its containing directory exist. zero-length strings are not considered valid file names. the following expression is used to validate the file names:
expr [string length <argument_value>]>0 && ![regexp {[\"*?<>:]} <argument_value>]
  • existingfile β€” the argument is valid if it matches with an existing file. this is the check performed to validate arguments of this type:
file exists <argument_value>
  • directory β€” the directory argument is validated exactly in the same way as the file arguments. same rules, different label πŸ’…
  • existingdirectory β€” the argument is valid if it matches with an existing directory. this is the check performed to validate arguments of this type:
file isdirectory <argument_value>

πŸ¦‹ DEFINING APPLICATION SPECIFIC ARGUMENT TYPES πŸ¦‹

want to add support for a brand new application specific argument type? bestie it is so easy. you just need to add a validation function Validation(<type>) into the namespace tepam. that function requires one argument, and it has to return 1 if the provided argument matches the relevant data type. otherwise it has to return 0. that's the whole assignment πŸŽ€

the validation command section of the "tepam.tcl" package provides sufficient examples of validation functions, since it implements the ones for the standard TEPAM types. so go peek at those if you need inspo.

here's a little extra code snippet showing the validation function for a custom argument type that requires values with a character string length of exactly 2:

proc tepam::Validate(two_char) {v} {expr {[string length $v]==2}}

✨ PROCEDURE CALLS ✨

πŸŽ€ HELP πŸŽ€

every procedure can be called with the -help flag. when you do, the procedure prints a generated help text to stdout and then returns without performing any additional actions. cute, informative, and it does not touch anything else. periodt.

taking the first procedure declared in PROCEDURE CALLS, the help request and the printed help text would be:

display message -help
->
NAME
      display message - Displays a simple message box
SYNOPSIS
      display message
            [-mtype <mtype>]
               Message type, default: "Warning", choices: {Info, Warning, Error}
            <text>
               Multiple text lines to display, type: string
DESCRIPTION
      This procedure allows displaying a configurable message box. The default
      message type that is created is a warning, but also errors and info can
      be generated.
      The procedure accepts multiple text lines.
EXAMPLE
      display message -mtype Warning "Save first your job"

the argument manager is checking if the last provided argument is -help, and it generates the requested help message if that's the case. so this one prints the help message too:

display message -mtype Info "It is 7:00" -help

but on the other hand, the following call will result in an error, because -help was not last, bestie:

display message -help -mtype Info "It is 7:00"
->
display message: Argument '-help' not known
🚨 order matters here fr fr β€” -help has to be at the very end or the argument manager won't recognize it as the help request.

πŸ’… INTERACTIVE PROCEDURE CALL πŸ’…

if Tk has been loaded, a procedure can be called with the -interactive flag to open a graphical form that lets you specify all the procedure arguments interactively. main character energy tbh. the following example makes sure the Tk library is loaded and shows the command line to call the procedure declared in PROCEDURE CALLS interactively:

package require Tk
display message -interactive

also the -interactive flag has to be placed at the last argument position, since that's what's required for the -help flag too. arguments defined before the -interactive flag will be ignored. so this next one is also a valid interactive procedure call:

display message -mtype Info "It is 7:00" -interactive

⭐ UNNAMED ARGUMENTS ⭐

unnamed arguments are typically provided to the called procedure as simple parameters. this calling form requires that the provided arguments strictly follow the order of the specified arguments. several parameters can be assigned to the last argument if that one has the -multiple attribute. so this declared procedure...

tepam::procedure {display_message} {
   -args {
      {mtype -choices {Info Warning Error}}
      {text -type string -multiple}
   }
} {
   puts "$mtype: [join $text]"
}

...can for example be called in the following ways:

display_message Info "It is PM 7:00."
-> Info: It is PM 7:00.

display_message Info "It is PM 7:00." "You should go home."
-> Info: It is PM 7:00. You should go home.

and the nice thing is that unnamed arguments can also be called as named arguments, which is super handy, for example if a user doesn't know the exact specified argument order. slay 🫢

display_message -mtype Info -text "It is PM 7:00."
-> Info: It is PM 7:00.

display_message -text "It is PM 7:00." -mtype Info
-> Info: It is PM 7:00.

display_message -mtype Info -text "It is PM 7:00." -text "You should go home."
-> Info: It is PM 7:00. You should go home.

display_message -text "It is PM 7:00." -text "You should go home." -mtype Info
-> Info: It is PM 7:00. You should go home.

πŸ‘‘ NAMED ARGUMENTS πŸ‘‘

named arguments have to be provided to a procedure in the form of parameter pairs composed by the argument names and the argument values. the order you provide them during a procedure call is irrelevant and does not have to match with the argument specification order. freedom, bestie 🌈

this declared procedure...

tepam::procedure {display_message} {
   -args {
      {-mtype -choices {Info Warning Error}}
      {-text -type string -multiple}
   }
} {
   puts "$mtype: [join $text]"
}

...can be called in the following ways:

display_message -mtype Info -text "It is PM 7:00."
-> Info: It is PM 7:00.

Also bestie, listen β€” named args that do not have the -multiple attribute can still be handed to the proc more than once?? Yeah. And the argument manager is like "okay whatever, I'm keeping the last one" πŸ’… Only the last value survives:

display_message -text "It is PM 7:00." -mtype Info
-> Info: It is PM 7:00.

display_message -mtype Info -text "It is PM 7:00." -text "You should go home."
-> Info: It is PM 7:00. You should go home.

display_message -text "It is PM 7:00." -text "You should go home." -mtype Info
-> Info: It is PM 7:00. You should go home.

🌸 UNNAMED ARGUMENTS FIRST, NAMED ARGUMENTS LATER (TK STYLE) 🌸

If a proc got defined while the variable tepam::named_arguments_first was set to 1, or if the proc attribute -named_arguments_first is 1, then it has to be called the Tk style way. Here's the demo proc we're gonna use for this whole section, no cap:

set tepam::named_arguments_first 1
tepam::procedure my_proc {
   -args {
      {-n1 -default ""}
      {-n2 -default ""}
      {u1 -default ""}
      {u2 -default ""}
   }
} {
   puts "n1:'$n1', n2:'$n2', u1:'$u1', u2:'$u2'"
}

So the unnamed args go at the end of the call, after all the named ones:

my_proc -n1 N1 -n2 N2 U1 U2
-> n1:'N1', n2:'N2', u1:'U1', u2:'U2'

And the argument parser's vibe check is literally: the first argument that doesn't start with the - character, plus everything after it, all counts as unnamed:

my_proc U1 U2
-> n1:'', n2:'', u1:'U1', u2:'U2'

Named args can be defined multiple times too, bestie. If that named arg has the -multiply attribute, all the values get collected into a list πŸŽ€ Otherwise only the last value provided gets retained:

my_proc -n1 N1 -n2 N2 -n1 M1 U1 U2
-> n1:'M1', n2:'N2', u1:'U1', u2:'U2'
⚠️ Which means the name of the first unnamed argument must not start with the - character. If it does, it gets read as the name of another named argument instead. This is so important when the first unnamed argument comes from a variable that could hold literally any string, fr fr:
my_proc -n1 N1 -n2 N2 "->" "<-"
-> my_proc: Argument '->' not known

set U1 "->"
my_proc -n1 N1 -n2 N2 $U1 U2
my_proc: Argument '->' not known

The -- flag is your bestie here β€” it splits the unnamed args from the named args with zero ambiguity. Everything after -- is treated as unnamed:

my_proc -n1 N1 -n2 N2 -- "->" "<-"
-> n1:'N1', n2:'N2', u1:'->', u2:'<-'

set U1 "->"
my_proc -n1 N1 -n2 N2 -- $U1 U2
-> n1:'N1', n2:'N2', u1:'->', u2:'<-'

πŸ¦‹ NAMED ARGUMENTS FIRST, UNNAMED ARGUMENTS LATER (TCL STYLE) πŸ¦‹

The Tk calling style gets picked when a proc is defined while tepam::named_arguments_first is set to 0, or when the proc attribute -named_arguments_first has been set to 0. Here's the demo proc for this section, iconic behaviour incoming:

set tepam::named_arguments_first 0
tepam::procedure my_proc {
   -args {
      {-n1 -default ""}
      {-n2 -default ""}
      {u1}
      {u2 -default "" -multiple}
   }
} {
   puts "n1:'$n1', n2:'$n2', u1:'$u1', u2:'$u2'"
}

Now the unnamed args have to come first, and the named ones after:

my_proc U1 U2 -n1 N1 -n2 N2
-> n1:'N1', n1:'N1', u1:'U1', u2:'U2'

The parser hands a value to each defined unnamed argument before it ever switches over to reading named arguments. This default behaviour shifts a little when there are unnamed arguments that are optional or that can take multiple values. Stay with me πŸ’–

  • A value only gets assigned to an unnamed argument that's optional (either it has the -optional attribute or it has a default value) if that value does not begin with the - character β€” or if no named arguments are defined at all. Otherwise, a value starting with - is treated as the name of a named argument.
  • Values keep getting assigned to an argument that has the -multiple attribute for as long as the parameter value doesn't start with the - character.

So values that start with - simply can't be assigned to optional unnamed arguments, which lowkey restricts how you can use the Tcl procedure calling style. The Tk style might be the move in some cases, since it lets you cleanly split the named args from the unnamed ones with the -- flag.

Okay now let's get less theoretical and actually look at the ways this proc can be called πŸ‘‘ First example: no parameters at all, which straight up errors because u1 is mandatory:

my_proc
-> my_proc: Required argument is missing: u1

The call is valid if one parameter is provided for u1:

my_proc U1
-> n1:'', n2:'', u1:'U1', u2:''

If you throw in more parameters that don't start with -, they get attributed to the unnamed arguments. U2 will eat 3 of them, because it accepts multiple values:

my_proc U1 U2 U3 U4
-> n1:'', n2:'', u1:'U1', u2:'U2 U3 U4'

The second a parameter starts with - and all unnamed arguments have been assigned, the argument manager tries to read that parameter as the name of a named argument. And the call fails if a value beginning with - gets assigned to an unnamed argument:

my_proc U1 U2 U3 U4 -U5
-> my_proc: Argument '-U5' not known

Attributing a parameter to a named argument also fails if there are undefined unnamed (non-optional) arguments. In that case the name spec just gets treated as a plain parameter value handed to the next unnamed argument. Which is definitely not what was intended here:

my_proc -n1 N1
-> n1:'', n2:'', u1:'-n1', u2:'N1'

Completely different story if values have already been assigned to all the mandatory unnamed arguments. Then a parameter beginning with - gets read as a name identifier for a named argument:

my_proc U1 -n1 N1
-> n1:'N1', n2:'', u1:'U1', u2:''

And no unnamed arguments are allowed behind the named ones, periodt:

my_proc U1 -n1 N1 U2
-> my_proc: Argument 'U2' is not an option

The -- flag has no special meaning if not all mandatory arguments have been assigned a value yet. It just gets attributed to one of the unnamed arguments like it's nothing:

my_proc -- -n1 N1
-> n1:'N1', n2:'', u1:'--', u2:''

But -- is simply ignored if the argument parser has already started handling the named arguments:

my_proc U1 -- -n1 N1
-> n1:'N1', n2:'', u1:'U1', u2:''

my_proc U1 -n1 N1 -- -n2 N2
-> n1:'N1', n2:'N2', u1:'U1', u2:''

πŸ“ RAW ARGUMENT LIST πŸ“

Sometimes the procedure body just needs to see the whole list of arguments from the call, and honestly, fair. That happens through the args variable, which always holds the unprocessed argument list 🫢

tepam::procedure {display_message} {
   -args {
      {-mtype -choices {Warning Error} -default Warning}
      {text -type string -multiple}

   }
} {
   puts "args: $args"
}
display_message -mtype Warning "It is 7:00"
-> args: -mtype Warning {It is 7:00}

πŸ‘€ SEE ALSO

  • tepam(n)
  • tepam::argument_dialogbox(n)

πŸ”‘ KEYWORDS

argument integrity, argument validation, arguments, procedure, subcommand


πŸ—‚ CATEGORY

Procedures, arguments, parameters, options


Copyright (c) 2009-2013, Andreas Drollinger


And that's the whole page bestie, we ate and left zero crumbs ✨ This one came from tcllib, version 0.5.0, and the manpage footer is where all the source/version deets live β€” so if you're ever squinting at the bottom of the terminal like "who made this", that's your answer πŸ’– mwah, go write some flawless procs πŸŽ€πŸ’…

πŸ“¦ where this page comes from