Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is the proper way to create hyphen arguments in TCL?

462 views
Skip to first unread message

Hassan Iqbal

unread,
Aug 18, 2021, 4:27:50 PM8/18/21
to
OK lets say I want to be able to call a function like this:

myfunc -param_action_a param_1 param_2 param_3 -param_action_b param_1 -no_param_action_c -no_param_action_d

Now there might be complex interdependencies because:
1. Some actions are compulsory to be specified, maybe one of many or all of many need to be specified when myfunc is called
2. Some actions are optional in any case and myfunc can always be successful without them being specified
3. Some actions are optional but can only be specified when certain other option exists but not otherwise
4. Some actions cannot be specified when certain others are specified and doing so leads to error

5. Some actions need parameters
6. Some actions do not need parameters
7. Some actions have default parameters maybe some or all
8. Some actions have no default parameters
9. The parameters must be validated to ensure that their value is not invalid

Now I have found that there exists these things:
1. parse_args
2. argparse
3. cmdline
4. Just hack a way using arrays and lists

Now I cannot for the life of me figure out the proper way to do this which satisfies all the above requirements. Only a "proper" solution can do that. I do not have a lot of time to compare and contrast the 4 methods above. Does TCL have a proper solution to the problem I have described?

Maybe someone from the TCL creators group can guide me here.

Gerald Lester

unread,
Aug 18, 2021, 5:00:38 PM8/18/21
to
My gut says you are attempting to put too much into your function.

Have multiple functions, one per action that only take the correct things.

Look into namespace ensembles.

Another option, use objects with your action being individual methods.


--
+----------------------------------------------------------------------+
| Gerald W. Lester, President, KNG Consulting LLC |
| Email: Gerald...@kng-consulting.net |
+----------------------------------------------------------------------+

Rich

unread,
Aug 18, 2021, 5:58:56 PM8/18/21
to
Hassan Iqbal <housebu...@gmail.com> wrote:
> OK lets say I want to be able to call a function like this:
>
> myfunc -param_action_a param_1 param_2 param_3 -param_action_b
> param_1 -no_param_action_c -no_param_action_d

I concur with Gerald's comments here, at first glance it appears you
may be building a monster.

> 3. cmdline
>
> Now I cannot for the life of me figure out the proper way to do this
> which satisfies all the above requirements. Only a "proper" solution
> can do that. I do not have a lot of time to compare and contrast the
> 4 methods above. Does TCL have a proper solution to the problem I
> have described?

Tcl has no built in "argument parser". So you are left with either
using an existing one or creating your own.

cmdline is part of Tcllib, and is utilized by at least nine (count
created by a quick grep) of the other Tcllib modules, so if there is
anything that somewhat comes close to being "official" then cmdline
would be closest to being "official".

So if cmdline will do what you need, then use it.

If not, then use (or create) one that does do what you need.

Harald Oehlmann

unread,
Aug 19, 2021, 2:36:55 AM8/19/21
to
Dear Hassan,

thank you for asking. Please look into cmdline from TclLib.
I would recommend this package.

It was a big discussion on including optional parameter to TCL core.
Unfortunately, this discussion did not lead to consensus. So, we are
stuck with this solution.
Please allow me to use the oppertunity to write, that this is a feature
which may be very helpful and finding a solution would be great.

Thank you,
Harald

Hassan Iqbal

unread,
Aug 19, 2021, 3:27:47 AM8/19/21
to
Thanks.

At the minimum, is there someone that has done comparison of these:

1. parse_args
2. argparse
3. cmdline

I know they are similar but don't know the actual differences between them and if one can actually fully replace the other.

Harald Oehlmann

unread,
Aug 19, 2021, 4:32:57 AM8/19/21
to
Hassan,
I did not try those. I think, everybody has his own implementation.
What I personally use for internal procedures, so I don't need error
check et:

proc test args {
# Use default arguments if an argument is not given
set args [dict merge {-par1 1 -par2 2} $args]
# Now show the resulting values
puts "-par1 is [dict get $args -par1]"
puts "-par2 is [dict get $args -par2]"
}

% test -par1 A
-par1 is A
-par2 is 2

Take care,
Harald

EL

unread,
Aug 19, 2021, 4:44:05 AM8/19/21
to
On 19.08.2021 09:27, Hassan Iqbal wrote:

> At the minimum, is there someone that has done comparison of these:
>
> 1. parse_args
> 2. argparse
> 3. cmdline


Not really. I never needed any of these, instead I use (and would
recommend) the approach that Gerald has suggested above:

# call
command subcmd1 -arg1 value1 ...
# etc.


proc command {subcmd args} {
$subcmd {*}$args
}

proc subcmd1 {subsubcmd args} {
$subsubcmd {*}$args
}

proc subcmd2 {subsubcmd args} {
...
}

proc subsubcmd1 {args} {
...
}

...


Then the args parsing is easier and can evtl. be done with simple [dict
exists], [switch], etc. in the sub commands. That reduces the complexity.


--
EL

John Peck

unread,
Aug 19, 2021, 2:10:29 PM8/19/21
to

ted brown

unread,
Aug 19, 2021, 4:02:35 PM8/19/21
to
This looks like a little language with myfunc as the interpreter. I
would first try to define your grammar a bit more precisely.

For example, can the parameters to these actions also start with a
hyphen? For simplicity, let's assume that won't happen, or if it does,
it's an error to be dealt with.

How do you intend to get these args which then amounts to being a little
script for your little language. Will you be hand coding them, or might
they be generated or read in from somewhere?

Are the actions to be done in order left to right? (I will assume so).

Do you have to do all action verifications before doing any actions at all?

Will the number of actions be fixed, or will you be adding new ones over
time?

My guess is that with just a few lines of tcl code you can quite easily
handle this. It might be easier than trying to find some existing
library code that might be a tricky fit. So, for me, I'd likely just
choose option 4 in your first post.

Let's abbreviate and rewrite your example for easier readability (with
q1 instead of p1 so not to confuse them) and group them on separate lines:

myfunc \
-a p1 p2 p3 \
-b q1 \
-c \
-d

Here, we have what amounts to what Gerald suggested previously, and
could be rather simply changed to tcl code:

a p1 p2 p3
b q1
c
d

Then each of a,b,c,d,... as procs would just validate their own args.

But if you are creating these dynamically, and need an interpreter,
and/or you must pre-validate everything before doing any actions, you
could say, use args for the single parameter to myfunc, and then do
something like this:

proc myfunc {args} {
puts "args= |$args| "
set actions [list]
set action [list]
# convert input to a list of lists
foreach arg $args {
if { [string index $arg 0] eq "-" } {
lappend actions $action ;# close prior
set action [list $arg] ;# start new action
} else {
lappend action $arg ;# add non - parms
}
}
lappend actions $action ;# add the final pending action
puts "actions= |$actions| "
}

For the above example, this would produce:

args= |-a p1 p2 p3 -b q1 -c -d|
actions= |{} {-a p1 p2 p3} {-b q1} -c -d|

You could either remove/skip the first empty one, or just don't generate it.

Next you can make a [foreach] pass on the actions list and validate each
action's parameters. If all ok, you could make a second pass invoking
them in order (as I assumed).

You would likely use a multi-way if/elseif..else.. (or switch) on the
types of actions that could be expanded easily. The above conversion to
a list of lists, would automatically adjust to any additional future
actions.

Uwe Klein

unread,
Aug 20, 2021, 2:58:13 AM8/20/21
to
Am 19.08.21 um 09:27 schrieb Hassan Iqbal:
> At the minimum, is there someone that has done comparison of these:
>
> 1. parse_args
> 2. argparse
> 3. cmdline
>
> I know they are similar but don't know the actual differences between them and if one can actually fully replace the other.
>

google :: tcl options parser
quite a range of links coming up.

a judicious amount on
https://wiki.tcl-lang.org/page/Command+Option+Parsing

Uwe

Gerald Lester

unread,
Aug 20, 2021, 11:50:33 AM8/20/21
to
And of course you could have your mini-language run in a (safe) slave
interpreter with only the Tcl commands you want and alias' that invoke
your procedures in the parent interpreter.

Hassan Iqbal

unread,
Aug 20, 2021, 12:35:21 PM8/20/21
to
I have some information to consider here, I shall think about it over the weekend.

.

unread,
Aug 21, 2021, 12:20:24 AM8/21/21
to
On 8/18/2021 1:27 PM, Hassan Iqbal wrote:
> OK lets say I want to be able to call a function like this:
>
> myfunc -param_action_a param_1 param_2 param_3 -param_action_b param_1 -no_param_action_c -no_param_action_d

Here is example code -- that does all of the -dash argument magic you
are looking for. I use this method on 100s, dare I say, 1000s of procs.

Note: I think there is terminology about "tokens" that's important to
understand and I mention that if you wish to google on it.

Taking your example, the first parameter, you showed was:
-param_action_a param_1 param_2 param_3
While each part of this is a token.
-param_action_a is <token1>
param_1 param_2 and param_3 are each their own separate tokens.

But what you really want to do is define this as 2 tokens:
-param_action_a is <token1>
"param_1 param_2 param_3" is <token2>
So that <token2> is now a list.


When token2 is defined as a list it shall be passed like so in tcl:

myfunc -param_action_a "item1 item2 item3"
or like so:
myfunc -param_action_a [list item1 item2 item3]

Now, with that in-hand -- here is the code to parse -dash arguments
properly:

# Examples:
# myproc -file <name>
# or:
# myproc -file <list_of_files> -remove_header true -overwrite
# or
# myproc -file <file> -file <file> ...

myproc {args} {
set procname "[dict get [info frame [info frame]] proc]"

set argn [llength $args]

set file_list ""
set remove_header false

for set {i 0} {$i < [expr {${argn}}] } { incr i } {
set arg [string tolower [lindex $args $i]]
switch -exact -- $arg {
-file {
set file_list [concat $file_list [lindex $args [incr i] ] ]
}
-remove_header {
set remove_header [lindex $args [incr i] ]
}
-overwrite {
set overwrite "1"
}
default {
set msg "Error: $procname Option not recognized: arg=\[$arg\],
args=\[$args\]"
puts $msg
error "$msg"
}
}
}

# body of the proc goes here...

}
0 new messages