Parsing Command Line Arguments - 2021.2 English

Vivado Design Suite User Guide: Using Tcl Scripting (UG894)

Document ID
UG894
Release Date
2021-11-17
Version
2021.2 English

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:

  1. Lines 1-6: Definition for the lshift procedure that removes the first element of a list.
  2. 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.
  3. Lines 19-44: Loop through all the command line arguments. When all the arguments have been processed, the args variable is empty.
  4. Line 20: The command line argument that needs to be processed is saved inside the flag variable. Use the lshift proc to get the argument and remove it from the args variable.
  5. 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 of flag. 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 with lshift args (line 24).

    The -v/-verbose option is just a boolean and therefore does not need any additional argument from args (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.

  6. 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.
  7. 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.
  8. 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

  1. Line 1: myproc2 is defined with a single argument, args, that can take any number of elements. However, since args is used later to set a Tcl array, it must have an even number of arguments.
  2. Line 3: Default values for the various options. Each option has one and only one value.
  3. The format of the list is:
    expanse="page">  <option1> <valueForOption1> <option2> <valueForOption2> … <optionN> <valueForOptionN> 
  4. Line 5: The Tcl array options is initialized with the default values.
  5. Line 7: The args variable overrides the default values
  6. 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.