2015-04-18 14:50:51 +00:00
|
|
|
# Some useful helpers for dealing with strings.
|
2015-04-17 18:22:59 +00:00
|
|
|
|
2015-04-03 19:53:33 +00:00
|
|
|
recipe string-equal [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-04-03 19:53:33 +00:00
|
|
|
a:address:array:character <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
a-len:number <- length *a
|
2015-04-03 19:53:33 +00:00
|
|
|
b:address:array:character <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
b-len:number <- length *b
|
2015-04-03 19:53:33 +00:00
|
|
|
# compare lengths
|
|
|
|
{
|
2015-04-12 05:24:33 +00:00
|
|
|
trace [string-equal], [comparing lengths]
|
2015-07-29 21:37:57 +00:00
|
|
|
length-equal?:boolean <- equal a-len, b-len
|
|
|
|
break-if length-equal?
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 0
|
2015-04-03 19:53:33 +00:00
|
|
|
}
|
|
|
|
# compare each corresponding character
|
2015-04-12 05:24:33 +00:00
|
|
|
trace [string-equal], [comparing characters]
|
2015-07-28 21:33:22 +00:00
|
|
|
i:number <- copy 0
|
2015-04-03 19:53:33 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-or-equal i, a-len
|
|
|
|
break-if done?
|
|
|
|
a2:character <- index *a, i
|
|
|
|
b2:character <- index *b, i
|
2015-04-03 19:53:33 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
chars-match?:boolean <- equal a2, b2
|
|
|
|
break-if chars-match?
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 0
|
2015-04-03 19:53:33 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
i <- add i, 1
|
2015-04-03 19:53:33 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 1
|
2015-04-03 19:53:33 +00:00
|
|
|
]
|
2015-04-06 19:02:38 +00:00
|
|
|
|
|
|
|
scenario string-equal-reflexive [
|
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30
|
2015-04-06 19:02:38 +00:00
|
|
|
x:address:array:character <- new [abc]
|
2015-07-29 21:37:57 +00:00
|
|
|
3:boolean/raw <- string-equal x, x
|
2015-04-06 19:02:38 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-06 19:02:38 +00:00
|
|
|
3 <- 1 # x == x for all x
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-04-08 07:47:04 +00:00
|
|
|
scenario string-equal-identical [
|
2015-04-06 19:02:38 +00:00
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30
|
2015-04-06 19:02:38 +00:00
|
|
|
x:address:array:character <- new [abc]
|
|
|
|
y:address:array:character <- new [abc]
|
2015-07-29 21:37:57 +00:00
|
|
|
3:boolean/raw <- string-equal x, y
|
2015-04-06 19:02:38 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-08 07:47:04 +00:00
|
|
|
3 <- 1 # abc == abc
|
2015-04-06 19:02:38 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-04-08 07:47:04 +00:00
|
|
|
scenario string-equal-distinct-lengths [
|
2015-04-06 19:02:38 +00:00
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30
|
2015-04-06 19:02:38 +00:00
|
|
|
x:address:array:character <- new [abc]
|
|
|
|
y:address:array:character <- new [abcd]
|
2015-07-29 21:37:57 +00:00
|
|
|
3:boolean/raw <- string-equal x, y
|
2015-04-06 19:02:38 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-08 07:47:04 +00:00
|
|
|
3 <- 0 # abc != abcd
|
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
trace-should-contain [
|
2015-04-13 17:09:46 +00:00
|
|
|
string-equal: comparing lengths
|
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
trace-should-not-contain [
|
2015-04-13 17:09:46 +00:00
|
|
|
string-equal: comparing characters
|
|
|
|
]
|
2015-04-08 07:47:04 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-equal-with-empty [
|
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30
|
2015-04-08 07:47:04 +00:00
|
|
|
x:address:array:character <- new []
|
|
|
|
y:address:array:character <- new [abcd]
|
2015-07-29 21:37:57 +00:00
|
|
|
3:boolean/raw <- string-equal x, y
|
2015-04-08 07:47:04 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-08 07:47:04 +00:00
|
|
|
3 <- 0 # "" != abcd
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-equal-common-lengths-but-distinct [
|
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30
|
2015-04-08 07:47:04 +00:00
|
|
|
x:address:array:character <- new [abc]
|
|
|
|
y:address:array:character <- new [abd]
|
2015-07-29 21:37:57 +00:00
|
|
|
3:boolean/raw <- string-equal x, y
|
2015-04-08 07:47:04 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-06 19:02:38 +00:00
|
|
|
3 <- 0 # abc != abd
|
|
|
|
]
|
|
|
|
]
|
2015-04-18 03:36:25 +00:00
|
|
|
|
|
|
|
# A new type to help incrementally construct strings.
|
|
|
|
container buffer [
|
2015-05-13 17:03:26 +00:00
|
|
|
length:number
|
2015-04-18 03:36:25 +00:00
|
|
|
data:address:array:character
|
|
|
|
]
|
|
|
|
|
2015-06-19 23:34:11 +00:00
|
|
|
recipe new-buffer [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-07-28 21:33:22 +00:00
|
|
|
#? $print default-space:address:array:location, 10/newline
|
2015-04-18 03:36:25 +00:00
|
|
|
result:address:buffer <- new buffer:type
|
2015-07-29 21:37:57 +00:00
|
|
|
len:address:number <- get-address *result, length:offset
|
|
|
|
*len:address:number <- copy 0
|
|
|
|
s:address:address:array:character <- get-address *result, data:offset
|
2015-07-03 18:57:23 +00:00
|
|
|
capacity:number, found?:boolean <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
assert found?, [new-buffer must get a capacity argument]
|
|
|
|
*s <- new character:type, capacity
|
|
|
|
#? $print *s, 10/newline
|
|
|
|
reply result
|
2015-04-18 03:36:25 +00:00
|
|
|
]
|
2015-04-18 04:51:13 +00:00
|
|
|
|
|
|
|
recipe grow-buffer [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-04-18 04:51:13 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
|
|
|
# double buffer size
|
2015-07-29 21:37:57 +00:00
|
|
|
x:address:address:array:character <- get-address *in, data:offset
|
|
|
|
oldlen:number <- length **x
|
|
|
|
newlen:number <- multiply oldlen, 2
|
|
|
|
olddata:address:array:character <- copy *x
|
|
|
|
*x <- new character:type, newlen
|
2015-04-18 04:51:13 +00:00
|
|
|
# copy old contents
|
2015-07-28 21:33:22 +00:00
|
|
|
i:number <- copy 0
|
2015-04-18 04:51:13 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-or-equal i, oldlen
|
|
|
|
break-if done?
|
|
|
|
src:character <- index *olddata, i
|
|
|
|
dest:address:character <- index-address **x, i
|
|
|
|
*dest <- copy src
|
|
|
|
i <- add i, 1
|
2015-04-18 04:51:13 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply in
|
2015-04-18 04:51:13 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
recipe buffer-full? [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-04-18 04:51:13 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- get *in, length:offset
|
|
|
|
s:address:array:character <- get *in, data:offset
|
|
|
|
capacity:number <- length *s
|
|
|
|
result:boolean <- greater-or-equal len, capacity
|
|
|
|
reply result
|
2015-04-18 04:51:13 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
# in:address:buffer <- buffer-append in:address:buffer, c:character
|
|
|
|
recipe buffer-append [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-04-18 04:51:13 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
|
|
|
c:character <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
len:address:number <- get-address *in, length:offset
|
2015-06-01 06:44:52 +00:00
|
|
|
{
|
|
|
|
# backspace? just drop last character if it exists and return
|
2015-07-29 21:37:57 +00:00
|
|
|
backspace?:boolean <- equal c, 8/backspace
|
|
|
|
break-unless backspace?
|
|
|
|
empty?:boolean <- lesser-or-equal *len, 0
|
|
|
|
reply-if empty?, in/same-as-ingredient:0
|
|
|
|
*len <- subtract *len, 1
|
|
|
|
reply in/same-as-ingredient:0
|
2015-06-01 06:44:52 +00:00
|
|
|
}
|
2015-04-18 04:51:13 +00:00
|
|
|
{
|
|
|
|
# grow buffer if necessary
|
2015-07-29 21:37:57 +00:00
|
|
|
full?:boolean <- buffer-full? in
|
|
|
|
break-unless full?
|
|
|
|
in <- grow-buffer in
|
2015-04-18 04:51:13 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
s:address:array:character <- get *in, data:offset
|
|
|
|
#? $print [array underlying buf: ], s, 10/newline
|
|
|
|
#? $print [index: ], *len, 10/newline
|
|
|
|
dest:address:character <- index-address *s, *len
|
|
|
|
#? $print [storing ], c, [ in ], dest, 10/newline
|
|
|
|
*dest <- copy c
|
|
|
|
*len <- add *len, 1
|
|
|
|
reply in/same-as-ingredient:0
|
2015-04-18 04:51:13 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario buffer-append-works [
|
|
|
|
run [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-07-28 21:33:22 +00:00
|
|
|
x:address:buffer <- new-buffer 3
|
2015-07-29 21:37:57 +00:00
|
|
|
s1:address:array:character <- get *x:address:buffer, data:offset
|
2015-07-28 21:33:22 +00:00
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 97 # 'a'
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 98 # 'b'
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 99 # 'c'
|
2015-07-29 21:37:57 +00:00
|
|
|
s2:address:array:character <- get *x:address:buffer, data:offset
|
2015-04-18 04:51:13 +00:00
|
|
|
1:boolean/raw <- equal s1:address:array:character, s2:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
2:array:character/raw <- copy *s2:address:array:character
|
2015-04-18 15:34:48 +00:00
|
|
|
+buffer-filled
|
2015-07-28 21:33:22 +00:00
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 100 # 'd'
|
2015-07-29 21:37:57 +00:00
|
|
|
s3:address:array:character <- get *x:address:buffer, data:offset
|
2015-04-18 15:34:48 +00:00
|
|
|
10:boolean/raw <- equal s1:address:array:character, s3:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
11:number/raw <- get *x:address:buffer, length:offset
|
|
|
|
12:array:character/raw <- copy *s3:address:array:character
|
2015-04-18 04:51:13 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-18 15:34:48 +00:00
|
|
|
# before +buffer-filled
|
2015-04-18 04:51:13 +00:00
|
|
|
1 <- 1 # no change in data pointer
|
|
|
|
2 <- 3 # size of data
|
|
|
|
3 <- 97 # data
|
|
|
|
4 <- 98
|
|
|
|
5 <- 99
|
2015-04-18 15:34:48 +00:00
|
|
|
# in the end
|
|
|
|
10 <- 0 # data pointer has grown
|
|
|
|
11 <- 4 # final length
|
|
|
|
12 <- 6 # but data's capacity has doubled
|
|
|
|
13 <- 97 # data
|
|
|
|
14 <- 98
|
|
|
|
15 <- 99
|
|
|
|
16 <- 100
|
|
|
|
17 <- 0
|
|
|
|
18 <- 0
|
2015-04-18 04:51:13 +00:00
|
|
|
]
|
|
|
|
]
|
2015-04-19 03:11:59 +00:00
|
|
|
|
2015-06-06 17:15:43 +00:00
|
|
|
scenario buffer-append-handles-backspace [
|
|
|
|
run [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-07-28 21:33:22 +00:00
|
|
|
x:address:buffer <- new-buffer 3
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 97 # 'a'
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 98 # 'b'
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 8/backspace
|
2015-06-06 17:15:43 +00:00
|
|
|
s:address:array:character <- buffer-to-array x:address:buffer
|
2015-07-29 21:37:57 +00:00
|
|
|
1:array:character/raw <- copy *s:address:array:character
|
2015-06-06 17:15:43 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
1 <- 1 # length
|
|
|
|
2 <- 97 # contents
|
|
|
|
3 <- 0
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-05-13 17:03:26 +00:00
|
|
|
# result:address:array:character <- integer-to-decimal-string n:number
|
2015-04-19 03:11:59 +00:00
|
|
|
recipe integer-to-decimal-string [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-05-13 17:03:26 +00:00
|
|
|
n:number <- next-ingredient
|
2015-04-19 03:11:59 +00:00
|
|
|
# is it zero?
|
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
break-if n
|
2015-04-19 03:11:59 +00:00
|
|
|
result:address:array:character <- new [0]
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-04-19 03:11:59 +00:00
|
|
|
}
|
|
|
|
# save sign
|
2015-07-28 21:33:22 +00:00
|
|
|
negate-result:boolean <- copy 0
|
2015-04-19 03:11:59 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
negative?:boolean <- lesser-than n, 0
|
|
|
|
break-unless negative?
|
|
|
|
negate-result <- copy 1
|
|
|
|
n <- multiply n, -1
|
2015-04-19 03:11:59 +00:00
|
|
|
}
|
|
|
|
# add digits from right to left into intermediate buffer
|
2015-07-28 21:33:22 +00:00
|
|
|
tmp:address:buffer <- new-buffer 30
|
|
|
|
digit-base:number <- copy 48 # '0'
|
2015-04-19 03:11:59 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- equal n, 0
|
|
|
|
break-if done?
|
|
|
|
n, digit:number <- divide-with-remainder n, 10
|
|
|
|
c:character <- add digit-base, digit
|
|
|
|
tmp:address:buffer <- buffer-append tmp, c
|
2015-04-19 03:11:59 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# add sign
|
|
|
|
{
|
|
|
|
break-unless negate-result:boolean
|
2015-07-29 21:37:57 +00:00
|
|
|
tmp <- buffer-append tmp, 45 # '-'
|
2015-04-19 03:11:59 +00:00
|
|
|
}
|
|
|
|
# reverse buffer into string result
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- get *tmp, length:offset
|
|
|
|
buf:address:array:character <- get *tmp, data:offset
|
|
|
|
result:address:array:character <- new character:type, len
|
|
|
|
i:number <- subtract len, 1 # source index, decreasing
|
|
|
|
j:number <- copy 0 # destination index, increasing
|
2015-04-19 03:11:59 +00:00
|
|
|
{
|
|
|
|
# while i >= 0
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- lesser-than i, 0
|
|
|
|
break-if done?
|
2015-04-19 03:11:59 +00:00
|
|
|
# result[j] = tmp[i]
|
2015-07-29 21:37:57 +00:00
|
|
|
src:character <- index *buf, i
|
|
|
|
dest:address:character <- index-address *result, j
|
|
|
|
*dest <- copy src
|
|
|
|
i <- subtract i, 1
|
|
|
|
j <- add j, 1
|
2015-04-19 03:11:59 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-04-19 03:11:59 +00:00
|
|
|
]
|
|
|
|
|
2015-05-27 21:56:01 +00:00
|
|
|
recipe buffer-to-array [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-05-27 21:56:01 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
|
|
|
{
|
|
|
|
# propagate null buffer
|
2015-07-29 21:37:57 +00:00
|
|
|
break-if in
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 0
|
2015-05-27 21:56:01 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- get *in, length:offset
|
|
|
|
#? $print [size ], len, 10/newline
|
|
|
|
s:address:array:character <- get *in, data:offset
|
2015-05-27 21:56:01 +00:00
|
|
|
# we can't just return s because it is usually the wrong length
|
2015-07-29 21:37:57 +00:00
|
|
|
result:address:array:character <- new character:type, len
|
2015-07-28 21:33:22 +00:00
|
|
|
i:number <- copy 0
|
2015-05-27 21:56:01 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
#? $print i #? 1
|
|
|
|
done?:boolean <- greater-or-equal i, len
|
|
|
|
break-if done?
|
|
|
|
src:character <- index *s, i
|
|
|
|
dest:address:character <- index-address *result, i
|
|
|
|
*dest <- copy src
|
|
|
|
i <- add i, 1
|
2015-05-27 21:56:01 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-05-27 21:56:01 +00:00
|
|
|
]
|
|
|
|
|
2015-04-19 03:11:59 +00:00
|
|
|
scenario integer-to-decimal-digit-zero [
|
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
1:address:array:character/raw <- integer-to-decimal-string 0
|
2015-07-29 21:37:57 +00:00
|
|
|
2:array:character/raw <- copy *1:address:array:character/raw
|
2015-04-19 03:11:59 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-21 05:20:39 +00:00
|
|
|
2:string <- [0]
|
2015-04-19 03:11:59 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario integer-to-decimal-digit-positive [
|
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
1:address:array:character/raw <- integer-to-decimal-string 234
|
2015-07-29 21:37:57 +00:00
|
|
|
2:array:character/raw <- copy *1:address:array:character/raw
|
2015-04-19 03:11:59 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-21 05:20:39 +00:00
|
|
|
2:string <- [234]
|
2015-04-19 03:11:59 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario integer-to-decimal-digit-negative [
|
|
|
|
run [
|
2015-07-28 21:33:22 +00:00
|
|
|
1:address:array:character/raw <- integer-to-decimal-string -1
|
2015-07-29 21:37:57 +00:00
|
|
|
2:array:character/raw <- copy *1:address:array:character/raw
|
2015-04-19 03:11:59 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-19 03:11:59 +00:00
|
|
|
2 <- 2
|
|
|
|
3 <- 45 # '-'
|
|
|
|
4 <- 49 # '1'
|
|
|
|
]
|
|
|
|
]
|
2015-04-19 07:13:08 +00:00
|
|
|
|
2015-05-23 19:35:10 +00:00
|
|
|
# result:address:array:character <- string-append a:address:array:character, b:address:array:character
|
2015-04-19 07:13:08 +00:00
|
|
|
recipe string-append [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-05-23 19:35:10 +00:00
|
|
|
# result = new character[a.length + b.length]
|
2015-04-19 07:13:08 +00:00
|
|
|
a:address:array:character <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
a-len:number <- length *a
|
2015-04-19 07:13:08 +00:00
|
|
|
b:address:array:character <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
b-len:number <- length *b
|
|
|
|
result-len:number <- add a-len, b-len
|
|
|
|
result:address:array:character <- new character:type, result-len
|
2015-04-19 07:13:08 +00:00
|
|
|
# copy a into result
|
2015-07-28 21:33:22 +00:00
|
|
|
result-idx:number <- copy 0
|
|
|
|
i:number <- copy 0
|
2015-04-19 07:13:08 +00:00
|
|
|
{
|
|
|
|
# while i < a.length
|
2015-07-29 21:37:57 +00:00
|
|
|
a-done?:boolean <- greater-or-equal i, a-len
|
|
|
|
break-if a-done?
|
2015-04-19 07:13:08 +00:00
|
|
|
# result[result-idx] = a[i]
|
2015-07-29 21:37:57 +00:00
|
|
|
out:address:character <- index-address *result, result-idx
|
|
|
|
in:character <- index *a, i
|
|
|
|
*out <- copy in
|
|
|
|
i <- add i, 1
|
|
|
|
result-idx <- add result-idx, 1
|
2015-04-19 07:13:08 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# copy b into result
|
2015-07-29 21:37:57 +00:00
|
|
|
i <- copy 0
|
2015-04-19 07:13:08 +00:00
|
|
|
{
|
|
|
|
# while i < b.length
|
2015-07-29 21:37:57 +00:00
|
|
|
b-done?:boolean <- greater-or-equal i, b-len
|
|
|
|
break-if b-done?
|
2015-04-19 07:13:08 +00:00
|
|
|
# result[result-idx] = a[i]
|
2015-07-29 21:37:57 +00:00
|
|
|
out:address:character <- index-address *result, result-idx
|
|
|
|
in:character <- index *b, i
|
|
|
|
*out <- copy in
|
|
|
|
i <- add i, 1
|
|
|
|
result-idx <- add result-idx, 1
|
2015-04-19 07:13:08 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-04-19 07:13:08 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-append-1 [
|
|
|
|
run [
|
|
|
|
1:address:array:character/raw <- new [hello,]
|
|
|
|
2:address:array:character/raw <- new [ world!]
|
|
|
|
3:address:array:character/raw <- string-append 1:address:array:character/raw, 2:address:array:character/raw
|
2015-07-29 21:37:57 +00:00
|
|
|
4:array:character/raw <- copy *3:address:array:character/raw
|
2015-04-19 07:13:08 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-21 05:20:39 +00:00
|
|
|
4:string <- [hello, world!]
|
2015-04-19 07:13:08 +00:00
|
|
|
]
|
|
|
|
]
|
2015-04-20 17:25:02 +00:00
|
|
|
|
|
|
|
# replace underscores in first with remaining args
|
|
|
|
# result:address:array:character <- interpolate template:address:array:character, ...
|
|
|
|
recipe interpolate [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-04-20 17:25:02 +00:00
|
|
|
template:address:array:character <- next-ingredient
|
|
|
|
# compute result-len, space to allocate for result
|
2015-07-29 21:37:57 +00:00
|
|
|
tem-len:number <- length *template
|
|
|
|
result-len:number <- copy tem-len
|
2015-04-20 17:25:02 +00:00
|
|
|
{
|
|
|
|
# while arg received
|
|
|
|
a:address:array:character, arg-received?:boolean <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
break-unless arg-received?
|
|
|
|
# result-len = result-len + arg.length - 1 (for the 'underscore' being replaced)
|
|
|
|
a-len:number <- length *a
|
|
|
|
result-len <- add result-len, a-len
|
|
|
|
result-len <- subtract result-len, 1
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
#? $print tem-len, [ ], $result-len, 10/newline
|
2015-04-20 17:25:02 +00:00
|
|
|
rewind-ingredients
|
|
|
|
_ <- next-ingredient # skip template
|
2015-07-29 21:37:57 +00:00
|
|
|
result:address:array:character <- new character:type, result-len
|
2015-04-20 17:25:02 +00:00
|
|
|
# repeatedly copy sections of template and 'holes' into result
|
2015-07-28 21:33:22 +00:00
|
|
|
result-idx:number <- copy 0
|
|
|
|
i:number <- copy 0
|
2015-04-20 17:25:02 +00:00
|
|
|
{
|
|
|
|
# while arg received
|
|
|
|
a:address:array:character, arg-received?:boolean <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
break-unless arg-received?
|
2015-04-20 17:25:02 +00:00
|
|
|
# copy template into result until '_'
|
|
|
|
{
|
|
|
|
# while i < template.length
|
2015-07-29 21:37:57 +00:00
|
|
|
tem-done?:boolean <- greater-or-equal i, tem-len
|
|
|
|
break-if tem-done?, +done:label
|
2015-04-20 17:25:02 +00:00
|
|
|
# while template[i] != '_'
|
2015-07-29 21:37:57 +00:00
|
|
|
in:character <- index *template, i
|
|
|
|
underscore?:boolean <- equal in, 95/_
|
|
|
|
break-if underscore?
|
2015-04-20 17:25:02 +00:00
|
|
|
# result[result-idx] = template[i]
|
2015-07-29 21:37:57 +00:00
|
|
|
out:address:character <- index-address *result, result-idx
|
|
|
|
*out <- copy in
|
|
|
|
i <- add i, 1
|
|
|
|
result-idx <- add result-idx, 1
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# copy 'a' into result
|
2015-07-28 21:33:22 +00:00
|
|
|
j:number <- copy 0
|
2015-04-20 17:25:02 +00:00
|
|
|
{
|
|
|
|
# while j < a.length
|
2015-07-29 21:37:57 +00:00
|
|
|
arg-done?:boolean <- greater-or-equal j, a-len
|
|
|
|
break-if arg-done?
|
2015-04-20 17:25:02 +00:00
|
|
|
# result[result-idx] = a[j]
|
2015-07-29 21:37:57 +00:00
|
|
|
in:character <- index *a, j
|
|
|
|
out:address:character <- index-address *result, result-idx
|
|
|
|
*out <- copy in
|
|
|
|
j <- add j, 1
|
|
|
|
result-idx <- add result-idx, 1
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# skip '_' in template
|
2015-07-29 21:37:57 +00:00
|
|
|
i <- add i, 1
|
2015-04-20 17:25:02 +00:00
|
|
|
loop # interpolate next arg
|
|
|
|
}
|
2015-05-16 20:17:36 +00:00
|
|
|
+done
|
2015-04-20 17:25:02 +00:00
|
|
|
# done with holes; copy rest of template directly into result
|
|
|
|
{
|
|
|
|
# while i < template.length
|
2015-07-29 21:37:57 +00:00
|
|
|
tem-done?:boolean <- greater-or-equal i, tem-len
|
|
|
|
break-if tem-done?
|
2015-04-20 17:25:02 +00:00
|
|
|
# result[result-idx] = template[i]
|
2015-07-29 21:37:57 +00:00
|
|
|
in:character <- index *template, i
|
|
|
|
out:address:character <- index-address *result, result-idx:number
|
|
|
|
*out <- copy in
|
|
|
|
i <- add i, 1
|
|
|
|
result-idx <- add result-idx, 1
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-04-20 17:25:02 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario interpolate-works [
|
|
|
|
#? dump run #? 1
|
|
|
|
run [
|
|
|
|
1:address:array:character/raw <- new [abc _]
|
|
|
|
2:address:array:character/raw <- new [def]
|
|
|
|
3:address:array:character/raw <- interpolate 1:address:array:character/raw, 2:address:array:character/raw
|
2015-07-29 21:37:57 +00:00
|
|
|
4:array:character/raw <- copy *3:address:array:character/raw
|
2015-04-20 17:25:02 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-21 05:20:39 +00:00
|
|
|
4:string <- [abc def]
|
2015-04-20 17:25:02 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario interpolate-at-start [
|
|
|
|
run [
|
|
|
|
1:address:array:character/raw <- new [_, hello!]
|
|
|
|
2:address:array:character/raw <- new [abc]
|
|
|
|
3:address:array:character/raw <- interpolate 1:address:array:character/raw, 2:address:array:character/raw
|
2015-07-29 21:37:57 +00:00
|
|
|
4:array:character/raw <- copy *3:address:array:character/raw
|
2015-04-20 17:25:02 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-21 05:20:39 +00:00
|
|
|
4:string <- [abc, hello!]
|
2015-04-20 17:25:02 +00:00
|
|
|
16 <- 0 # out of bounds
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario interpolate-at-end [
|
|
|
|
run [
|
|
|
|
1:address:array:character/raw <- new [hello, _]
|
|
|
|
2:address:array:character/raw <- new [abc]
|
|
|
|
3:address:array:character/raw <- interpolate 1:address:array:character/raw, 2:address:array:character/raw
|
2015-07-29 21:37:57 +00:00
|
|
|
4:array:character/raw <- copy *3:address:array:character/raw
|
2015-04-20 17:25:02 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
2015-04-21 05:20:39 +00:00
|
|
|
4:string <- [hello, abc]
|
2015-04-20 17:25:02 +00:00
|
|
|
]
|
|
|
|
]
|
2015-05-23 18:37:44 +00:00
|
|
|
|
2015-05-23 19:35:10 +00:00
|
|
|
# result:boolean <- space? c:character
|
2015-05-23 19:30:58 +00:00
|
|
|
recipe space? [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-05-23 19:30:58 +00:00
|
|
|
c:character <- next-ingredient
|
2015-05-23 19:35:10 +00:00
|
|
|
# most common case first
|
2015-07-29 21:37:57 +00:00
|
|
|
result:boolean <- equal c, 32/space
|
|
|
|
jump-if result +reply:label
|
|
|
|
result <- equal c, 10/newline
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 9/tab
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 13/carriage-return
|
|
|
|
jump-if result, +reply:label
|
2015-05-23 19:30:58 +00:00
|
|
|
# remaining uncommon cases in sorted order
|
|
|
|
# http://unicode.org code-points in unicode-set Z and Pattern_White_Space
|
2015-07-29 21:37:57 +00:00
|
|
|
result <- equal c, 11/ctrl-k
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 12/ctrl-l
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 133/ctrl-0085
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 160/no-break-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 5760/ogham-space-mark
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8192/en-quad
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8193/em-quad
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8194/en-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8195/em-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8196/three-per-em-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8197/four-per-em-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8198/six-per-em-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8199/figure-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8200/punctuation-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8201/thin-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8202/hair-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8206/left-to-right
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8207/right-to-left
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8232/line-separator
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8233/paragraph-separator
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8239/narrow-no-break-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 8287/medium-mathematical-space
|
|
|
|
jump-if result, +reply:label
|
|
|
|
result <- equal c, 12288/ideographic-space
|
|
|
|
jump-if result, +reply:label
|
2015-05-23 19:30:58 +00:00
|
|
|
+reply
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-05-23 19:30:58 +00:00
|
|
|
]
|
|
|
|
|
2015-05-23 19:35:10 +00:00
|
|
|
# result:address:array:character <- trim s:address:array:character
|
2015-05-23 18:37:44 +00:00
|
|
|
recipe trim [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-05-23 18:37:44 +00:00
|
|
|
s:address:array:character <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- length *s
|
2015-05-23 18:37:44 +00:00
|
|
|
# left trim: compute start
|
2015-07-28 21:33:22 +00:00
|
|
|
start:number <- copy 0
|
2015-05-23 18:37:44 +00:00
|
|
|
{
|
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
at-end?:boolean <- greater-or-equal start, len
|
|
|
|
break-unless at-end?
|
2015-07-28 21:33:22 +00:00
|
|
|
result:address:array:character <- new character:type, 0
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-05-23 18:37:44 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
curr:character <- index *s, start
|
|
|
|
whitespace?:boolean <- space? curr
|
|
|
|
break-unless whitespace?
|
|
|
|
start <- add start, 1
|
2015-05-23 18:37:44 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# right trim: compute end
|
2015-07-29 21:37:57 +00:00
|
|
|
end:number <- subtract len, 1
|
2015-05-23 18:37:44 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
not-at-start?:boolean <- greater-than end, start
|
|
|
|
assert not-at-start?, [end ran up against start]
|
|
|
|
curr:character <- index *s, end
|
|
|
|
whitespace?:boolean <- space? curr
|
|
|
|
break-unless whitespace?
|
|
|
|
end <- subtract end, 1
|
2015-05-23 18:37:44 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-05-23 19:35:10 +00:00
|
|
|
# result = new character[end+1 - start]
|
2015-07-29 21:37:57 +00:00
|
|
|
new-len:number <- subtract end, start, -1
|
|
|
|
result:address:array:character <- new character:type, new-len
|
2015-05-23 18:37:44 +00:00
|
|
|
# i = start, j = 0
|
2015-07-29 21:37:57 +00:00
|
|
|
i:number <- copy start
|
2015-07-28 21:33:22 +00:00
|
|
|
j:number <- copy 0
|
2015-05-23 18:37:44 +00:00
|
|
|
{
|
|
|
|
# while i <= end
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-than i, end
|
|
|
|
break-if done?
|
2015-05-23 18:37:44 +00:00
|
|
|
# result[j] = s[i]
|
2015-07-29 21:37:57 +00:00
|
|
|
src:character <- index *s, i
|
|
|
|
dest:address:character <- index-address *result, j
|
|
|
|
*dest <- copy src
|
|
|
|
i <- add i, 1
|
|
|
|
j <- add j, 1
|
2015-05-23 18:37:44 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-05-23 18:37:44 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario trim-unmodified [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- trim 1:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-05-23 18:37:44 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario trim-left [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [ abc]
|
|
|
|
2:address:array:character <- trim 1:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-05-23 18:37:44 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario trim-right [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc ]
|
|
|
|
2:address:array:character <- trim 1:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-05-23 18:37:44 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario trim-left-right [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [ abc ]
|
|
|
|
2:address:array:character <- trim 1:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-05-23 18:37:44 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
2015-05-23 19:30:58 +00:00
|
|
|
|
|
|
|
scenario trim-newline-tab [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [ abc
|
|
|
|
]
|
|
|
|
2:address:array:character <- trim 1:address:array:character
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-05-23 19:30:58 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
2015-06-19 19:28:36 +00:00
|
|
|
|
2015-07-29 21:37:57 +00:00
|
|
|
# next-index:number <- find-next text:address:array:character, pattern:character, idx:number
|
2015-06-19 19:28:36 +00:00
|
|
|
recipe find-next [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
text:address:array:character <- next-ingredient
|
|
|
|
pattern:character <- next-ingredient
|
|
|
|
idx:number <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- length *text
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
eof?:boolean <- greater-or-equal idx, len
|
|
|
|
break-if eof?
|
|
|
|
curr:character <- index *text, idx
|
|
|
|
found?:boolean <- equal curr, pattern
|
|
|
|
break-if found?
|
|
|
|
idx <- add idx, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply idx
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [a/b]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 1
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-empty [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new []
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 0
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-initial [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [/abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 0 # prefix match
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-final [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc/]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 3 # suffix match
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-missing [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 3 # no match
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-invalid-index [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 4/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 4 # no change
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-first [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [ab/c/]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 2 # first '/' of multiple
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-find-next-second [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [ab/c/]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:number <- find-next 1:address:array:character, 47/slash, 3/start-index
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
2 <- 4 # second '/' of multiple
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-07-29 21:37:57 +00:00
|
|
|
# idx:number <- find-substring text:address:array:character, pattern:address:array:character, idx:number
|
2015-06-19 19:28:36 +00:00
|
|
|
# like find-next, but searches for multiple characters
|
|
|
|
# fairly dumb algorithm
|
|
|
|
recipe find-substring [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
text:address:array:character <- next-ingredient
|
|
|
|
pattern:address:array:character <- next-ingredient
|
|
|
|
idx:number <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
first:character <- index *pattern, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
# repeatedly check for match at current idx
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- length *text
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
|
|
|
# does some unnecessary work checking for substrings even when there isn't enough of text left
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-or-equal idx, len
|
|
|
|
break-if done?
|
|
|
|
found?:boolean <- match-at text, pattern, idx
|
|
|
|
break-if found?
|
|
|
|
idx <- add idx, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
# optimization: skip past indices that definitely won't match
|
2015-07-29 21:37:57 +00:00
|
|
|
idx <- find-next text, first, idx
|
2015-06-19 19:28:36 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply idx
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario find-substring-1 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new [bc]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario find-substring-2 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abcd]
|
|
|
|
2:address:array:character <- new [bc]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:number <- find-substring 1:address:array:character, 2:address:array:character, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario find-substring-no-match [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new [bd]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 3 # not found
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario find-substring-suffix-match [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abcd]
|
|
|
|
2:address:array:character <- new [cd]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 2
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario find-substring-suffix-match-2 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abcd]
|
|
|
|
2:address:array:character <- new [cde]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 4 # not found
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
# result:boolean <- match-at text:address:array:character, pattern:address:array:character, idx:number
|
|
|
|
# checks if substring matches at index 'idx'
|
|
|
|
recipe match-at [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
text:address:array:character <- next-ingredient
|
|
|
|
pattern:address:array:character <- next-ingredient
|
|
|
|
idx:number <- next-ingredient
|
2015-07-29 21:37:57 +00:00
|
|
|
pattern-len:number <- length *pattern
|
2015-06-19 19:28:36 +00:00
|
|
|
# check that there's space left for the pattern
|
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
x:number <- length *text
|
|
|
|
x <- subtract x, pattern-len
|
|
|
|
enough-room?:boolean <- lesser-or-equal idx, x
|
|
|
|
break-if enough-room?
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 0/not-found
|
2015-06-19 19:28:36 +00:00
|
|
|
}
|
|
|
|
# check each character of pattern
|
2015-07-28 21:33:22 +00:00
|
|
|
pattern-idx:number <- copy 0
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-or-equal pattern-idx, pattern-len
|
|
|
|
break-if done?
|
|
|
|
c:character <- index *text, idx
|
|
|
|
exp:character <- index *pattern, pattern-idx
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
match?:boolean <- equal c, exp
|
|
|
|
break-if match?
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 0/not-found
|
2015-06-19 19:28:36 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
idx <- add idx, 1
|
|
|
|
pattern-idx <- add pattern-idx, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-28 21:33:22 +00:00
|
|
|
reply 1/found
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-checks-substring-at-index [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new [ab]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1 # match found
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-reflexive [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 1:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1 # match found
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-outside-bounds [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new [a]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 4
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 0 # never matches
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-empty-pattern [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new []
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1 # always matches empty pattern given a valid index
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-empty-pattern-outside-bound [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new []
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 4
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 0 # no match
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-empty-text [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new []
|
|
|
|
2:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 0 # no match
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-empty-against-empty [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new []
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 1:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1 # matches because pattern is also empty
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-inside-bounds [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new [bc]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1 # matches inner substring
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario match-at-inside-bounds-2 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
|
|
|
2:address:array:character <- new [bc]
|
2015-07-28 21:33:22 +00:00
|
|
|
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 0 # no match
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
# result:address:array:address:array:character <- split s:address:array:character, delim:character
|
|
|
|
recipe split [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
s:address:array:character <- next-ingredient
|
|
|
|
delim:character <- next-ingredient
|
|
|
|
# empty string? return empty array
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- length *s
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
empty?:boolean <- equal len, 0
|
|
|
|
break-unless empty?
|
2015-07-28 21:33:22 +00:00
|
|
|
result:address:array:address:array:character <- new location:type, 0
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-06-19 19:28:36 +00:00
|
|
|
}
|
|
|
|
# count #pieces we need room for
|
2015-07-28 21:33:22 +00:00
|
|
|
count:number <- copy 1 # n delimiters = n+1 pieces
|
|
|
|
idx:number <- copy 0
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
idx <- find-next s, delim, idx
|
|
|
|
done?:boolean <- greater-or-equal idx, len
|
|
|
|
break-if done?
|
|
|
|
idx <- add idx, 1
|
|
|
|
count <- add count, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# allocate space
|
2015-07-29 21:37:57 +00:00
|
|
|
result:address:array:address:array:character <- new location:type, count
|
2015-06-19 19:28:36 +00:00
|
|
|
# repeatedly copy slices start..end until delimiter into result[curr-result]
|
2015-07-28 21:33:22 +00:00
|
|
|
curr-result:number <- copy 0
|
|
|
|
start:number <- copy 0
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
|
|
|
# while next delim exists
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-or-equal start, len
|
|
|
|
break-if done?
|
|
|
|
end:number <- find-next s, delim, start
|
2015-06-19 19:28:36 +00:00
|
|
|
# copy start..end into result[curr-result]
|
2015-07-29 21:37:57 +00:00
|
|
|
dest:address:address:array:character <- index-address *result, curr-result
|
|
|
|
*dest <- string-copy s, start, end
|
2015-06-19 19:28:36 +00:00
|
|
|
# slide over to next slice
|
2015-07-29 21:37:57 +00:00
|
|
|
start <- add end, 1
|
|
|
|
curr-result <- add curr-result, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-split-1 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [a/b]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
|
2015-07-29 21:37:57 +00:00
|
|
|
3:number <- length *2:address:array:address:array:character
|
|
|
|
4:address:array:character <- index *2:address:array:address:array:character, 0
|
|
|
|
5:address:array:character <- index *2:address:array:address:array:character, 1
|
|
|
|
10:array:character <- copy *4:address:array:character
|
|
|
|
20:array:character <- copy *5:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 2 # length of result
|
|
|
|
10:string <- [a]
|
|
|
|
20:string <- [b]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-split-2 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [a/b/c]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
|
2015-07-29 21:37:57 +00:00
|
|
|
3:number <- length *2:address:array:address:array:character
|
|
|
|
4:address:array:character <- index *2:address:array:address:array:character, 0
|
|
|
|
5:address:array:character <- index *2:address:array:address:array:character, 1
|
|
|
|
6:address:array:character <- index *2:address:array:address:array:character, 2
|
|
|
|
10:array:character <- copy *4:address:array:character
|
|
|
|
20:array:character <- copy *5:address:array:character
|
|
|
|
30:array:character <- copy *6:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 3 # length of result
|
|
|
|
10:string <- [a]
|
|
|
|
20:string <- [b]
|
|
|
|
30:string <- [c]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-split-missing [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
|
2015-07-29 21:37:57 +00:00
|
|
|
3:number <- length *2:address:array:address:array:character
|
|
|
|
4:address:array:character <- index *2:address:array:address:array:character, 0
|
|
|
|
10:array:character <- copy *4:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 1 # length of result
|
|
|
|
10:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-split-empty [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new []
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
|
2015-07-29 21:37:57 +00:00
|
|
|
3:number <- length *2:address:array:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 0 # empty result
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-split-empty-piece [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [a/b//c]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
|
2015-07-29 21:37:57 +00:00
|
|
|
3:number <- length *2:address:array:address:array:character
|
|
|
|
4:address:array:character <- index *2:address:array:address:array:character, 0
|
|
|
|
5:address:array:character <- index *2:address:array:address:array:character, 1
|
|
|
|
6:address:array:character <- index *2:address:array:address:array:character, 2
|
|
|
|
7:address:array:character <- index *2:address:array:address:array:character, 3
|
|
|
|
10:array:character <- copy *4:address:array:character
|
|
|
|
20:array:character <- copy *5:address:array:character
|
|
|
|
30:array:character <- copy *6:address:array:character
|
|
|
|
40:array:character <- copy *7:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3 <- 4 # length of result
|
|
|
|
10:string <- [a]
|
|
|
|
20:string <- [b]
|
|
|
|
30:string <- []
|
|
|
|
40:string <- [c]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
# x:address:array:character, y:address:array:character <- split-first text:address:array:character, delim:character
|
|
|
|
recipe split-first [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
text:address:array:character <- next-ingredient
|
|
|
|
delim:character <- next-ingredient
|
|
|
|
# empty string? return empty strings
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- length *text
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
empty?:boolean <- equal len, 0
|
|
|
|
break-unless empty?
|
2015-06-19 19:28:36 +00:00
|
|
|
x:address:array:character <- new []
|
|
|
|
y:address:array:character <- new []
|
2015-07-29 21:37:57 +00:00
|
|
|
reply x, y
|
2015-06-19 19:28:36 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
idx:number <- find-next text, delim, 0
|
|
|
|
x:address:array:character <- string-copy text, 0, idx
|
|
|
|
idx <- add idx, 1
|
|
|
|
y:address:array:character <- string-copy text, idx, len
|
|
|
|
reply x, y
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-split-first [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [a/b]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:character, 3:address:array:character <- split-first 1:address:array:character, 47/slash
|
2015-07-29 21:37:57 +00:00
|
|
|
10:array:character <- copy *2:address:array:character
|
|
|
|
20:array:character <- copy *3:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
10:string <- [a]
|
|
|
|
20:string <- [b]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
# result:address:array:character <- string-copy buf:address:array:character, start:number, end:number
|
|
|
|
# todo: make this generic
|
|
|
|
recipe string-copy [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
buf:address:array:character <- next-ingredient
|
|
|
|
start:number <- next-ingredient
|
|
|
|
end:number <- next-ingredient
|
|
|
|
# if end is out of bounds, trim it
|
2015-07-29 21:37:57 +00:00
|
|
|
len:number <- length *buf
|
|
|
|
end:number <- min len, end
|
2015-06-19 19:28:36 +00:00
|
|
|
# allocate space for result
|
2015-07-29 21:37:57 +00:00
|
|
|
len <- subtract end, start
|
|
|
|
result:address:array:character <- new character:type, len
|
2015-06-19 19:28:36 +00:00
|
|
|
# copy start..end into result[curr-result]
|
2015-07-29 21:37:57 +00:00
|
|
|
src-idx:number <- copy start
|
2015-07-28 21:33:22 +00:00
|
|
|
dest-idx:number <- copy 0
|
2015-06-19 19:28:36 +00:00
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
done?:boolean <- greater-or-equal src-idx, end
|
|
|
|
break-if done?
|
|
|
|
src:character <- index *buf, src-idx
|
|
|
|
dest:address:character <- index-address *result, dest-idx
|
|
|
|
*dest <- copy src
|
|
|
|
src-idx <- add src-idx, 1
|
|
|
|
dest-idx <- add dest-idx, 1
|
2015-06-19 19:28:36 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply result
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-copy-copies-substring [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:character <- string-copy 1:address:array:character, 1, 3
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [bc]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-copy-out-of-bounds [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:character <- string-copy 1:address:array:character, 2, 4
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- [c]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
scenario string-copy-out-of-bounds-2 [
|
|
|
|
run [
|
|
|
|
1:address:array:character <- new [abc]
|
2015-07-28 21:33:22 +00:00
|
|
|
2:address:array:character <- string-copy 1:address:array:character, 3, 3
|
2015-07-29 21:37:57 +00:00
|
|
|
3:array:character <- copy *2:address:array:character
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
3:string <- []
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
recipe min [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
x:number <- next-ingredient
|
|
|
|
y:number <- next-ingredient
|
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
return-x?:boolean <- lesser-than x, y
|
|
|
|
break-if return-x?
|
|
|
|
reply y
|
2015-06-19 19:28:36 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply x
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
recipe max [
|
2015-07-14 05:43:16 +00:00
|
|
|
local-scope
|
2015-06-19 19:28:36 +00:00
|
|
|
x:number <- next-ingredient
|
|
|
|
y:number <- next-ingredient
|
|
|
|
{
|
2015-07-29 21:37:57 +00:00
|
|
|
return-x?:boolean <- greater-than x, y
|
|
|
|
break-if return-x?
|
|
|
|
reply y
|
2015-06-19 19:28:36 +00:00
|
|
|
}
|
2015-07-29 21:37:57 +00:00
|
|
|
reply x
|
2015-06-19 19:28:36 +00:00
|
|
|
]
|