https://github.com/akkartik/mu/blob/master/065duplex_list.mu
1
2
3 container duplex-list:_elem [
4 value:_elem
5 next:&:duplex-list:_elem
6 prev:&:duplex-list:_elem
7 ]
8
9 def push x:_elem, in:&:duplex-list:_elem/contained-in:result -> result:&:duplex-list:_elem [
10 local-scope
11 load-inputs
12 result:&:duplex-list:_elem <- new {(duplex-list _elem): type}
13 *result <- merge x, in, null
14 return-unless in
15 put *in, prev:offset, result
16 ]
17
18 def first in:&:duplex-list:_elem -> result:_elem [
19 local-scope
20 load-inputs
21 {
22 break-if in
23 zero:&:_elem <- new _elem:type
24 zero-result:_elem <- copy *zero
25 abandon zero
26 return zero-result
27 }
28 result <- get *in, value:offset
29 ]
30
31 def next in:&:duplex-list:_elem -> result:&:duplex-list:_elem/contained-in:in [
32 local-scope
33 load-inputs
34 return-unless in, null
35 result <- get *in, next:offset
36 ]
37
38 def prev in:&:duplex-list:_elem -> result:&:duplex-list:_elem/contained-in:in [
39 local-scope
40 load-inputs
41 return-unless in, null
42 result <- get *in, prev:offset
43 return result
44 ]
45
46 scenario duplex-list-handling [
47 run [
48 local-scope
49
50 10:num/raw <- copy 34
51 11:num/raw <- copy 35
52 list:&:duplex-list:num <- push 3, null
53 list <- push 4, list
54 list <- push 5, list
55 list2:&:duplex-list:num <- copy list
56 20:num/raw <- first list2
57 list2 <- next list2
58 21:num/raw <- first list2
59 list2 <- next list2
60 22:num/raw <- first list2
61 30:&:duplex-list:num/raw <- next list2
62 31:num/raw <- first 30:&:duplex-list:num/raw
63 32:&:duplex-list:num/raw <- next 30:&:duplex-list:num/raw
64 33:&:duplex-list:num/raw <- prev 30:&:duplex-list:num/raw
65 list2 <- prev list2
66 40:num/raw <- first list2
67 list2 <- prev list2
68 41:num/raw <- first list2
69 50:bool/raw <- equal list, list2
70 ]
71 memory-should-contain [
72 0 <- 0
73 10 <- 34
74 11 <- 35
75 20 <- 5
76 21 <- 4
77 22 <- 3
78 30 <- 0
79 31 <- 0
80 32 <- 0
81 33 <- 0
82 40 <- 4
83 41 <- 5
84 50 <- 1
85 ]
86 ]
87
88 def length l:&:duplex-list:_elem -> result:num [
89 local-scope
90 load-inputs
91 result <- copy 0
92 {
93 break-unless l
94 result <- add result, 1
95 l <- next l
96 loop
97 }
98 ]
99
100
101 def insert x:_elem, in:&:duplex-list:_elem -> in:&:duplex-list:_elem [
102 local-scope
103 load-inputs
104 new-node:&:duplex-list:_elem <- new {(duplex-list _elem): type}
105 *new-node <- put *new-node, value:offset, x
106
107 next-node:&:duplex-list:_elem <- get *in, next:offset
108 *in <- put *in, next:offset, new-node
109 *new-node <- put *new-node, prev:offset, in
110 *new-node <- put *new-node, next:offset, next-node
111 return-unless next-node
112 *next-node <- put *next-node, prev:offset, new-node
113 ]
114
115 scenario inserting-into-duplex-list [
116 local-scope
117 list:&:duplex-list:num <- push 3, null
118 list <- push 4, list
119 list <- push 5, list
120 run [
121 list2:&:duplex-list:num <- next list
122 list2 <- insert 6, list2
123
124 list2 <- copy list
125 10:num/raw <- first list2
126 list2 <- next list2
127 11:num/raw <- first list2
128 list2 <- next list2
129 12:num/raw <- first list2
130 list2 <- next list2
131 13:num/raw <- first list2
132 list2 <- prev list2
133 20:num/raw <- first list2
134 list2 <- prev list2
135 21:num/raw <- first list2
136 list2 <- prev list2
137 22:num/raw <- first list2
138 30:bool/raw <- equal list, list2
139 ]
140 memory-should-contain [
141 10 <- 5
142 11 <- 4
143 12 <- 6
144 13 <- 3
145 20 <- 6
146 21 <- 4
147 22 <- 5
148 30 <- 1
149 ]
150 ]
151
152 scenario inserting-at-end-of-duplex-list [
153 local-scope
154 list:&:duplex-list:num <- push 3, null
155 list <- push 4, list
156 list <- push 5, list
157 run [
158 list2:&:duplex-list:num <- next list
159 list2 <- next list2
160 list2 <- insert 6, list2
161
162 list2 <- copy list
163 10:num/raw <- first list2
164 list2 <- next list2
165 11:num/raw <- first list2
166 list2 <- next list2
167 12:num/raw <- first list2
168 list2 <- next list2
169 13:num/raw <- first list2
170 list2 <- prev list2
171 20:num/raw <- first list2
172 list2 <- prev list2
173 21:num/raw <- first list2
174 list2 <- prev list2
175 22:num/raw <- first list2
176 30:bool/raw <- equal list, list2
177 ]
178 memory-should-contain [
179 10 <- 5
180 11 <- 4
181 12 <- 3
182 13 <- 6
183 20 <- 3
184 21 <- 4
185 22 <- 5
186 30 <- 1
187 ]
188 ]
189
190 scenario inserting-after-start-of-duplex-list [
191 local-scope
192 list:&:duplex-list:num <- push 3, null
193 list <- push 4, list
194 list <- push 5, list
195 run [
196 list <- insert 6, list
197
198 list2:&:duplex-list:num <- copy list
199 10:num/raw <- first list2
200 list2 <- next list2
201 11:num/raw <- first list2
202 list2 <- next list2
203 12:num/raw <- first list2
204 list2 <- next list2
205 13:num/raw <- first list2
206 list2 <- prev list2
207 20:num/raw <- first list2
208 list2 <- prev list2
209 21:num/raw <- first list2
210 list2 <- prev list2
211 22:num/raw <- first list2
212 30:bool/raw <- equal list, list2
213 ]
214 memory-should-contain [
215 10 <- 5
216 11 <- 6
217 12 <- 4
218 13 <- 3
219 20 <- 4
220 21 <- 6
221 22 <- 5
222 30 <- 1
223 ]
224 ]
225
226
227
228
229
230 def remove x:&:duplex-list:_elem/contained-in:in, in:&:duplex-list:_elem -> in:&:duplex-list:_elem [
231 local-scope
232 load-inputs
233
234 return-unless x
235 next-node:&:duplex-list:_elem <- get *x, next:offset
236 prev-node:&:duplex-list:_elem <- get *x, prev:offset
237
238 *x <- put *x, next:offset, null
239 *x <- put *x, prev:offset, null
240
241 {
242 break-unless next-node
243 *next-node <- put *next-node, prev:offset, prev-node
244 }
245
246 {
247 break-unless prev-node
248 *prev-node <- put *prev-node, next:offset, next-node
249 return
250 }
251
252
253 return next-node
254 ]
255
256 scenario removing-from-duplex-list [
257 local-scope
258 list:&:duplex-list:num <- push 3, null
259 list <- push 4, list
260 list <- push 5, list
261 run [
262 list2:&:duplex-list:num <- next list
263 list <- remove list2, list
264 10:bool/raw <- equal list2, null
265
266 list2 <- copy list
267 11:num/raw <- first list2
268 list2 <- next list2
269 12:num/raw <- first list2
270 20:&:duplex-list:num/raw <- next list2
271 list2 <- prev list2
272 30:num/raw <- first list2
273 40:bool/raw <- equal list, list2
274 ]
275 memory-should-contain [
276 10 <- 0
277 11 <- 5
278 12 <- 3
279 20 <- 0
280 30 <- 5
281 40 <- 1
282 ]
283 ]
284
285 scenario removing-from-start-of-duplex-list [
286 local-scope
287 list:&:duplex-list:num <- push 3, null
288 list <- push 4, list
289 list <- push 5, list
290 run [
291 list <- remove list, list
292
293 list2:&:duplex-list:num <- copy list
294 10:num/raw <- first list2
295 list2 <- next list2
296 11:num/raw <- first list2
297 20:&:duplex-list:num/raw <- next list2
298 list2 <- prev list2
299 30:num/raw <- first list2
300 40:bool/raw <- equal list, list2
301 ]
302 memory-should-contain [
303 10 <- 4
304 11 <- 3
305 20 <- 0
306 30 <- 4
307 40 <- 1
308 ]
309 ]
310
311 scenario removing-from-end-of-duplex-list [
312 local-scope
313 list:&:duplex-list:num <- push 3, null
314 list <- push 4, list
315 list <- push 5, list
316 run [
317
318 list2:&:duplex-list:num <- next list
319 list2 <- next list2
320 list <- remove list2, list
321 10:bool/raw <- equal list2, null
322
323 list2 <- copy list
324 11:num/raw <- first list2
325 list2 <- next list2
326 12:num/raw <- first list2
327 20:&:duplex-list:num/raw <- next list2
328 list2 <- prev list2
329 30:num/raw <- first list2
330 40:bool/raw <- equal list, list2
331 ]
332 memory-should-contain [
333 10 <- 0
334 11 <- 5
335 12 <- 4
336 20 <- 0
337 30 <- 5
338 40 <- 1
339 ]
340 ]
341
342 scenario removing-from-singleton-duplex-list [
343 local-scope
344 list:&:duplex-list:num <- push 3, null
345 run [
346 list <- remove list, list
347 1:num/raw <- deaddress list
348 ]
349 memory-should-contain [
350 1 <- 0
351 ]
352 ]
353
354 def remove x:&:duplex-list:_elem/contained-in:in, n:num, in:&:duplex-list:_elem -> in:&:duplex-list:_elem [
355 local-scope
356 load-inputs
357 i:num <- copy 0
358 curr:&:duplex-list:_elem <- copy x
359 {
360 done?:bool <- greater-or-equal i, n
361 break-if done?
362 break-unless curr
363 next:&:duplex-list:_elem <- next curr
364 in <- remove curr, in
365 curr <- copy next
366 i <- add i, 1
367 loop
368 }
369 ]
370
371 scenario removing-multiple-from-duplex-list [
372 local-scope
373 list:&:duplex-list:num <- push 3, null
374 list <- push 4, list
375 list <- push 5, list
376 run [
377 list2:&:duplex-list:num <- next list
378 list <- remove list2, 2, list
379 stash list
380 ]
381 trace-should-contain [
382 app: 5
383 ]
384 ]
385
386
387
388
389
390
391 def remove-between start:&:duplex-list:_elem, end:&:duplex-list:_elem/contained-in:start -> start:&:duplex-list:_elem [
392 local-scope
393 load-inputs
394 next:&:duplex-list:_elem <- get *start, next:offset
395 nothing-to-delete?:bool <- equal next, end
396 return-if nothing-to-delete?
397 assert next, [malformed duplex list]
398
399
400 *next <- put *next, prev:offset, null
401 *start <- put *start, next:offset, end
402 {
403 break-if end
404 stash [spliced:] next
405 return
406 }
407
408
409 prev:&:duplex-list:_elem <- get *end, prev:offset
410 assert prev, [malformed duplex list - 2]
411 *prev <- put *prev, next:offset, null
412 stash [spliced:] next
413 *end <- put *end, prev:offset, start
414 ]
415
416 scenario remove-range [
417
418 local-scope
419 list:&:duplex-list:num <- push 18, null
420 list <- push 17, list
421 list <- push 16, list
422 list <- push 15, list
423 list <- push 14, list
424 list <- push 13, list
425 run [
426
427
428 list2:&:duplex-list:num <- next list
429 list2 <- next list2
430 list2 <- remove-between list2, null
431
432 10:num/raw <- get *list, value:offset
433 list <- next list
434 11:num/raw <- get *list, value:offset
435 list <- next list
436 12:num/raw <- get *list, value:offset
437 20:&:duplex-list:num/raw <- next list
438 ]
439 memory-should-contain [
440 10 <- 13
441 11 <- 14
442 12 <- 15
443 20 <- 0
444 ]
445 trace-should-contain [
446 app: spliced: 16 <-> 17 <-> 18
447 ]
448 ]
449
450 scenario remove-range-to-final [
451 local-scope
452
453 list:&:duplex-list:num <- push 18, null
454 list <- push 17, list
455 list <- push 16, list
456 list <- push 15, list
457 list <- push 14, list
458 list <- push 13, list
459 run [
460
461
462 list2:&:duplex-list:num <- next list
463
464 end:&:duplex-list:num <- next list2
465 end <- next end
466 end <- next end
467 end <- next end
468 remove-between list2, end
469
470 10:num/raw <- get *list, value:offset
471 list <- next list
472 11:num/raw <- get *list, value:offset
473 list <- next list
474 12:num/raw <- get *list, value:offset
475 20:&:duplex-list:num/raw <- next list
476 ]
477 memory-should-contain [
478 10 <- 13
479 11 <- 14
480 12 <- 18
481 20 <- 0
482 ]
483 trace-should-contain [
484 app: spliced: 15 <-> 16 <-> 17
485 ]
486 ]
487
488 scenario remove-range-to-penultimate [
489 local-scope
490
491 list:&:duplex-list:num <- push 18, null
492 list <- push 17, list
493 list <- push 16, list
494 list <- push 15, list
495 list <- push 14, list
496 list <- push 13, list
497 run [
498
499
500 list2:&:duplex-list:num <- next list
501
502 end:&:duplex-list:num <- next list2
503 end <- next end
504 end <- next end
505 remove-between list2, end
506
507 10:num/raw <- get *list, value:offset
508 list <- next list
509 11:num/raw <- get *list, value:offset
510 list <- next list
511 12:num/raw <- get *list, value:offset
512 list <- next list
513 13:num/raw <- get *list, value:offset
514 20:&:duplex-list:num/raw <- next list
515 ]
516 memory-should-contain [
517 10 <- 13
518 11 <- 14
519 12 <- 17
520 13 <- 18
521 20 <- 0
522 ]
523 trace-should-contain [
524 app: spliced: 15 <-> 16
525 ]
526 ]
527
528 scenario remove-range-empty [
529 local-scope
530
531 list:&:duplex-list:num <- push 15, null
532 list <- push 14, list
533 list <- push 13, list
534 run [
535
536 list2:&:duplex-list:num <- next list
537 remove-between list, list2
538
539 10:num/raw <- get *list, value:offset
540 list <- next list
541 11:num/raw <- get *list, value:offset
542 list <- next list
543 12:num/raw <- get *list, value:offset
544 20:&:duplex-list:num/raw <- next list
545 ]
546
547 memory-should-contain [
548 10 <- 13
549 11 <- 14
550 12 <- 15
551 20 <- 0
552 ]
553 ]
554
555 scenario remove-range-to-end [
556 local-scope
557
558 list:&:duplex-list:num <- push 18, null
559 list <- push 17, list
560 list <- push 16, list
561 list <- push 15, list
562 list <- push 14, list
563 list <- push 13, list
564 run [
565
566 list2:&:duplex-list:num <- next list
567 remove-between list2, null
568
569 10:num/raw <- get *list, value:offset
570 list <- next list
571 11:num/raw <- get *list, value:offset
572 20:&:duplex-list:num/raw <- next list
573 ]
574 memory-should-contain [
575 10 <- 13
576 11 <- 14
577 20 <- 0
578 ]
579 ]
580
581
582 def splice in:&:duplex-list:_elem, start:&:duplex-list:_elem/contained-in:in -> in:&:duplex-list:_elem [
583 local-scope
584 load-inputs
585 return-unless in
586 return-unless start
587 end:&:duplex-list:_elem <- last start
588 next:&:duplex-list:_elem <- next in
589 {
590 break-unless next
591 *end <- put *end, next:offset, next
592 *next <- put *next, prev:offset, end
593 }
594 *in <- put *in, next:offset, start
595 *start <- put *start, prev:offset, in
596 ]
597
598
599 def insert in:&:duplex-list:_elem, new:&:@:_elem -> in:&:duplex-list:_elem [
600 local-scope
601 load-inputs
602 return-unless in
603 return-unless new
604 len:num <- length *new
605 return-unless len
606 curr:&:duplex-list:_elem <- copy in
607 idx:num <- copy 0
608 {
609 done?:bool <- greater-or-equal idx, len
610 break-if done?
611 c:_elem <- index *new, idx
612 insert c, curr
613
614 curr <- next curr
615 idx <- add idx, 1
616 loop
617 }
618 ]
619
620 def append in:&:duplex-list:_elem, new:&:duplex-list:_elem/contained-in:in -> in:&:duplex-list:_elem [
621 local-scope
622 load-inputs
623 last:&:duplex-list:_elem <- last in
624 *last <- put *last, next:offset, new
625 return-unless new
626 *new <- put *new, prev:offset, last
627 ]
628
629 def last in:&:duplex-list:_elem -> result:&:duplex-list:_elem [
630 local-scope
631 load-inputs
632 result <- copy in
633 {
634 next:&:duplex-list:_elem <- next result
635 break-unless next
636 result <- copy next
637 loop
638 }
639 ]
640
641
642 def match x:&:duplex-list:_elem, y:&:@:_elem -> result:bool [
643 local-scope
644 load-inputs
645 i:num <- copy 0
646 max:num <- length *y
647 {
648 done?:bool <- greater-or-equal i, max
649 break-if done?
650 expected:_elem <- index *y, i
651 return-unless x, false/no-match
652 curr:_elem <- first x
653 curr-matches?:bool <- equal curr, expected
654 return-unless curr-matches?, false/no-match
655 x <- next x
656 i <- add i, 1
657 loop
658 }
659 return true/successful-match
660 ]
661
662 scenario duplex-list-match [
663 local-scope
664 list:&:duplex-list:char <- push 97/a, null
665 list <- push 98/b, list
666 list <- push 99/c, list
667 list <- push 100/d, list
668 run [
669 10:bool/raw <- match list, []
670 11:bool/raw <- match list, [d]
671 12:bool/raw <- match list, [dc]
672 13:bool/raw <- match list, [dcba]
673 14:bool/raw <- match list, [dd]
674 15:bool/raw <- match list, [dcbax]
675 ]
676 memory-should-contain [
677 10 <- 1
678 11 <- 1
679 12 <- 1
680 13 <- 1
681 14 <- 0
682 15 <- 0
683 ]
684 ]
685
686
687 def dump-from x:&:duplex-list:_elem [
688 local-scope
689 load-inputs
690 $print x, [: ]
691 {
692 break-unless x
693 c:_elem <- get *x, value:offset
694 $print c, [ ]
695 x <- next x
696 {
697 is-newline?:bool <- equal c, 10/newline
698 break-unless is-newline?
699 $print 10/newline
700 $print x, [: ]
701 }
702 loop
703 }
704 $print 10/newline, [---], 10/newline
705 ]
706
707 scenario stash-duplex-list [
708 local-scope
709 list:&:duplex-list:num <- push 1, null
710 list <- push 2, list
711 list <- push 3, list
712 run [
713 stash [list:], list
714 ]
715 trace-should-contain [
716 app: list: 3 <-> 2 <-> 1
717 ]
718 ]
719
720 def to-text in:&:duplex-list:_elem -> result:text [
721 local-scope
722 load-inputs
723 buf:&:buffer:char <- new-buffer 80
724 buf <- to-buffer in, buf
725 result <- buffer-to-array buf
726 ]
727
728
729 def to-text-line in:&:duplex-list:_elem -> result:text [
730 local-scope
731 load-inputs
732 buf:&:buffer:char <- new-buffer 80
733 buf <- to-buffer in, buf, 6
734 result <- buffer-to-array buf
735 ]
736
737 def to-buffer in:&:duplex-list:_elem, buf:&:buffer:char -> buf:&:buffer:char [
738 local-scope
739 load-inputs
740 {
741 break-if in
742 buf <- append buf, [[]]
743 return
744 }
745
746 val:_elem <- get *in, value:offset
747 buf <- append buf, val
748
749 next:&:duplex-list:_elem <- next in
750 nextn:num <- deaddress next
751 return-unless next
752 buf <- append buf, [ <-> ]
753
754 remaining:num, optional-input-found?:bool <- next-input
755 {
756 break-if optional-input-found?
757
758 buf <- to-buffer next, buf
759 return
760 }
761 {
762 break-unless remaining
763
764 remaining <- subtract remaining, 1
765 buf <- to-buffer next, buf, remaining
766 return
767 }
768
769 append buf, [...]
770 ]
771
772 scenario stash-empty-duplex-list [
773 local-scope
774 x:&:duplex-list:num <- copy null
775 run [
776 stash x
777 ]
778 trace-should-contain [
779 app: []
780 ]
781 ]