1 # A list links up multiple objects together to make them easier to manage.
3 # The objects must be of the same type. If you want to store multiple types in
4 # a single list, use an exclusive-container.
11 def push x:_elem, l:&:list:_elem -> result:&:list:_elem/contained-in:l [
14 result <- new {(list _elem): type}
18 def first in:&:list:_elem -> result:_elem [
21 result <- get *in, value:offset
24 def rest in:&:list:_elem -> result:&:list:_elem/contained-in:in [
27 result <- get *in, next:offset
30 scenario list-handling [
33 x:&:list:num <- push 3, null
41 20:&:list:num/raw <- rest x
43 memory-should-contain [
47 20 <- 0 # nothing left
51 def length l:&:list:_elem -> result:num [
57 result <- add result, 1
63 # insert 'x' after 'in'
64 def insert x:_elem, in:&:list:_elem -> in:&:list:_elem [
67 new-node:&:list:_elem <- new {(list _elem): type}
68 *new-node <- put *new-node, value:offset, x
69 next-node:&:list:_elem <- get *in, next:offset
70 *in <- put *in, next:offset, new-node
71 *new-node <- put *new-node, next:offset, next-node
74 scenario inserting-into-list [
76 list:&:list:num <- push 3, null
80 list2:&:list:num <- rest list # inside list
81 list2 <- insert 6, list2
84 10:num/raw <- first list2
86 11:num/raw <- first list2
88 12:num/raw <- first list2
90 13:num/raw <- first list2
92 memory-should-contain [
93 10 <- 5 # scanning next
95 12 <- 6 # inserted element
100 scenario inserting-at-end-of-list [
102 list:&:list:num <- push 3, null
106 list2:&:list:num <- rest list # inside list
107 list2 <- rest list2 # now at end of list
108 list2 <- insert 6, list2
109 # check structure like before
111 10:num/raw <- first list2
113 11:num/raw <- first list2
115 12:num/raw <- first list2
117 13:num/raw <- first list2
119 memory-should-contain [
120 10 <- 5 # scanning next
123 13 <- 6 # inserted element
127 scenario inserting-after-start-of-list [
129 list:&:list:num <- push 3, null
133 list <- insert 6, list
134 # check structure like before
135 list2:&:list:num <- copy list
136 10:num/raw <- first list2
138 11:num/raw <- first list2
140 12:num/raw <- first list2
142 13:num/raw <- first list2
144 memory-should-contain [
145 10 <- 5 # scanning next
146 11 <- 6 # inserted element
152 # remove 'x' from its surrounding list 'in'
154 # Returns null if and only if list is empty. Beware: in that case any other
155 # pointers to the head are now invalid.
156 def remove x:&:list:_elem/contained-in:in, in:&:list:_elem -> in:&:list:_elem [
159 # if 'x' is null, return
161 next-node:&:list:_elem <- rest x
162 # clear next pointer of 'x'
163 *x <- put *x, next:offset, null
164 # if 'x' is at the head of 'in', return the new head
165 at-head?:bool <- equal x, in
166 return-if at-head?, next-node
168 prev-node:&:list:_elem <- copy in
169 curr:&:list:_elem <- rest prev-node
172 found?:bool <- equal curr, x
174 prev-node <- copy curr
177 # set its next pointer to skip 'x'
178 *prev-node <- put *prev-node, next:offset, next-node
181 scenario removing-from-list [
183 list:&:list:num <- push 3, null
187 list2:&:list:num <- rest list # second element
188 list <- remove list2, list
189 10:bool/raw <- equal list2, null
190 # check structure like before
192 11:num/raw <- first list2
194 12:num/raw <- first list2
195 20:&:list:num/raw <- rest list2
197 memory-should-contain [
198 10 <- 0 # remove returned non-null
199 11 <- 5 # scanning next, skipping deleted element
201 20 <- 0 # no more elements
205 scenario removing-from-start-of-list [
207 list:&:list:num <- push 3, null
211 list <- remove list, list
212 # check structure like before
213 list2:&:list:num <- copy list
214 10:num/raw <- first list2
216 11:num/raw <- first list2
217 20:&:list:num/raw <- rest list2
219 memory-should-contain [
220 10 <- 4 # scanning next, skipping deleted element
222 20 <- 0 # no more elements
226 scenario removing-from-end-of-list [
228 list:&:list:num <- push 3, null
232 # delete last element
233 list2:&:list:num <- rest list
235 list <- remove list2, list
236 10:bool/raw <- equal list2, null
237 # check structure like before
239 11:num/raw <- first list2
241 12:num/raw <- first list2
242 20:&:list:num/raw <- rest list2
244 memory-should-contain [
245 10 <- 0 # remove returned non-null
246 11 <- 5 # scanning next, skipping deleted element
248 20 <- 0 # no more elements
252 scenario removing-from-singleton-list [
254 list:&:list:num <- push 3, null
256 list <- remove list, list
257 1:num/raw <- deaddress list
259 memory-should-contain [
260 1 <- 0 # back to an empty list
264 # reverse the elements of a list
265 # (contributed by Caleb Couch)
266 def reverse list:&:list:_elem temp:&:list:_elem/contained-in:result -> result:&:list:_elem [
269 return-unless list, temp
270 object:_elem <- first, list
272 temp <- push object, temp
273 result <- reverse list, temp
276 scenario reverse-list [
278 list:&:list:num <- push 1, null
284 stash [reversed:], list
286 trace-should-contain [
287 app: list: 3 -> 2 -> 1
288 app: reversed: 1 -> 2 -> 3
292 scenario stash-list [
294 list:&:list:num <- push 1, null
300 trace-should-contain [
301 app: list: 3 -> 2 -> 1
305 def to-text in:&:list:_elem -> result:text [
308 buf:&:buffer:char <- new-buffer 80
309 buf <- to-buffer in, buf
310 result <- buffer-to-array buf
313 # variant of 'to-text' which stops printing after a few elements (and so is robust to cycles)
314 def to-text-line in:&:list:_elem -> result:text [
317 buf:&:buffer:char <- new-buffer 80
318 buf <- to-buffer in, buf, 6 # max elements to display
319 result <- buffer-to-array buf
322 def to-buffer in:&:list:_elem, buf:&:buffer:char -> buf:&:buffer:char [
327 buf <- append buf, [[]]
330 # append in.value to buf
331 val:_elem <- get *in, value:offset
332 buf <- append buf, val
334 next:&:list:_elem <- rest in
335 nextn:num <- deaddress next
337 buf <- append buf, [ -> ]
339 remaining:num, optional-input-found?:bool <- next-input
341 break-if optional-input-found?
342 # unlimited recursion
343 buf <- to-buffer next, buf
347 break-unless remaining
349 remaining <- subtract remaining, 1
350 buf <- to-buffer next, buf, remaining
353 # past recursion depth; insert ellipses and stop
357 scenario stash-empty-list [
359 x:&:list:num <- copy null
363 trace-should-contain [