2015-06-18 17:21:38 +00:00
|
|
|
# A doubly linked list permits bidirectional traversal.
|
|
|
|
|
2015-11-05 08:12:18 +00:00
|
|
|
container duplex-list:_elem [
|
|
|
|
value:_elem
|
2016-09-17 19:55:10 +00:00
|
|
|
next:&:duplex-list:_elem
|
|
|
|
prev:&:duplex-list:_elem
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
|
|
|
|
2017-09-26 04:20:49 +00:00
|
|
|
def push x:_elem, in:&:duplex-list:_elem/contained-in:result -> result:&:duplex-list:_elem [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2016-09-17 19:55:10 +00:00
|
|
|
result:&:duplex-list:_elem <- new {(duplex-list _elem): type}
|
2018-06-17 18:20:53 +00:00
|
|
|
*result <- merge x, in, null
|
2017-09-26 04:20:49 +00:00
|
|
|
return-unless in
|
|
|
|
put *in, prev:offset, result
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
|
|
|
|
2016-09-17 19:55:10 +00:00
|
|
|
def first in:&:duplex-list:_elem -> result:_elem [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2018-06-17 18:20:53 +00:00
|
|
|
{
|
|
|
|
break-if in
|
|
|
|
zero:&:_elem <- new _elem:type
|
|
|
|
zero-result:_elem <- copy *zero
|
|
|
|
abandon zero
|
|
|
|
return zero-result
|
|
|
|
}
|
2015-11-05 08:12:18 +00:00
|
|
|
result <- get *in, value:offset
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
|
|
|
|
2016-09-17 19:55:10 +00:00
|
|
|
def next in:&:duplex-list:_elem -> result:&:duplex-list:_elem/contained-in:in [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2018-06-17 18:20:53 +00:00
|
|
|
return-unless in, null
|
2015-11-05 08:12:18 +00:00
|
|
|
result <- get *in, next:offset
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
|
|
|
|
2016-09-17 19:55:10 +00:00
|
|
|
def prev in:&:duplex-list:_elem -> result:&:duplex-list:_elem/contained-in:in [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2018-06-17 18:20:53 +00:00
|
|
|
return-unless in, null
|
2015-11-05 08:12:18 +00:00
|
|
|
result <- get *in, prev:offset
|
2016-03-08 09:30:14 +00:00
|
|
|
return result
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario duplex-list-handling [
|
|
|
|
run [
|
2016-06-05 15:38:00 +00:00
|
|
|
local-scope
|
2016-05-26 23:46:38 +00:00
|
|
|
# reserve locations 0-9 to check for missing null check
|
2016-09-17 17:28:25 +00:00
|
|
|
10:num/raw <- copy 34
|
|
|
|
11:num/raw <- copy 35
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- copy list
|
|
|
|
20:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
21:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
22:num/raw <- first list2
|
|
|
|
30:&:duplex-list:num/raw <- next list2
|
|
|
|
31:num/raw <- first 30:&:duplex-list:num/raw
|
|
|
|
32:&:duplex-list:num/raw <- next 30:&:duplex-list:num/raw
|
|
|
|
33:&:duplex-list:num/raw <- prev 30:&:duplex-list:num/raw
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
40:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
41:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
50:bool/raw <- equal list, list2
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2015-06-20 04:17:00 +00:00
|
|
|
0 <- 0 # no modifications to null pointers
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 34
|
|
|
|
11 <- 35
|
|
|
|
20 <- 5 # scanning next
|
|
|
|
21 <- 4
|
|
|
|
22 <- 3
|
|
|
|
30 <- 0 # null
|
|
|
|
31 <- 0 # first of null
|
|
|
|
32 <- 0 # next of null
|
|
|
|
33 <- 0 # prev of null
|
|
|
|
40 <- 4 # then start scanning prev
|
|
|
|
41 <- 5
|
|
|
|
50 <- 1 # list back at start
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
2017-03-31 15:11:45 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
def length l:&:duplex-list:_elem -> result:num [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-03-31 15:11:45 +00:00
|
|
|
result <- copy 0
|
|
|
|
{
|
|
|
|
break-unless l
|
|
|
|
result <- add result, 1
|
|
|
|
l <- next l
|
|
|
|
loop
|
|
|
|
}
|
2015-06-18 17:21:38 +00:00
|
|
|
]
|
2015-06-18 17:41:14 +00:00
|
|
|
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# insert 'x' after 'in'
|
2016-09-17 19:55:10 +00:00
|
|
|
def insert x:_elem, in:&:duplex-list:_elem -> in:&:duplex-list:_elem [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2016-09-17 19:55:10 +00:00
|
|
|
new-node:&:duplex-list:_elem <- new {(duplex-list _elem): type}
|
2016-04-16 18:31:33 +00:00
|
|
|
*new-node <- put *new-node, value:offset, x
|
|
|
|
# save old next before changing it
|
2016-09-17 19:55:10 +00:00
|
|
|
next-node:&:duplex-list:_elem <- get *in, next:offset
|
2016-04-16 18:31:33 +00:00
|
|
|
*in <- put *in, next:offset, new-node
|
|
|
|
*new-node <- put *new-node, prev:offset, in
|
|
|
|
*new-node <- put *new-node, next:offset, next-node
|
2016-03-08 09:30:14 +00:00
|
|
|
return-unless next-node
|
2016-04-16 18:31:33 +00:00
|
|
|
*next-node <- put *next-node, prev:offset, new-node
|
2015-06-18 17:41:14 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario inserting-into-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-09-29 02:48:56 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2015-06-18 17:41:14 +00:00
|
|
|
run [
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list # inside list
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- insert 6, list2
|
2015-06-18 17:41:14 +00:00
|
|
|
# check structure like before
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- copy list
|
2017-05-29 08:37:35 +00:00
|
|
|
10:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
13:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
20:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
21:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
22:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
30:bool/raw <- equal list, list2
|
2015-06-18 17:41:14 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 5 # scanning next
|
|
|
|
11 <- 4
|
|
|
|
12 <- 6 # inserted element
|
|
|
|
13 <- 3
|
|
|
|
20 <- 6 # then prev
|
|
|
|
21 <- 4
|
|
|
|
22 <- 5
|
|
|
|
30 <- 1 # list back at start
|
2015-06-18 17:41:14 +00:00
|
|
|
]
|
|
|
|
]
|
2015-06-18 17:50:54 +00:00
|
|
|
|
|
|
|
scenario inserting-at-end-of-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-09-29 02:48:56 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2015-06-18 17:50:54 +00:00
|
|
|
run [
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list # inside list
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2 # now at end of list
|
|
|
|
list2 <- insert 6, list2
|
2015-06-18 17:50:54 +00:00
|
|
|
# check structure like before
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- copy list
|
2017-05-29 08:37:35 +00:00
|
|
|
10:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
13:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
20:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
21:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
22:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
30:bool/raw <- equal list, list2
|
2015-06-18 17:50:54 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 5 # scanning next
|
|
|
|
11 <- 4
|
|
|
|
12 <- 3
|
|
|
|
13 <- 6 # inserted element
|
|
|
|
20 <- 3 # then prev
|
|
|
|
21 <- 4
|
|
|
|
22 <- 5
|
|
|
|
30 <- 1 # list back at start
|
2015-06-18 17:50:54 +00:00
|
|
|
]
|
|
|
|
]
|
2015-06-18 17:53:01 +00:00
|
|
|
|
|
|
|
scenario inserting-after-start-of-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-09-29 02:48:56 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2015-06-18 17:53:01 +00:00
|
|
|
run [
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- insert 6, list
|
2015-06-18 17:53:01 +00:00
|
|
|
# check structure like before
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- copy list
|
|
|
|
10:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
13:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
20:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
21:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
22:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
30:bool/raw <- equal list, list2
|
2015-06-18 17:53:01 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 5 # scanning next
|
|
|
|
11 <- 6 # inserted element
|
|
|
|
12 <- 4
|
|
|
|
13 <- 3
|
|
|
|
20 <- 4 # then prev
|
|
|
|
21 <- 6
|
|
|
|
22 <- 5
|
|
|
|
30 <- 1 # list back at start
|
2015-06-18 17:53:01 +00:00
|
|
|
]
|
|
|
|
]
|
2015-06-18 18:16:37 +00:00
|
|
|
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# remove 'x' from its surrounding list 'in'
|
2015-06-18 18:26:04 +00:00
|
|
|
#
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# Returns null if and only if list is empty. Beware: in that case any other
|
|
|
|
# pointers to the head are now invalid.
|
2016-09-17 19:55:10 +00:00
|
|
|
def remove x:&:duplex-list:_elem/contained-in:in, in:&:duplex-list:_elem -> in:&:duplex-list:_elem [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# if 'x' is null, return
|
2016-03-08 09:30:14 +00:00
|
|
|
return-unless x
|
2016-09-17 19:55:10 +00:00
|
|
|
next-node:&:duplex-list:_elem <- get *x, next:offset
|
|
|
|
prev-node:&:duplex-list:_elem <- get *x, prev:offset
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# null x's pointers
|
2018-06-17 18:20:53 +00:00
|
|
|
*x <- put *x, next:offset, null
|
|
|
|
*x <- put *x, prev:offset, null
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# if next-node is not null, set its prev pointer
|
2015-06-18 18:16:37 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
break-unless next-node
|
2016-04-16 18:31:33 +00:00
|
|
|
*next-node <- put *next-node, prev:offset, prev-node
|
2015-06-18 18:16:37 +00:00
|
|
|
}
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# if prev-node is not null, set its next pointer and return
|
2015-06-18 18:16:37 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
break-unless prev-node
|
2016-04-16 18:31:33 +00:00
|
|
|
*prev-node <- put *prev-node, next:offset, next-node
|
2016-03-08 09:30:14 +00:00
|
|
|
return
|
2015-06-18 18:16:37 +00:00
|
|
|
}
|
2016-04-16 18:31:33 +00:00
|
|
|
# if prev-node is null, then we removed the head node at 'in'
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
# return the new head rather than the old 'in'
|
2016-03-08 09:30:14 +00:00
|
|
|
return next-node
|
2015-06-18 18:16:37 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario removing-from-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-09-29 02:48:56 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2015-06-18 18:16:37 +00:00
|
|
|
run [
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list # second element
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- remove list2, list
|
2018-06-17 18:20:53 +00:00
|
|
|
10:bool/raw <- equal list2, null
|
2015-06-18 18:26:04 +00:00
|
|
|
# check structure like before
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- copy list
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- first list2
|
|
|
|
20:&:duplex-list:num/raw <- next list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
30:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
40:bool/raw <- equal list, list2
|
2015-06-18 18:26:04 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 0 # remove returned non-null
|
|
|
|
11 <- 5 # scanning next, skipping deleted element
|
|
|
|
12 <- 3
|
|
|
|
20 <- 0 # no more elements
|
|
|
|
30 <- 5 # prev of final element
|
|
|
|
40 <- 1 # list back at start
|
2015-06-18 18:26:04 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario removing-from-start-of-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-09-29 02:48:56 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2015-06-18 18:26:04 +00:00
|
|
|
run [
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- remove list, list
|
2015-06-18 18:16:37 +00:00
|
|
|
# check structure like before
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- copy list
|
|
|
|
10:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- first list2
|
|
|
|
20:&:duplex-list:num/raw <- next list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
30:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
40:bool/raw <- equal list, list2
|
2015-06-18 18:16:37 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 4 # scanning next, skipping deleted element
|
|
|
|
11 <- 3
|
|
|
|
20 <- 0 # no more elements
|
|
|
|
30 <- 4 # prev of final element
|
|
|
|
40 <- 1 # list back at start
|
2015-06-18 18:16:37 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-06-18 18:26:04 +00:00
|
|
|
scenario removing-from-end-of-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2016-09-29 02:48:56 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
2015-06-18 18:26:04 +00:00
|
|
|
run [
|
|
|
|
# delete last element
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
|
|
|
list <- remove list2, list
|
2018-06-17 18:20:53 +00:00
|
|
|
10:bool/raw <- equal list2, null
|
2015-06-18 18:26:04 +00:00
|
|
|
# check structure like before
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- copy list
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- first list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- first list2
|
|
|
|
20:&:duplex-list:num/raw <- next list2
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- prev list2
|
2017-05-29 08:37:35 +00:00
|
|
|
30:num/raw <- first list2
|
2016-09-17 17:30:24 +00:00
|
|
|
40:bool/raw <- equal list, list2
|
2015-06-18 18:26:04 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 0 # remove returned non-null
|
|
|
|
11 <- 5 # scanning next, skipping deleted element
|
|
|
|
12 <- 4
|
|
|
|
20 <- 0 # no more elements
|
|
|
|
30 <- 5 # prev of final element
|
|
|
|
40 <- 1 # list back at start
|
2015-06-18 18:26:04 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2016-06-17 00:02:08 +00:00
|
|
|
scenario removing-from-singleton-duplex-list [
|
2016-09-29 02:48:56 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2015-06-18 18:16:37 +00:00
|
|
|
run [
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- remove list, list
|
2018-06-17 06:19:59 +00:00
|
|
|
1:num/raw <- deaddress list
|
2015-06-18 18:16:37 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
experiment: treat pure ingredients as immutable
This isn't complete yet:
a) If you copy a variable the copy is not checked for mutations.
b) /contained-in might be a hack. It's needed because sometimes I want
to pass in two pointers to a data structure, say for deleting something
from a list. Both are conceptually the same structure, so it's
unnecessary for both to be products as well. There's also technical
reasons you *can't* return both, because if you pass in the same
variable to both ingredients (say you want to remove the first element
of a list), the products now must write to the same variable as well
(thanks to our earlier /same-as-ingredient constraint), and what value
gets written last is not something we want to be thinking about.
c) Even if we stick with /contained-in, it's ambiguous. I'm using it
sometimes to say "a belongs to b", sometimes to say "a _will_ belong to
b after the recipe returns. Perhaps that distinction doesn't matter.
We'll see.
d) Should we be encouraged to say ingredients are contained in products?
At the moment 'push' works only because of mu's incomplete analysis.
Once we fix a) above, it's unclear what the right header should be.
e) edit/ isn't close to working yet.
(No more commit numbers since I'm now starting a branch, and can't rely
on a stable ordering as I rebase. For the same reason I'm not including
the changes to .traces/, to minimize merge conflicts.)
2015-11-16 22:42:32 +00:00
|
|
|
1 <- 0 # back to an empty list
|
2015-06-18 18:16:37 +00:00
|
|
|
]
|
|
|
|
]
|
2015-08-27 04:42:26 +00:00
|
|
|
|
2017-05-29 08:37:35 +00:00
|
|
|
def remove x:&:duplex-list:_elem/contained-in:in, n:num, in:&:duplex-list:_elem -> in:&:duplex-list:_elem [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-05-29 08:37:35 +00:00
|
|
|
i:num <- copy 0
|
|
|
|
curr:&:duplex-list:_elem <- copy x
|
|
|
|
{
|
|
|
|
done?:bool <- greater-or-equal i, n
|
|
|
|
break-if done?
|
|
|
|
break-unless curr
|
|
|
|
next:&:duplex-list:_elem <- next curr
|
|
|
|
in <- remove curr, in
|
|
|
|
curr <- copy next
|
|
|
|
i <- add i, 1
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario removing-multiple-from-duplex-list [
|
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 3, null
|
2017-05-29 08:37:35 +00:00
|
|
|
list <- push 4, list
|
|
|
|
list <- push 5, list
|
|
|
|
run [
|
|
|
|
list2:&:duplex-list:num <- next list # second element
|
|
|
|
list <- remove list2, 2, list
|
|
|
|
stash list
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
app: 5
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2016-04-16 18:31:33 +00:00
|
|
|
# remove values between 'start' and 'end' (both exclusive).
|
|
|
|
# also clear pointers back out from start/end for hygiene.
|
|
|
|
# set end to 0 to delete everything past start.
|
|
|
|
# can't set start to 0 to delete everything before end, because there's no
|
|
|
|
# clean way to return the new head pointer.
|
2016-09-17 19:55:10 +00:00
|
|
|
def remove-between start:&:duplex-list:_elem, end:&:duplex-list:_elem/contained-in:start -> start:&:duplex-list:_elem [
|
2015-08-27 04:42:26 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2016-09-17 19:55:10 +00:00
|
|
|
next:&:duplex-list:_elem <- get *start, next:offset
|
2016-09-17 17:30:24 +00:00
|
|
|
nothing-to-delete?:bool <- equal next, end
|
2016-04-16 18:31:33 +00:00
|
|
|
return-if nothing-to-delete?
|
|
|
|
assert next, [malformed duplex list]
|
2015-08-27 07:26:57 +00:00
|
|
|
# start->next->prev = 0
|
|
|
|
# start->next = end
|
2018-06-17 18:20:53 +00:00
|
|
|
*next <- put *next, prev:offset, null
|
2016-04-16 18:31:33 +00:00
|
|
|
*start <- put *start, next:offset, end
|
2018-06-24 16:16:17 +00:00
|
|
|
{
|
|
|
|
break-if end
|
|
|
|
stash [spliced:] next
|
|
|
|
return
|
|
|
|
}
|
2015-08-27 07:26:57 +00:00
|
|
|
# end->prev->next = 0
|
|
|
|
# end->prev = start
|
2016-09-17 19:55:10 +00:00
|
|
|
prev:&:duplex-list:_elem <- get *end, prev:offset
|
2016-04-16 18:31:33 +00:00
|
|
|
assert prev, [malformed duplex list - 2]
|
2018-06-17 18:20:53 +00:00
|
|
|
*prev <- put *prev, next:offset, null
|
2018-06-24 16:16:17 +00:00
|
|
|
stash [spliced:] next
|
2016-04-16 18:31:33 +00:00
|
|
|
*end <- put *end, prev:offset, start
|
2015-08-27 04:42:26 +00:00
|
|
|
]
|
2015-08-27 07:26:57 +00:00
|
|
|
|
2015-09-01 19:38:21 +00:00
|
|
|
scenario remove-range [
|
|
|
|
# construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
|
2016-05-26 23:46:38 +00:00
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 18, null
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- push 17, list
|
|
|
|
list <- push 16, list
|
|
|
|
list <- push 15, list
|
|
|
|
list <- push 14, list
|
|
|
|
list <- push 13, list
|
2015-09-01 19:38:21 +00:00
|
|
|
run [
|
|
|
|
# delete 16 onwards
|
|
|
|
# first pointer: to the third element
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list
|
2016-05-26 23:46:38 +00:00
|
|
|
list2 <- next list2
|
2018-06-17 18:20:53 +00:00
|
|
|
list2 <- remove-between list2, null
|
2015-09-01 19:38:21 +00:00
|
|
|
# now check the list
|
2017-05-29 08:37:35 +00:00
|
|
|
10:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- get *list, value:offset
|
|
|
|
20:&:duplex-list:num/raw <- next list
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 13
|
|
|
|
11 <- 14
|
|
|
|
12 <- 15
|
|
|
|
20 <- 0
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
2018-06-24 16:16:17 +00:00
|
|
|
trace-should-contain [
|
|
|
|
app: spliced: 16 <-> 17 <-> 18
|
|
|
|
]
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
|
|
|
|
2016-04-16 18:31:33 +00:00
|
|
|
scenario remove-range-to-final [
|
2016-05-26 23:46:38 +00:00
|
|
|
local-scope
|
2015-09-01 19:38:21 +00:00
|
|
|
# construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 18, null
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- push 17, list
|
|
|
|
list <- push 16, list
|
|
|
|
list <- push 15, list
|
|
|
|
list <- push 14, list
|
|
|
|
list <- push 13, list
|
2015-09-01 19:38:21 +00:00
|
|
|
run [
|
|
|
|
# delete 15, 16 and 17
|
2016-04-16 18:31:33 +00:00
|
|
|
# start pointer: to the second element
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list
|
2016-04-16 18:31:33 +00:00
|
|
|
# end pointer: to the last (sixth) element
|
2017-05-29 08:37:35 +00:00
|
|
|
end:&:duplex-list:num <- next list2
|
2016-05-26 23:46:38 +00:00
|
|
|
end <- next end
|
|
|
|
end <- next end
|
|
|
|
end <- next end
|
|
|
|
remove-between list2, end
|
2015-09-01 19:38:21 +00:00
|
|
|
# now check the list
|
2017-05-29 08:37:35 +00:00
|
|
|
10:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- get *list, value:offset
|
|
|
|
20:&:duplex-list:num/raw <- next list
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 13
|
|
|
|
11 <- 14
|
|
|
|
12 <- 18
|
|
|
|
20 <- 0 # no more elements
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
2018-06-24 16:16:17 +00:00
|
|
|
trace-should-contain [
|
|
|
|
app: spliced: 15 <-> 16 <-> 17
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario remove-range-to-penultimate [
|
|
|
|
local-scope
|
|
|
|
# construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
|
|
|
|
list:&:duplex-list:num <- push 18, null
|
|
|
|
list <- push 17, list
|
|
|
|
list <- push 16, list
|
|
|
|
list <- push 15, list
|
|
|
|
list <- push 14, list
|
|
|
|
list <- push 13, list
|
|
|
|
run [
|
|
|
|
# delete 15 and 16
|
|
|
|
# start pointer: to the second element
|
|
|
|
list2:&:duplex-list:num <- next list
|
|
|
|
# end pointer: to the last (sixth) element
|
|
|
|
end:&:duplex-list:num <- next list2
|
|
|
|
end <- next end
|
|
|
|
end <- next end
|
|
|
|
remove-between list2, end
|
|
|
|
# now check the list
|
|
|
|
10:num/raw <- get *list, value:offset
|
|
|
|
list <- next list
|
|
|
|
11:num/raw <- get *list, value:offset
|
|
|
|
list <- next list
|
|
|
|
12:num/raw <- get *list, value:offset
|
|
|
|
list <- next list
|
|
|
|
13:num/raw <- get *list, value:offset
|
|
|
|
20:&:duplex-list:num/raw <- next list
|
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
10 <- 13
|
|
|
|
11 <- 14
|
|
|
|
12 <- 17
|
|
|
|
13 <- 18
|
|
|
|
20 <- 0 # no more elements
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
app: spliced: 15 <-> 16
|
|
|
|
]
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
|
|
|
|
2015-09-01 20:38:01 +00:00
|
|
|
scenario remove-range-empty [
|
2016-05-26 23:46:38 +00:00
|
|
|
local-scope
|
2016-04-16 18:31:33 +00:00
|
|
|
# construct a duplex list with three elements [13, 14, 15]
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 15, null
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- push 14, list
|
|
|
|
list <- push 13, list
|
2015-09-01 20:38:01 +00:00
|
|
|
run [
|
2016-04-16 18:31:33 +00:00
|
|
|
# delete between first and second element (i.e. nothing)
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list
|
2016-05-26 23:46:38 +00:00
|
|
|
remove-between list, list2
|
2015-09-01 20:38:01 +00:00
|
|
|
# now check the list
|
2017-05-29 08:37:35 +00:00
|
|
|
10:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
12:num/raw <- get *list, value:offset
|
|
|
|
20:&:duplex-list:num/raw <- next list
|
2016-04-16 18:31:33 +00:00
|
|
|
]
|
|
|
|
# no change
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 13
|
|
|
|
11 <- 14
|
|
|
|
12 <- 15
|
|
|
|
20 <- 0
|
2016-04-16 18:31:33 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario remove-range-to-end [
|
2016-05-26 23:46:38 +00:00
|
|
|
local-scope
|
2016-04-16 18:31:33 +00:00
|
|
|
# construct a duplex list with six elements [13, 14, 15, 16, 17, 18]
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 18, null
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- push 17, list
|
|
|
|
list <- push 16, list
|
|
|
|
list <- push 15, list
|
|
|
|
list <- push 14, list
|
|
|
|
list <- push 13, list
|
2016-04-16 18:31:33 +00:00
|
|
|
run [
|
|
|
|
# remove the third element and beyond
|
2017-05-29 08:37:35 +00:00
|
|
|
list2:&:duplex-list:num <- next list
|
2018-06-17 18:20:53 +00:00
|
|
|
remove-between list2, null
|
2016-04-16 18:31:33 +00:00
|
|
|
# now check the list
|
2017-05-29 08:37:35 +00:00
|
|
|
10:num/raw <- get *list, value:offset
|
2016-05-26 23:46:38 +00:00
|
|
|
list <- next list
|
2017-05-29 08:37:35 +00:00
|
|
|
11:num/raw <- get *list, value:offset
|
|
|
|
20:&:duplex-list:num/raw <- next list
|
2015-09-01 20:38:01 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2016-05-26 23:46:38 +00:00
|
|
|
10 <- 13
|
|
|
|
11 <- 14
|
|
|
|
20 <- 0
|
2015-09-01 20:38:01 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2017-05-27 06:50:50 +00:00
|
|
|
# insert list beginning at 'start' after 'in'
|
|
|
|
def splice in:&:duplex-list:_elem, start:&:duplex-list:_elem/contained-in:in -> in:&:duplex-list:_elem [
|
2015-08-27 07:26:57 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2016-03-08 09:30:14 +00:00
|
|
|
return-unless in
|
|
|
|
return-unless start
|
2017-05-27 06:50:50 +00:00
|
|
|
end:&:duplex-list:_elem <- last start
|
2016-09-17 19:55:10 +00:00
|
|
|
next:&:duplex-list:_elem <- next in
|
2015-08-27 07:31:15 +00:00
|
|
|
{
|
|
|
|
break-unless next
|
2017-05-27 06:50:50 +00:00
|
|
|
*end <- put *end, next:offset, next
|
2016-04-16 18:31:33 +00:00
|
|
|
*next <- put *next, prev:offset, end
|
2015-08-27 07:31:15 +00:00
|
|
|
}
|
2016-04-16 18:31:33 +00:00
|
|
|
*in <- put *in, next:offset, start
|
|
|
|
*start <- put *start, prev:offset, in
|
2015-08-27 07:26:57 +00:00
|
|
|
]
|
|
|
|
|
2017-05-27 07:17:50 +00:00
|
|
|
# insert contents of 'new' after 'in'
|
|
|
|
def insert in:&:duplex-list:_elem, new:&:@:_elem -> in:&:duplex-list:_elem [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-05-27 07:17:50 +00:00
|
|
|
return-unless in
|
|
|
|
return-unless new
|
|
|
|
len:num <- length *new
|
|
|
|
return-unless len
|
|
|
|
curr:&:duplex-list:_elem <- copy in
|
|
|
|
idx:num <- copy 0
|
|
|
|
{
|
|
|
|
done?:bool <- greater-or-equal idx, len
|
|
|
|
break-if done?
|
|
|
|
c:_elem <- index *new, idx
|
|
|
|
insert c, curr
|
|
|
|
# next iter
|
|
|
|
curr <- next curr
|
|
|
|
idx <- add idx, 1
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
2016-09-17 19:55:10 +00:00
|
|
|
def append in:&:duplex-list:_elem, new:&:duplex-list:_elem/contained-in:in -> in:&:duplex-list:_elem [
|
2015-09-01 19:38:21 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2016-09-17 19:55:10 +00:00
|
|
|
last:&:duplex-list:_elem <- last in
|
2016-04-16 18:31:33 +00:00
|
|
|
*last <- put *last, next:offset, new
|
2016-03-08 09:30:14 +00:00
|
|
|
return-unless new
|
2016-04-16 18:31:33 +00:00
|
|
|
*new <- put *new, prev:offset, last
|
2015-09-01 19:38:21 +00:00
|
|
|
]
|
|
|
|
|
2016-09-17 19:55:10 +00:00
|
|
|
def last in:&:duplex-list:_elem -> result:&:duplex-list:_elem [
|
2015-09-01 05:25:24 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2015-11-05 08:12:18 +00:00
|
|
|
result <- copy in
|
2015-09-01 05:25:24 +00:00
|
|
|
{
|
2016-09-17 19:55:10 +00:00
|
|
|
next:&:duplex-list:_elem <- next result
|
2015-09-01 05:25:24 +00:00
|
|
|
break-unless next
|
|
|
|
result <- copy next
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
2017-05-29 08:47:54 +00:00
|
|
|
# does a duplex list start with a certain sequence of elements?
|
|
|
|
def match x:&:duplex-list:_elem, y:&:@:_elem -> result:bool [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-05-29 08:47:54 +00:00
|
|
|
i:num <- copy 0
|
|
|
|
max:num <- length *y
|
|
|
|
{
|
|
|
|
done?:bool <- greater-or-equal i, max
|
|
|
|
break-if done?
|
|
|
|
expected:_elem <- index *y, i
|
2018-06-17 07:05:38 +00:00
|
|
|
return-unless x, false/no-match
|
2017-05-29 08:47:54 +00:00
|
|
|
curr:_elem <- first x
|
|
|
|
curr-matches?:bool <- equal curr, expected
|
2018-06-17 07:05:38 +00:00
|
|
|
return-unless curr-matches?, false/no-match
|
2017-05-29 08:47:54 +00:00
|
|
|
x <- next x
|
|
|
|
i <- add i, 1
|
|
|
|
loop
|
|
|
|
}
|
2018-06-17 07:05:38 +00:00
|
|
|
return true/successful-match
|
2017-05-29 08:47:54 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario duplex-list-match [
|
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:char <- push 97/a, null
|
2017-05-29 08:47:54 +00:00
|
|
|
list <- push 98/b, list
|
|
|
|
list <- push 99/c, list
|
|
|
|
list <- push 100/d, list
|
|
|
|
run [
|
|
|
|
10:bool/raw <- match list, []
|
|
|
|
11:bool/raw <- match list, [d]
|
|
|
|
12:bool/raw <- match list, [dc]
|
|
|
|
13:bool/raw <- match list, [dcba]
|
|
|
|
14:bool/raw <- match list, [dd]
|
|
|
|
15:bool/raw <- match list, [dcbax]
|
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
10 <- 1 # matches []
|
|
|
|
11 <- 1 # matches [d]
|
|
|
|
12 <- 1 # matches [dc]
|
|
|
|
13 <- 1 # matches [dcba]
|
|
|
|
14 <- 0 # does not match [dd]
|
|
|
|
15 <- 0 # does not match [dcbax]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-08-27 07:26:57 +00:00
|
|
|
# helper for debugging
|
2016-09-17 19:55:10 +00:00
|
|
|
def dump-from x:&:duplex-list:_elem [
|
2015-08-27 07:26:57 +00:00
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2015-08-27 07:26:57 +00:00
|
|
|
$print x, [: ]
|
|
|
|
{
|
|
|
|
break-unless x
|
2015-11-05 08:12:18 +00:00
|
|
|
c:_elem <- get *x, value:offset
|
2015-08-27 07:26:57 +00:00
|
|
|
$print c, [ ]
|
2015-11-15 20:43:11 +00:00
|
|
|
x <- next x
|
2015-08-27 07:26:57 +00:00
|
|
|
{
|
2016-09-17 17:30:24 +00:00
|
|
|
is-newline?:bool <- equal c, 10/newline
|
2015-08-27 07:26:57 +00:00
|
|
|
break-unless is-newline?
|
|
|
|
$print 10/newline
|
|
|
|
$print x, [: ]
|
|
|
|
}
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
$print 10/newline, [---], 10/newline
|
|
|
|
]
|
2017-05-27 07:52:28 +00:00
|
|
|
|
|
|
|
scenario stash-duplex-list [
|
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
list:&:duplex-list:num <- push 1, null
|
2017-05-27 07:52:28 +00:00
|
|
|
list <- push 2, list
|
|
|
|
list <- push 3, list
|
|
|
|
run [
|
|
|
|
stash [list:], list
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
app: list: 3 <-> 2 <-> 1
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
def to-text in:&:duplex-list:_elem -> result:text [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-05-27 07:52:28 +00:00
|
|
|
buf:&:buffer:char <- new-buffer 80
|
|
|
|
buf <- to-buffer in, buf
|
|
|
|
result <- buffer-to-array buf
|
|
|
|
]
|
|
|
|
|
|
|
|
# variant of 'to-text' which stops printing after a few elements (and so is robust to cycles)
|
|
|
|
def to-text-line in:&:duplex-list:_elem -> result:text [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-05-27 07:52:28 +00:00
|
|
|
buf:&:buffer:char <- new-buffer 80
|
|
|
|
buf <- to-buffer in, buf, 6 # max elements to display
|
|
|
|
result <- buffer-to-array buf
|
|
|
|
]
|
|
|
|
|
|
|
|
def to-buffer in:&:duplex-list:_elem, buf:&:buffer:char -> buf:&:buffer:char [
|
|
|
|
local-scope
|
2017-12-04 07:25:40 +00:00
|
|
|
load-inputs
|
2017-05-27 07:52:28 +00:00
|
|
|
{
|
|
|
|
break-if in
|
|
|
|
buf <- append buf, [[]]
|
|
|
|
return
|
|
|
|
}
|
|
|
|
# append in.value to buf
|
|
|
|
val:_elem <- get *in, value:offset
|
|
|
|
buf <- append buf, val
|
|
|
|
# now prepare next
|
|
|
|
next:&:duplex-list:_elem <- next in
|
2018-06-17 06:19:59 +00:00
|
|
|
nextn:num <- deaddress next
|
2017-05-27 07:52:28 +00:00
|
|
|
return-unless next
|
|
|
|
buf <- append buf, [ <-> ]
|
|
|
|
# and recurse
|
2017-12-04 07:25:40 +00:00
|
|
|
remaining:num, optional-input-found?:bool <- next-input
|
2017-05-27 07:52:28 +00:00
|
|
|
{
|
2017-12-04 07:25:40 +00:00
|
|
|
break-if optional-input-found?
|
2017-05-27 07:52:28 +00:00
|
|
|
# unlimited recursion
|
|
|
|
buf <- to-buffer next, buf
|
|
|
|
return
|
|
|
|
}
|
|
|
|
{
|
|
|
|
break-unless remaining
|
|
|
|
# limited recursion
|
|
|
|
remaining <- subtract remaining, 1
|
|
|
|
buf <- to-buffer next, buf, remaining
|
|
|
|
return
|
|
|
|
}
|
|
|
|
# past recursion depth; insert ellipses and stop
|
|
|
|
append buf, [...]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario stash-empty-duplex-list [
|
|
|
|
local-scope
|
2018-06-17 18:20:53 +00:00
|
|
|
x:&:duplex-list:num <- copy null
|
2017-05-27 07:52:28 +00:00
|
|
|
run [
|
|
|
|
stash x
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
app: []
|
|
|
|
]
|
|
|
|
]
|