diff options
author | griffin <briang42@easystreet.net> | 2022-08-19 18:08:39 (GMT) |
---|---|---|
committer | griffin <briang42@easystreet.net> | 2022-08-19 18:08:39 (GMT) |
commit | f9e3b4dd740c0d808fef53f9eba4b44e67734c34 (patch) | |
tree | b3d402586b3073876cd55494a2ab063aac23eaec | |
parent | 8bf1d3fbf168db2b518b750ba6554b7e33796815 (diff) | |
download | tcl-f9e3b4dd740c0d808fef53f9eba4b44e67734c34.zip tcl-f9e3b4dd740c0d808fef53f9eba4b44e67734c34.tar.gz tcl-f9e3b4dd740c0d808fef53f9eba4b44e67734c34.tar.bz2 |
Reimplement AbstrctList type structure to simplify. fix various bugs.
-rw-r--r-- | doc/lseq.n | 104 | ||||
-rw-r--r-- | generic/tcl.decls | 5 | ||||
-rw-r--r-- | generic/tcl.h | 126 | ||||
-rw-r--r-- | generic/tclAbstractList.c | 315 | ||||
-rw-r--r-- | generic/tclAbstractList.h | 30 | ||||
-rw-r--r-- | generic/tclCmdIL.c | 327 | ||||
-rw-r--r-- | generic/tclDecls.h | 10 | ||||
-rw-r--r-- | generic/tclExecute.c | 32 | ||||
-rw-r--r-- | generic/tclInt.h | 29 | ||||
-rw-r--r-- | generic/tclStubInit.c | 1 | ||||
-rw-r--r-- | tests/lseq.test | 72 |
11 files changed, 589 insertions, 462 deletions
diff --git a/doc/lseq.n b/doc/lseq.n new file mode 100644 index 0000000..e6ba7a6 --- /dev/null +++ b/doc/lseq.n @@ -0,0 +1,104 @@ +'\" +'\" 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 the range. The "count" option is used +to define a count of the number of elements in the list. A short form use of +the command, with a +single count value, will create a range from 0 to count-1. + +The \fBlseq\fR command can produce both increasing and decreasing sequences. When +both Start and End are provided without a Step value, then if Start <= End, +the sequence will be increasing and if Start > End it will be decreasing. If a +Step vale is included, it's sign should agree with the direction of the +sequence (descending -> negative and ascending -> positive), otherwise an +empty list is returned. For example: + +.CS \" + % lseq 1 to 5 ;# increasing + 1 2 3 4 5 + + % lseq 5 to 1 ;# decreasing + 5 4 3 2 1 + + % lseq 6 to 1 by 2 ;# decreasing, step wrong sign, empty list + + % lseq 1 to 5 by 0 ;# all step sizes of 0 produce an empty list + +.\" +.CE + +The numeric arguments, \fIStart\fR, \fIEnd\fR, \fIStep\fR, and \fICount\fR, +may also be a valid expression. The expression will be evaluate and the +numeric result be used. An expression that does not evaluate to a number will +produce an invalid argument error. + +.SH EXAMPLES +.CS +.\" + + lseq 3 + \(-> 0 1 2 + + 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 459923c..671c89c 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -2514,10 +2514,7 @@ declare 677 { Tcl_Obj *Tcl_AbstractListObjReverse(Tcl_Obj *abstractListPtr) } declare 678 { - Tcl_Obj *Tcl_NewAbstractListObj(Tcl_Interp *interp, const char* typeName, size_t requiredSize) -} -declare 679 { - int Tcl_AbstractListSetProc(Tcl_Obj *objPtr, Tcl_AbstractListProcType ptype, void *proc) + Tcl_Obj *Tcl_NewAbstractListObj(Tcl_Interp *interp, const Tcl_AbstractListType* vTablePtr) } # ----- BASELINE -- FOR -- 8.7.0 ----- # diff --git a/generic/tcl.h b/generic/tcl.h index 8c72ed0..a6894f4 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -725,15 +725,21 @@ typedef void (Tcl_MainLoopProc) (void); /* Abstract List functions */ typedef struct Tcl_Obj* (Tcl_ALNewObjProc) (int objc, struct Tcl_Obj *objv[]); -typedef void (Tcl_ALDupRepProc) (struct Tcl_Obj *srcPtr, struct Tcl_Obj *copyPtr); +typedef void (Tcl_ALDupRepProc) (struct Tcl_Obj *srcPtr, struct Tcl_Obj *copyPtr); typedef Tcl_WideInt (Tcl_ALLengthProc) (struct Tcl_Obj *listPtr); typedef struct Tcl_Obj* (Tcl_ALIndexProc) (struct Tcl_Obj *listPtr, Tcl_WideInt index); -typedef struct Tcl_Obj* (Tcl_ALSliceProc) (struct Tcl_Obj *listPtr, Tcl_WideInt fromIdx, Tcl_WideInt toIdx); +typedef struct Tcl_Obj* (Tcl_ALSliceProc) (struct Tcl_Obj *listPtr, Tcl_WideInt fromIdx, + Tcl_WideInt toIdx); typedef struct Tcl_Obj* (Tcl_ALReverseProc) (struct Tcl_Obj *listPtr); +typedef int (Tcl_ALGetElements) (Tcl_Interp *interp, struct Tcl_Obj *listPtr, + int *objcptr, struct Tcl_Obj ***objvptr); +typedef void (Tcl_ALFreeConcreteRep) (struct Tcl_Obj *listPtr); +typedef void (Tcl_ALToStringRep) (struct Tcl_Obj *listPtr); typedef enum { TCL_ABSL_NEW, TCL_ABSL_DUPREP, TCL_ABSL_LENGTH, TCL_ABSL_INDEX, - TCL_ABSL_SLICE, TCL_ABSL_REVERSE + TCL_ABSL_SLICE, TCL_ABSL_REVERSE, TCL_ABSL_GETELEMENTS, TCL_ABSL_FREEREP, + TCL_ABSL_TOSTRING } Tcl_AbstractListProcType; typedef struct Tcl_AbstractListVersion_ *Tcl_AbstractListVersion; @@ -830,80 +836,72 @@ typedef struct Tcl_Obj { #define TCL_ABSTRACTLIST_VERSION_1 ((Tcl_AbstractListVersion) 0x1) -typedef struct AbstractList { +/* Virtual function dispatch a la Tcl_ObjType but for AbstractList */ +typedef struct Tcl_AbstractListType { Tcl_AbstractListVersion version;/* Structure version */ - - size_t repSize; /* value size */ const char *typeName; /* Custom value reference */ - Tcl_Obj **elements; /* Used only by Tcl_AbstractListGetElements */ - + /* List emulation functions */ - Tcl_ALNewObjProc *newObjProc; /* How to create a new Tcl_Obj of this - custom type */ - Tcl_ALDupRepProc *dupRepProc; /* How to duplicate a internal rep of this - custom type */ - Tcl_ALLengthProc *lengthProc; /* Return the [llength] of the - AbstractList */ - Tcl_ALIndexProc *indexProc; /* Return a value (Tcl_Obj) for - [lindex $al $index] */ - Tcl_ALSliceProc *sliceProc; /* Return an AbstractList for - [lrange $al $start $end] */ - Tcl_ALReverseProc *reverseProc; /* Return an AbstractList for - [lreverse $al] */ - /* Must Be Last -- Don't Move! */ - void* abstractValue; - -} AbstractList; - -static inline AbstractList* Tcl_AbstractListRepPtr(Tcl_Obj *abstractListObjPtr) + Tcl_ALNewObjProc *newObjProc; /* How to create a new Tcl_Obj of this + ** custom type */ + Tcl_ALDupRepProc *dupRepProc; /* How to duplicate a internal rep of this + ** custom type */ + Tcl_ALLengthProc *lengthProc; /* Return the [llength] of the + ** AbstractList */ + Tcl_ALIndexProc *indexProc; /* Return a value (Tcl_Obj) for + ** [lindex $al $index] */ + Tcl_ALSliceProc *sliceProc; /* Return an AbstractList for + ** [lrange $al $start $end] */ + Tcl_ALReverseProc *reverseProc; /* Return an AbstractList for + ** [lreverse $al] */ + Tcl_ALGetElements *getElementsProc; /* Return an objv[] of all elements in + ** the list */ + Tcl_ALFreeConcreteRep *freeRepProc; /* Free ConcreteRep internals if + necessary */ + Tcl_ALToStringRep *toStringProc; /* Optimized "to-string" conversion + * for updating the string rep */ + +} Tcl_AbstractListType; + +extern const Tcl_ObjType tclAbstractListType; + +/* + * Returns pointer to the concrete type or NULL if not AbstractList or + * not abstract list of the same type as concrete type + */ +static inline Tcl_AbstractListType *Tcl_AbstractListGetType( + Tcl_Obj *objPtr) { - return (AbstractList *) ((abstractListObjPtr)->internalRep.twoPtrValue.ptr1); + if (objPtr->typePtr != &tclAbstractListType) { + return NULL; + } + return (Tcl_AbstractListType *) objPtr->internalRep.twoPtrValue.ptr1; } -static inline void* Tcl_AbstractListGetTypeRep( +/* Returns the storage used by the concrete abstract list type */ +static inline void* Tcl_AbstractListGetConcreteRep( Tcl_Obj *objPtr) /* Object of type AbstractList */ -{ - const Tcl_ObjInternalRep *irPtr; - irPtr = objPtr ? &((objPtr)->internalRep) : NULL; - return irPtr ? (void *)(irPtr->twoPtrValue.ptr2) : NULL; -} - -/* Enforce type checking on supplied function pointers while isolating - * internal struct details */ - -EXTERN int Tcl_AbstractListSetProc(Tcl_Obj *objPtr, Tcl_AbstractListProcType ptype, void *proc); - -static inline int Tcl_SetAbstractListNewProc(Tcl_Obj *objPtr, Tcl_ALNewObjProc *proc) -{ - return Tcl_AbstractListSetProc(objPtr, TCL_ABSL_NEW, proc); -} - -static inline int Tcl_SetAbstractListLengthProc(Tcl_Obj *objPtr, Tcl_ALLengthProc *proc) -{ - return Tcl_AbstractListSetProc(objPtr, TCL_ABSL_LENGTH, proc); -} - -static inline int Tcl_SetAbstractListSliceProc(Tcl_Obj *objPtr, Tcl_ALSliceProc *proc) -{ - return Tcl_AbstractListSetProc(objPtr, TCL_ABSL_SLICE, proc); -} - -static inline int Tcl_SetAbstractListIndexProc(Tcl_Obj *objPtr, Tcl_ALIndexProc *proc) -{ - return Tcl_AbstractListSetProc(objPtr, TCL_ABSL_INDEX, proc); -} - -static inline int Tcl_SetAbstractListReverseProc(Tcl_Obj *objPtr, Tcl_ALReverseProc *proc) { - return Tcl_AbstractListSetProc(objPtr, TCL_ABSL_REVERSE, proc); + /* Public function, must check for NULL */ + if (objPtr == NULL || objPtr->typePtr != &tclAbstractListType) { + return NULL; + } + return objPtr->internalRep.twoPtrValue.ptr2; } -static inline int Tcl_SetAbstractListDupRepProc(Tcl_Obj *objPtr, Tcl_ALDupRepProc *proc) +/* + * Sets the storage used by the concrete abstract list type + * Caller has to ensure type is AbstractList. Existing rep will be + * overwritten so caller has to free previous rep if necessary. + */ +static inline void Tcl_AbstractListSetConcreteRep( + Tcl_Obj *objPtr, /* Object of type AbstractList */ + void *repPtr) /* New representation */ { - return Tcl_AbstractListSetProc(objPtr, TCL_ABSL_DUPREP, proc); + /* assert(objPtr->typePtr == &tclAbstractListType); */ + objPtr->internalRep.twoPtrValue.ptr2 = repPtr; } - /* *---------------------------------------------------------------------------- * The following structure contains the state needed by Tcl_SaveResult. No-one diff --git a/generic/tclAbstractList.c b/generic/tclAbstractList.c index f7e997f..bbffc2d 100644 --- a/generic/tclAbstractList.c +++ b/generic/tclAbstractList.c @@ -1,7 +1,7 @@ /* * tclAbstractList.h -- * - * The AbstractList Obj Type -- a psuedo List + * The AbstractList Obj Type -- a psuedo List * * Copyright © 2022 by Brian Griffin. All rights reserved. * @@ -29,7 +29,7 @@ static void UpdateStringOfAbstractList (Tcl_Obj *listPtr); * * The abstract list object is a special case of Tcl list represented by a set * of functions. - * + * */ const Tcl_ObjType tclAbstractListType = { @@ -84,33 +84,18 @@ Tcl_AbstractListObjLength(Tcl_Obj *abstractListObjPtr) */ Tcl_Obj* -Tcl_NewAbstractListObj(Tcl_Interp *interp, const char* typeName, size_t requiredSize) +Tcl_NewAbstractListObj(Tcl_Interp *interp, const Tcl_AbstractListType* vTablePtr) { Tcl_Obj *objPtr; - size_t repSize; - AbstractList *abstractListRepPtr; Tcl_ObjInternalRep itr; (void)interp; TclNewObj(objPtr); - repSize = sizeof(AbstractList) + requiredSize; - abstractListRepPtr = (AbstractList*)ckalloc(repSize); - abstractListRepPtr->version = TCL_ABSTRACTLIST_VERSION_1; - abstractListRepPtr->repSize = repSize; - abstractListRepPtr->typeName = typeName;; - abstractListRepPtr->elements = NULL; - abstractListRepPtr->newObjProc = NULL; - abstractListRepPtr->dupRepProc = NULL; - abstractListRepPtr->lengthProc = NULL; - abstractListRepPtr->indexProc = NULL; - abstractListRepPtr->sliceProc = NULL; - abstractListRepPtr->reverseProc = NULL; - itr.twoPtrValue.ptr1 = abstractListRepPtr; - itr.twoPtrValue.ptr2 = (((char*)abstractListRepPtr) + offsetof(AbstractList,abstractValue)); + itr.twoPtrValue.ptr1 = (void*)vTablePtr; /* dispatch table for concrete type */ + itr.twoPtrValue.ptr2 = NULL; Tcl_StoreInternalRep(objPtr, &tclAbstractListType, &itr); Tcl_InvalidateStringRep(objPtr); return objPtr; } - /* *---------------------------------------------------------------------- @@ -133,14 +118,22 @@ Tcl_NewAbstractListObj(Tcl_Interp *interp, const char* typeName, size_t required Tcl_Obj* Tcl_AbstractListObjIndex(Tcl_Obj *abstractListObjPtr, Tcl_WideInt index) { - AbstractList *abstractListRepPtr; + Tcl_AbstractListType *typePtr; Tcl_Obj *elementObjPtr; - if (abstractListObjPtr->typePtr != &tclAbstractListType) { - Tcl_Panic("Tcl_AbstractListObjIndex called without and AbstractList Obj."); + + typePtr = Tcl_AbstractListGetType(abstractListObjPtr); + /* + * The general assumption is that the obj is assumed first to be a List, + * and only ends up here because it has been determinded to be an + * AbstractList. If that's not the case, then a mistake has been made. To + * attempt to try a List call (e.g. shimmer) could potentially loop(?) + * So: if called from List code, then something has gone wrong; if called + * from user code, then user has made a mistake. + */ + if (typePtr == NULL) { + Tcl_Panic("Tcl_AbstractListObjIndex called without and AbstractList Obj."); } - abstractListRepPtr = (AbstractList*) - abstractListObjPtr->internalRep.twoPtrValue.ptr1; - elementObjPtr = abstractListRepPtr->indexProc(abstractListObjPtr, index); + elementObjPtr = typePtr->indexProc(abstractListObjPtr, index); return elementObjPtr; } @@ -166,18 +159,12 @@ Tcl_AbstractListObjIndex(Tcl_Obj *abstractListObjPtr, Tcl_WideInt index) void FreeAbstractListInternalRep(Tcl_Obj *abstractListObjPtr) { - AbstractList *abstractListRepPtr = - (AbstractList *) abstractListObjPtr->internalRep.twoPtrValue.ptr1; - if (abstractListRepPtr->elements) { - Tcl_WideInt i, llen = abstractListRepPtr->lengthProc(abstractListObjPtr); - for(i=0; i<llen; i++) { - if (abstractListRepPtr->elements[i]) { - Tcl_DecrRefCount(abstractListRepPtr->elements[i]); - } - } - ckfree((char*)abstractListRepPtr->elements); + Tcl_AbstractListType *typePtr = Tcl_AbstractListGetType(abstractListObjPtr); + + if (TclAbstractListHasProc(abstractListObjPtr, TCL_ABSL_FREEREP)) { + /* call the free callback for the concrete rep */ + typePtr->freeRepProc(abstractListObjPtr); } - ckfree((char *) abstractListRepPtr); abstractListObjPtr->internalRep.twoPtrValue.ptr1 = NULL; abstractListObjPtr->internalRep.twoPtrValue.ptr2 = NULL; } @@ -202,31 +189,28 @@ FreeAbstractListInternalRep(Tcl_Obj *abstractListObjPtr) static void DupAbstractListInternalRep(srcPtr, copyPtr) Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ - Tcl_Obj *copyPtr; /* Object with internal rep to set. */ + Tcl_Obj *copyPtr; /* Object with internal rep to set. + * Internal rep must be clear, it is stomped */ { - AbstractList *srcAbstractListRepPtr = Tcl_AbstractListRepPtr(srcPtr); - AbstractList *copyAbstractListRepPtr; - size_t repSize; - /* - * Allocate a new ArithSeries structure. - */ + Tcl_AbstractListType *typePtr; + typePtr = AbstractListGetType(srcPtr); + copyPtr->internalRep.twoPtrValue.ptr1 = typePtr; + copyPtr->internalRep.twoPtrValue.ptr2 = NULL; - repSize = srcAbstractListRepPtr->repSize; // example: sizeof(AbstractList) + sizeof(ArithSeries) - copyAbstractListRepPtr = (AbstractList*) ckalloc(repSize); - *copyAbstractListRepPtr = *srcAbstractListRepPtr; - copyPtr->internalRep.twoPtrValue.ptr1 = copyAbstractListRepPtr; - copyPtr->internalRep.twoPtrValue.ptr2 = (((char*)copyAbstractListRepPtr) + offsetof(AbstractList,abstractValue)); - copyAbstractListRepPtr->typeName = srcAbstractListRepPtr->typeName; - copyPtr->typePtr = &tclAbstractListType; - - copyAbstractListRepPtr->elements = NULL; /* Let new object repopulate it's elements on demand. */ + /* Now do concrete type dup. It is responsible for calling + Tcl_AbstractListSetConcreteRep to initialize ptr2 */ - if (srcAbstractListRepPtr->dupRepProc) { - srcAbstractListRepPtr->dupRepProc(srcPtr, copyPtr); + if (typePtr->dupRepProc) { + typePtr->dupRepProc(srcPtr, copyPtr); + } else { + /* TODO - or set it to NULL instead? */ + copyPtr->internalRep.twoPtrValue.ptr2 = + srcPtr->internalRep.twoPtrValue.ptr2; } + copyPtr->typePtr = &tclAbstractListType; } - + /* *---------------------------------------------------------------------- * @@ -253,28 +237,36 @@ DupAbstractListInternalRep(srcPtr, copyPtr) *---------------------------------------------------------------------- */ -/* works for arithseries, not for arbitary element values */ -#if 0 // begin depricated static void UpdateStringOfAbstractList(Tcl_Obj *abstractListObjPtr) { - AbstractList *abstractListRepPtr = - (AbstractList*) abstractListObjPtr->internalRep.twoPtrValue.ptr1; + Tcl_AbstractListType *typePtr; char *p, *str; Tcl_Obj *eleObj; Tcl_WideInt length = 0; int llen, slen, i; + typePtr = AbstractListGetType(abstractListObjPtr); + + /* + * If concrete type has a better way to generate the string, + * let it do it. + */ + if (TclAbstractListHasProc(abstractListObjPtr, TCL_ABSL_TOSTRING)) { + typePtr->toStringProc(abstractListObjPtr); + return; + } + /* * Pass 1: estimate space. */ - llen = abstractListRepPtr->lengthProc(abstractListObjPtr); + llen = typePtr->lengthProc(abstractListObjPtr); if (llen <= 0) { Tcl_InitStringRep(abstractListObjPtr, NULL, 0); return; } for (i = 0; i < llen; i++) { - eleObj = abstractListRepPtr->indexProc(abstractListObjPtr, i); + eleObj = typePtr->indexProc(abstractListObjPtr, i); Tcl_GetStringFromObj(eleObj, &slen); length += slen + 1; /* one more for the space char */ Tcl_DecrRefCount(eleObj); @@ -286,7 +278,7 @@ UpdateStringOfAbstractList(Tcl_Obj *abstractListObjPtr) p = Tcl_InitStringRep(abstractListObjPtr, NULL, length); for (i = 0; i < llen; i++) { - eleObj = abstractListRepPtr->indexProc(abstractListObjPtr, i); + eleObj = typePtr->indexProc(abstractListObjPtr, i); str = Tcl_GetStringFromObj(eleObj, &slen); strcpy(p, str); p[slen] = ' '; @@ -296,75 +288,6 @@ UpdateStringOfAbstractList(Tcl_Obj *abstractListObjPtr) if (length > 0) abstractListObjPtr->bytes[length-1] = '\0'; abstractListObjPtr->length = length-1; } -#endif // end depricated - -static void -UpdateStringOfAbstractList( - Tcl_Obj *abstractListObjPtr) /* AbstractList object with string rep to update. */ -{ -# define LOCAL_SIZE 64 - char localFlags[LOCAL_SIZE], *flagPtr = NULL; - ListSizeT numElems, i, length, bytesNeeded = 0; - const char *elem, *start; - Tcl_Obj *elemObj; - char *dst; - AbstractList* abstractListRepPtr = AbstractListGetInternalRep(abstractListObjPtr); - - numElems = abstractListRepPtr->lengthProc(abstractListObjPtr); - /* Handle empty list case first, so rest of the routine is simpler. */ - - if (numElems == 0) { - Tcl_InitStringRep(abstractListObjPtr, NULL, 0); - return; - } - - /* Pass 1: estimate space, gather flags. */ - - if (numElems <= LOCAL_SIZE) { - flagPtr = localFlags; - } else { - /* We know numElems <= LIST_MAX, so this is safe. */ - flagPtr = (char *)ckalloc(numElems); - } - for (i = 0; i < numElems; i++) { - flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0); - elemObj = abstractListRepPtr->indexProc(abstractListObjPtr, i); - elem = TclGetStringFromObj(elemObj, &length); - bytesNeeded += TclScanElement(elem, length, flagPtr+i); - if (bytesNeeded < 0) { - /* TODO - what is the max #define for Tcl9? */ - Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); - } - Tcl_DecrRefCount(elemObj); - } - /* TODO - what is the max #define for Tcl9? */ - if (bytesNeeded > INT_MAX - numElems + 1) { - Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); - } - bytesNeeded += numElems - 1; - - /* - * Pass 2: copy into string rep buffer. - */ - - start = dst = Tcl_InitStringRep(abstractListObjPtr, NULL, bytesNeeded); - TclOOM(dst, bytesNeeded); - for (i = 0; i < numElems; i++) { - flagPtr[i] |= (i ? TCL_DONT_QUOTE_HASH : 0); - elemObj = abstractListRepPtr->indexProc(abstractListObjPtr, i); - elem = TclGetStringFromObj(elemObj, &length); - dst += TclConvertElement(elem, length, dst, flagPtr[i]); - *dst++ = ' '; - Tcl_DecrRefCount(elemObj); - } - - /* Set the string length to what was actually written, the safe choice */ - (void) Tcl_InitStringRep(abstractListObjPtr, NULL, dst - 1 - start); - - if (flagPtr != localFlags) { - ckfree(flagPtr); - } -} /* *---------------------------------------------------------------------- @@ -395,6 +318,10 @@ SetAbstractListFromAny(interp, objPtr) { (void)interp; (void)objPtr; + /* TODO - at some future point, should just shimmer to a traditional + * Tcl list (but only when those are implemented under the AbstractList) + * interface. + */ Tcl_Panic("SetAbstractListFromAny: should never be called"); return TCL_ERROR; } @@ -429,10 +356,8 @@ TclAbstractListObjCopy( * to be returned. */ { Tcl_Obj *copyPtr; - AbstractList *abstractListRepPtr; - abstractListRepPtr = AbstractListGetInternalRep(abstractListObjPtr); - if (NULL == abstractListRepPtr) { + if (!TclHasInternalRep(abstractListObjPtr, &tclAbstractListType)) { if (SetAbstractListFromAny(interp, abstractListObjPtr) != TCL_OK) { /* We know this is going to panic, but it's the message we want */ return NULL; @@ -471,9 +396,25 @@ Tcl_AbstractListObjRange( Tcl_WideInt fromIdx, /* Index of first element to include. */ Tcl_WideInt toIdx) /* Index of last element to include. */ { - AbstractList *abstractListRepPtr = - AbstractListGetInternalRep(abstractListObjPtr); - return abstractListRepPtr->sliceProc(abstractListObjPtr, fromIdx, toIdx); + Tcl_AbstractListType *typePtr; + if (!TclHasInternalRep(abstractListObjPtr, &tclAbstractListType)) { + if (SetAbstractListFromAny(NULL, abstractListObjPtr) != TCL_OK) { + /* We know this is going to panic, but it's the message we want */ + return NULL; + } + } + typePtr = Tcl_AbstractListGetType(abstractListObjPtr); + /* + * sliceProc can be NULL, then revert to List. Note: [lrange] + * command also checks for NULL sliceProc, and won't call AbstractList + */ + if (typePtr->sliceProc) { + return typePtr->sliceProc(abstractListObjPtr, fromIdx, toIdx); + } else { + /* TODO ?shimmer avoided? */ + Tcl_Obj *newObj = TclListObjCopy(NULL, abstractListObjPtr); + return newObj ? TclListObjRange(newObj, (ListSizeT)fromIdx, (ListSizeT)toIdx) : NULL; + } } /* @@ -500,9 +441,16 @@ Tcl_Obj * Tcl_AbstractListObjReverse( Tcl_Obj *abstractListObjPtr) /* List object to take a range from. */ { - AbstractList *abstractListRepPtr = - AbstractListGetInternalRep(abstractListObjPtr); - return abstractListRepPtr->reverseProc(abstractListObjPtr); + Tcl_AbstractListType *typePtr; + if (!TclHasInternalRep(abstractListObjPtr, &tclAbstractListType) || + !TclAbstractListHasProc(abstractListObjPtr, TCL_ABSL_REVERSE)) { + if (SetAbstractListFromAny(NULL, abstractListObjPtr) != TCL_OK) { + /* We know this is going to panic, but it's the message we want */ + return NULL; + } + } + typePtr = Tcl_AbstractListGetType(abstractListObjPtr); + return typePtr->reverseProc(abstractListObjPtr); } @@ -547,38 +495,21 @@ Tcl_AbstractListObjGetElements( { if (TclHasInternalRep(objPtr,&tclAbstractListType)) { - AbstractList *abstractListRepPtr = - AbstractListGetInternalRep(objPtr); - Tcl_Obj **objv; - int i, objc = abstractListRepPtr->lengthProc(objPtr); - - if (objc > 0) { - if (abstractListRepPtr->elements) { - /* If this exists, it has already been populated */ - objv = abstractListRepPtr->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; - } - abstractListRepPtr->elements = objv; - for (i = 0; i < objc; i++) { - objv[i] = abstractListRepPtr->indexProc(objPtr, i); - Tcl_IncrRefCount(objv[i]); - } - } - } else { - objv = NULL; - } - *objvPtr = objv; - *objcPtr = objc; + Tcl_AbstractListType *typePtr = Tcl_AbstractListGetType(objPtr); + + if (TclAbstractListHasProc(objPtr, TCL_ABSL_GETELEMENTS)) { + int status = typePtr->getElementsProc(interp, objPtr, objcPtr, objvPtr); + /* TODO -- Add error message here, or propagate interp down */ + return status; + } else { + if (interp) { + Tcl_SetObjResult( + interp, + Tcl_NewStringObj("GetElements not supported!", -1)); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); + } + } + return TCL_ERROR; } else { if (interp != NULL) { Tcl_SetObjResult( @@ -591,44 +522,6 @@ Tcl_AbstractListObjGetElements( return TCL_OK; } -int -Tcl_AbstractListSetProc( - Tcl_Obj *objPtr, - Tcl_AbstractListProcType ptype, - void *proc) -{ - AbstractList* al_internalRep = AbstractListGetInternalRep(objPtr); - - if (al_internalRep == NULL) { - return TCL_ERROR; - } - - switch (ptype) { - case TCL_ABSL_NEW: - al_internalRep->newObjProc = (Tcl_ALNewObjProc *)proc; - break; - case TCL_ABSL_DUPREP: - al_internalRep->dupRepProc = (Tcl_ALDupRepProc *)proc; - break; - case TCL_ABSL_LENGTH: - al_internalRep->lengthProc = (Tcl_ALLengthProc *)proc; - break; - case TCL_ABSL_INDEX: - al_internalRep->indexProc = (Tcl_ALIndexProc *)proc; - break; - case TCL_ABSL_SLICE: - al_internalRep->sliceProc = (Tcl_ALSliceProc *)proc; - break; - case TCL_ABSL_REVERSE: - al_internalRep->reverseProc = (Tcl_ALReverseProc *)proc; - break; - default: - return TCL_ERROR; - } - return TCL_OK; -} - - /* * Local Variables: * mode: c diff --git a/generic/tclAbstractList.h b/generic/tclAbstractList.h index dc45709..80ae269 100644 --- a/generic/tclAbstractList.h +++ b/generic/tclAbstractList.h @@ -1,7 +1,7 @@ /* * tclAbstractList.h -- * - * The AbstractList Obj Type -- a psuedo List + * The AbstractList Obj Type -- a psuedo List * * Copyright © 2022 by Brian Griffin. All rights reserved. * @@ -14,31 +14,21 @@ #include "tclInt.h" -static inline AbstractList* AbstractListGetInternalRep( - Tcl_Obj *objPtr) /* Object of type AbstractList */ -{ - const Tcl_ObjInternalRep *irPtr; - irPtr = TclFetchInternalRep((objPtr), &tclAbstractListType); - return irPtr ? (AbstractList *)(irPtr->twoPtrValue.ptr1) : NULL; -} - - static inline const char* Tcl_AbstractListTypeName( Tcl_Obj *objPtr) /* Should be of type AbstractList */ { - AbstractList *abstractListRepPtr = - AbstractListGetInternalRep(objPtr); - return (abstractListRepPtr->typeName - ? abstractListRepPtr->typeName - : (objPtr->typePtr - ? objPtr->typePtr->name - : "pure string")); + Tcl_AbstractListType *typePtr; + typePtr = Tcl_AbstractListGetType(objPtr); + if (typePtr && typePtr->typeName) { + return typePtr->typeName; + } else { + return "abstractlist"; + } } - -Tcl_Obj* Tcl_NewAbstractListObj(Tcl_Interp *interp, const char* typeName, size_t requiredSize); -int Tcl_AbstractListCheckedSetProc(Tcl_Obj *objPtr, Tcl_AbstractListProcType ptype, void** procPtr); +Tcl_Obj *Tcl_NewAbstractListObj(Tcl_Interp *interp, const Tcl_AbstractListType *); +int Tcl_AbstractListCheckedSetProc(Tcl_Obj *objPtr, Tcl_AbstractListProcType ptype, void **procPtr); Tcl_WideInt Tcl_AbstractListObjLength(Tcl_Obj *abstractListPtr); Tcl_Obj* Tcl_AbstractListObjIndex(Tcl_Obj *abstractListPtr, Tcl_WideInt index); Tcl_Obj* Tcl_AbstractListObjRange(Tcl_Obj *abstractListPtr, Tcl_WideInt fromIdx, Tcl_WideInt toIdx); diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index dd2b6cf..91a22ec 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -187,12 +187,13 @@ typedef enum Sequence_Decoded { * but it's faster to cache it inside the internal representation. */ typedef struct ArithSeries { - const char *name; - Tcl_WideInt start; - Tcl_WideInt end; - Tcl_WideInt step; - Tcl_WideInt len; - + Tcl_WideInt start; /* first (left most) value */ + Tcl_WideInt end; /* last (right most or greater) value */ + Tcl_WideInt step; /* increment value */ + Tcl_WideInt len; /* total number of elements in list (has priority + ** over "end") */ + Tcl_Obj **elements; /* List elements array, only used when absolutely + ** necessary. */ } ArithSeries; @@ -3915,8 +3916,8 @@ Tcl_LsearchObjCmd( } Tcl_SetObjResult(interp, itemPtr); } else { - Tcl_Obj *elObj; - TclNewIndexObj(elObj, index); + Tcl_Obj *elObj; + TclNewIndexObj(elObj, index); Tcl_SetObjResult(interp, elObj); } } else if (index < 0) { @@ -3984,14 +3985,13 @@ Tcl_WideInt Tcl_ArithSeriesObjLength(Tcl_Obj *arithSeriesPtr); Tcl_Obj*Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index); Tcl_Obj *TclArithSeriesObjRange(Tcl_Obj *arithSeriesPtr, Tcl_WideInt fromIdx, Tcl_WideInt toIdx); Tcl_Obj *TclArithSeriesObjReverse(Tcl_Obj *arithSeriesObjPtr); +int TclArithSeriesGetElements(Tcl_Interp *interp, Tcl_Obj *arithSeriesObjPtr, + int *objcPtr, Tcl_Obj ***objvPtr); +static void UpdateStringOfArithSeries(Tcl_Obj *arithSeriesObjPtr); -#define ArithSeriesRepPtr(arithSeriesObjPtr) \ - (ArithSeries *) ((arithSeriesObjPtr)->internalRep.twoPtrValue.ptr2) - -#define ArithSeriesIndexM(arithSeriesRepPtr, index) \ - (arithSeriesRepPtr)->start+((index)*arithSeriesRepPtr->step) +#define ArithSeriesIndexM(arithSeriesPtr, index) \ + (arithSeriesPtr)->start+((index) * (arithSeriesPtr)->step) - /* *---------------------------------------------------------------------- * @@ -4028,10 +4028,11 @@ ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step) /* *---------------------------------------------------------------------- * - * DupAbstractListInternalRep -- + * DupArithSeriesRep -- * - * Initialize the internal representation of a AbstractList Tcl_Obj to a - * copy of the internal representation of an existing arithseries object. + * Initialize the internal representation of a ArithSeries abstract list + * Tcl_Obj to a copy of the internal representation of an existing + * arithseries object. * * Results: * None. @@ -4045,17 +4046,59 @@ ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step) static void DupArithSeriesRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr) { - ArithSeries *srcArithSeries = (ArithSeries*)Tcl_AbstractListGetTypeRep(srcPtr); - ArithSeries *copyArithSeries = (ArithSeries*)Tcl_AbstractListGetTypeRep(copyPtr); + ArithSeries *srcArithSeries = (ArithSeries*)Tcl_AbstractListGetConcreteRep(srcPtr); + ArithSeries *copyArithSeries = (ArithSeries *)ckalloc(sizeof(ArithSeries)); - copyArithSeries->name = "arithseries"; - copyArithSeries->start = srcArithSeries->start; - copyArithSeries->end = srcArithSeries->end; - copyArithSeries->step = srcArithSeries->step; - copyArithSeries->len = srcArithSeries->len; + *copyArithSeries = *srcArithSeries; + /* Note: we do not have to be worry about existing internal rep because + copyPtr is supposed to be freshly initialized */ + Tcl_AbstractListSetConcreteRep(copyPtr, copyArithSeries); +} + + +/* + *---------------------------------------------------------------------- + * + * FreeArithSeriesRep -- + * + * Free any allocated memory in the ArithSeries Rep + * + * Results: + * None. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ +static void FreeArithSeriesRep(Tcl_Obj *arithSeriesObjPtr) /* Free any allocated memory */ +{ + ArithSeries *arithSeriesPtr = (ArithSeries*)Tcl_AbstractListGetConcreteRep(arithSeriesObjPtr); + if (arithSeriesPtr) { + if (arithSeriesPtr->elements) { + ckfree((char*)arithSeriesPtr->elements); + arithSeriesPtr->elements = NULL; + } + ckfree((char*)arithSeriesPtr); + } } + + +static Tcl_AbstractListType arithSeriesType = { + TCL_ABSTRACTLIST_VERSION_1, + "arithseries", + Tcl_NewArithSeriesObj, + DupArithSeriesRep, + Tcl_ArithSeriesObjLength, + Tcl_ArithSeriesObjIndex, + TclArithSeriesObjRange, + TclArithSeriesObjReverse, + TclArithSeriesGetElements, + FreeArithSeriesRep, + UpdateStringOfArithSeries +}; + /* *---------------------------------------------------------------------- * @@ -4079,30 +4122,30 @@ Tcl_Obj * 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; - ArithSeries *arithSeriesRepPtr; - static const char *arithSeriesName = "arithseries"; - if (length == -1) return NULL; /* Invalid range error */ - - arithSeriesPtr = Tcl_NewAbstractListObj(NULL, arithSeriesName, sizeof (ArithSeries)); - arithSeriesRepPtr = (ArithSeries*)Tcl_AbstractListGetTypeRep(arithSeriesPtr); - arithSeriesRepPtr->start = start; - arithSeriesRepPtr->end = end; - arithSeriesRepPtr->step = step; - arithSeriesRepPtr->len = length; - Tcl_SetAbstractListNewProc( arithSeriesPtr, Tcl_NewArithSeriesObj ); - Tcl_SetAbstractListLengthProc( arithSeriesPtr, Tcl_ArithSeriesObjLength ); - Tcl_SetAbstractListIndexProc( arithSeriesPtr, Tcl_ArithSeriesObjIndex ); - Tcl_SetAbstractListSliceProc( arithSeriesPtr, TclArithSeriesObjRange ); - Tcl_SetAbstractListReverseProc( arithSeriesPtr, TclArithSeriesObjReverse ); - Tcl_SetAbstractListDupRepProc( arithSeriesPtr, DupArithSeriesRep ); - - if (length > 0) { - Tcl_InvalidateStringRep(arithSeriesPtr); - } else { - Tcl_InitStringRep(arithSeriesPtr, NULL, 0); + ArithSeries *arithSeriesPtr; + Tcl_Obj *arithSeriesObj; + + if (length <= 0) { + TclNewObj(arithSeriesObj); + return arithSeriesObj; } - return arithSeriesPtr; + + /* Allocate internal representation */ + arithSeriesPtr = (ArithSeries*)ckalloc(sizeof(ArithSeries)); + arithSeriesPtr->start = start; + arithSeriesPtr->end = end; + arithSeriesPtr->step = step; + arithSeriesPtr->len = length; + arithSeriesPtr->elements = NULL; + + /* Store the internal rep in a new AbstrictList Tcl_Obj */ + arithSeriesObj = Tcl_NewAbstractListObj(NULL, &arithSeriesType); + Tcl_AbstractListSetConcreteRep(arithSeriesObj,arithSeriesPtr); + + if (length == 0) { + Tcl_InitStringRep(arithSeriesObj, NULL, 0); + } + return arithSeriesObj; } Tcl_Obj * @@ -4145,18 +4188,18 @@ Tcl_NewArithSeriesObj(int objc, Tcl_Obj *objv[]) Tcl_Obj* Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesObjPtr, Tcl_WideInt index) { - ArithSeries *arithSeriesRepPtr; + ArithSeries *arithSeriesPtr; Tcl_WideInt element; - if (arithSeriesObjPtr->typePtr != &tclAbstractListType) { - Tcl_Panic("Tcl_ArithSeriesObjIndex called with a not ArithSeries Obj."); - } - arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesObjPtr); - if (index < 0 || index >= arithSeriesRepPtr->len) + assert(Tcl_AbstractListGetType(arithSeriesObjPtr) == &arithSeriesType); + + arithSeriesPtr = (ArithSeries*)Tcl_AbstractListGetConcreteRep(arithSeriesObjPtr); + + if (index < 0 || index >= arithSeriesPtr->len) return NULL; /* List[i] = Start + (Step * i) */ - element = ArithSeriesIndexM(arithSeriesRepPtr, index); + element = ArithSeriesIndexM(arithSeriesPtr, index); return Tcl_NewWideIntObj(element); } @@ -4180,8 +4223,10 @@ Tcl_ArithSeriesObjIndex(Tcl_Obj *arithSeriesObjPtr, Tcl_WideInt index) */ Tcl_WideInt Tcl_ArithSeriesObjLength(Tcl_Obj *arithSeriesObjPtr) { - ArithSeries *arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesObjPtr); - return arithSeriesRepPtr->len; + assert(Tcl_AbstractListGetType(arithSeriesObjPtr) == &arithSeriesType); + + ArithSeries *arithSeriesPtr = (ArithSeries*)Tcl_AbstractListGetConcreteRep(arithSeriesObjPtr); + return arithSeriesPtr->len; } /* @@ -4209,12 +4254,12 @@ TclArithSeriesObjRange( Tcl_WideInt fromIdx, /* Index of first element to include. */ Tcl_WideInt toIdx) /* Index of last element to include. */ { - ArithSeries *arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesObjPtr); - Tcl_WideInt start = -1, end = -1, step, len; - Tcl_Obj *fromObj, *toObj; + ArithSeries *arithSeriesPtr; + Tcl_WideInt start = -1, end = -1, step, len; - arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesObjPtr); + assert(Tcl_AbstractListGetType(arithSeriesObjPtr) == &arithSeriesType); + arithSeriesPtr = (ArithSeries*)Tcl_AbstractListGetConcreteRep(arithSeriesObjPtr); if (fromIdx < 0) { fromIdx = 0; @@ -4225,18 +4270,11 @@ TclArithSeriesObjRange( return obj; } - fromObj = Tcl_ArithSeriesObjIndex(arithSeriesObjPtr, fromIdx); - if (fromObj == NULL) return NULL; - toObj = Tcl_ArithSeriesObjIndex(arithSeriesObjPtr, toIdx); - if (toObj == NULL) return NULL; - Tcl_GetWideIntFromObj(NULL, fromObj, &start); - Tcl_DecrRefCount(fromObj); - Tcl_GetWideIntFromObj(NULL, toObj, &end); - Tcl_DecrRefCount(toObj); - step = arithSeriesRepPtr->step; + start = ArithSeriesIndexM(arithSeriesPtr, fromIdx); + end = ArithSeriesIndexM(arithSeriesPtr, toIdx); + step = arithSeriesPtr->step; len = ArithSeriesLen(start, end, step); - if (Tcl_IsShared(arithSeriesObjPtr) || ((arithSeriesObjPtr->refCount > 1))) { return TclNewArithSeriesObj(start, end, step, len); @@ -4253,10 +4291,10 @@ TclArithSeriesObjRange( TclInvalidateStringRep(arithSeriesObjPtr); - arithSeriesRepPtr->start = start; - arithSeriesRepPtr->end = end; - arithSeriesRepPtr->step = step; - arithSeriesRepPtr->len = len; + arithSeriesPtr->start = start; + arithSeriesPtr->end = end; + arithSeriesPtr->step = step; + arithSeriesPtr->len = len; return arithSeriesObjPtr; } @@ -4269,13 +4307,16 @@ Tcl_Obj * TclArithSeriesObjReverse( Tcl_Obj *arithSeriesObjPtr) /* List object to take a range from. */ { - ArithSeries *arithSeriesPtr = ArithSeriesRepPtr(arithSeriesObjPtr); + ArithSeries *arithSeriesPtr; Tcl_Obj *resultObjPtr; Tcl_WideInt rstart, rend, rstep, len; + assert(Tcl_AbstractListGetType(arithSeriesObjPtr) == &arithSeriesType); + arithSeriesPtr = (ArithSeries*)Tcl_AbstractListGetConcreteRep(arithSeriesObjPtr); + len = arithSeriesPtr->len; rend = ArithSeriesIndexM(arithSeriesPtr, 0); - rstart = ArithSeriesIndexM(arithSeriesPtr, len-1); + rstart = ArithSeriesIndexM(arithSeriesPtr, (len-1)); rstep = -arithSeriesPtr->step; if (Tcl_IsShared(arithSeriesObjPtr)) { @@ -4289,7 +4330,113 @@ TclArithSeriesObjReverse( } return resultObjPtr; } +/* +** Handle ArithSeries GetElements call +*/ + +int +TclArithSeriesGetElements( + Tcl_Interp *interp, /* Used to report errors if not NULL. */ + Tcl_Obj *arithSeriesObjPtr, /* ArithSeries 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(arithSeriesObjPtr,&tclAbstractListType)) { + ArithSeries *arithSeriesPtr = (ArithSeries*)Tcl_AbstractListGetConcreteRep(arithSeriesObjPtr); + Tcl_AbstractListType *typePtr; + Tcl_Obj **objv; + int i, objc; + + typePtr = Tcl_AbstractListGetType(arithSeriesObjPtr); + + objc = Tcl_ArithSeriesObjLength(arithSeriesObjPtr); + + if (objvPtr == NULL) { + if (objcPtr) { + *objcPtr = objc; + return TCL_OK; + } + return TCL_ERROR; + } + + if (objc && objvPtr && arithSeriesPtr->elements) { + objv = arithSeriesPtr->elements; + } else if (objc > 0) { + 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; + } + for (i = 0; i < objc; i++) { + objv[i] = typePtr->indexProc(arithSeriesObjPtr, i); + } + } else { + objv = NULL; + } + arithSeriesPtr->elements = objv; + *objvPtr = objv; + *objcPtr = objc; + } else { + if (interp != NULL) { + Tcl_SetObjResult( + interp, + Tcl_ObjPrintf("value is not an abstract list")); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "UNKNOWN", NULL); + } + return TCL_ERROR; + } + return TCL_OK; +} + +static void +UpdateStringOfArithSeries(Tcl_Obj *arithSeriesObjPtr) +{ + char *p, *str; + Tcl_Obj *eleObj; + Tcl_WideInt length = 0; + int llen, slen, i; + + /* + * Pass 1: estimate space. + */ + llen = Tcl_ArithSeriesObjLength(arithSeriesObjPtr); + if (llen <= 0) { + Tcl_InitStringRep(arithSeriesObjPtr, NULL, 0); + return; + } + for (i = 0; i < llen; i++) { + eleObj = Tcl_ArithSeriesObjIndex(arithSeriesObjPtr, i); + Tcl_GetStringFromObj(eleObj, &slen); + length += slen + 1; /* one more for the space char */ + Tcl_DecrRefCount(eleObj); + } + + /* + * Pass 2: generate the string repr. + */ + + p = Tcl_InitStringRep(arithSeriesObjPtr, NULL, length); + for (i = 0; i < llen; i++) { + eleObj = Tcl_ArithSeriesObjIndex(arithSeriesObjPtr, i); + str = Tcl_GetStringFromObj(eleObj, &slen); + strcpy(p, str); + p[slen] = ' '; + p += slen+1; + Tcl_DecrRefCount(eleObj); + } + if (length > 0) arithSeriesObjPtr->bytes[length-1] = '\0'; + arithSeriesObjPtr->length = length-1; +} /* *---------------------------------------------------------------------- @@ -4488,11 +4635,7 @@ Tcl_LseqObjCmd( 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 - } + elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list if (elementCount < 0) elementCount = 0; break; @@ -4501,11 +4644,7 @@ Tcl_LseqObjCmd( 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 - } + elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list if (elementCount < 0) elementCount = 0; break; @@ -4527,7 +4666,6 @@ Tcl_LseqObjCmd( elementCount = values[0]; step = values[2]; end = start + (step * elementCount); - elementCount = step ? (start-end+step)/step : 0; // 0 step -> empty list break; case LSEQ_COUNT: start = values[0]; @@ -4551,6 +4689,7 @@ Tcl_LseqObjCmd( start = values[0]; end = values[2]; step = values[3]; + elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list break; case LSEQ_COUNT: start = values[0]; @@ -4592,11 +4731,7 @@ Tcl_LseqObjCmd( 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 - } + elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list break; /* lseq n 'to' n 'by' n */ @@ -4619,13 +4754,7 @@ Tcl_LseqObjCmd( case LSEQ_TO: start = values[0]; end = values[2]; - if ((step == 0) || // 0 step --> empty list - (start < end && step < 0) || // step sign mismatch with end-start - (start > end && step > 0)) { // --> empty list - elementCount = 0; - } else { - elementCount = (end-start+step)/step; - } + elementCount = step ? (end-start+step)/step : 0; // 0 step -> empty list break; case LSEQ_COUNT: start = values[0]; diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 3cc257c..03e397d 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -1987,10 +1987,7 @@ EXTERN Tcl_Obj * Tcl_AbstractListObjRange(Tcl_Obj *abstractListPtr, EXTERN Tcl_Obj * Tcl_AbstractListObjReverse(Tcl_Obj *abstractListPtr); /* 678 */ EXTERN Tcl_Obj * Tcl_NewAbstractListObj(Tcl_Interp *interp, - const char*typeName, size_t requiredSize); -/* 679 */ -EXTERN int Tcl_AbstractListSetProc(Tcl_Obj *objPtr, - Tcl_AbstractListProcType ptype, void *proc); + const Tcl_AbstractListType*vTablePtr); typedef struct { const struct TclPlatStubs *tclPlatStubs; @@ -2704,8 +2701,7 @@ typedef struct TclStubs { Tcl_Obj * (*tcl_AbstractListObjIndex) (Tcl_Obj *abstractListPtr, Tcl_WideInt index); /* 675 */ Tcl_Obj * (*tcl_AbstractListObjRange) (Tcl_Obj *abstractListPtr, Tcl_WideInt fromIdx, Tcl_WideInt toIdx); /* 676 */ Tcl_Obj * (*tcl_AbstractListObjReverse) (Tcl_Obj *abstractListPtr); /* 677 */ - Tcl_Obj * (*tcl_NewAbstractListObj) (Tcl_Interp *interp, const char*typeName, size_t requiredSize); /* 678 */ - int (*tcl_AbstractListSetProc) (Tcl_Obj *objPtr, Tcl_AbstractListProcType ptype, void *proc); /* 679 */ + Tcl_Obj * (*tcl_NewAbstractListObj) (Tcl_Interp *interp, const Tcl_AbstractListType*vTablePtr); /* 678 */ } TclStubs; extern const TclStubs *tclStubsPtr; @@ -4094,8 +4090,6 @@ extern const TclStubs *tclStubsPtr; (tclStubsPtr->tcl_AbstractListObjReverse) /* 677 */ #define Tcl_NewAbstractListObj \ (tclStubsPtr->tcl_NewAbstractListObj) /* 678 */ -#define Tcl_AbstractListSetProc \ - (tclStubsPtr->tcl_AbstractListSetProc) /* 679 */ #endif /* defined(USE_TCL_STUBS) */ diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 2e72d86..7a0456b 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -4863,15 +4863,15 @@ TEBCresume( /* special case for AbstractList */ if (TclHasInternalRep(valuePtr,&tclAbstractListType)) { - AbstractList *abstractListRepPtr = - (AbstractList*) valuePtr->internalRep.twoPtrValue.ptr1; - length = abstractListRepPtr->lengthProc(valuePtr); + Tcl_AbstractListType *typePtr; + typePtr = Tcl_AbstractListGetType(valuePtr); + length = typePtr->lengthProc(valuePtr); if (TclGetIntForIndexM(interp, value2Ptr, length-1, &index)!=TCL_OK) { CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } - objResultPtr = abstractListRepPtr->indexProc(valuePtr, index); + objResultPtr = typePtr->indexProc(valuePtr, index); goto lindexDone; } @@ -4896,7 +4896,7 @@ TEBCresume( } objResultPtr = TclLindexList(interp, valuePtr, value2Ptr); - + lindexDone: if (!objResultPtr) { TRACE_ERROR(interp); @@ -4928,9 +4928,9 @@ TEBCresume( /* special case for AbstractList */ if (TclHasInternalRep(valuePtr,&tclAbstractListType)) { - AbstractList* abstractListRepPtr = - (AbstractList *) valuePtr->internalRep.twoPtrValue.ptr1; - length = abstractListRepPtr->lengthProc(valuePtr); + Tcl_AbstractListType *typePtr; + typePtr = Tcl_AbstractListGetType(valuePtr); + length = typePtr->lengthProc(valuePtr); /* Decode end-offset index values. */ @@ -4938,7 +4938,7 @@ TEBCresume( /* Compute value @ index */ if (index >= 0 && index < length) { - objResultPtr = abstractListRepPtr->indexProc(valuePtr, index); + objResultPtr = typePtr->indexProc(valuePtr, index); } else { TclNewObj(objResultPtr); } @@ -4963,9 +4963,9 @@ TEBCresume( } else { TclNewObj(objResultPtr); } - + lindexFastPath2: - + TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr))); NEXT_INST_F(pcAdjustment, 1, 1); @@ -5142,13 +5142,11 @@ TEBCresume( fromIdx = TclIndexDecode(fromIdx, objc - 1); { - AbstractList* abstractListRepPtr = - TclHasInternalRep(valuePtr,&tclAbstractListType) - ? (AbstractList*)valuePtr->internalRep.twoPtrValue.ptr1 - : NULL; + Tcl_AbstractListType *typePtr; + typePtr = Tcl_AbstractListGetType(valuePtr); - if (abstractListRepPtr && TclAbstractListHasProc(valuePtr, TCL_ABSL_SLICE)) { - objResultPtr = abstractListRepPtr->sliceProc(valuePtr, fromIdx, toIdx); + if (typePtr && TclAbstractListHasProc(valuePtr, TCL_ABSL_SLICE)) { + objResultPtr = typePtr->sliceProc(valuePtr, fromIdx, toIdx); } else { objResultPtr = TclListObjRange(valuePtr, fromIdx, toIdx); } diff --git a/generic/tclInt.h b/generic/tclInt.h index bb79e28..38927dc 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2632,31 +2632,40 @@ typedef struct ListRep { (((listObj_)->typePtr == &tclListType) ? ListObjIsCanonical((listObj_)) : 0) +#define AbstractListGetType(abstractListObjPtr) \ + (Tcl_AbstractListType *) ((abstractListObjPtr)->internalRep.twoPtrValue.ptr1) + static inline Tcl_WideInt AbstractListObjLength(Tcl_Obj* abstractListObjPtr) { - AbstractList *oaRepPtr = - (AbstractList *)abstractListObjPtr->internalRep.twoPtrValue.ptr1; - return oaRepPtr->lengthProc(abstractListObjPtr); + Tcl_AbstractListType *typePtr = + (Tcl_AbstractListType *) abstractListObjPtr->internalRep.twoPtrValue.ptr1; + return typePtr->lengthProc(abstractListObjPtr); } static inline int TclAbstractListHasProc(Tcl_Obj* abstractListObjPtr, Tcl_AbstractListProcType ptype) { - AbstractList* repPtr = Tcl_AbstractListRepPtr(abstractListObjPtr); + Tcl_AbstractListType *typePtr = AbstractListGetType(abstractListObjPtr); switch (ptype) { case TCL_ABSL_NEW: - return (repPtr->newObjProc != NULL); + return (typePtr->newObjProc != NULL); case TCL_ABSL_DUPREP: - return (repPtr->dupRepProc != NULL); + return (typePtr->dupRepProc != NULL); case TCL_ABSL_LENGTH: - return (repPtr->lengthProc != NULL); + return (typePtr->lengthProc != NULL); case TCL_ABSL_INDEX: - return (repPtr->indexProc != NULL); + return (typePtr->indexProc != NULL); case TCL_ABSL_SLICE: - return (repPtr->sliceProc != NULL); + return (typePtr->sliceProc != NULL); case TCL_ABSL_REVERSE: - return (repPtr->reverseProc != NULL); + return (typePtr->reverseProc != NULL); + case TCL_ABSL_GETELEMENTS: + return (typePtr->getElementsProc != NULL); + case TCL_ABSL_FREEREP: + return (typePtr->freeRepProc != NULL); + case TCL_ABSL_TOSTRING: + return (typePtr->toStringProc != NULL); } return 0; } diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index e5312b2..5c66f7e 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -2041,7 +2041,6 @@ const TclStubs tclStubs = { Tcl_AbstractListObjRange, /* 676 */ Tcl_AbstractListObjReverse, /* 677 */ Tcl_NewAbstractListObj, /* 678 */ - Tcl_AbstractListSetProc, /* 679 */ }; /* !END!: Do not edit above this line. */ diff --git a/tests/lseq.test b/tests/lseq.test index 082111b..0919813 100644 --- a/tests/lseq.test +++ b/tests/lseq.test @@ -73,33 +73,21 @@ 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.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.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.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.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 @@ -113,6 +101,18 @@ 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??"} +test lseq-1.20 {bug: wrong length computed} { + lseq 1 to 10 -1 +} {} + +test lseq-1.21 {n n by n} { + lseq 66 84 by 3 +} {66 69 72 75 78 81 84} + +test lseq-1.22 {n n by -n} { + lseq 84 66 by -3 +} {84 81 78 75 72 69 66} + # # Short-hand use cases # @@ -182,6 +182,17 @@ 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} +# Covered: {10 1 2 } {1 10 2} {1 10 -2} {1 1 1} {1 1 1} {-5 17 3} +# Missing: {- - +} {- - -} {- + -} {+ - -} {- - +} {+ + -} +test lseq-2.18 {signs} { + list [lseq -10 -1 2] \ + [lseq -10 -1 -1] \ + [lseq -10 1 -3] \ + [lseq 10 -1 -4] \ + [lseq -10 -1 3] \ + [lseq 10 1 -5] + +} {{-10 -8 -6 -4 -2} {} {} {10 6 2} {-10 -7 -4 -1} {10 5}} test lseq-3.1 {experiement} { set ans {} @@ -216,8 +227,9 @@ test lseq-3.4 {error case} -body { } -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} + set s [lseq 25 by 6] + list $s length=[llength $s] +} {{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} length=25} test lseq-3.6 {error case} -body { lseq 1 7 or 3 @@ -335,7 +347,7 @@ test lseq-3.22 {edge case} { 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} @@ -366,6 +378,10 @@ test lseq-3.28 {lreverse bug in ArithSeries} {} { 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-3.29 {edge case: negative count} { + lseq -15 +} {} + test lseq-4.1 {end expressions} { set start 7 lseq $start $start+11 @@ -377,11 +393,11 @@ test lseq-4.2 {start expressions} { lmap t $tl {expr {$t - $base + 60}} } {0 10 20 30 40 50 60} - # cleanup ::tcltest::cleanupTests return +# # Local Variables: # mode: tcl # End: |