It can be useful and sometimes necessary to write procedures that use external parameters or arguments as they can broaden the spectrum of usage of the procedure without having to write too much redundant code. A single procedure that can handle multiple contexts is easier to use and maintain that multiple procedures that cover the same range of contexts with duplicated code.
This is especially useful when the procedure is being used interactively. It is a lot friendlier for the user to be able to specify some command line options like with any Vivado commands.
Tcl provides an easy way to do this through the args
variable. The keyword args
used
inside the list of arguments of a procedure can match any number of elements, including
none. The args
variable is a regular Tcl list that can
be processed and analyzed like any Tcl list.
There are multiple techniques to parse the command line arguments, and the example code below shows just one of them:
01 proc lshift listVar {
02 upvar 1 $listVar L
03 set r [lindex $L 0]
04 set L [lreplace $L [set L 0] 0]
05 return $r
06 }
07
08
09 proc myproc { args } {
10
11 #-------------------------------------------------------
12 # Process command line arguments
13 #-------------------------------------------------------
14 set error 0
15 set help 0
16 set verbose 0
17 set ports {}
18 # if {[llength $args] == 0} { incr help }; # Uncomment if necessary
19 while {[llength $args]} {
20 set flag [lshift args]
21 switch -exact -- $flag {
22 -p -
23 -ports {
24 set ports [lshift args]
25 }
26 -v -
27 -verbose {
28 set verbose 1
29 }
30 -h -
31 -help {
32 incr help
33 }
34 default {
35 if {[string match "-*" $flag]} {
36 puts " ERROR - option '$flag' is not a valid option."
37 incr error
38 } else {
39 puts "ERROR - option '$flag' is not a valid option."
40 incr error
41 }
42 }
43 }
44 }
45
46 if {$help} {
47 set callerflag [lindex [info level [expr [info level] -1]] 0]
48 # <-- HELP
49 puts [format {
50 Usage: %s
51 [-ports|-p <listOfPorts>]
52 [-verbose|-v]
53 [-help|-h]
54
55 Description: xxxxxxxxxxxxxxxxxxx.
56 xxxxxxxxxxxxxxxxxxx.
57
58 Example:
59 %s -port xxxxxxxxxxxxxxx
60
61 } $callerflag $callerflag ]
62 # HELP -->
63 return -code ok {}
64 }
65
66 # Check validity of arguments. Increment $error to generate an error
67
68 if {$error} {
69 return -code error {Oops, something is not correct}
70 }
71
72 # Do something
73
74 return -code ok {}
75 }
Explanations:
- Lines 1-6: Definition for the
lshift
procedure that removes the first element of a list. - Line 9:
myproc
is defined with a single argument,args
, that can take any number of elements. In this example code,myproc
supports three command line options:-ports <
string
>
,-verbose
, and-help
. - Lines 19-44: Loop through all the command line arguments. When all
the arguments have been processed, the
args
variable is empty. - Line 20: The command line argument that needs to be processed is
saved inside the
flag
variable. Use thelshift
proc to get the argument and remove it from theargs
variable. - Lines 21-43: Check the content of the
flag
variable against all the valid arguments. The switch statement uses the-exact
option so that the full option name is checked against the content offlag
. For example, to define the ports, the user needs to specify-p
or-ports
.The
-p
/-ports
option takes a command line argument that is being read and removed from the list args withlshift
args (line 24).The
-v
/-verbose
option is just a boolean and therefore does not need any additional argument fromargs
(line 28).Lines 31-33: Check for the
-h
/-help
options.Lines 36-38: Check for any command line argument starting with "-" (without quotes). In this sample proc, they are not supported.
Lines 39-40: Check for a command line argument that does not start with "-" (without quotes). In this example proc, they are not supported.
- Lines 46-64: Display the embedded Help if
-h
/-help
has been specified. Those lines as well as lines 30-33 can be removed if the proc does not need to provide any embedded Help. - Lines 68-70: Check if any error has occurred. Typically, some
additional code to check the validity of the arguments should happen before line 68.
If there would be any error or, for example, incompatibility between the command
line options provided by the user, then the
error
variable could be incremented which would then trigger line 69. - Line 73 and beyond: Add your code here.
The above code parses the command line arguments and searches for an
exact match with the supported options (line 21). However, there are some cases when it
might be better to match the command line arguments against some expressions instead of
searching for an exact match. This is done by using the -glob
switch instead of the -exact
switch
on line 21. See the following example.
21 switch -glob -- $flag {
22 -p* -
23 -ports {
24 set ports [lshift args]
25 }
26 -v* -
27 -verbose {
28 set verbose 1
29 }
30 -h* -
31 -help {
32 incr help
33 }
34 default {
35 if {[string match "-*" $flag]} {
36 puts " ERROR - option '$flag' is not a valid option."
37 incr error
38 } else {
39 puts "ERROR - option '$flag' is not a valid option."
40 incr error
41 }
42 }
43 }
Lines 22, 26 and 30 illustrate some expressions using the "*
" as a wildcard. The above code matches any string
starting with -p
as a valid command line option to
define the ports, for example -pfoo
.
Example
Although the example procedure, myproc
, is acceptable for an interactive command, it
has some runtime overhead due to the parsing of the arguments. The runtime overhead
might not be acceptable for a low-level procedure that is called many times. A
different technique can be used to add some command line arguments to a procedure
that needs very little runtime overhead. This is done by assigning the list of
commands line arguments to a Tcl array. However, this implies that each command line
option has one and only one argument. See the following example.
01 proc myproc2 { args } {
02 # Default values
03 set defaults [list -p 123 -v 0]
04 # First, assign default values
05 array set options $defaults
06 # Then, override with user choice
07 array set options $args
08
09 set ports $options(-p)
10 set verbose $options(-v)
11 set error 0
12
13 # Check validity of arguments. Increment $error to generate an error
14
15 if {$error} {
16 return -code error {Oops, something is not correct}
17 }
18
19 # Do something
20
21 return -code ok {}
22 }
Explanations
- Line 1:
myproc2
is defined with a single argument,args
, that can take any number of elements. However, sinceargs
is used later to set a Tcl array, it must have an even number of arguments. - Line 3: Default values for the various options. Each option has one and only one value.
- The format of the list is:
expanse="page"> <option1> <valueForOption1> <option2> <valueForOption2> … <optionN> <valueForOptionN>
- Line 5: The Tcl array
options
is initialized with the default values. - Line 7: The
args
variable overrides the default values - Line 9-10: The value of each option is being read with
$options(<option>)
. It is also possible to check that an option exist with the following.if [info exists options(<option>)] { … }
Note: The command line options that are working as a flag and have no intrinsic value are easily implemented by passing, for example, a value of 0 or 1 with the option. In the previous example procedure, the flag-v
is turned on with:myproc2 -v 1
.