TIP:            195
Title:          Script Access to Tcl_GetIndexFromObj
Version:        $Revision: 1.10 $
Author:         Peter Spjuth <peter.spjuth@space.se>
State:          Draft
Type:           Project
Vote:           Pending
Created:        02-May-2004
Post-History:   
Keywords:       Tcl
Obsoletes:      105
Tcl-Version:    8.5

~ Abstract

This TIP adds a new command to support matching of strings to unique
prefixes of patterns, similar to Tcl's existing subcommand-name
matching or Tk's option-name matching.

~ Rationale

When code (particularly in script libraries) wants to support shortest
unique prefix matching in the manner of the Tcl core (as provided by
''Tcl_GetIndexFromObj'') currently either the prefixes have to be
precomputed (by hand or by script) or the matching has to be done
backwards.  In the first case, this is either error-prone or requires
an extra piece of code that has to be developed by the programmer.  In
the second case, the code has to be converted into a pattern which is
matched against the list of supported options in some way, which is
either inefficient or has hazards if the string being matched contains
characters that are meaningful to the matching engine being used.
Instead, it would be far nicer if we could make the core support this
directly, so that script authors could just say what they mean.

See also [105], which the text above comes from.

Another benefit of this command is getting an error message that
looks like those nice and informative error messages that built in
commands have.  The proposed command includes a flag to control
the error behaviour.

Another use of prefix matching is for completion, such as a combobox
showing matching alternatives, or tab completion at a prompt.

The former needs a list of matching elements, and the latter needs
a longest common prefix.  

These functionalities are similiar enough to group them but different
enought to make it a bad idea for flags, so a prefix ensemble seems
approriate for them.

~ Proposed Change

To support this, I propose adding a '''prefix''' ensemble with the
following subcommands

 * '''prefix''' '''match''' ?'''-exact'''? ?'''-message''' ''string''? ?'''-error''' ''level''? ''list'' ''string''

 * '''prefix''' '''longest''' ''list'' ''string''

 * '''prefix''' '''all''' ''list'' ''string''

All commands are given a list of possibilities and a string to match.

'''prefix''' '''match''' returns the matching element in the list or an
error.  Basically it does what ''Tcl_GetIndexFromObj'' does except it
returns a string instead of an index.  The options '''-exact''' and '''-message''' corresponds to the ''flags'' and ''msg'' arguments to
''Tcl_GetIndexFromObj''.  The default value for '''-message''' is
"option".

The '''-error''' level tells how to behave when no match is found.
At level 0, no error is generated and an empty string is returned.
At level 1 (default), a normal error message is returned.
At level 2, the error message is returned so that it shows up as
an error from the caller, i.e. it behaves like
[[return -code error -level 2]].

'''prefix''' '''longest''' returns the longest common prefix within
the group that matches the given string.  If there is no match, 
an empty string is returned.

'''prefix''' '''all''' returns a list of all matching elements.

~ Examples

Basic use:

|% prefix match {apa bepa cepa} apa
|apa
|% prefix match {apa bepa cepa} a
|apa
|% prefix match -exact {apa bepa cepa} a
|bad option "a": must be apa, bepa, or cepa
|% prefix match -message "switch" {apa ada bepa cepa} a
|ambiguous switch "a": must be apa, ada, bepa, or cepa
|% prefix longest {fblocked fconfigure fcopy file fileevent flush} fc
|fco
|% prefix all {fblocked fconfigure fcopy file fileevent flush} fc
|fconfigure fcopy

Simplifying option matching:

|array set opts {-apa 1 -bepa "" -cepa 0}
|foreach {arg val} $args {
|    set opts([prefix match {-apa -bepa -cepa} $arg]) $val
|}

Switch similar to [105]:

|switch -- [prefix match {apa bepa cepa} $arg] {
|    apa  { }
|    bepa { }
|    cepa { }
|}

~ Alternative names

Alternative names for the command that have been suggested are
'''tcl::prefix''', '''string prefix''', '''string disambiguate''',
'''string prefixmatch''', '''string matchprefix''' and
'''lsearch -prefix'''.

Any name based on ''Tcl_GetIndexFromObj'' feels wrong since this
command does not get any index.

~ Discussion

Pascal Scheffers wrote:

I am not very fond of new toplevel commands, as they have a habit
of breaking existing code, or, more likely, being redefined by
existing code.  How about '''string prefix ...''' or
'''lsearch -prefix'''? It feels like something that
could logically reside inside string handling or list handling.

Donal Fellows wrote:

Hmm, a new matching style for '''lsearch''' could work (yet another
option for an overly scary command!) especially in conjunction with
some of the other options like -inline, but I suspect it'll have to be
'''string prefix''' or something like that since then we can easily
state that the behaviour is not to necessarily return the first
matching element. That was what scuppered #105 after all (much to my
annoyance.)

Peter Spjuth: TIP was at this point altered to suggest '''string disambiguate''' after a suggestion from Donal.

Donal Fellows wrote:

I'd suggest that the '''-exact''' option be dropped, as it is trivially
implementable using '''lsearch -exact''', '''dict exists''', '''info exists''',
'''switch''', etc. It's also corresponding to a very rarely used flag to
Tcl_GetIndexFromObj.

Peter Spjuth: Other ways to do '''-exact''' doesn't give the
standardized error message so it has some value for those that want
that functionality.  The Core uses TCL_EXACT in a few places where it makes sense so the flag can be useful.

Voices were raised against the proposed '''string disambiguate''' and Jeff
Hobbs pointed out other prefix related functions that are useful.  This
lead to the prefix ensemble suggestion.

~ Reference Implementation

An old version is at:
http://sourceforge.net/tracker/index.php?func=detail&aid=1040206&group_id=10894&atid=310894

~ Copyright

This document has been placed in the public domain.
