diff options
author | griffin <briang42@easystreet.net> | 2022-08-14 01:22:24 (GMT) |
---|---|---|
committer | griffin <briang42@easystreet.net> | 2022-08-14 01:22:24 (GMT) |
commit | 83338ca1ed9e949a6a68ea80c7bdc4a103ea1c05 (patch) | |
tree | 791d0010332337f17032bc78c568801cb82ef49c | |
parent | c0b4b17c115f5bd0872e62ff51bf9230c41a3089 (diff) | |
download | tcl-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.n | 81 | ||||
-rw-r--r-- | generic/tcl.decls | 11 | ||||
-rw-r--r-- | generic/tclBasic.c | 2 | ||||
-rw-r--r-- | generic/tclCmdAH.c | 4 | ||||
-rw-r--r-- | generic/tclCmdIL.c | 489 | ||||
-rw-r--r-- | generic/tclCmdMZ.c | 409 | ||||
-rw-r--r-- | generic/tclDecls.h | 18 | ||||
-rw-r--r-- | generic/tclInt.h | 17 | ||||
-rw-r--r-- | generic/tclListObj.c | 125 | ||||
-rw-r--r-- | generic/tclStubInit.c | 3 | ||||
-rw-r--r-- | tests/lseq.test | 387 |
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: |