summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgriffin <briang42@easystreet.net>2022-08-14 01:22:24 (GMT)
committergriffin <briang42@easystreet.net>2022-08-14 01:22:24 (GMT)
commit83338ca1ed9e949a6a68ea80c7bdc4a103ea1c05 (patch)
tree791d0010332337f17032bc78c568801cb82ef49c
parentc0b4b17c115f5bd0872e62ff51bf9230c41a3089 (diff)
downloadtcl-83338ca1ed9e949a6a68ea80c7bdc4a103ea1c05.zip
tcl-83338ca1ed9e949a6a68ea80c7bdc4a103ea1c05.tar.gz
tcl-83338ca1ed9e949a6a68ea80c7bdc4a103ea1c05.tar.bz2
Rename command from "range" to "lseq".
Remove public C API calls, not part of the TIP. Fix shimmer in [join] command. Implement GetElements call for ArithSeries.
-rw-r--r--doc/lseq.n81
-rw-r--r--generic/tcl.decls11
-rw-r--r--generic/tclBasic.c2
-rw-r--r--generic/tclCmdAH.c4
-rw-r--r--generic/tclCmdIL.c489
-rw-r--r--generic/tclCmdMZ.c409
-rw-r--r--generic/tclDecls.h18
-rw-r--r--generic/tclInt.h17
-rw-r--r--generic/tclListObj.c125
-rw-r--r--generic/tclStubInit.c3
-rw-r--r--tests/lseq.test387
11 files changed, 1066 insertions, 480 deletions
diff --git a/doc/lseq.n b/doc/lseq.n
new file mode 100644
index 0000000..0e452d8
--- /dev/null
+++ b/doc/lseq.n
@@ -0,0 +1,81 @@
+'\"
+'\" Copyright (c) 2022 Eric Taylor. All rights reserved.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.TH lseq n 8.7 Tcl "Tcl Built-In Commands"
+.so man.macros
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+lseq \- Build a numeric sequence returned as a list
+.SH SYNOPSIS
+\fBlseq \fIStart \fR?(\fB..\fR|\fBto\fR)? \fIEnd\fR ??\fBby\fR? \fIStep\fR?
+
+\fBlseq \fIStart \fBcount\fR \fICount\fR ??\fBby\fR? \fIStep\fR?
+
+\fBlseq \fICount\fR ?\fBby \fIStep\fR?
+.BE
+.SH DESCRIPTION
+.PP
+The \fBlseq\fR command creates a sequence of numeric values using the given
+parameters \fIStart\fR, \fIEnd\fR, and \fIStep\fR. The \fIoperation\fR
+argument ".." or "to" defines an inclusive range. The "count" option is used
+to define a count of the number of elements in the list. The short form with a
+single count value will create a range from 0 to count-1.
+
+The numeric arguments, \fIStart\fR, \fIEnd\fR, \fIStep\fR, and \fICount\fR,
+can also be a valid expression. the lseq command will evaluate the expression
+and use the numeric result, or error as with any invalid argument value.
+
+.SH EXAMPLES
+.CS
+.\"
+
+ lseq 3
+ \(-> 0 1
+
+ lseq 3 0
+ \(-> 3 2 1 0
+
+ lseq 10 .. 1 by 2
+ \(-> 10 8 6 4 2
+
+ set l [lseq 0 -5]
+ \(-> 0 -1 -2 -3 -4 -5
+
+ foreach i [lseq [llength $l]] {
+ puts l($i)=[lindex $l $i]
+ }
+ \(-> l(0)=0
+ l(1)=-1
+ l(2)=-2
+ l(3)=-3
+ l(4)=-4
+ l(5)=-5
+
+ foreach i [lseq [llength $l]-1 0] {
+ puts l($i)=[lindex $l $i]
+ }
+ \(-> l(5)=-5
+ l(4)=-4
+ l(3)=-3
+ l(2)=-2
+ l(1)=-1
+ l(0)=0
+
+ set sqrs [lmap i [lseq 1 10] {expr $i*$i}]
+ \(-> 1 4 9 16 25 36 49 64 81 100
+.\"
+.CE
+.SH "SEE ALSO"
+foreach(n), list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n),
+lmap(n), lpop(n), lrange(n), lremove(n), lreplace(n),
+lreverse(n), lsearch(n), lset(n), lsort(n)
+.SH KEYWORDS
+element, index, list
+'\" Local Variables:
+'\" mode: nroff
+'\" fill-column: 78
+'\" End:
diff --git a/generic/tcl.decls b/generic/tcl.decls
index a200bbb..99c0e25 100644
--- a/generic/tcl.decls
+++ b/generic/tcl.decls
@@ -2501,17 +2501,6 @@ declare 672 {
declare 673 {
int TclGetUniChar(Tcl_Obj *objPtr, int index)
}
-declare 674 generic {
- Tcl_Obj * Tcl_NewArithSeriesObj(Tcl_WideInt start,
- Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len)
-}
-declare 675 generic {
- int Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index,
- Tcl_WideInt *element)
-}
-declare 676 generic {
- Tcl_WideInt Tcl_ArithSeriesObjLength(Tcl_Obj *arithSeriesPtr)
-}
# ----- BASELINE -- FOR -- 8.7.0 ----- #
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index b78e983..6727118 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -322,11 +322,11 @@ static const CmdInfo builtInCmds[] = {
{"lreplace", Tcl_LreplaceObjCmd, TclCompileLreplaceCmd, NULL, CMD_IS_SAFE},
{"lreverse", Tcl_LreverseObjCmd, NULL, NULL, CMD_IS_SAFE},
{"lsearch", Tcl_LsearchObjCmd, NULL, NULL, CMD_IS_SAFE},
+ {"lseq", Tcl_LseqObjCmd, NULL, NULL, CMD_IS_SAFE},
{"lset", Tcl_LsetObjCmd, TclCompileLsetCmd, NULL, CMD_IS_SAFE},
{"lsort", Tcl_LsortObjCmd, NULL, NULL, CMD_IS_SAFE},
{"package", Tcl_PackageObjCmd, NULL, TclNRPackageObjCmd, CMD_IS_SAFE},
{"proc", Tcl_ProcObjCmd, NULL, NULL, CMD_IS_SAFE},
- {"range", Tcl_RangeObjCmd, NULL, NULL, CMD_IS_SAFE},
{"regexp", Tcl_RegexpObjCmd, TclCompileRegexpCmd, NULL, CMD_IS_SAFE},
{"regsub", Tcl_RegsubObjCmd, TclCompileRegsubCmd, NULL, CMD_IS_SAFE},
{"rename", Tcl_RenameObjCmd, NULL, NULL, CMD_IS_SAFE},
diff --git a/generic/tclCmdAH.c b/generic/tclCmdAH.c
index 73ef295..f31eabc 100644
--- a/generic/tclCmdAH.c
+++ b/generic/tclCmdAH.c
@@ -2830,7 +2830,7 @@ EachloopCmd(
goto done;
}
/* Don't compute values here, wait until the last momement */
- statePtr->argcList[i] = Tcl_ArithSeriesObjLength(statePtr->vCopyList[i]);
+ statePtr->argcList[i] = TclArithSeriesObjLength(statePtr->vCopyList[i]);
} else {
/* List values */
statePtr->aCopyList[i] = TclListObjCopy(interp, objv[2+i*2]);
@@ -2969,7 +2969,7 @@ ForeachAssignments(
if (k < statePtr->argcList[i]) {
if (isarithseries) {
Tcl_WideInt value;
- if (Tcl_ArithSeriesObjIndex(statePtr->vCopyList[i], k, &value) != TCL_OK) {
+ if (TclArithSeriesObjIndex(statePtr->vCopyList[i], k, &value) != TCL_OK) {
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (setting %s loop variable \"%s\")",
(statePtr->resultList != NULL ? "lmap" : "foreach"),
diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c
index 8213d45..065bc2a 100644
--- a/generic/tclCmdIL.c
+++ b/generic/tclCmdIL.c
@@ -95,6 +95,23 @@ typedef struct {
#define SORTMODE_ASCII_NC 8
/*
+ * Definitions for [lseq] command
+ */
+static const char *const seq_operations[] = {
+ "..", "to", "count", "by", NULL
+};
+typedef enum Sequence_Operators {
+ RANGE_DOTS, RANGE_TO, RANGE_COUNT, RANGE_BY
+} SequenceOperators;
+static const char *const seq_step_keywords[] = {"by", NULL};
+typedef enum Step_Operators {
+ STEP_BY = 4
+} SequenceByMode;
+typedef enum Sequence_Decoded {
+ NoneArg, NumericArg, RangeKeywordArg, ByKeywordArg
+} SequenceDecoded;
+
+/*
* Forward declarations for procedures defined in this file:
*/
@@ -2182,7 +2199,7 @@ Tcl_JoinObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* The argument objects. */
{
- int length, listLen;
+ int length, listLen, isArithSeries = 0;
Tcl_Obj *resObjPtr = NULL, *joinObjPtr, **elemPtrs;
if ((objc < 2) || (objc > 3)) {
@@ -2195,9 +2212,14 @@ Tcl_JoinObjCmd(
* pointer to its array of element pointers.
*/
- if (TclListObjGetElementsM(interp, objv[1], &listLen,
+ if (TclHasInternalRep(objv[1],&tclArithSeriesType)) {
+ isArithSeries = 1;
+ listLen = TclArithSeriesObjLength(objv[1]);
+ } else {
+ if (TclListObjGetElementsM(interp, objv[1], &listLen,
&elemPtrs) != TCL_OK) {
- return TCL_ERROR;
+ return TCL_ERROR;
+ }
}
if (listLen == 0) {
@@ -2206,7 +2228,15 @@ Tcl_JoinObjCmd(
}
if (listLen == 1) {
/* One element; return it */
- Tcl_SetObjResult(interp, elemPtrs[0]);
+ if (isArithSeries) {
+ Tcl_WideInt value;
+ if (TclArithSeriesObjIndex(objv[1], 0, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(value));
+ } else {
+ Tcl_SetObjResult(interp, elemPtrs[0]);
+ }
return TCL_OK;
}
@@ -2220,19 +2250,40 @@ Tcl_JoinObjCmd(
int i;
TclNewObj(resObjPtr);
- for (i = 0; i < listLen; i++) {
- if (i > 0) {
+ if (isArithSeries) {
+ Tcl_WideInt value;
+ for (i = 0; i < listLen; i++) {
+ if (i > 0) {
+
+ /*
+ * NOTE: This code is relying on Tcl_AppendObjToObj() **NOT**
+ * to shimmer joinObjPtr. If it did, then the case where
+ * objv[1] and objv[2] are the same value would not be safe.
+ * Accessing elemPtrs would crash.
+ */
+
+ Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
+ }
+ if (TclArithSeriesObjIndex(objv[1], i, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_AppendObjToObj(resObjPtr, Tcl_NewWideIntObj(value));
+ }
+ } else {
+ for (i = 0; i < listLen; i++) {
+ if (i > 0) {
- /*
- * NOTE: This code is relying on Tcl_AppendObjToObj() **NOT**
- * to shimmer joinObjPtr. If it did, then the case where
- * objv[1] and objv[2] are the same value would not be safe.
- * Accessing elemPtrs would crash.
- */
+ /*
+ * NOTE: This code is relying on Tcl_AppendObjToObj() **NOT**
+ * to shimmer joinObjPtr. If it did, then the case where
+ * objv[1] and objv[2] are the same value would not be safe.
+ * Accessing elemPtrs would crash.
+ */
- Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
+ Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
+ }
+ Tcl_AppendObjToObj(resObjPtr, elemPtrs[i]);
}
- Tcl_AppendObjToObj(resObjPtr, elemPtrs[i]);
}
}
Tcl_DecrRefCount(joinObjPtr);
@@ -3081,17 +3132,17 @@ Tcl_LreverseObjCmd(
ArithSeries *arithSeriesPtr = ArithSeriesRepPtr(objv[1]);
Tcl_WideInt rstart, rend, rstep, len;
- len = Tcl_ArithSeriesObjLength(objv[1]);
- if (Tcl_ArithSeriesObjIndex(objv[1], 0, &rend) != TCL_OK) {
+ len = TclArithSeriesObjLength(objv[1]);
+ if (TclArithSeriesObjIndex(objv[1], 0, &rend) != TCL_OK) {
return TCL_ERROR;
}
- if (Tcl_ArithSeriesObjIndex(objv[1], (len-1), &rstart) != TCL_OK) {
+ if (TclArithSeriesObjIndex(objv[1], (len-1), &rstart) != TCL_OK) {
return TCL_ERROR;
}
rstep = -arithSeriesPtr->step;
if (Tcl_IsShared(objv[1])) {
- Tcl_Obj *resultObj = Tcl_NewArithSeriesObj(rstart, rend, rstep, len);
+ Tcl_Obj *resultObj = TclNewArithSeriesObj(rstart, rend, rstep, len);
Tcl_SetObjResult(interp, resultObj);
} else {
@@ -4002,6 +4053,401 @@ Tcl_LsetObjCmd(
/*
*----------------------------------------------------------------------
*
+ * SequenceIdentifyArgument --
+ * (for [lseq] command)
+ *
+ * Given a Tcl_Obj, identify if it is a keyword or a number
+ *
+ * Return Value
+ * 0 - failure, unexpected value
+ * 1 - value is a number
+ * 2 - value is an operand keyword
+ * 3 - value is a by keyword
+ *
+ * The decoded value will be assigned to the appropriate
+ * pointer, if supplied.
+ */
+
+static SequenceDecoded
+SequenceIdentifyArgument(
+ Tcl_Interp *interp, /* for error reporting */
+ Tcl_Obj *argPtr, /* Argument to decode */
+ Tcl_WideInt *intValuePtr, /* Return numeric value */
+ int *keywordIndexPtr) /* Return keyword enum */
+{
+ int status;
+ Tcl_WideInt number;
+ SequenceOperators opmode;
+ SequenceByMode bymode;
+
+ status = Tcl_GetWideIntFromObj(NULL, argPtr, &number);
+ if (status != TCL_OK) {
+ /* Check for an index expression */
+ long value;
+ Tcl_InterpState savedstate;
+ savedstate = Tcl_SaveInterpState(interp, status);
+ if (Tcl_ExprLongObj(interp, argPtr, &value) != TCL_OK) {
+ status = Tcl_RestoreInterpState(interp, savedstate);
+ } else {
+ status = Tcl_RestoreInterpState(interp, savedstate);
+ if (intValuePtr) {
+ *intValuePtr = value;
+ }
+ return NumericArg;
+ }
+ } else {
+ if (intValuePtr) {
+ *intValuePtr = number;
+ }
+ return NumericArg;
+ }
+
+ status = Tcl_GetIndexFromObj(NULL, argPtr, seq_operations,
+ "range operation", 0, &opmode);
+ if (status == TCL_OK) {
+ if (keywordIndexPtr) {
+ *keywordIndexPtr = opmode;
+ }
+ return RangeKeywordArg;
+ }
+
+ status = Tcl_GetIndexFromObj(NULL, argPtr, seq_step_keywords,
+ "step keyword", 0, &bymode);
+ if (status == TCL_OK) {
+ if (keywordIndexPtr) {
+ *keywordIndexPtr = bymode;
+ }
+ return ByKeywordArg;
+ }
+ return NoneArg;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_LseqObjCmd --
+ *
+ * This procedure is invoked to process the "range" Tcl command. See
+ * the user documentation for details on what it does.
+ *
+ * Enumerated possible argument patterns:
+ *
+ * 1:
+ * range n
+ * 2:
+ * range n n
+ * 3:
+ * range n n n
+ * range n 'to' n
+ * range n 'count' n
+ * range n 'by' n
+ * 4:
+ * range n 'to' n n
+ * range n n 'by' n
+ * range n 'count' n n
+ * 5:
+ * range n 'to' n 'by' n
+ * range n 'count' n 'by' n
+ *
+ * Results:
+ * A standard Tcl object result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_LseqObjCmd(
+ TCL_UNUSED(ClientData),
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* The argument objects. */
+{
+ Tcl_WideInt elementCount = -1;
+ Tcl_WideInt start = 0, end = 0, step = 0, number = 0;
+ Tcl_WideInt values[5];
+ int status, keyword;
+ Tcl_Obj *arithSeriesPtr;
+ SequenceOperators opmode;
+ SequenceDecoded decoded;
+ int i, arg_key = 0, value_i = 0;
+
+ /*
+ * Create a decoding key by looping through the arguments and identify
+ * what kind of argument each one is. Encode each argument as a decimal
+ * digit.
+ */
+ if (objc > 6) {
+ /* Too many arguments */
+ arg_key=0;
+ } else for (i=1; i<objc; i++) {
+ arg_key = (arg_key * 10);
+ decoded = SequenceIdentifyArgument(interp, objv[i], &number, &keyword);
+ switch (decoded) {
+
+ case NoneArg:
+ /*
+ * Unrecognizable argument
+ * Reproduce operation error message
+ */
+ status = Tcl_GetIndexFromObj(interp, objv[i], seq_operations,
+ "operation", 0, &opmode);
+ goto done;
+
+ case NumericArg:
+ arg_key += NumericArg;
+ values[value_i] = number;
+ value_i++;
+ break;
+
+ case RangeKeywordArg:
+ arg_key += RangeKeywordArg;
+ values[value_i] = keyword;
+ value_i++;
+ break;
+
+ case ByKeywordArg:
+ arg_key += ByKeywordArg;
+ values[value_i] = keyword;
+ value_i++;
+ break;
+
+ default:
+ arg_key += 9; // Error state
+ value_i++;
+ break;
+ }
+ }
+
+ /*
+ * The key encoding defines a valid set of arguments, or indicates an
+ * error condition; process the values accordningly.
+ */
+ switch (arg_key) {
+
+/* No argument */
+ case 0:
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "n ??op? n ??by? n??");
+ status = TCL_ERROR;
+ goto done;
+ break;
+
+/* range n */
+ case 1:
+ start = 0;
+ elementCount = (values[0] <= 0 ? 0 : values[0]);
+ end = values[0]-1;
+ step = 1;
+ break;
+
+/* range n n */
+ case 11:
+ start = values[0];
+ end = values[1];
+ step = (start <= end) ? 1 : -1;
+ if (start <= end) {
+ elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
+ } else {
+ elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
+ }
+ if (elementCount < 0) elementCount = 0;
+ break;
+
+/* range n n n */
+ case 111:
+ start = values[0];
+ end = values[1];
+ step = values[2];
+ if (start <= end) {
+ elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
+ } else {
+ elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
+ }
+ if (elementCount < 0) elementCount = 0;
+ break;
+
+/* range n 'to' n */
+/* range n 'count' n */
+/* range n 'by' n */
+ case 121:
+ opmode = (SequenceOperators)values[1];
+ switch (opmode) {
+ case RANGE_DOTS:
+ case RANGE_TO:
+ start = values[0];
+ end = values[2];
+ step = (start <= end) ? 1 : -1;
+ elementCount = step ? (start-end+step)/step : 0; // 0 step -> empty list
+ break;
+ case RANGE_BY:
+ start = 0;
+ elementCount = values[0];
+ step = values[2];
+ end = start + (step * elementCount);
+ elementCount = step ? (start-end+step)/step : 0; // 0 step -> empty list
+ break;
+ case RANGE_COUNT:
+ start = values[0];
+ elementCount = (values[2] >= 0 ? values[2] : 0);
+ step = 1;
+ end = start + (step * elementCount);
+ break;
+ default:
+ status = TCL_ERROR;
+ goto done;
+ }
+ break;
+
+/* range n 'to' n n */
+/* range n 'count' n n */
+ case 1211:
+ opmode = (SequenceOperators)values[1];
+ switch (opmode) {
+ case RANGE_DOTS:
+ case RANGE_TO:
+ start = values[0];
+ end = values[2];
+ step = values[3];
+ break;
+ case RANGE_COUNT:
+ start = values[0];
+ elementCount = (values[2] >= 0 ? values[2] : 0);
+ step = values[3];
+ end = start + (step * elementCount);
+ break;
+ case RANGE_BY:
+ /* Error case */
+ status = TCL_ERROR;
+ goto done;
+ break;
+ default:
+ status = TCL_ERROR;
+ goto done;
+ break;
+ }
+ break;
+
+/* range n n 'by' n */
+ case 1121:
+ start = values[0];
+ end = values[1];
+ opmode = (SequenceOperators)values[2];
+ switch (opmode) {
+ case RANGE_BY:
+ step = values[3];
+ break;
+ case RANGE_DOTS:
+ case RANGE_TO:
+ case RANGE_COUNT:
+ default:
+ status = TCL_ERROR;
+ goto done;
+ break;
+ }
+ if (start <= end) {
+ elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
+ } else {
+ elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
+ }
+ break;
+
+/* range n 'to' n 'by' n */
+/* range n 'count' n 'by' n */
+ case 12121:
+ start = values[0];
+ opmode = (SequenceOperators)values[3];
+ switch (opmode) {
+ case RANGE_BY:
+ step = values[4];
+ break;
+ default:
+ status = TCL_ERROR;
+ goto done;
+ break;
+ }
+ opmode = (SequenceOperators)values[1];
+ switch (opmode) {
+ case RANGE_DOTS:
+ case RANGE_TO:
+ start = values[0];
+ end = values[2];
+ if ((step == 0) ||
+ (start < end && step < 0) ||
+ (start > end && step > 0)) {
+ elementCount = 0;
+ } else {
+ elementCount = (end-start+step)/step;
+ }
+ break;
+ case RANGE_COUNT:
+ start = values[0];
+ elementCount = (values[2] >= 0 ? values[2] : 0);
+ if (step != 0) {
+ end = start + (step * elementCount);
+ } else {
+ end = start;
+ elementCount = 0; /* empty list when step is 0 */
+ }
+ break;
+ default:
+ status = TCL_ERROR;
+ goto done;
+ break;
+ }
+ break;
+
+/* Error cases: incomplete arguments */
+ case 12:
+ opmode = (SequenceOperators)values[1]; goto KeywordError; break;
+ case 112:
+ opmode = (SequenceOperators)values[2]; goto KeywordError; break;
+ case 1212:
+ opmode = (SequenceOperators)values[3]; goto KeywordError; break;
+ KeywordError:
+ status = TCL_ERROR;
+ switch (opmode) {
+ case RANGE_DOTS:
+ case RANGE_TO:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "missing \"to\" value."));
+ break;
+ case RANGE_COUNT:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "missing \"count\" value."));
+ break;
+ case RANGE_BY:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "missing \"by\" value."));
+ break;
+ }
+ status = TCL_ERROR;
+ goto done;
+ break;
+
+/* All other argument errors */
+ default:
+ Tcl_WrongNumArgs(interp, 1, objv, "n ??op? n ??by? n??");
+ goto done;
+ break;
+ }
+
+ /*
+ * Success! Now lets create the series object.
+ */
+ arithSeriesPtr = TclNewArithSeriesObj(start, end, step, elementCount);
+ Tcl_SetObjResult(interp, arithSeriesPtr);
+ status = TCL_OK;
+
+ done:
+ return status;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_LsortObjCmd --
*
* This procedure is invoked to process the "lsort" Tcl command. See the
@@ -4267,8 +4713,13 @@ Tcl_LsortObjCmd(
sortInfo.compareCmdPtr = newCommandPtr;
}
- sortInfo.resultCode = TclListObjGetElementsM(interp, listObj,
+ if (TclHasInternalRep(listObj,&tclArithSeriesType)) {
+ sortInfo.resultCode = TclArithSeriesGetElements(interp,
+ listObj, &length, &listObjPtrs);
+ } else {
+ sortInfo.resultCode = TclListObjGetElementsM(interp, listObj,
&length, &listObjPtrs);
+ }
if (sortInfo.resultCode != TCL_OK || length <= 0) {
goto done;
}
diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c
index c48771a..cff182d 100644
--- a/generic/tclCmdMZ.c
+++ b/generic/tclCmdMZ.c
@@ -16,7 +16,6 @@
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
-#include <math.h>
#include "tclInt.h"
#include "tclCompile.h"
#include "tclRegexp.h"
@@ -64,23 +63,6 @@ const char tclDefaultTrimSet[] =
"\xEF\xBB\xBF" /* zero width no-break space (U+feff) */
;
-/*
- * Definitions for [lseq] command
- */
-static const char *const seq_operations[] = {
- "..", "to", "count", "by", NULL
-};
-typedef enum Sequence_Operators {
- RANGE_DOTS, RANGE_TO, RANGE_COUNT, RANGE_BY
-} SequenceOperators;
-static const char *const seq_step_keywords[] = {"by", NULL};
-typedef enum Step_Operators {
- STEP_BY = 4
-} SequenceByMode;
-typedef enum Sequence_Decoded {
- NoneArg, NumericArg, RangeKeywordArg, ByKeywordArg
-} SequenceDecoded;
-
/*
*----------------------------------------------------------------------
@@ -125,397 +107,6 @@ Tcl_PwdObjCmd(
/*
*----------------------------------------------------------------------
*
- * SequenceIdentifyArgument --
- *
- * Given a Tcl_Obj, identify if it is a keyword or a number
- *
- * Return Value
- * 0 - failure, unexpected value
- * 1 - value is a number
- * 2 - value is an operand keyword
- * 3 - value is a by keyword
- *
- * The decoded value will be assigned to the appropriate
- * pointer, if supplied.
- */
-
-static SequenceDecoded
-SequenceIdentifyArgument(
- Tcl_Interp *interp, /* for error reporting */
- Tcl_Obj *argPtr, /* Argument to decode */
- Tcl_WideInt *intValuePtr, /* Return numeric value */
- int *keywordIndexPtr) /* Return keyword enum */
-{
- int status;
- Tcl_WideInt number;
- SequenceOperators opmode;
- SequenceByMode bymode;
-
- status = Tcl_GetWideIntFromObj(NULL, argPtr, &number);
- if (status != TCL_OK) {
- /* Check for an index expression */
- long value;
- Tcl_InterpState savedstate;
- savedstate = Tcl_SaveInterpState(interp, status);
- if (Tcl_ExprLongObj(interp, argPtr, &value) != TCL_OK) {
- status = Tcl_RestoreInterpState(interp, savedstate);
- } else {
- status = Tcl_RestoreInterpState(interp, savedstate);
- if (intValuePtr) {
- *intValuePtr = value;
- }
- return NumericArg;
- }
- } else {
- if (intValuePtr) {
- *intValuePtr = number;
- }
- return NumericArg;
- }
-
- status = Tcl_GetIndexFromObj(NULL, argPtr, seq_operations,
- "range operation", 0, &opmode);
- if (status == TCL_OK) {
- if (keywordIndexPtr) {
- *keywordIndexPtr = opmode;
- }
- return RangeKeywordArg;
- }
-
- status = Tcl_GetIndexFromObj(NULL, argPtr, seq_step_keywords,
- "step keyword", 0, &bymode);
- if (status == TCL_OK) {
- if (keywordIndexPtr) {
- *keywordIndexPtr = bymode;
- }
- return ByKeywordArg;
- }
- return NoneArg;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_RangeObjCmd --
- *
- * This procedure is invoked to process the "range" Tcl command. See
- * the user documentation for details on what it does.
- *
- * Enumerated possible argument patterns:
- *
- * 1:
- * range n
- * 2:
- * range n n
- * 3:
- * range n n n
- * range n 'to' n
- * range n 'count' n
- * range n 'by' n
- * 4:
- * range n 'to' n n
- * range n n 'by' n
- * range n 'count' n n
- * 5:
- * range n 'to' n 'by' n
- * range n 'count' n 'by' n
- *
- * Results:
- * A standard Tcl object result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_RangeObjCmd(
- TCL_UNUSED(ClientData),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* The argument objects. */
-{
- Tcl_WideInt elementCount = -1;
- Tcl_WideInt start = 0, end = 0, step = 0, number = 0;
- Tcl_WideInt values[5];
- int status, keyword;
- Tcl_Obj *arithSeriesPtr;
- SequenceOperators opmode;
- SequenceDecoded decoded;
- int i, arg_key = 0, value_i = 0;
-
- /*
- * Create a decoding key by looping through the arguments and identify
- * what kind of argument each one is. Encode each argument as a decimal
- * digit.
- */
- if (objc > 6) {
- /* Too many arguments */
- arg_key=0;
- } else for (i=1; i<objc; i++) {
- arg_key = (arg_key * 10);
- decoded = SequenceIdentifyArgument(interp, objv[i], &number, &keyword);
- switch (decoded) {
-
- case NoneArg:
- /*
- * Unrecognizable argument
- * Reproduce operation error message
- */
- status = Tcl_GetIndexFromObj(interp, objv[i], seq_operations,
- "operation", 0, &opmode);
- goto done;
-
- case NumericArg:
- arg_key += NumericArg;
- values[value_i] = number;
- value_i++;
- break;
-
- case RangeKeywordArg:
- arg_key += RangeKeywordArg;
- values[value_i] = keyword;
- value_i++;
- break;
-
- case ByKeywordArg:
- arg_key += ByKeywordArg;
- values[value_i] = keyword;
- value_i++;
- break;
-
- default:
- arg_key += 9; // Error state
- value_i++;
- break;
- }
- }
-
- /*
- * The key encoding defines a valid set of arguments, or indicates an
- * error condition; process the values accordningly.
- */
- switch (arg_key) {
-
-/* No argument */
- case 0:
- Tcl_WrongNumArgs(interp, 1, objv,
- "n ??op? n ??by? n??");
- status = TCL_ERROR;
- goto done;
- break;
-
-/* range n */
- case 1:
- start = 0;
- elementCount = (values[0] <= 0 ? 0 : values[0]);
- end = values[0]-1;
- step = 1;
- break;
-
-/* range n n */
- case 11:
- start = values[0];
- end = values[1];
- step = (start <= end) ? 1 : -1;
- if (start <= end) {
- elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
- } else {
- elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
- }
- if (elementCount < 0) elementCount = 0;
- break;
-
-/* range n n n */
- case 111:
- start = values[0];
- end = values[1];
- step = values[2];
- if (start <= end) {
- elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
- } else {
- elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
- }
- if (elementCount < 0) elementCount = 0;
- break;
-
-/* range n 'to' n */
-/* range n 'count' n */
-/* range n 'by' n */
- case 121:
- opmode = (SequenceOperators)values[1];
- switch (opmode) {
- case RANGE_DOTS:
- case RANGE_TO:
- start = values[0];
- end = values[2];
- step = (start <= end) ? 1 : -1;
- elementCount = step ? (start-end+step)/step : 0; // 0 step -> empty list
- break;
- case RANGE_BY:
- start = 0;
- elementCount = values[0];
- step = values[2];
- end = start + (step * elementCount);
- elementCount = step ? (start-end+step)/step : 0; // 0 step -> empty list
- break;
- case RANGE_COUNT:
- start = values[0];
- elementCount = (values[2] >= 0 ? values[2] : 0);
- step = 1;
- end = start + (step * elementCount);
- break;
- default:
- status = TCL_ERROR;
- goto done;
- }
- break;
-
-/* range n 'to' n n */
-/* range n 'count' n n */
- case 1211:
- opmode = (SequenceOperators)values[1];
- switch (opmode) {
- case RANGE_DOTS:
- case RANGE_TO:
- start = values[0];
- end = values[2];
- step = values[3];
- break;
- case RANGE_COUNT:
- start = values[0];
- elementCount = (values[2] >= 0 ? values[2] : 0);
- step = values[3];
- end = start + (step * elementCount);
- break;
- case RANGE_BY:
- /* Error case */
- status = TCL_ERROR;
- goto done;
- break;
- default:
- status = TCL_ERROR;
- goto done;
- break;
- }
- break;
-
-/* range n n 'by' n */
- case 1121:
- start = values[0];
- end = values[1];
- opmode = (SequenceOperators)values[2];
- switch (opmode) {
- case RANGE_BY:
- step = values[3];
- break;
- case RANGE_DOTS:
- case RANGE_TO:
- case RANGE_COUNT:
- default:
- status = TCL_ERROR;
- goto done;
- break;
- }
- if (start <= end) {
- elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
- } else {
- elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
- }
- break;
-
-/* range n 'to' n 'by' n */
-/* range n 'count' n 'by' n */
- case 12121:
- start = values[0];
- opmode = (SequenceOperators)values[3];
- switch (opmode) {
- case RANGE_BY:
- step = values[4];
- break;
- default:
- status = TCL_ERROR;
- goto done;
- break;
- }
- opmode = (SequenceOperators)values[1];
- switch (opmode) {
- case RANGE_DOTS:
- case RANGE_TO:
- start = values[0];
- end = values[2];
- if ((step == 0) ||
- (start <= end && step < 0) ||
- (start >= end && step > 0)) {
- elementCount = 0;
- } else if (start <= end) {
- elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list
- } else if (step > 0) {
- elementCount = step ? (start-end-step)/(-step) : 0; // 0 step -> empty list
- }
- break;
- case RANGE_COUNT:
- start = values[0];
- elementCount = (values[2] >= 0 ? values[2] : 0);
- end = start + (step * elementCount);
- break;
- default:
- status = TCL_ERROR;
- goto done;
- break;
- }
- break;
-
-/* Error cases: incomplete arguments */
- case 12:
- opmode = values[1]; goto KeywordError; break;
- case 112:
- opmode = values[2]; goto KeywordError; break;
- case 1212:
- opmode = values[3]; goto KeywordError; break;
- KeywordError:
- status = TCL_ERROR;
- switch (opmode) {
- case RANGE_DOTS:
- case RANGE_TO:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "missing \"to\" value."));
- break;
- case RANGE_COUNT:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "missing \"count\" value."));
- break;
- case RANGE_BY:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "missing \"by\" value."));
- break;
- }
- status = TCL_ERROR;
- goto done;
- break;
-
-/* All other argument errors */
- default:
- Tcl_WrongNumArgs(interp, 1, objv, "n ??op? n ??by? n??");
- goto done;
- break;
- }
-
- /*
- * Success! Now lets create the series object.
- */
- arithSeriesPtr = Tcl_NewArithSeriesObj(start, end, step, elementCount);
- Tcl_SetObjResult(interp, arithSeriesPtr);
- status = TCL_OK;
-
- done:
- return status;
-}
-
-/*
- *----------------------------------------------------------------------
- *
* Tcl_RegexpObjCmd --
*
* This procedure is invoked to process the "regexp" Tcl command. See
diff --git a/generic/tclDecls.h b/generic/tclDecls.h
index bf32563..b869c97 100644
--- a/generic/tclDecls.h
+++ b/generic/tclDecls.h
@@ -1975,15 +1975,6 @@ EXTERN const char * TclUtfAtIndex(const char *src, int index);
EXTERN Tcl_Obj * TclGetRange(Tcl_Obj *objPtr, int first, int last);
/* 673 */
EXTERN int TclGetUniChar(Tcl_Obj *objPtr, int index);
-/* 674 */
-EXTERN Tcl_Obj * Tcl_NewArithSeriesObj(Tcl_WideInt start,
- Tcl_WideInt end, Tcl_WideInt step,
- Tcl_WideInt len);
-/* 675 */
-EXTERN int Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr,
- Tcl_WideInt index, Tcl_WideInt *element);
-/* 676 */
-EXTERN Tcl_WideInt Tcl_ArithSeriesObjLength(Tcl_Obj *arithSeriesPtr);
typedef struct {
const struct TclPlatStubs *tclPlatStubs;
@@ -2693,9 +2684,6 @@ typedef struct TclStubs {
const char * (*tclUtfAtIndex) (const char *src, int index); /* 671 */
Tcl_Obj * (*tclGetRange) (Tcl_Obj *objPtr, int first, int last); /* 672 */
int (*tclGetUniChar) (Tcl_Obj *objPtr, int index); /* 673 */
- Tcl_Obj * (*tcl_NewArithSeriesObj) (Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len); /* 674 */
- int (*tcl_ArithSeriesObjIndex) (Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_WideInt *element); /* 675 */
- Tcl_WideInt (*tcl_ArithSeriesObjLength) (Tcl_Obj *arithSeriesPtr); /* 676 */
} TclStubs;
extern const TclStubs *tclStubsPtr;
@@ -4074,12 +4062,6 @@ extern const TclStubs *tclStubsPtr;
(tclStubsPtr->tclGetRange) /* 672 */
#define TclGetUniChar \
(tclStubsPtr->tclGetUniChar) /* 673 */
-#define Tcl_NewArithSeriesObj \
- (tclStubsPtr->tcl_NewArithSeriesObj) /* 674 */
-#define Tcl_ArithSeriesObjIndex \
- (tclStubsPtr->tcl_ArithSeriesObjIndex) /* 675 */
-#define Tcl_ArithSeriesObjLength \
- (tclStubsPtr->tcl_ArithSeriesObjLength) /* 676 */
#endif /* defined(USE_TCL_STUBS) */
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 76b6469..bfbf1bc 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2489,6 +2489,7 @@ typedef struct ArithSeries {
Tcl_WideInt end;
Tcl_WideInt step;
Tcl_WideInt len;
+ Tcl_Obj **elements;
Tcl_Obj *wideObjPtr; /* Used to speedup [foreach] reusing the same obj. */
} ArithSeries;
@@ -2496,7 +2497,7 @@ typedef struct ArithSeries {
(ArithSeries *) ((arithSeriesObjPtr)->internalRep.twoPtrValue.ptr1)
#define ArithSeriesIndexM(arithSeriesRepPtr, index) \
- (arithSeriesRepPtr)->start+((index)*arithSeriesRepPtr->step)
+ (arithSeriesRepPtr)->start+((index) * arithSeriesRepPtr->step)
/*
@@ -2943,8 +2944,16 @@ MODULE_SCOPE void TclArgumentGet(Tcl_Interp *interp, Tcl_Obj *obj,
CmdFrame **cfPtrPtr, int *wordPtr);
MODULE_SCOPE Tcl_Obj * TclArithSeriesObjCopy(Tcl_Interp *interp,
Tcl_Obj *arithSeriesPtr);
+MODULE_SCOPE int TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr,
+ Tcl_WideInt index, Tcl_WideInt *element);
+MODULE_SCOPE Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr);
MODULE_SCOPE Tcl_Obj * TclArithSeriesObjRange(Tcl_Obj *arithSeriesPtr,
int fromIdx, int toIdx);
+MODULE_SCOPE int TclArithSeriesGetElements(Tcl_Interp *interp,
+ Tcl_Obj *objPtr, int *objcPtr, Tcl_Obj ***objvPtr);
+MODULE_SCOPE Tcl_Obj * TclNewArithSeriesObj(Tcl_WideInt start,
+ Tcl_WideInt end, Tcl_WideInt step,
+ Tcl_WideInt len);
MODULE_SCOPE int TclAsyncNotifier(int sigNumber, Tcl_ThreadId threadId,
ClientData clientData, int *flagPtr, int value);
MODULE_SCOPE void TclAsyncMarkFromNotifier(void);
@@ -3575,6 +3584,9 @@ MODULE_SCOPE int Tcl_LreverseObjCmd(ClientData clientData,
MODULE_SCOPE int Tcl_LsearchObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
+MODULE_SCOPE int Tcl_LseqObjCmd(ClientData clientData,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
MODULE_SCOPE int Tcl_LsetObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
@@ -3601,9 +3613,6 @@ MODULE_SCOPE int Tcl_PutsObjCmd(ClientData clientData,
MODULE_SCOPE int Tcl_PwdObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
-MODULE_SCOPE int Tcl_RangeObjCmd(ClientData clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const objv[]);
MODULE_SCOPE int Tcl_ReadObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
diff --git a/generic/tclListObj.c b/generic/tclListObj.c
index 2bcca64..d62583a 100644
--- a/generic/tclListObj.c
+++ b/generic/tclListObj.c
@@ -848,7 +848,7 @@ Tcl_ListObjIndex(
if (listRepPtr == NULL && TclHasInternalRep(listPtr,&tclArithSeriesType)) {
Tcl_WideInt widint;
- if (Tcl_ArithSeriesObjIndex(listPtr, index, &widint) == TCL_OK) {
+ if (TclArithSeriesObjIndex(listPtr, index, &widint) == TCL_OK) {
*objPtrPtr = Tcl_NewWideIntObj(widint);
return TCL_OK;
}
@@ -917,7 +917,7 @@ Tcl_ListObjLength(
int length;
if (TclHasInternalRep(listPtr,&tclArithSeriesType)) {
- *intPtr = Tcl_ArithSeriesObjLength(listPtr);
+ *intPtr = TclArithSeriesObjLength(listPtr);
return TCL_OK;
}
@@ -2053,7 +2053,7 @@ SetListFromAny(
* because it can be done an order of magnitude faster
* and may occur frequently.
*/
- Tcl_WideInt wideLen = Tcl_ArithSeriesObjLength(objPtr), j;
+ Tcl_WideInt wideLen = TclArithSeriesObjLength(objPtr), j;
ArithSeries *arithSeriesRepPtr = (ArithSeries*)
objPtr->internalRep.twoPtrValue.ptr1;
listRepPtr = AttemptNewList(interp, wideLen, NULL);
@@ -2325,7 +2325,7 @@ ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step)
/*
*----------------------------------------------------------------------
*
- * Tcl_NewArithSeriesObj --
+ * TclNewArithSeriesObj --
*
* Creates a new ArithSeries object. The returned object has
* refcount = 0.
@@ -2341,7 +2341,7 @@ ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step)
*----------------------------------------------------------------------
*/
Tcl_Obj *
-Tcl_NewArithSeriesObj(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len)
+TclNewArithSeriesObj(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len)
{
Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step));
Tcl_Obj *arithSeriesPtr;
@@ -2355,6 +2355,7 @@ Tcl_NewArithSeriesObj(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_
arithSeriesRepPtr->end = end;
arithSeriesRepPtr->step = step;
arithSeriesRepPtr->len = length;
+ arithSeriesRepPtr->elements = NULL;
arithSeriesRepPtr->wideObjPtr = Tcl_NewWideIntObj(0);
Tcl_IncrRefCount(arithSeriesRepPtr->wideObjPtr);
arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr;
@@ -2370,7 +2371,7 @@ Tcl_NewArithSeriesObj(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_
/*
*----------------------------------------------------------------------
*
- * Tcl_ArithSeriesObjIndex --
+ * TclArithSeriesObjIndex --
*
* Returns the element with the specified index in the list
* represented by the specified Arithmentic Sequence object.
@@ -2390,12 +2391,12 @@ Tcl_NewArithSeriesObj(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_
*/
int
-Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_WideInt *element)
+TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_WideInt *element)
{
ArithSeries *arithSeriesRepPtr;
if (arithSeriesPtr->typePtr != &tclArithSeriesType) {
- Tcl_Panic("Tcl_ArithSeriesObjIndex called with a not ArithSeries Obj.");
+ Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj.");
}
arithSeriesRepPtr = (ArithSeries*)
arithSeriesPtr->internalRep.twoPtrValue.ptr1;
@@ -2409,7 +2410,7 @@ Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_WideInt
/*
*----------------------------------------------------------------------
*
- * Tcl_ArithSeriesObjLength
+ * TclArithSeriesObjLength
*
* Returns the length of the arithmentic series.
*
@@ -2423,7 +2424,7 @@ Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_WideInt
*
*----------------------------------------------------------------------
*/
-Tcl_WideInt Tcl_ArithSeriesObjLength(Tcl_Obj *arithSeriesPtr)
+Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr)
{
ArithSeries *arithSeriesRepPtr = (ArithSeries*)
arithSeriesPtr->internalRep.twoPtrValue.ptr1;
@@ -2453,6 +2454,16 @@ FreeArithSeriesInternalRep(Tcl_Obj *arithSeriesPtr)
{
ArithSeries *arithSeriesRepPtr =
(ArithSeries *) arithSeriesPtr->internalRep.twoPtrValue.ptr1;
+ if (arithSeriesRepPtr->elements) {
+ Tcl_WideInt i;
+ Tcl_Obj**elmts = arithSeriesRepPtr->elements;
+ for(i=0; i<arithSeriesRepPtr->len; i++) {
+ if (elmts[i]) {
+ Tcl_DecrRefCount(elmts[i]);
+ }
+ }
+ ckfree((char *) arithSeriesRepPtr->elements);
+ }
Tcl_DecrRefCount(arithSeriesRepPtr->wideObjPtr);
ckfree((char *) arithSeriesRepPtr);
arithSeriesPtr->internalRep.twoPtrValue.ptr1 = NULL;
@@ -2492,6 +2503,7 @@ DupArithSeriesInternalRep(srcPtr, copyPtr)
copyArithSeriesRepPtr->end = srcArithSeriesRepPtr->end;
copyArithSeriesRepPtr->step = srcArithSeriesRepPtr->step;
copyArithSeriesRepPtr->len = srcArithSeriesRepPtr->len;
+ copyArithSeriesRepPtr->elements = NULL;
copyArithSeriesRepPtr->wideObjPtr = Tcl_NewWideIntObj(0);
Tcl_IncrRefCount(copyArithSeriesRepPtr->wideObjPtr);
@@ -2689,14 +2701,14 @@ TclArithSeriesObjRange(
return obj;
}
- Tcl_ArithSeriesObjIndex(arithSeriesPtr, fromIdx, &start);
- Tcl_ArithSeriesObjIndex(arithSeriesPtr, toIdx, &end);
+ TclArithSeriesObjIndex(arithSeriesPtr, fromIdx, &start);
+ TclArithSeriesObjIndex(arithSeriesPtr, toIdx, &end);
step = arithSeriesRepPtr->step;
len = ArithSeriesLen(start, end, step);
if (Tcl_IsShared(arithSeriesPtr) ||
((arithSeriesPtr->refCount > 1))) {
- return Tcl_NewArithSeriesObj(start, end, step, len);
+ return TclNewArithSeriesObj(start, end, step, len);
}
/*
@@ -2714,9 +2726,96 @@ TclArithSeriesObjRange(
arithSeriesRepPtr->end = end;
arithSeriesRepPtr->step = step;
arithSeriesRepPtr->len = len;
+ arithSeriesRepPtr->elements = NULL;
return arithSeriesPtr;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclArithSeriesGetElements --
+ *
+ * This function returns an (objc,objv) array of the elements in a list
+ * object.
+ *
+ * Results:
+ * The return value is normally TCL_OK; in this case *objcPtr is set to
+ * the count of list elements and *objvPtr is set to a pointer to an
+ * array of (*objcPtr) pointers to each list element. If listPtr does not
+ * refer to an Abstract List object and the object can not be converted
+ * to one, TCL_ERROR is returned and an error message will be left in the
+ * interpreter's result if interp is not NULL.
+ *
+ * The objects referenced by the returned array should be treated as
+ * readonly and their ref counts are _not_ incremented; the caller must
+ * do that if it holds on to a reference. Furthermore, the pointer and
+ * length returned by this function may change as soon as any function is
+ * called on the list object; be careful about retaining the pointer in a
+ * local data structure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclArithSeriesGetElements(
+ Tcl_Interp *interp, /* Used to report errors if not NULL. */
+ Tcl_Obj *objPtr, /* AbstractList object for which an element
+ * array is to be returned. */
+ int *objcPtr, /* Where to store the count of objects
+ * referenced by objv. */
+ Tcl_Obj ***objvPtr) /* Where to store the pointer to an array of
+ * pointers to the list's objects. */
+{
+ if (TclHasInternalRep(objPtr,&tclArithSeriesType)) {
+ ArithSeries *arithSeriesRepPtr;
+ Tcl_Obj **objv;
+ int i, objc;
+
+ ArithSeriesGetInternalRep(objPtr, arithSeriesRepPtr);
+ objc = arithSeriesRepPtr->len;
+ if (objc > 0) {
+ if (arithSeriesRepPtr->elements) {
+ /* If this exists, it has already been populated */
+ objv = arithSeriesRepPtr->elements;
+ } else {
+ /* Construct the elements array */
+ objv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*) * objc);
+ if (objv == NULL) {
+ if (interp) {
+ Tcl_SetObjResult(
+ interp,
+ Tcl_NewStringObj("max length of a Tcl list exceeded", -1));
+ Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
+ }
+ return TCL_ERROR;
+ }
+ arithSeriesRepPtr->elements = objv;
+ for (i = 0; i < objc; i++) {
+ Tcl_WideInt wi = ArithSeriesIndexM(arithSeriesRepPtr, (Tcl_WideInt)i);
+ objv[i] = Tcl_NewWideIntObj(wi);
+ Tcl_IncrRefCount(objv[i]);
+ }
+ }
+ } else {
+ objv = NULL;
+ }
+ *objvPtr = objv;
+ *objcPtr = objc;
+ } else {
+ if (interp != NULL) {
+ Tcl_SetObjResult(
+ interp,
+ Tcl_ObjPrintf("value is not an arithseries"));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "UNKNOWN", NULL);
+ }
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
/*
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index f8d3cde..2b7952d 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -2034,9 +2034,6 @@ const TclStubs tclStubs = {
TclUtfAtIndex, /* 671 */
TclGetRange, /* 672 */
TclGetUniChar, /* 673 */
- Tcl_NewArithSeriesObj, /* 674 */
- Tcl_ArithSeriesObjIndex, /* 675 */
- Tcl_ArithSeriesObjLength, /* 676 */
};
/* !END!: Do not edit above this line. */
diff --git a/tests/lseq.test b/tests/lseq.test
new file mode 100644
index 0000000..082111b
--- /dev/null
+++ b/tests/lseq.test
@@ -0,0 +1,387 @@
+# Commands covered: lseq
+#
+# This file contains a collection of tests for one or more of the Tcl
+# built-in commands. Sourcing this file into Tcl runs the tests and
+# generates output for errors. No output means no errors were found.
+#
+# Copyright © 2003 Simon Geard.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+if {"::tcltest" ni [namespace children]} {
+ package require tcltest 2.5
+ namespace import -force ::tcltest::*
+}
+
+testConstraint arithSeriesDouble 0
+testConstraint arithSeriesShimmer 1
+testConstraint arithSeriesShimmerOk 0
+
+## Arg errors
+test lseq-1.1 {error cases} -body {
+ lseq
+} \
+ -returnCodes 1 \
+ -result {wrong # args: should be "lseq n ??op? n ??by? n??"}
+
+
+test lseq-1.2 {step magnitude} {
+ lseq 10 .. 1 by -2 ;# or this could be an error - or not
+} {10 8 6 4 2}
+
+test lseq-1.3 {synergy between int and double} {
+ set rl [lseq 25. to 5. by -5]
+ set il [lseq 25 to 5 by -5]
+ lmap r $rl i $il { if {$r ne "" && $i ne ""} {expr {int($r) == $i}} else {list $r $i} }
+} {1 1 1 1 1}
+
+test lseq-1.4 {integer decreasing} {
+ lseq 10 .. 1
+} {10 9 8 7 6 5 4 3 2 1}
+
+test lseq-1.5 {integer increasing} {
+ lseq 1 .. 10
+} {1 2 3 4 5 6 7 8 9 10}
+
+test lseq-1.6 {integer decreasing with step} {
+ lseq 10 .. 1 by -2
+} {10 8 6 4 2}
+
+test lseq-1.7 {real increasing lseq} arithSeriesDouble {
+ lseq 5.0 to 15.
+} {5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0}
+
+test lseq-1.8 {real increasing lseq with step} arithSeriesDouble {
+ lseq 5.0 to 25. by 5
+} {5.0 10.0 15.0 20.0 25.0}
+
+test lseq-1.9 {real decreasing with step} arithSeriesDouble {
+ lseq 25. to 5. by -5
+} {25.0 20.0 15.0 10.0 5.0}
+
+# note, 10 cannot be in such a list, but allowed
+test lseq-1.10 {integer lseq with step} {
+ lseq 1 to 10 by 2
+} {1 3 5 7 9}
+
+test lseq-1.11 {error case: increasing wrong step direction} {
+ lseq 1 to 10 by -2
+} {}
+
+test lseq-1.12 {decreasing lseq with step} arithSeriesDouble {
+ lseq 25. to -25. by -5
+} { 25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0}
+
+test lseq-1.13 {count operation} {
+ -body {
+ lseq 5 count 5
+ }
+ -result {5 6 7 8 9}
+}
+
+test lseq-1.14 {count with step} {
+ -body {
+ lseq 5 count 5 by 2
+ }
+ -result {5 7 9 11 13}
+}
+
+test lseq-1.15 {count with decreasing step} {
+ -body {
+ lseq 5 count 5 by -2
+ }
+ -result {5 3 1 -1 -3}
+}
+
+test lseq-1.16 {large numbers} {
+ -body {
+ lseq [expr {int(1e6)}] [expr {int(2e6)}] [expr {int(1e5)}]
+ }
+ -result {1000000 1100000 1200000 1300000 1400000 1500000 1600000 1700000 1800000 1900000 2000000}
+}
+
+test lseq-1.17 {too many arguments} -body {
+ lseq 12 to 24 by 2 with feeling
+} -returnCodes 1 -result {wrong # args: should be "lseq n ??op? n ??by? n??"}
+
+test lseq-1.18 {too many arguments extra valid keyword} -body {
+ lseq 12 to 24 by 2 count
+} -returnCodes 1 -result {wrong # args: should be "lseq n ??op? n ??by? n??"}
+
+test lseq-1.19 {too many arguments extra numeric value} -body {
+ lseq 12 to 24 by 2 7
+} -returnCodes 1 -result {wrong # args: should be "lseq n ??op? n ??by? n??"}
+
+#
+# Short-hand use cases
+#
+test lseq-2.2 {step magnitude} {
+ lseq 10 1 2 ;# this is an empty case since step has wrong sign
+} {}
+
+test lseq-2.3 {step wrong sign} arithSeriesDouble {
+ lseq 25. 5. 5 ;# ditto - empty list
+} {}
+
+test lseq-2.4 {integer decreasing} {
+ lseq 10 1
+} {10 9 8 7 6 5 4 3 2 1}
+
+test lseq-2.5 {integer increasing} {
+ lseq 1 10
+} {1 2 3 4 5 6 7 8 9 10}
+
+test lseq-2.6 {integer decreasing with step} {
+ lseq 10 1 by -2
+} {10 8 6 4 2}
+
+test lseq-2.7 {real increasing lseq} arithSeriesDouble {
+ lseq 5.0 15.
+} {5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0}
+
+
+test lseq-2.8 {real increasing lseq with step} arithSeriesDouble {
+ lseq 5.0 25. 5
+} {5.0 10.0 15.0 20.0 25.0}
+
+
+test lseq-2.9 {real decreasing with step} arithSeriesDouble {
+ lseq 25. 5. -5
+} {25.0 20.0 15.0 10.0 5.0}
+
+test lseq-2.10 {integer lseq with step} {
+ lseq 1 10 2
+} {1 3 5 7 9}
+
+test lseq-2.11 {error case: increasing wrong step direction} {
+ lseq 1 10 -2
+} {}
+
+test lseq-2.12 {decreasing lseq with step} arithSeriesDouble {
+ lseq 25. -25. -5
+} { 25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0}
+
+test lseq-2.13 {count only operation} {
+ lseq 5
+} {0 1 2 3 4}
+
+test lseq-2.14 {count with step} {
+ lseq 5 count 5 2
+} {5 7 9 11 13}
+
+test lseq-2.15 {count with decreasing step} {
+ lseq 5 count 5 -2
+} {5 3 1 -1 -3}
+
+test lseq-2.16 {large numbers} {
+ lseq 1e6 2e6 1e5
+} {1000000 1100000 1200000 1300000 1400000 1500000 1600000 1700000 1800000 1900000 2000000}
+
+test lseq-2.17 {large numbers} arithSeriesDouble {
+ lseq 1e6 2e6 1e5
+} {1000000.0 1100000.0 1200000.0 1300000.0 1400000.0 1500000.0 1600000.0 1700000.0 1800000.0 1900000.0 2000000.0}
+
+
+test lseq-3.1 {experiement} {
+ set ans {}
+ foreach factor [lseq 2.0 10.0] {
+ set start 1
+ set end 10
+ for {set step 1} {$step < 1e8} {} {
+ set l [lseq $start to $end by $step]
+ if {[llength $l] != 10} {
+ lappend ans $factor $step [llength $l] $l
+ }
+ set step [expr {$step * $factor}]
+ set end [expr {$end * $factor}]
+ }
+ }
+ if {$ans eq {}} {
+ set ans OK
+ }
+ set ans
+} {OK}
+
+test lseq-3.2 {error case} -body {
+ lseq foo
+} -returnCodes 1 -result {bad operation "foo": must be .., to, count, or by}
+
+test lseq-3.3 {error case} -body {
+ lseq 10 foo
+} -returnCodes 1 -result {bad operation "foo": must be .., to, count, or by}
+
+test lseq-3.4 {error case} -body {
+ lseq 25 or 6
+} -returnCodes 1 -result {bad operation "or": must be .., to, count, or by}
+
+test lseq-3.5 {simple count and step arguments} {
+ lseq 25 by 6
+} {0 6 12 18 24 30 36 42 48 54 60 66 72 78 84 90 96 102 108 114 120 126 132 138 144 150}
+
+test lseq-3.6 {error case} -body {
+ lseq 1 7 or 3
+} -returnCodes 1 -result {bad operation "or": must be .., to, count, or by}
+
+test lseq-3.7 {lmap lseq} {
+ lmap x [lseq 5] { expr {$x * $x} }
+} {0 1 4 9 16}
+
+test lseq-3.8 {lrange lseq} {
+ set r [lrange [lseq 1 100] 10 20]
+ lindex [tcl::unsupported::representation $r] 3
+} {arithseries}
+
+test lseq-3.9 {lassign lseq} arithSeriesShimmer {
+ set r [lseq 15]
+ set r2 [lassign $r a b]
+ list [lindex [tcl::unsupported::representation $r] 3] $a $b \
+ [lindex [tcl::unsupported::representation $r2] 3]
+} {arithseries 0 1 arithseries}
+
+test lseq-3.10 {lsearch lseq must shimmer?} arithSeriesShimmer {
+ set r [lseq 15 0]
+ set a [lsearch $r 9]
+ list [lindex [tcl::unsupported::representation $r] 3] $a
+} {list 6}
+
+test lseq-3.11 {lreverse lseq} {
+ set r [lseq 15 0]
+ set a [lreverse $r]
+ join [list \
+ [lindex [tcl::unsupported::representation $r] 3] \
+ $r \
+ [lindex [tcl::unsupported::representation $a] 3] \
+ $a] \n
+} {arithseries
+15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+arithseries
+0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
+
+test lseq-3.12 {in operator} {
+ set r [lseq 9]
+ set i [expr {7 in $r}]
+ set j [expr {10 ni $r}]
+ set k [expr {-1 in $r}]
+ set l [expr {4 ni $r}]
+ list $i $j $k $l [lindex [tcl::unsupported::representation $r] 3]
+} {1 1 0 0 arithseries}
+
+test lseq-3.13 {lmap lseq shimmer} arithSeriesShimmer {
+ set r [lseq 15]
+ set rep-before [lindex [tcl::unsupported::representation $r] 3]
+ set m [lmap i $r { expr {$i * 7} }]
+ set rep-after [lindex [tcl::unsupported::representation $r] 3]
+ set rep-m [lindex [tcl::unsupported::representation $m] 3]
+ list $r ${rep-before} ${rep-after} ${rep-m} $m
+} {{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14} arithseries arithseries list {0 7 14 21 28 35 42 49 56 63 70 77 84 91 98}}
+
+test lseq-3.14 {array for shimmer} arithSeriesShimmerOk {
+ array set testarray {a Test for This great Function}
+ set vars [lseq 2]
+ set vars-rep [lindex [tcl::unsupported::representation $vars] 3]
+ array for $vars testarray {
+ lappend keys $0
+ lappend vals $1
+ }
+ # Since hash order is not guaranteed, have to validate content ignoring order
+ set valk [lmap k $keys {expr {$k in {a for great}}}]
+ set valv [lmap v $vals {expr {$v in {Test This Function}}}]
+ set vars-after [lindex [tcl::unsupported::representation $vars] 3]
+ list ${vars-rep} $valk $valv ${vars-after}
+} {arithseries {1 1 1} {1 1 1} arithseries}
+
+test lseq-3.15 {join for shimmer} arithSeriesShimmer {
+ set r [lseq 3]
+ set rep-before [lindex [tcl::unsupported::representation $r] 3]
+ set str [join $r :]
+ set rep-after [lindex [tcl::unsupported::representation $r] 3]
+ list ${rep-before} $str ${rep-after}
+} {arithseries 0:1:2 arithseries}
+
+test lseq-3.16 {error case} -body {
+ lseq 16 to
+} -returnCodes 1 -result {missing "to" value.}
+
+test lseq-3.17 {error case} -body {
+ lseq 17 to 13 by
+} -returnCodes 1 -result {missing "by" value.}
+
+test lseq-3.18 {error case} -body {
+ lseq 18 count
+} -returnCodes 1 -result {missing "count" value.}
+
+test lseq-3.19 {edge case} -body {
+ lseq 1 count 5 by 0
+} -result {}
+# 1 1 1 1 1
+
+# My thought is that this is likely a user error, since they can always use lrepeat for this.
+
+test lseq-3.20 {edge case} -body {
+ lseq 1 to 1 by 0
+} -result {}
+
+# hmmm, I guess this is right, in a way, so...
+
+test lseq-3.21 {edge case} {
+ lseq 1 to 1 by 1
+} {1}
+
+test lseq-3.22 {edge case} {
+ lseq 1 1 1
+} {1}
+
+test lseq-3.23 {edge case} {
+ llength [lseq 1 1 1]
+} {1}
+
+test lseq-3.24 {edge case} {
+ llength [lseq 1 to 1 1]
+} {1}
+
+test lseq-3.25 {edge case} {
+ llength [lseq 1 to 1 by 1]
+} {1}
+
+test lseq-3.26 {lsort shimmer} arithSeriesShimmer {
+ set r [lseq 15 0]
+ set rep-before [lindex [tcl::unsupported::representation $r] 3]
+ set lexical_sort [lsort $r]
+ set rep-after [lindex [tcl::unsupported::representation $r] 3]
+ list ${rep-before} $lexical_sort ${rep-after}
+} {arithseries {0 1 10 11 12 13 14 15 2 3 4 5 6 7 8 9} arithseries}
+
+test lseq-3.27 {lreplace shimmer} arithSeriesShimmer {
+ set r [lseq 15 0]
+ set rep-before [lindex [tcl::unsupported::representation $r] 3]
+ set lexical_sort [lreplace $r 3 5 A B C]
+ set rep-after [lindex [tcl::unsupported::representation $r] 3]
+ list ${rep-before} $lexical_sort ${rep-after}
+} {arithseries {15 14 13 A B C 9 8 7 6 5 4 3 2 1 0} arithseries}
+
+test lseq-3.28 {lreverse bug in ArithSeries} {} {
+ set r [lseq -5 17 3]
+ set rr [lreverse $r]
+ list $r $rr [string equal $r [lreverse $rr]]
+} {{-5 -2 1 4 7 10 13 16} {16 13 10 7 4 1 -2 -5} 1}
+
+test lseq-4.1 {end expressions} {
+ set start 7
+ lseq $start $start+11
+} {7 8 9 10 11 12 13 14 15 16 17 18}
+
+test lseq-4.2 {start expressions} {
+ set base [clock seconds]
+ set tl [lseq $base-60 $base 10]
+ lmap t $tl {expr {$t - $base + 60}}
+} {0 10 20 30 40 50 60}
+
+
+# cleanup
+::tcltest::cleanupTests
+return
+
+# Local Variables:
+# mode: tcl
+# End: