TIP #103 Version 1.4: Argument Expansion Command

This is not necessarily the current version of this TIP.


TIP:103
Title:Argument Expansion Command
Version:$Revision: 1.4 $
Author:Peter Spjuth <peter dot spjuth at space dot se>
State:Draft
Type:Project
Tcl-Version:8.5
Vote:Pending
Created:Saturday, 15 June 2002

Abstract

This TIP proposes to add a command that can perform argument expansion in a safe and efficient manner.

Introduction

Many commands take a variable number of arguments and often you find yourself with those arguments in a list. This list must then be expanded into individual arguments to the command. This is currently done with eval:

eval destroy [winfo children .]

This is a bit obscure and also very error prone when the command becomes more complex. It is also inefficient and not object safe, why a command specialised in doing this would be better.

Rationale

There have been suggestions of introducing some new syntax to Tcl to handle argument expansion. That is a big and controversial step, and not anything this TIP wants to meddle in. A command can improve every point where eval has shortcomings and thus give a good result with less means.

Such a command can be done in several ways and below the choice in this TIP's specification is defended.

As examples three statements are used which will be repeated for different alternatives. This is the eval version:

eval destroy [winfo children .]
eval button .b $stdargs -text \$mytext -bd $border
eval exec \$prog $opts1 [getMoreopts] \$file1 \$file2

The eval version would be even more complex if the lists that are to be expanded are not known to be pure. To be really safe the last would be:

eval exec \$prog [lrange $opts1 0 end] [lrange [getMoreopts] 0 end] \$file1 \$file2

With the proposed command they become:

expand { destroy @[winfo children .] }
expand { button .b @$stdargs -text $mytext -bd $border }
expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }

An alternative to having a local syntax is to point at the arguments that should be expanded, either by index:

expand {end} destroy [winfo children .]
expand {2} button .b $stdargs -text $mytext -bd $border
expand {2 3} exec $prog $opts1 [getMoreopts] $file1 $file2

Or by some flag mechanism:

expand destroy + [winfo children .]
expand button .b + $stdargs -text - $mytext -bd $border
expand exec - $prog + $opts1 + [getMoreopts] - $file1 - $file2

Those lack in writability/readability/maintainability in a disturbing manner.

For the choice of local syntax the first goal is that it should not violate Tcl's rules, which simplifies implementation since Tcl's parser can do the job.

Any char that fulfils that could be used but the choice fell on @ since that char is odd enough to be visible which helps readability.

An alternative syntax could be:

expand { destroy <[winfo children .]> }
expand { button .b <$stdargs> -text $mytext -bd $border }
expand { exec $prog <$opts1> <[getMoreopts]> $file1 $file2 }

Using enclosing symbols suggests that they may affect grouping, which they would not if Tcl's parser shall be used. Thus a single char is less likely to cause confusion.

Specification

A new command "expand" is added. It takes one argument, which contains a Tcl script consisting of one command. The script may contain comments but only one command is permitted.

The command is processed in the following manner:

  1. Parse into words according to Tcl's standard rules.

  2. Any word starting with @ is remembered and the @ is removed.

  3. Perform Tcl's normal execution steps on the new line up to the point where the command should have been called.

  4. Expand the arguments that should be expanded.

  5. Execute the command.

The return value of expand is the return value of the command.

Note 1: A word should really start with @ to trigger expansion which means that words like these are not expanded:

cmd "@$temp" {@home} \@[something]

Note 2: Even though it makes most sense to use @ with words like:

cmd @$var @[somecmd $arg]

the rules allow usages like this:

cmd @$var$apa @[foo]xy[apa]

Note 3: If removal of the @ changes word boundaries of the command it leads to a tricky case:

cmd @"word1 word2"

Handling this properly is both tricky and rather useless. It is probably best to just declare it illegal.

Forward compatibility

One aspect of choosing a syntax here is to think about the future. Should there later be a wish for a global syntax for argument expansion it would be nice if it were the same as the one chosen in the expand command. From this point of view @ may be a bad choise since that might not be likely for a global syntax. If an agreement can be made for what may be acceptable in the future, this should affect the specification in this TIP.

If a single character like @ is chosen for a global expand syntax it means a backwards compatibility break. So, what chars are likely to be used by people and thus causing problems when backwards compatibility is broken?

Some food for thought about different chars:

!   if !$var {...}
%   format %d $var
&   exec something &
'   # Looks like a grouping char?
(   # Looks like a grouping char, confuses paren-matching
)   # Odd enough as opening, but would confuse any paren-matching
*   string match *$word* $line
+   wm geometry +10+10
,
-   incr a -1
.   button .b
/   open /home/xxxx
:   set ::var
<   bind . <Button-1>   # Looks like a grouping char?
=   expr $a == $b
>   exec something > somewhere
?
@   # Real example: it is used in Bras.
^   regexp ^hejsan $line
_   # Word char
`   # Looks like a grouping char?
|   open |prog
~   cd ~$user

Example usage of those that seem reasonable:

expand { exec $prog @$opts1 @[getMoreopts] $file1 $file2 }
expand { exec $prog '$opts1 '[getMoreopts] $file1 $file2 }
expand { exec $prog `$opts1 `[getMoreopts] $file1 $file2 }
expand { exec $prog ,$opts1 ,[getMoreopts] $file1 $file2 }
expand { exec $prog ?$opts1 ?[getMoreopts] $file1 $file2 }

For comparison, the syntax that has been proposed earlier that would not break backwards compatibility:

expand { exec $prog {}$opts1 {}[getMoreopts] $file1 $file2 }
expand { exec $prog {expand}$opts1 {expand}[getMoreopts] $file1 $file2 }

Discussion

When first issued the TIP caused some discussion on c.l.t. Until a summary is made, here is the thread:

http://groups.google.com/groups?th=9e77d5836b06ab1b

Reference Implementation

Patch #570201

http://sourceforge.net/tracker/index.php?func=detail&aid=570201&group_id=10894&atid=310894

Copyright

This document has been placed in the public domain.


Powered by TclThis is not necessarily the current version of this TIP.

TIP AutoGenerator - written by Donal K. Fellows