Lists contents
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;
17Constant LIST_LENGTH_F = 1;
18Constant LIST_ITEM_BASE = 2;
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
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];
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
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
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);
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);
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
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);
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];
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];
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];