I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

Lists.i6t

Lists contents

Block Format.

A list is a variable-length array of values all of which have the same kind. (Compare a combination, a fixed-length array of values with possibly different kinds.) The short block for a list is a pointer to the long block. The long block consists of the strong kind ID of the items (not of the list itself!), followed by the number of items, followed by one word for each item.

16Constant LIST_ITEM_KOV_F = 0; ! The kind of the items 17Constant LIST_LENGTH_F = 1; ! The number of items 18Constant LIST_ITEM_BASE = 2; ! List items begin at this entry

KOV Support.

See the BlockValues.i6t segment for the specification of the following routines.

25[ LIST_OF_TY_Support task arg1 arg2 arg3; 26    switch(task) { 27        CREATE_KOVS: return LIST_OF_TY_Create(arg1, arg2); 28        DESTROY_KOVS: LIST_OF_TY_Destroy(arg1); 29        MAKEMUTABLE_KOVS: return 1; 30        COPYKIND_KOVS: return LIST_OF_TY_CopyKind(arg1, arg2); 31        COPYQUICK_KOVS: return LIST_OF_TY_QuickCopy(arg1, arg2); 32        COPYSB_KOVS: BlkValueCopySB1(arg1, arg2); 33        KINDDATA_KOVS: return LIST_OF_TY_KindData(arg1, arg2); 34        EXTENT_KOVS: return BlkValueRead(arg1, LIST_LENGTH_F) + LIST_ITEM_BASE; 35        COPY_KOVS: LIST_OF_TY_Copy(arg1, arg2, arg3); 36        COMPARE_KOVS: return LIST_OF_TY_Compare(arg1, arg2); 37        HASH_KOVS: return LIST_OF_TY_Hash(arg1); 38        DEBUG_KOVS: print " = {", (LIST_OF_TY_Say) arg1, "} of kind ", 39                              BlkValueRead(arg1, LIST_ITEM_KOV_F); 40    } 41    ! We choose not to respond to: CAST_KOVS, READ_FILE_KOVS, WRITE_FILE_KOVS 42    rfalse; 43];

Creation.

Lists are by default created empty but in a block-value with enough capacity to hold 25 items, this being what's left in a 32-word block once all overheads are taken care of: 4 words are consumed by the header, then 2 more by the list metadata entries below.

52[ LIST_OF_TY_Create skov sb list; 53    skov = KindBaseTerm(skov, 0); 54    list = FlexAllocate(27*WORDSIZE, LIST_OF_TY, BLK_FLAG_MULTIPLE + BLK_FLAG_WORD); 55    BlkValueWrite(list, LIST_ITEM_KOV_F, skov, true); 56    BlkValueWrite(list, LIST_LENGTH_F, 0, true); 57    sb = BlkValueCreateSB1(sb, list); 58    return sb; 59];

Destruction.

If the list items are themselves block-values, they must all be freed before the list itself can be freed.

66[ LIST_OF_TY_Destroy list no_items i k; 67    k = BlkValueRead(list, LIST_ITEM_KOV_F); 68    if (KOVIsBlockValue(k)) { 69        no_items = BlkValueRead(list, LIST_LENGTH_F); 70        for (i=0: i<no_items: i++) BlkValueFree(BlkValueRead(list, i+LIST_ITEM_BASE)); 71    } 72];

Copying.

Again, if the list contains block-values then they must be duplicated rather than bitwise copied as pointers.

Note that we use the pre-copy stage to remember the kind of value stored in the list. Type-checking will make sure this isn't abused: cases where it's important include copying the empty list into a list of rooms (it should not as a result acquire the kind "list of values"), or copying a list of people into a list of things (which should remain a list of things.)

86[ LIST_OF_TY_CopyKind to from; 87    BlkValueWrite(to, LIST_ITEM_KOV_F, BlkValueRead(from, LIST_ITEM_KOV_F)); 88]; 89 90[ LIST_OF_TY_QuickCopy to from; 91    if (BlkValueRead(to, LIST_ITEM_KOV_F) ~= BlkValueRead(from, LIST_ITEM_KOV_F)) 92        rfalse; 93    rtrue; 94]; 95 96[ LIST_OF_TY_KindData list; 97    return BlkValueRead(list, LIST_ITEM_KOV_F); 98]; 99 100[ LIST_OF_TY_Copy lto lfrom precopied_list_kov no_items i nv bk val splk; 101    no_items = BlkValueRead(lfrom, LIST_LENGTH_F); 102    bk = BlkValueRead(lfrom, LIST_ITEM_KOV_F); 103    if (precopied_list_kov ~= 0 or UNKNOWN_TY) 104        BlkValueWrite(lto, LIST_ITEM_KOV_F, precopied_list_kov); 105    else BlkValueWrite(lto, LIST_ITEM_KOV_F, bk); 106    if (KOVIsBlockValue(bk)) { 107        for (i=0: i<no_items: i++) { 108            val = BlkValueRead(lfrom, i+LIST_ITEM_BASE); 109            if (precopied_list_kov ~= 0 or UNKNOWN_TY) 110                nv = BlkValueCreate(precopied_list_kov); 111            else 112                nv = BlkValueCreate(bk); 113            BlkValueCopy(nv, val); 114            BlkValueWrite(lto, i+LIST_ITEM_BASE, nv); 115        } 116    } 117];

Comparison.

Lists of a given kind of value are always grouped together, in this comparison: but the effect of that is unlikely to be noticed since NI's type-checker will probably prevent comparisons of lists of differing items in any case. The next criterion is length: a short list precedes a long one. Beyond that, we use the list's own preferred comparison function to judge the items in turn, stopping as soon as a pair of corresponding items differs: thus we sort lists of equal size in lexicographic order.

Since the comparison function depends only on the KOV, it may seem wasteful of a word of memory to store it in the list, given that we are already storing the KOV in any case. But we do this because comparisons have to be fast: we don't want to incur the overhead of translating KOV to comparison function.

134[ LIST_OF_TY_Compare listleft listright delta no_items i cf; 135    delta = BlkValueRead(listleft, LIST_LENGTH_F) - BlkValueRead(listright, LIST_LENGTH_F); 136    if (delta) return delta; 137    no_items = BlkValueRead(listleft, LIST_LENGTH_F); 138    if (no_items == 0) return 0; 139    delta = BlkValueRead(listleft, LIST_ITEM_KOV_F) - BlkValueRead(listright, LIST_ITEM_KOV_F); 140    if (delta) return delta; 141    cf = LIST_OF_TY_ComparisonFn(listleft); 142    if (cf == 0 or UnsignedCompare) { 143        for (i=0: i<no_items: i++) { 144            delta = BlkValueRead(listleft, i+LIST_ITEM_BASE) - 145                BlkValueRead(listright, i+LIST_ITEM_BASE); 146            if (delta) return delta; 147        } 148    } else { 149        for (i=0: i<no_items: i++) { 150            delta = cf(BlkValueRead(listleft, i+LIST_ITEM_BASE), 151                BlkValueRead(listright, i+LIST_ITEM_BASE)); 152            if (delta) return delta; 153        } 154    } 155    return 0; 156]; 157 158[ LIST_OF_TY_ComparisonFn list; 159    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 160    return KOVComparisonFunction(BlkValueRead(list, LIST_ITEM_KOV_F)); 161]; 162 163[ LIST_OF_TY_Distinguish txb1 txb2; 164    if (LIST_OF_TY_Compare(txb1, txb2) == 0) rfalse; 165    rtrue; 166];

Hashing.

171[ LIST_OF_TY_Hash list len kov rv i; 172    rv = 0; 173    len = BlkValueRead(list, LIST_LENGTH_F); 174    kov = BlkValueRead(list, LIST_ITEM_KOV_F); 175    for (i=0: i<len: i++) 176        rv = rv * 33 + GetHashValue(kov, BlkValueRead(list, i+LIST_ITEM_BASE)); 177    return rv; 178];

Printing.

Unusually, this function can print the value in one of several formats: 0 for a comma-separated list; 1 for a braced, set-notation list; 2 for a comma-separated list with definite articles, which only makes sense if the list contains objects; 3 for a comma-separated list with indefinite articles. Note that a list in this sense is not printed using the ListWriter.i6t code for elaborate lists of objects, and it doesn't use the "listing contents of..." activity in any circumstances.

190[ LIST_OF_TY_Say list format no_items v i bk; 191    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return; 192    no_items = BlkValueRead(list, LIST_LENGTH_F); 193    bk = KindAtomic(BlkValueRead(list, LIST_ITEM_KOV_F)); 194    ! print no_items, " of kov=", BlkValueRead(list, LIST_ITEM_KOV_F), ":"; 195    if (format == 1) print "{"; 196    for (i=0:i<no_items:i++) { 197        v = BlkValueRead(list, i+LIST_ITEM_BASE); 198        switch (format) { 199            2: print (the) v; 200            3: print (a) v; 201            default: 202                if (bk == LIST_OF_TY) LIST_OF_TY_Say(v, 1); 203                else if ((bk == TEXT_TY) && (format == 1)) { 204                    print "~"; PrintKindValuePair(bk, v); print "~"; 205                } 206                else PrintKindValuePair(bk, v); 207        } 208        if (i<no_items-2) print ", "; 209        if (i==no_items-2) { 210            if (format == 1) print ", "; else { 211                #ifdef SERIAL_COMMA; if (no_items ~= 2) print ","; #endif; 212                LIST_WRITER_INTERNAL_RM('C'); 213            } 214        } 215    } 216    if (format == 1) print "}"; 217    prior_named_list = no_items; prior_named_list_gender = -1; 218];

List From Description.

That completes the compulsory services required for this KOV to function: from here on, the remaining routines provide definitions of stored action-related phrases in the Standard Rules.

Given a description D which applies to some objects and not others – say, "lighted rooms adjacent to the Transport Hub" – we can cast this into a list of all objects satisfying D with the following routine. Slightly wastefully of time, we have to iterate through the objects twice in order first to work out the length of list we will need, and then to transcribe them.

233[ LIST_OF_TY_Desc list desc kov obj no_items ex len i; 234    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return false; 235    ex = BlkValueLBCapacity(list); 236    len = desc(-3); 237    if (len+LIST_ITEM_BASE > ex) { 238        if (BlkValueSetLBCapacity(list, len+LIST_ITEM_BASE) == false) 239            return 0; 240    } 241    if (kov) BlkValueWrite(list, LIST_ITEM_KOV_F, kov); 242    else BlkValueWrite(list, LIST_ITEM_KOV_F, OBJECT_TY); 243    BlkValueWrite(list, LIST_LENGTH_F, len); 244    obj = 0; 245    for (i=0: i<len: i++) { 246        obj = desc(-2, obj, i); 247        ! print "i = ", i, " and obj = ", obj, "^"; 248        BlkValueWrite(list, i+LIST_ITEM_BASE, obj); 249    } 250    return list; 251];

Find Item.

We test whether a list list includes a value equal to v or not. Equality here is in the sense of the list's comparison function: thus for texts or other lists, say, deep comparisons rather than simple pointer comparisons are performed. In other words, one copy of "Alert" is equal to another.

260[ LIST_OF_TY_FindItem list v i no_items cf; 261    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) rfalse; 262    cf = LIST_OF_TY_ComparisonFn(list); 263    no_items = BlkValueRead(list, LIST_LENGTH_F); 264    if (cf == 0 or UnsignedCompare) { 265        for (i=0: i<no_items: i++) 266            if (v == BlkValueRead(list, i+LIST_ITEM_BASE)) rtrue; 267    } else { 268        for (i=0: i<no_items: i++) 269            if (cf(v, BlkValueRead(list, i+LIST_ITEM_BASE)) == 0) rtrue; 270    } 271    rfalse; 272];

Insert Item.

The following routine inserts an item into the list. If this would break the size of the current block-value, then we extend by at least enough room to hold at least another 16 entries.

In the call LIST_OF_TY_InsertItem(list, v, posnflag, posn, nodups), only the first two arguments are compulsory. (a) If nodups is set, and an item equal to v is already present in the list, we return and do nothing. (nodups means "no duplicates".) (b) Otherwise, if posnflag is false, we append a new entry v at the back of the given list. (c) Otherwise, when posnflag is true, posn indicates the insertion position, from 1 (before the current first item) to N+1 (after the last), where N is the number of items in the list at present.

290[ LIST_OF_TY_InsertItem list v posnflag posn nodups i no_items ex nv contents_kind; 291    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return false; 292    if (nodups && (LIST_OF_TY_FindItem(list, v))) return list; 293    no_items = BlkValueRead(list, LIST_LENGTH_F); 294    BlkValueWrite(list, LIST_LENGTH_F, no_items); ! Forces the list to be mutable 295    contents_kind = BlkValueRead(list, LIST_ITEM_KOV_F); 296    if ((posnflag) && ((posn<1) || (posn > no_items+1))) { 297        print "*** Couldnt add at entry ", posn, " in the list "; 298        LIST_OF_TY_Say(list, true); 299        print ", which has entries in the range 1 to ", no_items, " ***^"; 300        RunTimeProblem(RTP_LISTRANGEERROR); 301        rfalse; 302    } 303    ex = BlkValueLBCapacity(list); 304    if (no_items+LIST_ITEM_BASE+1 > ex) { 305        if (BlkValueSetLBCapacity(list, ex+16) == false) return 0; 306    } 307    if (KOVIsBlockValue(contents_kind)) { 308        nv = BlkValueCreate(contents_kind); 309        BlkValueCopy(nv, v); 310        v = nv; 311    } 312    if (posnflag) { 313        posn--; 314        for (i=no_items:i>posn:i--) { 315            BlkValueWrite(list, i+LIST_ITEM_BASE, 316                BlkValueRead(list, i-1+LIST_ITEM_BASE)); 317        } 318        BlkValueWrite(list, posn+LIST_ITEM_BASE, v); 319    } else { 320        BlkValueWrite(list, no_items+LIST_ITEM_BASE, v); 321    } 322    BlkValueWrite(list, LIST_LENGTH_F, no_items+1); 323    return list; 324];

Append List.

Instead of adjoining a single value, we adjoin an entire second list, which must be of a compatible kind of value (something which NI's type-checking machinery polices for us). Except that we have a list more rather than a value v to insert, the specification is the same as for LIST_OF_TY_InsertItem.

334[ LIST_OF_TY_AppendList list more posnflag posn nodups v i j no_items msize ex nv; 335    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return false; 336    if ((more==0) || (BlkValueWeakKind(more) ~= LIST_OF_TY)) return list; 337    no_items = BlkValueRead(list, LIST_LENGTH_F); 338    BlkValueWrite(list, LIST_LENGTH_F, no_items); ! Forces the list to be mutable 339    if ((posnflag) && ((posn<1) || (posn > no_items+1))) { 340        print "*** Couldnt add at entry ", posn, " in the list "; 341        LIST_OF_TY_Say(list, true); 342        print ", which has entries in the range 1 to ", no_items, " ***^"; 343        RunTimeProblem(RTP_LISTRANGEERROR); 344        rfalse; 345    } 346    msize = BlkValueRead(more, LIST_LENGTH_F); 347    ex = BlkValueLBCapacity(list); 348    if (no_items+msize+LIST_ITEM_BASE > ex) { 349        if (BlkValueSetLBCapacity(list, no_items+msize+LIST_ITEM_BASE+8) == false) 350            return 0; 351    } 352    if (posnflag) { 353        posn--; 354        for (i=no_items+msize:i>=posn+msize:i--) { 355            BlkValueWrite(list, i+LIST_ITEM_BASE, 356                BlkValueRead(list, i-msize+LIST_ITEM_BASE)); 357        } 358        ! BlkValueWrite(list, posn, v); 359        for (j=0: j<msize: j++) { 360            v = BlkValueRead(more, j+LIST_ITEM_BASE); 361            if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) { 362                nv = BlkValueCreate(BlkValueRead(list, LIST_ITEM_KOV_F)); 363                BlkValueCopy(nv, v); 364                v = nv; 365            } 366            BlkValueWrite(list, posn+j+LIST_ITEM_BASE, v); 367        } 368    } else { 369        for (i=0, j=0: i<msize: i++) { 370            v = BlkValueRead(more, i+LIST_ITEM_BASE); 371            if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) { 372                nv = BlkValueCreate(BlkValueRead(list, LIST_ITEM_KOV_F)); 373                BlkValueCopy(nv, v); 374                v = nv; 375            } 376            if ((nodups == 0) || (LIST_OF_TY_FindItem(list, v) == false)) { 377                BlkValueWrite(list, no_items+j+LIST_ITEM_BASE, v); 378                j++; 379            } 380        } 381    } 382    BlkValueWrite(list, LIST_LENGTH_F, no_items+j); 383    return list; 384];

Remove Value.

We remove every instance of the value v from the given list. If the optional flag forgive is set, then we make no complaint if no value of v was present in the first place: otherwise, we issue a run-time problem.

Note that if the list contains block-values then the value must be properly destroyed with BlkValueFree before being overwritten as the items shuffle down.

395[ LIST_OF_TY_RemoveValue list v forgive i j no_items odsize f cf delendum; 396    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) rfalse; 397    cf = LIST_OF_TY_ComparisonFn(list); 398    no_items = BlkValueRead(list, LIST_LENGTH_F); odsize = no_items; 399    BlkValueWrite(list, LIST_LENGTH_F, no_items); ! Forces the list to be mutable 400    for (i=0: i<no_items: i++) { 401        delendum = BlkValueRead(list, i+LIST_ITEM_BASE); 402        if (cf == 0 or UnsignedCompare) 403            f = (v == delendum); 404        else 405            f = (cf(v, delendum) == 0); 406        if (f) { 407            if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) 408                BlkValueFree(delendum); 409            for (j=i+1: j<no_items: j++) 410                BlkValueWrite(list, j-1+LIST_ITEM_BASE, 411                    BlkValueRead(list, j+LIST_ITEM_BASE)); 412            no_items--; i--; 413            BlkValueWrite(list, LIST_LENGTH_F, no_items); 414        } 415    } 416    if (odsize ~= no_items) rfalse; 417    if (forgive) rfalse; 418    print "*** Couldnt remove: the value "; 419    PrintKindValuePair(BlkValueRead(list, LIST_ITEM_KOV_F), v); 420    print " was not present in the list "; 421    LIST_OF_TY_Say(list, true); 422    print " ***^"; 423    RunTimeProblem(RTP_LISTRANGEERROR); 424];

Remove Item Range.

We excise items from to to from the given list, which numbers its items upwards from 1. If the optional flag forgive is set, then we truncate a range overspilling the actual list, and we make no complaint if it turns out that there is then nothing to be done: otherwise, in either event, we issue a run-time problem.

Once again, we destroy any block-values whose pointers will be overwritten as the list shuffles down to fill the void.

437[ LIST_OF_TY_RemoveItemRange list from to forgive i d no_items; 438    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) rfalse; 439    no_items = BlkValueRead(list, LIST_LENGTH_F); 440    if ((from > to) || (from <= 0) || (to > no_items)) { 441        if (forgive) { 442            if (from <= 0) from = 1; 443            if (to >= no_items) to = no_items; 444            if (from > to) return list; 445        } else { 446            print "*** Couldnt remove entries ", from, " to ", to, " from the list "; 447            LIST_OF_TY_Say(list, true); 448            print ", which has entries in the range 1 to ", no_items, " ***^"; 449            RunTimeProblem(RTP_LISTRANGEERROR); 450            rfalse; 451        } 452    } 453    to--; from--; 454    d = to-from+1; 455    if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) 456        for (i=0: i<d: i++) 457            BlkValueFree(BlkValueRead(list, from+i+LIST_ITEM_BASE)); 458    for (i=from: i<no_items-d: i++) 459        BlkValueWrite(list, i+LIST_ITEM_BASE, 460            BlkValueRead(list, i+d+LIST_ITEM_BASE)); 461    BlkValueWrite(list, LIST_LENGTH_F, no_items-d); 462    return list; 463];

Remove List.

We excise all values from the removal list rlist, wherever they occur in list. Inevitably, given that we haven't sorted these lists and can spare neither time nor storage to do so, this is an expensive process with a running time proportional to the product of the two list sizes: we accept that as an overhead because in practice the rlist is almost always small in real-world use.

If the initial lists were disjoint, so that no removals occur, we always forgive the user: the request was not necessarily a foolish one, it only happened in this situation to be unhelpful.

478[ LIST_OF_TY_Remove_List list rlist i j k v w no_items odsize rsize cf f; 479    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) rfalse; 480    no_items = BlkValueRead(list, LIST_LENGTH_F); odsize = no_items; 481    rsize = BlkValueRead(rlist, LIST_LENGTH_F); 482    cf = LIST_OF_TY_ComparisonFn(list); 483    for (i=0: i<no_items: i++) { 484        v = BlkValueRead(list, i+LIST_ITEM_BASE); 485        for (k=0: k<rsize: k++) { 486            w = BlkValueRead(rlist, k+LIST_ITEM_BASE); 487            if (cf == 0 or UnsignedCompare) 488                f = (v == w); 489            else 490                f = (cf(v, w) == 0); 491            if (f) { 492                if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) 493                    BlkValueFree(v); 494                for (j=i+1: j<no_items: j++) 495                    BlkValueWrite(list, j+LIST_ITEM_BASE-1, 496                        BlkValueRead(list, j+LIST_ITEM_BASE)); 497                no_items--; i--; 498                BlkValueWrite(list, LIST_LENGTH_F, no_items); 499                break; 500            } 501        } 502    } 503    rfalse; 504];

Get Length.

509[ LIST_OF_TY_GetLength list; 510    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 511    return BlkValueRead(list, LIST_LENGTH_F); 512]; 513 514[ LIST_OF_TY_Empty list; 515    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) rfalse; 516    if (BlkValueRead(list, LIST_LENGTH_F) == 0) rtrue; 517    rfalse; 518];

Set Length.

This is rather harder: it might lengthen the list, in which case we have to pad out with the default value for the kind of value stored – padding a list of numbers with 0s, a list of texts with copies of the empty text, and so on – creating such block-values as might be needed; or else it might shorten the list, in which case we must cut items, destroying them properly if they were block-values.

LIST_OF_TY_SetLength(list, newsize, this_way_only, truncation_end) alters the length of the given list to newsize. If this_way_only is 1, the list is only allowed to grow, and nothing happens if we have asked to shrink it; if it is -1, the list is only allowed to shrink; if it is 0, the list is allowed either to grow or shrink. In the event that the list does have to shrink, entries must be removed, and we remove from the end if truncation_end is 1, or from the start if it is -1.

537[ LIST_OF_TY_SetLength list newsize this_way_only truncation_end no_items ex i dv; 538    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 539    if (newsize < 0) return RunTimeProblem(RTP_LISTSIZENEGATIVE, newsize); 540    BlkMakeMutable(list); 541    no_items = BlkValueRead(list, LIST_LENGTH_F); 542    if (no_items < newsize) { 543        if (this_way_only == -1) return list; 544        ex = BlkValueLBCapacity(list); 545        if (newsize+LIST_ITEM_BASE > ex) { 546            if (BlkValueSetLBCapacity(list, newsize+LIST_ITEM_BASE) == false) 547                return 0; 548        } 549        dv = DefaultValueOfKOV(BlkValueRead(list, LIST_ITEM_KOV_F)); 550        for (i=no_items: i<newsize: i++) 551            BlkValueWrite(list, LIST_ITEM_BASE+i, dv); 552        BlkValueWrite(list, LIST_LENGTH_F, newsize); 553    } 554    if (no_items > newsize) { 555        if (this_way_only == 1) return list; 556        if (truncation_end == -1) { 557            if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) 558                for (i=0: i<no_items-newsize: i++) 559                    BlkValueFree(BlkValueRead(list, LIST_ITEM_BASE+i)); 560            for (i=0: i<newsize: i++) 561                BlkValueWrite(list, LIST_ITEM_BASE+i, 562                    BlkValueRead(list, LIST_ITEM_BASE+no_items-newsize+i)); 563        } else { 564            if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) 565                for (i=newsize: i<no_items: i++) 566                    BlkValueFree(BlkValueRead(list, LIST_ITEM_BASE+i)); 567        } 568        BlkValueWrite(list, LIST_LENGTH_F, newsize); 569    } 570    return list; 571];

Get Item.

576[ LIST_OF_TY_GetItem list i forgive no_items; 577    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return false; 578    no_items = BlkValueRead(list, LIST_LENGTH_F); 579    if ((i<=0) || (i>no_items)) { 580        if (forgive) return false; 581        print "*** Couldnt read from entry ", i, " of a list which"; 582        switch (no_items) { 583            0: print " is empty ***^"; 584            1: print " has only one entry, numbered 1 ***^"; 585            default: print " has entries numbered from 1 to ", no_items, " ***^"; 586        } 587        RunTimeProblem(RTP_LISTRANGEERROR); 588        if (no_items >= 1) i = 1; 589        else return false; 590    } 591    return BlkValueRead(list, LIST_ITEM_BASE+i-1); 592];

Write Item.

The slightly odd name for this function comes about because our usual way to convert an rvalue such as LIST_OF_TY_GetItem(L, 4) is to prefix Write, so that it becomes WriteLIST_OF_TY_GetItem(L, 4).

600[ WriteLIST_OF_TY_GetItem list i val no_items; 601    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return false; 602    no_items = BlkValueRead(list, LIST_LENGTH_F); 603    if ((i<=0) || (i>no_items)) { 604        print "*** Couldnt write to list entry ", i, " of a list which"; 605        switch (no_items) { 606            0: print " is empty ***^"; 607            1: print " has only one entry, numbered 1 ***^"; 608            default: print " has entries numbered from 1 to ", no_items, " ***^"; 609        } 610        return RunTimeProblem(RTP_LISTRANGEERROR); 611    } 612    BlkValueWrite(list, LIST_ITEM_BASE+i-1, val); 613];

Put Item.

Higher-level code should not use Write_LIST_OF_TY_GetItem, because it does not properly keep track of block-value copying: the following should be used instead.

621[ LIST_OF_TY_PutItem list i v no_items nv; 622    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return false; 623    no_items = BlkValueRead(list, LIST_LENGTH_F); 624    if (KOVIsBlockValue(BlkValueRead(list, LIST_ITEM_KOV_F))) { 625        nv = BlkValueCreate(BlkValueRead(list, LIST_ITEM_KOV_F)); 626        BlkValueCopy(nv, v); 627        v = nv; 628    } 629    if ((i<=0) || (i>no_items)) return false; 630    BlkValueWrite(list, LIST_ITEM_BASE+i-1, v); 631];

Multiple Object List.

The parser uses one data structure which is really a list: but which can't be represented as such because the heap might not exist. This is the multiple object list, which is used to handle commands like TAKE ALL by firing off a sequence of actions with one of the objects taken from entries in turn of the list. The following converts it to a list structure.

641[ LIST_OF_TY_Mol list len i; 642    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 643    len = multiple_object-->0; 644    LIST_OF_TY_SetLength(list, len); 645    for (i=1: i<=len: i++) 646        LIST_OF_TY_PutItem(list, i, multiple_object-->i); 647    return list; 648]; 649 650[ LIST_OF_TY_Set_Mol list len i; 651    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 652    len = BlkValueRead(list, LIST_LENGTH_F); 653    if (len > 63) len = 63; 654    multiple_object-->0 = len; 655    for (i=1: i<=len: i++) 656        multiple_object-->i = BlkValueRead(list, LIST_ITEM_BASE+i-1); 657];

Reversing.

Reversing a list is, happily, a very efficient operation when the list contains block-values: because the pointers are rearranged but none is duplicated or destroyed, we can for once ignore the fact that they are pointers to block-values and simply move them around like any other data.

666[ LIST_OF_TY_Reverse list no_items i v; 667    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 668    no_items = BlkValueRead(list, LIST_LENGTH_F); 669    if (no_items < 2) return list; 670    for (i=0:i*2<no_items:i++) { 671        v = BlkValueRead(list, LIST_ITEM_BASE+i); 672        BlkValueWrite(list, LIST_ITEM_BASE+i, 673            BlkValueRead(list, LIST_ITEM_BASE+no_items-1-i)); 674        BlkValueWrite(list, LIST_ITEM_BASE+no_items-1-i, v); 675    } 676    return list; 677];

Rotation.

The same is true of rotation. Here, "forwards" rotation means towards the end of the list, "backwards" means towards the start.

684[ LIST_OF_TY_Rotate list backwards no_items i v; 685    if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; 686    no_items = BlkValueRead(list, LIST_LENGTH_F); 687    if (no_items < 2) return list; 688    if (backwards) { 689        v = BlkValueRead(list, LIST_ITEM_BASE); 690        for (i=0:i<no_items-1:i++) 691            BlkValueWrite(list, LIST_ITEM_BASE+i, 692                BlkValueRead(list, LIST_ITEM_BASE+i+1)); 693        BlkValueWrite(list, no_items-1+LIST_ITEM_BASE, v); 694    } else { 695        v = BlkValueRead(list, no_items-1+LIST_ITEM_BASE); 696        for (i=no_items-1:i>0:i--) 697            BlkValueWrite(list, LIST_ITEM_BASE+i, 698                BlkValueRead(list, LIST_ITEM_BASE+i-1)); 699        BlkValueWrite(list, LIST_ITEM_BASE, v); 700    } 701    return list; 702];

Sorting.

And the same, again, is true of sorting: but we do have to take note of block values when it comes to performing comparisons, because we can only compare items in the list by looking at their contents, not the pointers to their contents.

LIST_OF_TY_Sort(list, dir, prop) sorts the given list in ascending order if dir is 1, in descending order if dir is -1, or in random order if dir is 2. The comparison used is the one for the kind of value stored in the list, unless the optional argument prop is supplied, in which case we sort based not on the item values but on their values for the property prop. (This only makes sense if the list contains objects.)

718Global LIST_OF_TY_Sort_cf; 719 720[ LIST_OF_TY_Sort list dir prop cf i j no_items v; 721    BlkMakeMutable(list); 722    no_items = BlkValueRead(list, LIST_LENGTH_F); 723    if (dir == 2) { 724        if (no_items < 2) return; 725        for (i=1:i<no_items:i++) { 726            j = random(i+1) - 1; 727            v = BlkValueRead(list, LIST_ITEM_BASE+i); 728            BlkValueWrite(list, LIST_ITEM_BASE+i, BlkValueRead(list, LIST_ITEM_BASE+j)); 729            BlkValueWrite(list, LIST_ITEM_BASE+j, v); 730        } 731        return; 732    } 733    SetSortDomain(ListSwapEntries, ListCompareEntries); 734    if (cf) LIST_OF_TY_Sort_cf = BlkValueCompare; 735    else LIST_OF_TY_Sort_cf = 0; 736    SortArray(list, prop, dir, no_items, false, 0); 737]; 738 739[ ListSwapEntries list i j v; 740    if (i==j) return; 741    v = BlkValueRead(list, LIST_ITEM_BASE+i-1); 742    BlkValueWrite(list, LIST_ITEM_BASE+i-1, BlkValueRead(list, LIST_ITEM_BASE+j-1)); 743    BlkValueWrite(list, LIST_ITEM_BASE+j-1, v); 744]; 745 746[ ListCompareEntries list col i j d cf; 747    if (i==j) return 0; 748    i = BlkValueRead(list, LIST_ITEM_BASE+i-1); 749    j = BlkValueRead(list, LIST_ITEM_BASE+j-1); 750    if (I7S_Col) { 751        if (i provides I7S_Col) i=i.I7S_Col; else i=0; 752        if (j provides I7S_Col) j=j.I7S_Col; else j=0; 753        cf = LIST_OF_TY_Sort_cf; 754    } else { 755        cf = LIST_OF_TY_ComparisonFn(list); 756    } 757    if (cf == 0) { 758        if (i > j) return 1; 759        if (i < j) return -1; 760        return 0; 761    } else 762        return cf(i, j); 763];