Tcl Source Code

Changes On Branch tip-615
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch tip-615 Excluding Merge-Ins

This is equivalent to a diff from 94e9d6ea9b to 544f0f0fc5

2022-04-04
08:51
Fix TIP #613 implementation, when (indexPtr) is more than a simple variable name. Thanks, @ashok! check-in: 8ca41d8daf user: jan.nijtmans tags: core-8-branch
2022-04-01
15:28
Start handling 'last' arguments Leaf check-in: 544f0f0fc5 user: jan.nijtmans tags: tip-615
13:16
Merge 8.7 check-in: 56bf4c5824 user: jan.nijtmans tags: tip-616-for-8.7
07:32
Undo (unnecessary) change in tclMain.c check-in: 8b0ce05c29 user: jan.nijtmans tags: tip-615
07:13
Merge 8.7 check-in: d2474ffcdc user: jan.nijtmans tags: tip-615
2022-03-31
11:07
Merge 8.7. Make room for TIP #622 stub entries check-in: 3a50a6e775 user: jan.nijtmans tags: tip-618
2022-03-29
13:57
Merge 8.7 check-in: 73d9f7b823 user: jan.nijtmans tags: full-utf-for-87
13:41
Merge 8.7 check-in: 29f2111bc0 user: jan.nijtmans tags: trunk, main
13:40
Don't bother UINT2PTR, since INT2PTR is just as good. check-in: 94e9d6ea9b user: jan.nijtmans tags: core-8-branch
08:42
Merge 8.6 check-in: f7ec4ed591 user: jan.nijtmans tags: core-8-branch

Changes to generic/tclCmdIL.c.

2682
2683
2684
2685
2686
2687
2688




2689
2690
2691
2692
2693
2694
2695
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699







+
+
+
+







	return result;
    }

    result = TclGetIntForIndexM(interp, objv[3], /*endValue*/ listLen - 1,
	    &last);
    if (result != TCL_OK) {
	return result;
    }
    if ((last == -1) && Tcl_GetString(objv[3])[0] == '\0') {
       /* TIP #615: empty string for 'last' means 'end' */
       last = listLen - 1;
    }

    Tcl_SetObjResult(interp, TclListObjRange(objv[1], first, last));
    return TCL_OK;
}

/*

Changes to generic/tclCmdMZ.c.

1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537




1538
1539
1540
1541
1542
1543
1544
1545




1546
1547
1548
1549
1550
1551
1552
1527
1528
1529
1530
1531
1532
1533




1534
1535
1536
1537
1538
1539
1540
1541




1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552







-
-
-
-
+
+
+
+




-
-
-
-
+
+
+
+







    int i, failat = 0, result = 1, strict = 0, index, length1, length2;
    Tcl_Obj *objPtr, *failVarObj = NULL;
    Tcl_WideInt w;

    static const char *const isClasses[] = {
	"alnum",	"alpha",	"ascii",	"control",
	"boolean",	"dict",		"digit",	"double",
	"entier",	"false",	"graph",	"integer",
	"list",		"lower",	"print",	"punct",
	"space",	"true",		"upper",	"unicode",
	"wideinteger", "wordchar",	"xdigit",	NULL
	"entier",	"false",	"graph",	"index",
	"integer",	"list",		"lower",	"print",
	"punct",	"space",	"true",		"upper",
	"unicode",	"wideinteger", "wordchar",	"xdigit",	NULL
    };
    enum isClassesEnum {
	STR_IS_ALNUM,	STR_IS_ALPHA,	STR_IS_ASCII,	STR_IS_CONTROL,
	STR_IS_BOOL,	STR_IS_DICT,	STR_IS_DIGIT,	STR_IS_DOUBLE,
	STR_IS_ENTIER,	STR_IS_FALSE,	STR_IS_GRAPH,	STR_IS_INT,
	STR_IS_LIST,	STR_IS_LOWER,	STR_IS_PRINT,	STR_IS_PUNCT,
	STR_IS_SPACE,	STR_IS_TRUE,	STR_IS_UPPER,	STR_IS_UNICODE,
	STR_IS_WIDE,	STR_IS_WORD,	STR_IS_XDIGIT
	STR_IS_ENTIER,	STR_IS_FALSE,	STR_IS_GRAPH,	STR_IS_INDEX,
	STR_IS_INT,	STR_IS_LIST,	STR_IS_LOWER,	STR_IS_PRINT,
	STR_IS_PUNCT,	STR_IS_SPACE,	STR_IS_TRUE,	STR_IS_UPPER,
	STR_IS_UNICODE,	STR_IS_WIDE,	STR_IS_WORD,	STR_IS_XDIGIT
    };
    static const char *const isOptions[] = {
	"-strict", "-failindex", NULL
    };
    enum isOptionsEnum {
	OPT_STRICT, OPT_FAILIDX
    };
1705
1706
1707
1708
1709
1710
1711










1712
1713
1714
1715
1716
1717
1718
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728







+
+
+
+
+
+
+
+
+
+







		TclFreeInternalRep(objPtr);
	    }
	}
	break;
    }
    case STR_IS_GRAPH:
	chcomp = Tcl_UniCharIsGraph;
	break;
    case STR_IS_INDEX:
	if (TclHasInternalRep(objPtr, &tclIntType) ||
		TclHasInternalRep(objPtr, &tclEndOffsetType) ||
		TclHasInternalRep(objPtr, &tclBignumType)) {
	    break;
	}
	if (Tcl_GetIntForIndex(NULL, objPtr, 0, &i) != TCL_OK) {
	    result = 0;
	}
	break;
    case STR_IS_INT:
    case STR_IS_ENTIER:
	if (TclHasInternalRep(objPtr, &tclIntType) ||
		TclHasInternalRep(objPtr, &tclBignumType)) {
	    break;
	}
2295
2296
2297
2298
2299
2300
2301




2302
2303
2304
2305
2306
2307
2308
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322







+
+
+
+








    length = Tcl_GetCharLength(objv[1]) - 1;

    if (TclGetIntForIndexM(interp, objv[2], length, &first) != TCL_OK ||
	    TclGetIntForIndexM(interp, objv[3], length, &last) != TCL_OK) {
	return TCL_ERROR;
    }
    if ((last == -1) && Tcl_GetString(objv[3])[0] == '\0') {
	/* TIP #615: empty string for 'last' means 'end' */
	last = length;
    }

    if (last >= 0) {
	Tcl_SetObjResult(interp, Tcl_GetRange(objv[1], first, last));
    }
    return TCL_OK;
}

2396
2397
2398
2399
2400
2401
2402




2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427







+
+
+
+








    length = Tcl_GetCharLength(objv[1]);
    end = length - 1;

    if (TclGetIntForIndexM(interp, objv[2], end, &first) != TCL_OK ||
	    TclGetIntForIndexM(interp, objv[3], end, &last) != TCL_OK) {
	return TCL_ERROR;
    }
    if ((last == -1) && Tcl_GetString(objv[3])[0] == '\0') {
	/* TIP #615: empty string for 'last' means 'end' */
	last = end;
    }

    /*
     * The following test screens out most empty substrings as candidates for
     * replacement. When they are detected, no replacement is done, and the
     * result is the original string.
     */

Changes to generic/tclCompCmdsSZ.c.

502
503
504
505
506
507
508
509
510
511
512




513
514
515
516
517
518
519
520




521
522
523
524
525
526
527
502
503
504
505
506
507
508




509
510
511
512
513
514
515
516




517
518
519
520
521
522
523
524
525
526
527







-
-
-
-
+
+
+
+




-
-
-
-
+
+
+
+







    CompileEnv *envPtr)		/* Holds resulting instructions. */
{
    DefineLineInformation;	/* TIP #280 */
    Tcl_Token *tokenPtr = TokenAfter(parsePtr->tokenPtr);
    static const char *const isClasses[] = {
	"alnum",	"alpha",	"ascii",	"control",
	"boolean",	"dict",		"digit",	"double",
	"entier",	"false",	"graph",	"integer",
	"list",		"lower",	"print",	"punct",
	"space",	"true",		"upper",	"unicode",
	"wideinteger", "wordchar",	"xdigit",	NULL
	"entier",	"false",	"graph",	"index",
	"integer",	"list",		"lower",	"print",
	"punct",	"space",	"true",		"upper",
	"unicode",	"wideinteger", "wordchar",	"xdigit",	NULL
    };
    enum isClassesEnum {
	STR_IS_ALNUM,	STR_IS_ALPHA,	STR_IS_ASCII,	STR_IS_CONTROL,
	STR_IS_BOOL,	STR_IS_DICT,	STR_IS_DIGIT,	STR_IS_DOUBLE,
	STR_IS_ENTIER,	STR_IS_FALSE,	STR_IS_GRAPH,	STR_IS_INT,
	STR_IS_LIST,	STR_IS_LOWER,	STR_IS_PRINT,	STR_IS_PUNCT,
	STR_IS_SPACE,	STR_IS_TRUE,	STR_IS_UPPER,	STR_IS_UNICODE,
	STR_IS_WIDE,	STR_IS_WORD,	STR_IS_XDIGIT
	STR_IS_ENTIER,	STR_IS_FALSE,	STR_IS_GRAPH,	STR_IS_INDEX,
	STR_IS_INT,	STR_IS_LIST,	STR_IS_LOWER,	STR_IS_PRINT,
	STR_IS_PUNCT,	STR_IS_SPACE,	STR_IS_TRUE,	STR_IS_UPPER,
	STR_IS_UNICODE,	STR_IS_WIDE,	STR_IS_WORD,	STR_IS_XDIGIT
    };
    int t, range, allowEmpty = 0, end;
    InstStringClassType strClassType;
    Tcl_Obj *isClass;

    if (parsePtr->numWords < 3 || parsePtr->numWords > 6) {
	return TCL_ERROR;
709
710
711
712
713
714
715


716
717
718
719
720
721
722
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724







+
+







	    TclAdjustStackDepth(-1, envPtr);
	    FIXJUMP1(	satisfied);
	}
	PUSH(		"1");
	FIXJUMP1(	end);
	return TCL_OK;
    }
    case STR_IS_INDEX:
	return TCL_ERROR;

    case STR_IS_INT:
    case STR_IS_WIDE:
    case STR_IS_ENTIER:
	if (allowEmpty) {
	    int testNumType;

Changes to generic/tclInt.h.

2762
2763
2764
2765
2766
2767
2768

2769
2770
2771
2772
2773
2774
2775
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776







+







 */

MODULE_SCOPE const Tcl_ObjType tclBignumType;
MODULE_SCOPE const Tcl_ObjType tclBooleanType;
MODULE_SCOPE const Tcl_ObjType tclByteArrayType;
MODULE_SCOPE const Tcl_ObjType tclByteCodeType;
MODULE_SCOPE const Tcl_ObjType tclDoubleType;
MODULE_SCOPE const Tcl_ObjType tclEndOffsetType;
MODULE_SCOPE const Tcl_ObjType tclIntType;
MODULE_SCOPE const Tcl_ObjType tclListType;
MODULE_SCOPE const Tcl_ObjType tclDictType;
MODULE_SCOPE const Tcl_ObjType tclProcBodyType;
MODULE_SCOPE const Tcl_ObjType tclStringType;
MODULE_SCOPE const Tcl_ObjType tclEnsembleCmdType;
MODULE_SCOPE const Tcl_ObjType tclRegexpType;

Changes to generic/tclListObj.c.

1437
1438
1439
1440
1441
1442
1443
1444


1445
1446
1447
1448

1449
1450
1451
1452
1453
1454
1455
1437
1438
1439
1440
1441
1442
1443

1444
1445
1446
1447
1448

1449
1450
1451
1452
1453
1454
1455
1456







-
+
+



-
+







    int index;			/* Current index in the list - discarded. */
    Tcl_Obj *indexListCopy;
    List *listRepPtr;

    /*
     * Determine whether the index arg designates a list or a single index.
     * We have to be careful about the order of the checks to avoid repeated
     * shimmering; see TIP #22 and #23 for details.
     * shimmering; see TIP #22 and #23 for details. Don't allow "" as single
     * index here, since it cannot be distinguished from an empty list.
     */

    ListGetInternalRep(indexArgPtr, listRepPtr);
    if (listRepPtr == NULL
    if (listRepPtr == NULL && Tcl_GetString(indexArgPtr)[0]
	    && TclGetIntForIndexM(NULL, indexArgPtr, INT_MAX - 1, &index) == TCL_OK) {
	/*
	 * indexArgPtr designates a single index.
	 */

	return TclLsetFlat(interp, listPtr, 1, &indexArgPtr, valuePtr);

Changes to generic/tclUtil.c.

120
121
122
123
124
125
126
127
128
129

130
131














132

133
134
135
136

137
138
139

140
141
142
143
144
145
146
120
121
122
123
124
125
126



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146
147

148
149
150

151
152
153
154
155
156
157
158







-
-
-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+



-
+


-
+







			    int *literalPtr);
/*
 * The following is the Tcl object type definition for an object that
 * represents a list index in the form, "end-offset". It is used as a
 * performance optimization in Tcl_GetIntForIndex. The internal rep is
 * stored directly in the wideValue, so no memory management is required
 * for it. This is a caching internalrep, keeping the result of a parse
 * around. This type is only created from a pre-existing string, so an
 * updateStringProc will never be called and need not exist. The type
 * is unregistered, so has no need of a setFromAnyProc either.
 * around. The type is unregistered, so has no need of a setFromAnyProc.
 */

static void
UpdateStringOfIndex(
    Tcl_Obj *objPtr)	/* Index object whose string rep to update. */
{
    /* The only situation that the string rep can be missing is when it
     * represents TCL_INDEX_NONE. In all other situations, the string
     * rep is never thrown away. See TclNewIndexObj() */
    if ((objPtr)->internalRep.wideValue != WIDE_MIN) {
	Tcl_Panic("String rep of index %" TCL_LL_MODIFIER "d cannot be generated",
		(long long)(objPtr)->internalRep.wideValue);
    }
    TclInitStringRep(objPtr, NULL, 0);
}

static const Tcl_ObjType endOffsetType = {
const Tcl_ObjType tclEndOffsetType = {
    "end-offset",			/* name */
    NULL,				/* freeIntRepProc */
    NULL,				/* dupIntRepProc */
    NULL,				/* updateStringProc */
    UpdateStringOfIndex,		/* updateStringProc */
    NULL				/* setFromAnyProc */
};


/*
 *	*	STRING REPRESENTATION OF LISTS	*	*	*
 *
 * The next several routines implement the conversions of strings to and from
 * Tcl lists. To understand their operation, the rules of parsing and
 * generating the string representation of lists must be known.  Here we
 * describe them in one place.
3754
3755
3756
3757
3758
3759
3760
3761

3762
3763
3764
3765
3766
3767
3768
3769
3770
3771





3772
3773
3774
3775
3776
3777
3778
3766
3767
3768
3769
3770
3771
3772

3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795







-
+










+
+
+
+
+







    Tcl_WideInt *widePtr)       /* Location filled in with an integer
                                 * representing an index. */
{
    Tcl_ObjInternalRep *irPtr;
    Tcl_WideInt offset = -1;	/* Offset in the "end-offset" expression - 1 */
    ClientData cd;

    while ((irPtr = TclFetchInternalRep(objPtr, &endOffsetType)) == NULL) {
    while ((irPtr = TclFetchInternalRep(objPtr, &tclEndOffsetType)) == NULL) {
	Tcl_ObjInternalRep ir;
	int length;
	const char *bytes = TclGetStringFromObj(objPtr, &length);

	if (*bytes != 'e') {
	    int numType;
	    const char *opPtr;
	    int t1 = 0, t2 = 0;

	    /* Value doesn't start with "e" */

	    if (length == 0) {
		offset = WIDE_MIN;
		goto parseOK;
	    }

	    /* If we reach here, the string rep of objPtr exists. */

	    /*
	     * The valid index syntax does not include any value that is
	     * a list of more than one element. This is necessary so that
	     * lists of index values can be reliably distinguished from any
3940
3941
3942
3943
3944
3945
3946
3947

3948
3949
3950
3951
3952
3953
3954
3957
3958
3959
3960
3961
3962
3963

3964
3965
3966
3967
3968
3969
3970
3971







-
+







		}
	    }
	}

    parseOK:
	/* Success. Store the new internal rep. */
	ir.wideValue = offset;
	Tcl_StoreInternalRep(objPtr, &endOffsetType, &ir);
	Tcl_StoreInternalRep(objPtr, &tclEndOffsetType, &ir);
    }

    offset = irPtr->wideValue;

    if (offset == WIDE_MAX) {
	*widePtr = endValue + 1;
    } else if (offset == WIDE_MIN) {
4045
4046
4047
4048
4049
4050
4051
4052

4053
4054
4055
4056
4057
4058
4059
4062
4063
4064
4065
4066
4067
4068

4069
4070
4071
4072
4073
4074
4075
4076







-
+







    int after,		/* Value to return for index after end */
    int *indexPtr)	/* Where to write the encoded answer, not NULL */
{
    Tcl_WideInt wide;
    int idx;

    if (TCL_OK == GetWideForIndex(interp, objPtr, (unsigned)TCL_INDEX_END , &wide)) {
	const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objPtr, &endOffsetType);
	const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objPtr, &tclEndOffsetType);
	if (irPtr && irPtr->wideValue >= 0) {
	    /* "int[+-]int" syntax, works the same here as "int" */
	    irPtr = NULL;
	}
	/*
	 * We parsed an end+offset index value.
	 * wide holds the offset value in the range WIDE_MIN...WIDE_MAX.

Changes to tests/lpop.test.

42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56







-
+







test lpop-1.6 {error conditions} -returnCodes error -body {
    set l "x y"
    lpop l end+1
} -result {index "end+1" out of range}
test lpop-1.7 {error conditions} -returnCodes error -body {
    set l "x y"
    lpop l {}
} -match glob -result {bad index *}
} -result {index "" out of range}
test lpop-1.8 {error conditions} -returnCodes error -body {
    set l "x y"
    lpop l 0 0 0 0 1
} -result {index "1" out of range}
test lpop-1.9 {error conditions} -returnCodes error -body {
    set l "x y"
    lpop l {1 0}

Changes to tests/string.test.

525
526
527
528
529
530
531
532

533
534
535

536
537
538
539
540
541
542
525
526
527
528
529
530
531

532
533
534

535
536
537
538
539
540
541
542







-
+


-
+







    list [catch {run {string is alpha -failin str}} msg] $msg
} {1 {wrong # args: should be "string is alpha ?-strict? ?-failindex var? str"}}
test string-6.4.$noComp {string is, too many args} {
    list [catch {run {string is alpha -failin var -strict str more}} msg] $msg
} {1 {wrong # args: should be "string is class ?-strict? ?-failindex var? str"}}
test string-6.5.$noComp {string is, class check} {
    list [catch {run {string is bogus str}} msg] $msg
} {1 {bad class "bogus": must be alnum, alpha, ascii, control, boolean, dict, digit, double, entier, false, graph, integer, list, lower, print, punct, space, true, upper, unicode, wideinteger, wordchar, or xdigit}}
} {1 {bad class "bogus": must be alnum, alpha, ascii, control, boolean, dict, digit, double, entier, false, graph, index, integer, list, lower, print, punct, space, true, upper, unicode, wideinteger, wordchar, or xdigit}}
test string-6.6.$noComp {string is, ambiguous class} {
    list [catch {run {string is al str}} msg] $msg
} {1 {ambiguous class "al": must be alnum, alpha, ascii, control, boolean, dict, digit, double, entier, false, graph, integer, list, lower, print, punct, space, true, upper, unicode, wideinteger, wordchar, or xdigit}}
} {1 {ambiguous class "al": must be alnum, alpha, ascii, control, boolean, dict, digit, double, entier, false, graph, index, integer, list, lower, print, punct, space, true, upper, unicode, wideinteger, wordchar, or xdigit}}
test string-6.7.$noComp {string is alpha, all ok} {
    run {string is alpha -strict -failindex var abc}
} 1
test string-6.8.$noComp {string is, error in var} {
    list [run {string is alpha -failindex var abc5def}] $var
} {0 3}
test string-6.9.$noComp {string is, var shouldn't get set} {
2027
2028
2029
2030
2031
2032
2033

2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044

2045
2046
2047
2048
2049
2050
2051
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044

2045
2046
2047
2048
2049
2050
2051
2052







+










-
+







        [run {string is ascii $s}] \
        [run {string is control $s}] \
        [run {string is boolean $s}] \
        [run {string is digit $s}] \
        [run {string is double $s}] \
        [run {string is false $s}] \
        [run {string is graph $s}] \
        [run {string is index $s}] \
        [run {string is integer $s}] \
        [run {string is lower $s}] \
        [run {string is print $s}] \
        [run {string is punct $s}] \
        [run {string is space $s}] \
        [run {string is true $s}] \
        [run {string is upper $s}] \
        [run {string is wordchar $s}] \
        [run {string is xdigit $s}] \

} {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
} {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
test string-23.2.$noComp {string is command with empty string} {
    set s ""
    list \
        [run {string is alnum -strict $s}] \
        [run {string is alpha -strict $s}] \
        [run {string is ascii -strict $s}] \
        [run {string is control -strict $s}] \

Changes to tests/util.test.

692
693
694
695
696
697
698
699

700
701
702
703
704
705
706
692
693
694
695
696
697
698

699
700
701
702
703
704
705
706







-
+







    string index abcd { -1+2 }
} b
test util-9.18 {Tcl_GetIntForIndex} {
    string index abcd { -1--2 }
} b
test util-9.19 {Tcl_GetIntForIndex} -body {
    string index a {}
} -returnCodes error -match glob -result *
} -result {}
test util-9.20 {Tcl_GetIntForIndex} -body {
    string index a { }
} -returnCodes error -match glob -result *
test util-9.21 {Tcl_GetIntForIndex} -body {
    string index a " \r\t\n"
} -returnCodes error -match glob -result *
test util-9.22 {Tcl_GetIntForIndex} -body {