aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jim.c285
-rw-r--r--jim_tcl.txt86
-rw-r--r--tests/lsearch.test113
-rw-r--r--tests/lsort.test48
4 files changed, 441 insertions, 91 deletions
diff --git a/jim.c b/jim.c
index 7c54628..9c4406e 100644
--- a/jim.c
+++ b/jim.c
@@ -139,6 +139,8 @@ static char JimEmptyStringRep[] = "";
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
int flags);
+static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc,
+ Jim_Obj **resultObj, int flags);
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
@@ -6722,7 +6724,7 @@ Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
* sure that the list object can't shimmer while the vector returned
* is in use, this vector is the one stored inside the internal representation
* of the list object. This function is not exported, extensions should
- * always access to the List object elements using Jim_ListIndex(). */
+ * always access to the List object elements using Jim_ListGetIndex(). */
static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
Jim_Obj ***listVec)
{
@@ -6755,8 +6757,8 @@ struct lsort_info {
JIM_LSORT_COMMAND
} type;
int order;
- int index;
- int indexed;
+ Jim_Obj **indexv;
+ int indexc;
int unique;
int (*subfn)(Jim_Obj **, Jim_Obj **);
};
@@ -6767,8 +6769,8 @@ static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
Jim_Obj *lObj, *rObj;
- if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
- Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
+ if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
+ Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
longjmp(sort_info->jmpbuf, JIM_ERR);
}
return sort_info->subfn(&lObj, &rObj);
@@ -6910,7 +6912,7 @@ static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsor
return -1; /* Should not be run but keeps static analysers happy */
}
- if (info->indexed) {
+ if (info->indexc) {
/* Need to interpose a "list index" function */
info->subfn = fn;
fn = ListSortIndexHelper;
@@ -7059,6 +7061,57 @@ int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPt
return JIM_OK;
}
+/* Get the value from the list associated to the specified list indices.
+ * Return JIM_ERR if an index is invalid (and sets an error message).
+ * Returns -1 if the list index is out of range.
+ * In this case, if flags includes JIM_ERRMSG, an error result is set.
+ * Otherwise, returns JIM_OK and sets *resultObj to the indexed value.
+ * (This is the only case where *resultObj is set)
+ */
+static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
+ Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
+{
+ int i;
+ int static_idxes[5];
+ int *idxes = static_idxes;
+ int ret = JIM_OK;
+
+ if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
+ idxes = Jim_Alloc(indexc * sizeof(*idxes));
+ }
+
+ /* In the rare, contrived case where an index is also the list (or an element)
+ * we need to extract the indices first.
+ */
+ for (i = 0; i < indexc; i++) {
+ ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
+ if (ret != JIM_OK) {
+ goto err;
+ }
+ }
+
+ for (i = 0; i < indexc; i++) {
+ Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
+ if (!objPtr) {
+ if (flags & JIM_ERRMSG) {
+ if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
+ Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
+ }
+ else {
+ Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
+ }
+ }
+ return -1;
+ }
+ listPtr = objPtr;
+ }
+ *resultObj = listPtr;
+err:
+ if (idxes != static_idxes)
+ Jim_Free(idxes);
+ return ret;
+}
+
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
Jim_Obj *newObjPtr, int flags)
{
@@ -7096,7 +7149,10 @@ int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
listObjPtr = objPtr;
if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
goto err;
- if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
+
+ objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
+ if (objPtr == NULL) {
+ Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
goto err;
}
if (Jim_IsShared(objPtr)) {
@@ -12694,35 +12750,24 @@ static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
/* [lindex] */
static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- Jim_Obj *objPtr, *listObjPtr;
- int i;
- int idx;
+ Jim_Obj *objPtr;
+ int ret;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
return JIM_ERR;
}
- objPtr = argv[1];
- Jim_IncrRefCount(objPtr);
- for (i = 2; i < argc; i++) {
- listObjPtr = objPtr;
- if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
- Jim_DecrRefCount(interp, listObjPtr);
- return JIM_ERR;
- }
- if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
- /* Returns an empty object if the index
- * is out of range. */
- Jim_DecrRefCount(interp, listObjPtr);
- Jim_SetEmptyResult(interp);
- return JIM_OK;
- }
- Jim_IncrRefCount(objPtr);
- Jim_DecrRefCount(interp, listObjPtr);
+ ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
+ if (ret < 0) {
+ /* Returns an empty object if the index
+ * is out of range. */
+ ret = JIM_OK;
+ Jim_SetEmptyResult(interp);
}
- Jim_SetResult(interp, objPtr);
- Jim_DecrRefCount(interp, objPtr);
- return JIM_OK;
+ else if (ret == JIM_OK) {
+ Jim_SetResult(interp, objPtr);
+ }
+ return ret;
}
/* [llength] */
@@ -12741,11 +12786,11 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
{
static const char * const options[] = {
"-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
- NULL
+ "-stride", "-index", NULL
};
enum
{ OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
- OPT_COMMAND };
+ OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
int i;
int opt_bool = 0;
int opt_not = 0;
@@ -12756,12 +12801,14 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
int rc = JIM_OK;
Jim_Obj *listObjPtr = NULL;
Jim_Obj *commandObj = NULL;
+ Jim_Obj *indexObj = NULL;
int match_flags = 0;
+ long stride = 1;
if (argc < 3) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv,
- "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
+ "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
return JIM_ERR;
}
@@ -12803,6 +12850,24 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
case OPT_GLOB:
opt_match = option;
break;
+ case OPT_INDEX:
+ if (i >= argc - 2) {
+ goto wrongargs;
+ }
+ indexObj = argv[++i];
+ break;
+ case OPT_STRIDE:
+ if (i >= argc - 2) {
+ goto wrongargs;
+ }
+ if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
+ return JIM_ERR;
+ }
+ if (stride < 1) {
+ Jim_SetResultString(interp, "stride length must be at least 1", -1);
+ return JIM_ERR;
+ }
+ break;
}
}
@@ -12812,6 +12877,12 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
}
argv += i;
+ listlen = Jim_ListLength(interp, argv[0]);
+ if (listlen % stride) {
+ Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
+ return JIM_ERR;
+ }
+
if (opt_all) {
listObjPtr = Jim_NewListObj(interp, NULL, 0);
}
@@ -12822,10 +12893,41 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
Jim_IncrRefCount(commandObj);
}
- listlen = Jim_ListLength(interp, argv[0]);
- for (i = 0; i < listlen; i++) {
+ for (i = 0; i < listlen; i += stride) {
int eq = 0;
- Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
+ Jim_Obj *searchListObj;
+ Jim_Obj *objPtr;
+ int offset;
+
+ if (indexObj) {
+ int indexlen = Jim_ListLength(interp, indexObj);
+ if (stride == 1) {
+ searchListObj = Jim_ListGetIndex(interp, argv[0], i);
+ }
+ else {
+ searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
+ }
+ Jim_IncrRefCount(searchListObj);
+ rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
+ if (rc != JIM_OK) {
+ Jim_DecrRefCount(interp, searchListObj);
+ rc = JIM_ERR;
+ goto done;
+ }
+ /* now indexObj is the object to compare */
+ offset = 0;
+ }
+ else {
+ /* No -index, so we have an implicit {0} as indexObj */
+ searchListObj = argv[0];
+ offset = i;
+ objPtr = Jim_ListGetIndex(interp, searchListObj, i);
+ Jim_IncrRefCount(searchListObj);
+ }
+ /* At this point objPtr represents the object to search against and
+ * searchListObj represents the list we search in (offset .. offset + stride - 1)
+ * both need to have reference counts decremented when done
+ */
switch (opt_match) {
case OPT_EXACT:
@@ -12840,22 +12942,15 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
case OPT_COMMAND:
eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
if (eq < 0) {
- if (listObjPtr) {
- Jim_FreeNewObj(interp, listObjPtr);
- }
+ Jim_DecrRefCount(interp, searchListObj);
rc = JIM_ERR;
goto done;
}
break;
}
- /* If we have a non-match with opt_bool, opt_not, !opt_all, can't exit early */
- if (!eq && opt_bool && opt_not && !opt_all) {
- continue;
- }
-
+ /* Got a match (or non-match for opt_not), or (opt_bool && opt_all) */
if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
- /* Got a match (or non-match for opt_not), or (opt_bool && opt_all) */
Jim_Obj *resultObj;
if (opt_bool) {
@@ -12864,22 +12959,36 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
else if (!opt_inline) {
resultObj = Jim_NewIntObj(interp, i);
}
- else {
+ else if (stride == 1) {
resultObj = objPtr;
}
+ else if (opt_all) {
+ /* Add the entire sublist directly for -all -stride > 1 */
+ ListInsertElements(listObjPtr, -1, stride,
+ searchListObj->internalRep.listValue.ele + offset);
+ }
+ else {
+ resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
+ }
if (opt_all) {
- Jim_ListAppendElement(interp, listObjPtr, resultObj);
+ /* The stride > 1 case has already been handled above */
+ if (stride == 1) {
+ Jim_ListAppendElement(interp, listObjPtr, resultObj);
+ }
}
else {
Jim_SetResult(interp, resultObj);
+ Jim_DecrRefCount(interp, searchListObj);
goto done;
}
}
+ Jim_DecrRefCount(interp, searchListObj);
}
if (opt_all) {
Jim_SetResult(interp, listObjPtr);
+ listObjPtr = NULL;
}
else {
/* No match */
@@ -12892,6 +13001,9 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
}
done:
+ if (listObjPtr) {
+ Jim_FreeNewObj(interp, listObjPtr);
+ }
if (commandObj) {
Jim_DecrRefCount(interp, commandObj);
}
@@ -13028,25 +13140,30 @@ static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
{
static const char * const options[] = {
- "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
+ "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
+ "-stride", NULL
+ };
+ enum {
+ OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
+ OPT_STRIDE
};
- enum
- { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
Jim_Obj *resObj;
int i;
int retCode;
int shared;
+ long stride = 1;
struct lsort_info info;
if (argc < 2) {
+wrongargs:
Jim_WrongNumArgs(interp, 1, argv, "?options? list");
return JIM_ERR;
}
info.type = JIM_LSORT_ASCII;
info.order = 1;
- info.indexed = 0;
+ info.indexc = 0;
info.unique = 0;
info.command = NULL;
info.interp = interp;
@@ -13088,28 +13205,72 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg
info.command = argv[i + 1];
i++;
break;
+ case OPT_STRIDE:
+ if (i >= argc - 2) {
+ goto wrongargs;
+ }
+ if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
+ return JIM_ERR;
+ }
+ if (stride < 2) {
+ Jim_SetResultString(interp, "stride length must be at least 2", -1);
+ return JIM_ERR;
+ }
+ break;
case OPT_INDEX:
if (i >= (argc - 2)) {
+badindex:
Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
return JIM_ERR;
}
- if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
- return JIM_ERR;
+ JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
+ if (info.indexc == 0) {
+ goto badindex;
}
- info.indexed = 1;
i++;
break;
}
}
resObj = argv[argc - 1];
- if ((shared = Jim_IsShared(resObj)))
- resObj = Jim_DuplicateObj(interp, resObj);
- retCode = ListSortElements(interp, resObj, &info);
- if (retCode == JIM_OK) {
- Jim_SetResult(interp, resObj);
- }
- else if (shared) {
- Jim_FreeNewObj(interp, resObj);
+ if (stride > 1) {
+ Jim_Obj *tmpListObj;
+ Jim_Obj **elements;
+ int listlen;
+ int i;
+
+ JimListGetElements(interp, resObj, &listlen, &elements);
+ if (listlen % stride) {
+ Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
+ return JIM_ERR;
+ }
+ /* Need to create a new list of lists for sorting */
+ tmpListObj = Jim_NewListObj(interp, NULL, 0);
+ Jim_IncrRefCount(tmpListObj);
+ for (i = 0; i < listlen; i += stride) {
+ Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
+ }
+ retCode = ListSortElements(interp, tmpListObj, &info);
+ if (retCode == JIM_OK) {
+ resObj = Jim_NewListObj(interp, NULL, 0);
+ /* Now we need to unpack the result back into a flat list */
+ for (i = 0; i < listlen; i += stride) {
+ Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
+ }
+ Jim_SetResult(interp, resObj);
+ }
+ Jim_DecrRefCount(interp, tmpListObj);
+ }
+ else {
+ if ((shared = Jim_IsShared(resObj))) {
+ resObj = Jim_DuplicateObj(interp, resObj);
+ }
+ retCode = ListSortElements(interp, resObj, &info);
+ if (retCode == JIM_OK) {
+ Jim_SetResult(interp, resObj);
+ }
+ else if (shared) {
+ Jim_FreeNewObj(interp, resObj);
+ }
}
return retCode;
}
diff --git a/jim_tcl.txt b/jim_tcl.txt
index 14cee1e..1889ac9 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -61,6 +61,9 @@ Changes since 0.80
4. `loop` can now omit the start value
5. Add the `xtrace` command for execution trace support
6. Add `history keep`
+7. Add support for `lsearch -index` and `lsearch -stride`, the latter per TIP 351
+8. `lsort -index` now supports multiple indices
+9. Add support for `lsort -stride`
Changes between 0.79 and 0.80
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3371,35 +3374,80 @@ the list are to be matched against pattern and must have one of the values below
+*-nocase*+::
Causes comparisons to be handled in a case-insensitive manner.
++*-index* 'indexList'+::
+ This option is designed for use when searching within nested lists. The
+ 'indexList' gives a path of indices (much as might be used with
+ the lindex or lset commands) within each element to allow the location
+ of the term being matched against.
+
++*-stride* 'strideLength'+::
+ If this option is specified, the list is treated as consisting of
+ groups of 'strideLength' elements and the groups are searched by
+ either their first element or, if the +-index+ option is used,
+ by the element within each group given by the first index passed to
+ +-index+ (which is then ignored by +-index+). The resulting
+ index always points to the first element in a group.
+ ::
+ The list length must be an integer multiple of 'strideLength', which
+ in turn must be at least 1. A 'strideLength' of 1 is the default and
+ indicates no grouping.
+
lsort
~~~~~
-+*lsort* ?*-index* 'listindex'? ?*-nocase|-integer|-real|-command* 'cmdname'? ?*-unique*? ?*-decreasing*|*-increasing*? 'list'+
++*lsort* '?options? list'+
Sort the elements of +'list'+, returning a new list in sorted order.
By default, ASCII (or UTF-8) sorting is used, with the result in increasing order.
-If +-nocase+ is specified, comparisons are case-insensitive.
-
-If +-integer+ is specified, numeric sorting is used.
-
-If +-real+ is specified, floating point number sorting is used.
+Note that only one sort type may be selected with +-integer+, +-real+, +-nocase+ or +-command+
+with last option being used.
-If +-command 'cmdname'+ is specified, +'cmdname'+ is treated as a command
-name. For each comparison, +'cmdname $value1 $value2+' is called which
-should compare the values and return an integer less than, equal
-to, or greater than zero if the +'$value1'+ is to be considered less
-than, equal to, or greater than +'$value2'+, respectively.
++*-integer*+::
+ Sort using numeric (integer) comparison.
-If +-decreasing+ is specified, the resulting list is in the opposite
-order to what it would be otherwise. +-increasing+ is the default.
++*-real*+::
+ Sort using floating point comparison.
-If +-unique+ is specified, then only the last set of duplicate elements found in the list will be retained.
-Note that duplicates are determined relative to the comparison used in the sort. Thus if +-index 0+ is used,
-+{1 a}+ and +{1 b}+ would be considered duplicates and only the second element, +{1 b}+, would be retained.
++*-nocase*+::
+ Sort using using string comparison without regard for case.
-If +-index 'listindex'+ is specified, each element of the list is treated as a list and
-the given index is extracted from the list for comparison. The list index may
-be any valid list index, such as +1+, +end+ or +end-2+.
++*-command* 'cmdname'+::
+ +'cmdname'+ is treated as a command name. For each comparison,
+ +'cmdname $value1 $value2+' is called which
+ should compare the values and return an integer less than, equal
+ to, or greater than zero if the +'$value1'+ is to be considered less
+ than, equal to, or greater than +'$value2'+, respectively.
+
++*-increasing*+::
+ The resulting list is in ascending order, from smallest/lowest to largest/highest.
+ This is the default and does not need to be specified.
+
++*-decreasing*+::
+ The resulting list is in the opposite order to what it would be otherwise.
+
++*-unique*+::
+ Only the last set of duplicate elements found in the list will
+ be retained. Note that duplicates are determined relative to the
+ comparison used in the sort. Thus if +-index 0+ is used, +{1 a}+ and
+ +{1 b}+ would be considered duplicates and only the second element,
+ +{1 b}+, would be retained.
+
++*-index* 'indexList'+::
+ This option is designed for use when sorting nested lists. The
+ 'indexList' gives a path of indices (much as might be used with
+ the lindex or lset commands) within each element to specify the
+ value to be used for comparison.
+
++*-stride* 'strideLength'+::
+ If this option is specified, the list is treated as consisting of
+ groups of 'strideLength' elements and the groups are sorted by
+ either their first element or, if the +-index+ option is used,
+ by the element within each group given by the first index passed to
+ +-index+ (which is then ignored by +-index+). The resulting list
+ is once again a flat list.
+ ::
+ The list length must be an integer multiple of 'strideLength', which
+ in turn must be at least 2.
defer
~~~~~
diff --git a/tests/lsearch.test b/tests/lsearch.test
index ff1342a..f8aa08d 100644
--- a/tests/lsearch.test
+++ b/tests/lsearch.test
@@ -187,4 +187,117 @@ test lsearch-6.10 {lsearch -not -bool -all -nocase} jim {
lsearch -not -bool -glob -all -nocase {a1 a2 b1 b2 a3 b3} B*
} {1 1 0 0 1 0}
+test lsearch-17.1 {lsearch -index option, basic functionality} {
+ lsearch -index 1 {{a c} {a b} {a a}} a
+} 2
+test lsearch-17.2 {lsearch -index option, basic functionality} {
+ lsearch -index 1 -exact {{a c} {a b} {a a}} a
+} 2
+test lsearch-17.3 {lsearch -index option, basic functionality} {
+ lsearch -index 1 -glob {{ab cb} {ab bb} {ab ab}} b*
+} 1
+test lsearch-17.4 {lsearch -index option, basic functionality} {
+ lsearch -index 1 -regexp {{ab cb} {ab bb} {ab ab}} {[cb]b}
+} 0
+test lsearch-17.5 {lsearch -index option, basic functionality} {
+ lsearch -all -index 0 -exact {{a c} {a b} {d a}} a
+} {0 1}
+test lsearch-17.6 {lsearch -index option, basic functionality} {
+ lsearch -all -index 1 -glob {{ab cb} {ab bb} {db bx}} b*
+} {1 2}
+test lsearch-17.7 {lsearch -index option, basic functionality} {
+ lsearch -all -index 1 -regexp {{ab cb} {ab bb} {ab ab}} {[cb]b}
+} {0 1}
+test lsearch-17.8 {lsearch -index option, empty argument} {
+ lsearch -index {} a a
+} 0
+test lsearch-17.9 {lsearch -index option, empty argument} {
+ lsearch -index {} a a
+} [lsearch a a]
+test lsearch-17.10 {lsearch -index option, empty argument} {
+ lsearch -index {} [list \{] \{
+} 0
+test lsearch-17.11 {lsearch -index option, empty argument} {
+ lsearch -index {} [list \{] \{
+} [lsearch [list \{] \{]
+test lsearch-17.12 {lsearch -index option, encoding aliasing} -body {
+ lsearch -index -2 a a
+} -returnCodes error -result {index "-2" out of range}
+test lsearch-17.13 {lsearch -index option, encoding aliasing} -body {
+ lsearch -index -1-1 a a
+} -returnCodes error -result {index "-1-1" out of range}
+test lsearch-17.14 {lsearch -index option, encoding aliasing} -body {
+ lsearch -index end--1 a a
+} -returnCodes error -result {index "end--1" out of range}
+test lsearch-17.15 {lsearch -index option, encoding aliasing} -body {
+ lsearch -index end+1 a a
+} -returnCodes error -result {index "end+1" out of range}
+test lsearch-17.16 {lsearch -index option, encoding aliasing} -body {
+ lsearch -index end+2 a a
+} -returnCodes error -result {index "end+2" out of range}
+
+test lsearch-20.1 {lsearch -index option, index larger than sublists} -body {
+ lsearch -index 2 {{a c} {a b} {a a}} a
+} -returnCodes error -result {element 2 missing from sublist "a c"}
+test lsearch-20.2 {lsearch -index option, malformed index} -body {
+ lsearch -index foo {{a c} {a b} {a a}} a
+} -returnCodes error -match glob -result {bad index *}
+
+test lsearch-23.1 {lsearch -stride option, errors} -body {
+ lsearch -stride {a b} a
+} -returnCodes error -match glob -result {*}
+test lsearch-23.2 {lsearch -stride option, errors} -body {
+ lsearch -stride 0 {a b} a
+} -returnCodes error -result {stride length must be at least 1}
+test lsearch-23.3 {lsearch -stride option, errors} -body {
+ lsearch -stride 2 {a b c} a
+} -returnCodes error -result {list size must be a multiple of the stride length}
+test lsearch-23.4 {lsearch -stride option, errors} -body {
+ lsearch -stride 5 {a b c} a
+} -returnCodes error -result {list size must be a multiple of the stride length}
+test lsearch-23.5 {lsearch -stride option, errors} -body {
+ # Stride equal to length is ok
+ lsearch -stride 3 {a b c} a
+} -result 0
+
+test lsearch-24.1 {lsearch -stride option} -body {
+ lsearch -stride 2 {a b c d e f g h} d
+} -result -1
+test lsearch-24.2 {lsearch -stride option} -body {
+ lsearch -stride 2 {a b c d e f g h} e
+} -result 4
+test lsearch-24.3 {lsearch -stride option} -body {
+ lsearch -stride 3 {a b c d e f g h i} e
+} -result -1
+test lsearch-24.4 {lsearch -stride option} -body {
+ # Result points first in group
+ lsearch -stride 3 -index 1 {a b c d e f g h i} e
+} -result 3
+test lsearch-24.5 {lsearch -stride option} -body {
+ lsearch -inline -stride 2 {a b c d e f g h} d
+} -result {}
+test lsearch-24.6 {lsearch -stride option} -body {
+ # Inline result is a "single element" strided list
+ lsearch -inline -stride 2 {a b c d e f g h} e
+} -result "e f"
+test lsearch-24.7 {lsearch -stride option} -body {
+ lsearch -inline -stride 3 {a b c d e f g h i} e
+} -result {}
+test lsearch-24.8 {lsearch -stride option} -body {
+ lsearch -inline -stride 3 -index 1 {a b c d e f g h i} e
+} -result "d e f"
+test lsearch-24.9 {lsearch -stride option} -body {
+ lsearch -all -inline -stride 3 -index 1 {a b c d e f g e i} e
+} -result "d e f g e i"
+test lsearch-24.10 {lsearch -stride option} -body {
+ lsearch -all -inline -stride 3 -index 0 {a b c d e f a e i} a
+} -result "a b c a e i"
+test lsearch-24.11 {lsearch -stride option} -body {
+ # Stride 1 is same as no stride
+ lsearch -stride 1 {a b c d e f g h} d
+} -result 3
+test lsearch-24.12 {lsearch -stride -index with missing elements} -body {
+ lsearch -stride 1 -index {1 1} {a b c} c
+} -returnCodes error -result {element 1 missing from sublist "a"}
+
testreport
diff --git a/tests/lsort.test b/tests/lsort.test
index dd5a019..5297568 100644
--- a/tests/lsort.test
+++ b/tests/lsort.test
@@ -17,7 +17,7 @@ test lsort-1.1 {Tcl_LsortObjCmd procedure} jim {
} {1 {wrong # args: should be "lsort ?options? list"}}
test lsort-1.2 {Tcl_LsortObjCmd procedure} jim {
list [catch {lsort -foo {1 3 2 5}} msg] $msg
-} {1 {bad option "-foo": must be -ascii, -command, -decreasing, -increasing, -index, -integer, -nocase, -real, or -unique}}
+} {1 {bad option "-foo": must be -ascii, -command, -decreasing, -increasing, -index, -integer, -nocase, -real, -stride, or -unique}}
test lsort-1.3 {Tcl_LsortObjCmd procedure, default options} {
lsort {d e c b a \{ d35 d300}
} {a b c d d300 d35 e \{}
@@ -131,12 +131,12 @@ test lsort-3.1 {SortCompare procedure, skip comparisons after error} {
test lsort-3.2 {lsort -real, returning indices} {
lsort -decreasing -real {1.2 34.5 34.5 5.6}
} {34.5 34.5 5.6 1.2}
-test lsort-3.3 {SortCompare procedure, -index option} jim {
- list [catch {lsort -integer -index 2 {{20 10} {15 30 40}}} msg] $msg
-} {1 {list index out of range}}
-test lsort-3.5 {SortCompare procedure, -index option} jim {
- list [catch {lsort -integer -index 2 {{20 10 13} {15}}} msg] $msg
-} {1 {list index out of range}}
+test lsort-3.3 {SortCompare procedure, -index option} -body {
+ lsort -integer -index 2 {{20 10} {15 30 40}}
+} -returnCodes error -result {element 2 missing from sublist "20 10"}
+test lsort-3.5 {SortCompare procedure, -index option} -body {
+ lsort -integer -index 2 {{20 10 13} {15}}
+} -returnCodes error -result {index "2" out of range}
test lsort-3.6 {SortCompare procedure, -index option} {
lsort -integer -index 2 {{1 15 30} {2 5 25} {3 25 20}}
} {{3 25 20} {2 5 25} {1 15 30}}
@@ -202,12 +202,40 @@ test lsort-3.22 {lsort, unique sort with index} {
set vallist
} {0 4 5}
-test lsort-4.26 {DefaultCompare procedure, signed characters} utf8 {
- lsort [list "abc\u80" "abc"]
-} [list "abc" "abc\u80"]
test lsort-5.1 "Sort case insensitive" {
lsort -nocase {ba aB aa ce}
} {aa aB ba ce}
+test cmdIL-1.30 {Tcl_LsortObjCmd procedure, -stride option} {
+ lsort -stride 2 {f e d c b a}
+} {b a d c f e}
+test cmdIL-1.31 {Tcl_LsortObjCmd procedure, -stride option} {
+ lsort -stride 3 {f e d c b a}
+} {c b a f e d}
+test cmdIL-1.32 {lsort -stride errors} -returnCodes error -body {
+ lsort -stride foo bar
+} -result {expected integer but got "foo"}
+test cmdIL-1.33 {lsort -stride errors} -returnCodes error -body {
+ lsort -stride 1 bar
+} -result {stride length must be at least 2}
+test cmdIL-1.34 {lsort -stride errors} -returnCodes error -body {
+ lsort -stride 2 {a b c}
+} -result {list size must be a multiple of the stride length}
+test cmdIL-1.35 {lsort -stride errors} -returnCodes error -body {
+ lsort -stride 2 -index 3 {a b c d}
+} -match glob -result {*}
+test cmdIL-1.36 {lsort -stride and -index: Bug 2918962} {
+ lsort -stride 2 -index {0 1} {
+ {{c o d e} 54321} {{b l a h} 94729}
+ {{b i g} 12345} {{d e m o} 34512}
+ }
+} {{{b i g} 12345} {{d e m o} 34512} {{c o d e} 54321} {{b l a h} 94729}}
+test cmdIL-1.41 {lsort -stride and -index} -body {
+ lsort -stride 2 -index -2 {a 2 b 1}
+} -returnCodes error -result {index "-2" out of range}
+test cmdIL-1.42 {lsort -stride and-index} -body {
+ lsort -stride 2 -index -1-1 {a 2 b 1}
+} -returnCodes error -result {index "-1-1" out of range}
+
testreport