Skip to content

Commit efe4092

Browse files
committed
Implement TIP 730: switch -integer
2 parents 395d3d0 + 7cb9f9d commit efe4092

File tree

4 files changed

+433
-45
lines changed

4 files changed

+433
-45
lines changed

doc/switch.n

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,63 @@
1212
.SH NAME
1313
switch \- Evaluate one of several scripts, depending on a given value
1414
.SH SYNOPSIS
15-
\fBswitch \fR?\fIoptions\fR?\fI string pattern body \fR?\fIpattern body \fR...?
15+
\fBswitch \fR?\fIoptions\fR?\fI value pattern body \fR?\fIpattern body \fR...?
1616
.sp
17-
\fBswitch \fR?\fIoptions\fR?\fI string \fR{\fIpattern body \fR?\fIpattern body \fR...?}
17+
\fBswitch \fR?\fIoptions\fR?\fI value \fR{\fIpattern body \fR?\fIpattern body \fR...?}
1818
.BE
1919
.SH DESCRIPTION
2020
.PP
21-
The \fBswitch\fR command matches its \fIstring\fR argument against each of
21+
The \fBswitch\fR command matches its \fIvalue\fR argument against each of
2222
the \fIpattern\fR arguments in order.
23-
As soon as it finds a \fIpattern\fR that matches \fIstring\fR it
23+
As soon as it finds a \fIpattern\fR that matches \fIvalue\fR it
2424
evaluates the following \fIbody\fR argument by passing it recursively
2525
to the Tcl interpreter and returns the result of that evaluation.
2626
If the last \fIpattern\fR argument is \fBdefault\fR then it matches
2727
anything.
2828
If no \fIpattern\fR argument
29-
matches \fIstring\fR and no default is given, then the \fBswitch\fR
29+
matches \fIvalue\fR and no default is given, then the \fBswitch\fR
3030
command returns an empty string.
3131
.PP
3232
If the initial arguments to \fBswitch\fR start with \fB\-\fR then
3333
they are treated as options
3434
unless there are exactly two arguments to \fBswitch\fR (in which case the
35-
first must the \fIstring\fR and the second must be the
35+
first must the \fIvalue\fR and the second must be the
3636
\fIpattern\fR/\fIbody\fR list).
3737
The following options are currently supported:
3838
.\" OPTION: -exact
3939
.TP 10
4040
\fB\-exact\fR
4141
.
42-
Use exact matching when comparing \fIstring\fR to a pattern. This
42+
Use exact matching when comparing \fIvalue\fR to a pattern. This
4343
is the default.
4444
.\" OPTION: -glob
4545
.TP 10
4646
\fB\-glob\fR
4747
.
48-
When matching \fIstring\fR to the patterns, use glob-style matching
48+
When matching \fIvalue\fR to the patterns, use glob-style matching
4949
(i.e. the same as implemented by the \fBstring match\fR command).
50+
.\" OPTION: -integer
51+
.TP 10
52+
\fB\-integer\fR
53+
.VS 9.1
54+
.\" TIP #730
55+
When matching \fIvalue\fR to the patterns, use integer comparisons. Note
56+
that this makes using a non-integer \fIvalue\fR or \fIpattern\fR (other
57+
than a final \fBdefault\fR) into an error.
58+
.VE 9.1
5059
.\" OPTION: -regexp
5160
.TP 10
5261
\fB\-regexp\fR
5362
.
54-
When matching \fIstring\fR to the patterns, use regular
63+
When matching \fIvalue\fR to the patterns, use regular
5564
expression matching
5665
(as described in the \fBre_syntax\fR reference page).
5766
.\" OPTION: -nocase
5867
.TP 10
5968
\fB\-nocase\fR
6069
.
6170
Causes comparisons to be handled in a case-insensitive manner.
71+
Not supported with the \fB\-integer\fR option.
6272
.\" OPTION: -matchvar
6373
.TP 10
6474
\fB\-matchvar\fI varName\fR
@@ -67,7 +77,7 @@ This option (only legal when \fB\-regexp\fR is also specified)
6777
specifies the name of a variable into which the list of matches
6878
found by the regular expression engine will be written. The first
6979
element of the list written will be the overall substring of the input
70-
string (i.e. the \fIstring\fR argument to \fBswitch\fR) matched, the
80+
string (i.e. the \fIvalue\fR argument to \fBswitch\fR) matched, the
7181
second element of the list will be the substring matched by the first
7282
capturing parenthesis in the regular expression that matched, and so
7383
on. When a \fBdefault\fR branch is taken, the variable will have the
@@ -84,7 +94,7 @@ found by the regular expression engine will be written. The first
8494
element of the list written will be a two-element list specifying the
8595
index of the start and index of the first character after the end of
8696
the overall substring of the input
87-
string (i.e. the \fIstring\fR argument to \fBswitch\fR) matched, in a
97+
string (i.e. the \fIvalue\fR argument to \fBswitch\fR) matched, in a
8898
similar way to the \fB\-indices\fR option to the \fBregexp\fR can
8999
obtain. Similarly, the second element of the list refers to the first
90100
capturing parenthesis in the regular expression that matched, and so
@@ -96,7 +106,7 @@ time as the \fB\-matchvar\fR option.
96106
\fB\-\|\-\fR
97107
.
98108
Marks the end of options. The argument following this one will
99-
be treated as \fIstring\fR even if it starts with a \fB\-\fR.
109+
be treated as \fIvalue\fR even if it starts with a \fB\-\fR.
100110
This is not required when the matching patterns and bodies are grouped
101111
together in a single argument.
102112
.PP
@@ -184,6 +194,26 @@ exactly matched is easily obtained using the \fB\-matchvar\fR option:
184194
}
185195
}
186196
.CE
197+
.PP
198+
.VS 9.1
199+
Deciding what to do with a procedure based on the number of arguments:
200+
.PP
201+
.CS
202+
proc example args {
203+
\fBswitch\fR -integer -- [llength $args] {
204+
0 {
205+
puts "no arguments"
206+
}
207+
1 {
208+
puts "one argument: [lindex $args 0]"
209+
}
210+
default {
211+
puts "many arguments: $args"
212+
}
213+
}
214+
}
215+
.CE
216+
.VE 9.1
187217
.SH "SEE ALSO"
188218
for(n), if(n), regexp(n)
189219
.SH KEYWORDS

generic/tclCmdMZ.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3452,9 +3452,10 @@ TclNRSwitchObjCmd(
34523452
int noCase;
34533453
Tcl_Size patternLength, j;
34543454
const char *pattern;
3455-
Tcl_Obj *stringObj, *indexVarObj, *matchVarObj;
3455+
Tcl_Obj *valueObj, *indexVarObj, *matchVarObj;
34563456
Tcl_Obj *const *savedObjv = objv;
34573457
Tcl_RegExp regExpr = NULL;
3458+
Tcl_WideInt intValue = 0, armValue;
34583459
Interp *iPtr = (Interp *) interp;
34593460
int pc = 0;
34603461
int bidx = 0; /* Index of body argument. */
@@ -3468,12 +3469,12 @@ TclNRSwitchObjCmd(
34683469
*/
34693470

34703471
static const char *const options[] = {
3471-
"-exact", "-glob", "-indexvar", "-matchvar", "-nocase", "-regexp",
3472-
"--", NULL
3472+
"-exact", "-glob", "-indexvar", "-integer", "-matchvar", "-nocase",
3473+
"-regexp", "--", NULL
34733474
};
34743475
enum switchOptionsEnum {
3475-
OPT_EXACT, OPT_GLOB, OPT_INDEXV, OPT_MATCHV, OPT_NOCASE, OPT_REGEXP,
3476-
OPT_LAST
3476+
OPT_EXACT, OPT_GLOB, OPT_INDEXV, OPT_INTEGER, OPT_MATCHV, OPT_NOCASE,
3477+
OPT_REGEXP, OPT_LAST
34773478
} index;
34783479
typedef int (*strCmpFn_t)(const char *, const char *);
34793480
strCmpFn_t strCmpFn = TclUtfCmp;
@@ -3580,8 +3581,15 @@ TclNRSwitchObjCmd(
35803581
"MODERESTRICTION", (char *)NULL);
35813582
return TCL_ERROR;
35823583
}
3584+
if (noCase && mode == OPT_INTEGER) {
3585+
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
3586+
"-nocase option cannot be used with -integer option"));
3587+
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH",
3588+
"MODERESTRICTION", (char *)NULL);
3589+
return TCL_ERROR;
3590+
}
35833591

3584-
stringObj = objv[i];
3592+
valueObj = objv[i];
35853593
objc -= i + 1;
35863594
objv += i + 1;
35873595
bidx = i + 1; /* First after the match string. */
@@ -3673,6 +3681,12 @@ TclNRSwitchObjCmd(
36733681
return TCL_ERROR;
36743682
}
36753683

3684+
if (mode == OPT_INTEGER) {
3685+
if (Tcl_GetWideIntFromObj(interp, valueObj, &intValue) != TCL_OK) {
3686+
return TCL_ERROR;
3687+
}
3688+
}
3689+
36763690
for (i = 0; i < objc; i += 2) {
36773691
/*
36783692
* See if the pattern matches the string.
@@ -3712,12 +3726,12 @@ TclNRSwitchObjCmd(
37123726

37133727
switch (mode) {
37143728
case OPT_EXACT:
3715-
if (strCmpFn(TclGetString(stringObj), pattern) == 0) {
3729+
if (strCmpFn(TclGetString(valueObj), pattern) == 0) {
37163730
goto matchFound;
37173731
}
37183732
break;
37193733
case OPT_GLOB:
3720-
if (Tcl_StringCaseMatch(TclGetString(stringObj),pattern,noCase)) {
3734+
if (Tcl_StringCaseMatch(TclGetString(valueObj),pattern,noCase)) {
37213735
goto matchFound;
37223736
}
37233737
break;
@@ -3727,7 +3741,7 @@ TclNRSwitchObjCmd(
37273741
if (regExpr == NULL) {
37283742
return TCL_ERROR;
37293743
} else {
3730-
int matched = Tcl_RegExpExecObj(interp, regExpr, stringObj, 0,
3744+
int matched = Tcl_RegExpExecObj(interp, regExpr, valueObj, 0,
37313745
numMatchesSaved, 0);
37323746

37333747
if (matched < 0) {
@@ -3737,6 +3751,13 @@ TclNRSwitchObjCmd(
37373751
}
37383752
}
37393753
break;
3754+
case OPT_INTEGER:
3755+
if (Tcl_GetWideIntFromObj(interp, objv[i], &armValue) != TCL_OK) {
3756+
return TCL_ERROR;
3757+
} else if (intValue == armValue) {
3758+
goto matchFound;
3759+
}
3760+
break;
37403761
}
37413762
}
37423763
return TCL_OK;
@@ -3786,7 +3807,7 @@ TclNRSwitchObjCmd(
37863807
Tcl_Obj *substringObj;
37873808

37883809
if (info.matches[j].end > 0) {
3789-
substringObj = Tcl_GetRange(stringObj,
3810+
substringObj = Tcl_GetRange(valueObj,
37903811
info.matches[j].start, info.matches[j].end-1);
37913812
} else {
37923813
TclNewObj(substringObj);

0 commit comments

Comments
 (0)