recipe string-equal [
local-scope
a:address:array:character <- next-ingredient
a-len:number <- length *a
b:address:array:character <- next-ingredient
b-len:number <- length *b
{
trace [string-equal], [comparing lengths]
length-equal?:boolean <- equal a-len, b-len
break-if length-equal?
reply 0
}
trace [string-equal], [comparing characters]
i:number <- copy 0
{
done?:boolean <- greater-or-equal i, a-len
break-if done?
a2:character <- index *a, i
b2:character <- index *b, i
{
chars-match?:boolean <- equal a2, b2
break-if chars-match?
reply 0
}
i <- add i, 1
loop
}
reply 1
]
scenario string-equal-reflexive [
run [
default-space:address:array:location <- new location:type, 30
x:address:array:character <- new [abc]
3:boolean/raw <- string-equal x, x
]
memory-should-contain [
3 <- 1
]
]
scenario string-equal-identical [
run [
default-space:address:array:location <- new location:type, 30
x:address:array:character <- new [abc]
y:address:array:character <- new [abc]
3:boolean/raw <- string-equal x, y
]
memory-should-contain [
3 <- 1
]
]
scenario string-equal-distinct-lengths [
run [
default-space:address:array:location <- new location:type, 30
x:address:array:character <- new [abc]
y:address:array:character <- new [abcd]
3:boolean/raw <- string-equal x, y
]
memory-should-contain [
3 <- 0
]
trace-should-contain [
string-equal: comparing lengths
]
trace-should-not-contain [
string-equal: comparing characters
]
]
scenario string-equal-with-empty [
run [
default-space:address:array:location <- new location:type, 30
x:address:array:character <- new []
y:address:array:character <- new [abcd]
3:boolean/raw <- string-equal x, y
]
memory-should-contain [
3 <- 0
]
]
scenario string-equal-common-lengths-but-distinct [
run [
default-space:address:array:location <- new location:type, 30
x:address:array:character <- new [abc]
y:address:array:character <- new [abd]
3:boolean/raw <- string-equal x, y
]
memory-should-contain [
3 <- 0
]
]
container buffer [
length:number
data:address:array:character
]
recipe new-buffer [
local-scope
result:address:buffer <- new buffer:type
len:address:number <- get-address *result, length:offset
*len:address:number <- copy 0
s:address:address:array:character <- get-address *result, data:offset
capacity:number, found?:boolean <- next-ingredient
assert found?, [new-buffer must get a capacity argument]
*s <- new character:type, capacity
reply result
]
recipe grow-buffer [
local-scope
in:address:buffer <- next-ingredient
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
i:number <- copy 0
{
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
loop
}
reply in
]
recipe buffer-full? [
local-scope
in:address:buffer <- next-ingredient
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
]
recipe buffer-append [
local-scope
in:address:buffer <- next-ingredient
c:character <- next-ingredient
len:address:number <- get-address *in, length:offset
{
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
}
{
full?:boolean <- buffer-full? in
break-unless full?
in <- grow-buffer in
}
s:address:array:character <- get *in, data:offset
dest:address:character <- index-address *s, *len
*dest <- copy c
*len <- add *len, 1
reply in/same-as-ingredient:0
]
scenario buffer-append-works [
run [
local-scope
x:address:buffer <- new-buffer 3
s1:address:array:character <- get *x:address:buffer, data:offset
x:address:buffer <- buffer-append x:address:buffer, 97
x:address:buffer <- buffer-append x:address:buffer, 98
x:address:buffer <- buffer-append x:address:buffer, 99
s2:address:array:character <- get *x:address:buffer, data:offset
1:boolean/raw <- equal s1:address:array:character, s2:address:array:character
2:array:character/raw <- copy *s2:address:array:character
+buffer-filled
x:address:buffer <- buffer-append x:address:buffer, 100
s3:address:array:character <- get *x:address:buffer, data:offset
10:boolean/raw <- equal s1:address:array:character, s3:address:array:character
11:number/raw <- get *x:address:buffer, length:offset
12:array:character/raw <- copy *s3:address:array:character
]
memory-should-contain [
1 <- 1
2 <- 3
3 <- 97
4 <- 98
5 <- 99
10 <- 0
11 <- 4
12 <- 6
13 <- 97
14 <- 98
15 <- 99
16 <- 100
17 <- 0
18 <- 0
]
]
scenario buffer-append-handles-backspace [
run [
local-scope
x:address:buffer <- new-buffer 3
x:address:buffer <- buffer-append x:address:buffer, 97
x:address:buffer <- buffer-append x:address:buffer, 98
x:address:buffer <- buffer-append x:address:buffer, 8/backspace
s:address:array:character <- buffer-to-array x:address:buffer
1:array:character/raw <- copy *s:address:array:character
]
memory-should-contain [
1 <- 1
2 <- 97
3 <- 0
]
]
recipe integer-to-decimal-string [
local-scope
n:number <- next-ingredient
{
break-if n
result:address:array:character <- new [0]
reply result
}
negate-result:boolean <- copy 0
{
negative?:boolean <- lesser-than n, 0
break-unless negative?
negate-result <- copy 1
n <- multiply n, -1
}
tmp:address:buffer <- new-buffer 30
digit-base:number <- copy 48
{
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
loop
}
{
break-unless negate-result:boolean
tmp <- buffer-append tmp, 45
}
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
j:number <- copy 0
{
done?:boolean <- lesser-than i, 0
break-if done?
src:character <- index *buf, i
dest:address:character <- index-address *result, j
*dest <- copy src
i <- subtract i, 1
j <- add j, 1
loop
}
reply result
]
recipe buffer-to-array [
local-scope
in:address:buffer <- next-ingredient
{
break-if in
reply 0
}
len:number <- get *in, length:offset
s:address:array:character <- get *in, data:offset
result:address:array:character <- new character:type, len
i:number <- copy 0
{
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
loop
}
reply result
]
scenario integer-to-decimal-digit-zero [
run [
1:address:array:character/raw <- integer-to-decimal-string 0
2:array:character/raw <- copy *1:address:array:character/raw
]
memory-should-contain [
2:string <- [0]
]
]
scenario integer-to-decimal-digit-positive [
run [
1:address:array:character/raw <- integer-to-decimal-string 234
2:array:character/raw <- copy *1:address:array:character/raw
]
memory-should-contain [
2:string <- [234]
]
]
scenario integer-to-decimal-digit-negative [
run [
1:address:array:character/raw <- integer-to-decimal-string -1
2:array:character/raw <- copy *1:address:array:character/raw
]
memory-should-contain [
2 <- 2
3 <- 45
4 <- 49
]
]
recipe string-append [
local-scope
a:address:array:character <- next-ingredient
a-len:number <- length *a
b:address:array:character <- next-ingredient
b-len:number <- length *b
result-len:number <- add a-len, b-len
result:address:array:character <- new character:type, result-len
result-idx:number <- copy 0
i:number <- copy 0
{
a-done?:boolean <- greater-or-equal i, a-len
break-if a-done?
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
loop
}
i <- copy 0
{
b-done?:boolean <- greater-or-equal i, b-len
break-if b-done?
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
loop
}
reply result
]
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
4:array:character/raw <- copy *3:address:array:character/raw
]
memory-should-contain [
4:string <- [hello, world!]
]
]
recipe interpolate [
local-scope
template:address:array:character <- next-ingredient
tem-len:number <- length *template
result-len:number <- copy tem-len
{
a:address:array:character, arg-received?:boolean <- next-ingredient
break-unless arg-received?
a-len:number <- length *a
result-len <- add result-len, a-len
result-len <- subtract result-len, 1
loop
}
rewind-ingredients
_ <- next-ingredient
result:address:array:character <- new character:type, result-len
result-idx:number <- copy 0
i:number <- copy 0
{
a:address:array:character, arg-received?:boolean <- next-ingredient
break-unless arg-received?
{
tem-done?:boolean <- greater-or-equal i, tem-len
break-if tem-done?, +done:label
in:character <- index *template, i
underscore?:boolean <- equal in, 95/_
break-if underscore?
out:address:character <- index-address *result, result-idx
*out <- copy in
i <- add i, 1
result-idx <- add result-idx, 1
loop
}
j:number <- copy 0
{
arg-done?:boolean <- greater-or-equal j, a-len
break-if arg-done?
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
loop
}
i <- add i, 1
loop
}
+done
{
tem-done?:boolean <- greater-or-equal i, tem-len
break-if tem-done?
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
loop
}
reply result
]
scenario interpolate-works [
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
4:array:character/raw <- copy *3:address:array:character/raw
]
memory-should-contain [
4:string <- [abc def]
]
]
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
4:array:character/raw <- copy *3:address:array:character/raw
]
memory-should-contain [
4:string <- [abc, hello!]
16 <- 0
]
]
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
4:array:character/raw <- copy *3:address:array:character/raw
]
memory-should-contain [
4:string <- [hello, abc]
]
]
recipe space? [
local-scope
c:character <- next-ingredient
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
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
+reply
reply result
]
recipe trim [
local-scope
s:address:array:character <- next-ingredient
len:number <- length *s
start:number <- copy 0
{
{
at-end?:boolean <- greater-or-equal start, len
break-unless at-end?
result:address:array:character <- new character:type, 0
reply result
}
curr:character <- index *s, start
whitespace?:boolean <- space? curr
break-unless whitespace?
start <- add start, 1
loop
}
end:number <- subtract len, 1
{
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
loop
}
new-len:number <- subtract end, start, -1
result:address:array:character <- new character:type, new-len
i:number <- copy start
j:number <- copy 0
{
done?:boolean <- greater-than i, end
break-if done?
src:character <- index *s, i
dest:address:character <- index-address *result, j
*dest <- copy src
i <- add i, 1
j <- add j, 1
loop
}
reply result
]
scenario trim-unmodified [
run [
1:address:array:character <- new [abc]
2:address:array:character <- trim 1:address:array:character
3:array:character <- copy *2:address:array:character
]
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
3:array:character <- copy *2:address:array:character
]
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
3:array:character <- copy *2:address:array:character
]
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
3:array:character <- copy *2:address:array:character
]
memory-should-contain [
3:string <- [abc]
]
]
scenario trim-newline-tab [
run [
1:address:array:character <- new [ abc
]
2:address:array:character <- trim 1:address:array:character
3:array:character <- copy *2:address:array:character
]
memory-should-contain [
3:string <- [abc]
]
]
recipe find-next [
local-scope
text:address:array:character <- next-ingredient
pattern:character <- next-ingredient
idx:number <- next-ingredient
len:number <- length *text
{
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
loop
}
reply idx
]
scenario string-find-next [
run [
1:address:array:character <- new [a/b]
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
]
memory-should-contain [
2 <- 1
]
]
scenario string-find-next-empty [
run [
1:address:array:character <- new []
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
]
memory-should-contain [
2 <- 0
]
]
scenario string-find-next-initial [
run [
1:address:array:character <- new [/abc]
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
]
memory-should-contain [
2 <- 0
]
]
scenario string-find-next-final [
run [
1:address:array:character <- new [abc/]
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
]
memory-should-contain [
2 <- 3
]
]
scenario string-find-next-missing [
run [
1:address:array:character <- new [abc]
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
]
memory-should-contain [
2 <- 3
]
]
scenario string-find-next-invalid-index [
run [
1:address:array:character <- new [abc]
2:number <- find-next 1:address:array:character, 47/slash, 4/start-index
]
memory-should-contain [
2 <- 4
]
]
scenario string-find-next-first [
run [
1:address:array:character <- new [ab/c/]
2:number <- find-next 1:address:array:character, 47/slash, 0/start-index
]
memory-should-contain [
2 <- 2
]
]
scenario string-find-next-second [
run [
1:address:array:character <- new [ab/c/]
2:number <- find-next 1:address:array:character, 47/slash, 3/start-index
]
memory-should-contain [
2 <- 4
]
]
recipe find-substring [
local-scope
text:address:array:character <- next-ingredient
pattern:address:array:character <- next-ingredient
idx:number <- next-ingredient
first:character <- index *pattern, 0
len:number <- length *text
{
done?:boolean <- greater-or-equal idx, len
break-if done?
found?:boolean <- match-at text, pattern, idx
break-if found?
idx <- add idx, 1
idx <- find-next text, first, idx
loop
}
reply idx
]
scenario find-substring-1 [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [bc]
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 1
]
]
scenario find-substring-2 [
run [
1:address:array:character <- new [abcd]
2:address:array:character <- new [bc]
3:number <- find-substring 1:address:array:character, 2:address:array:character, 1
]
memory-should-contain [
3 <- 1
]
]
scenario find-substring-no-match [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [bd]
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 3
]
]
scenario find-substring-suffix-match [
run [
1:address:array:character <- new [abcd]
2:address:array:character <- new [cd]
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 2
]
]
scenario find-substring-suffix-match-2 [
run [
1:address:array:character <- new [abcd]
2:address:array:character <- new [cde]
3:number <- find-substring 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 4
]
]
recipe match-at [
local-scope
text:address:array:character <- next-ingredient
pattern:address:array:character <- next-ingredient
idx:number <- next-ingredient
pattern-len:number <- length *pattern
{
x:number <- length *text
x <- subtract x, pattern-len
enough-room?:boolean <- lesser-or-equal idx, x
break-if enough-room?
reply 0/not-found
}
pattern-idx:number <- copy 0
{
done?:boolean <- greater-or-equal pattern-idx, pattern-len
break-if done?
c:character <- index *text, idx
exp:character <- index *pattern, pattern-idx
{
match?:boolean <- equal c, exp
break-if match?
reply 0/not-found
}
idx <- add idx, 1
pattern-idx <- add pattern-idx, 1
loop
}
reply 1/found
]
scenario match-at-checks-substring-at-index [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [ab]
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 1
]
]
scenario match-at-reflexive [
run [
1:address:array:character <- new [abc]
3:boolean <- match-at 1:address:array:character, 1:address:array:character, 0
]
memory-should-contain [
3 <- 1
]
]
scenario match-at-outside-bounds [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [a]
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 4
]
memory-should-contain [
3 <- 0
]
]
scenario match-at-empty-pattern [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new []
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 1
]
]
scenario match-at-empty-pattern-outside-bound [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new []
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 4
]
memory-should-contain [
3 <- 0
]
]
scenario match-at-empty-text [
run [
1:address:array:character <- new []
2:address:array:character <- new [abc]
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 0
]
]
scenario match-at-empty-against-empty [
run [
1:address:array:character <- new []
3:boolean <- match-at 1:address:array:character, 1:address:array:character, 0
]
memory-should-contain [
3 <- 1
]
]
scenario match-at-inside-bounds [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [bc]
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 1
]
memory-should-contain [
3 <- 1
]
]
scenario match-at-inside-bounds-2 [
run [
1:address:array:character <- new [abc]
2:address:array:character <- new [bc]
3:boolean <- match-at 1:address:array:character, 2:address:array:character, 0
]
memory-should-contain [
3 <- 0
]
]
recipe split [
local-scope
s:address:array:character <- next-ingredient
delim:character <- next-ingredient
len:number <- length *s
{
empty?:boolean <- equal len, 0
break-unless empty?
result:address:array:address:array:character <- new location:type, 0
reply result
}
count:number <- copy 1
idx:number <- copy 0
{
idx <- find-next s, delim, idx
done?:boolean <- greater-or-equal idx, len
break-if done?
idx <- add idx, 1
count <- add count, 1
loop
}
result:address:array:address:array:character <- new location:type, count
curr-result:number <- copy 0
start:number <- copy 0
{
done?:boolean <- greater-or-equal start, len
break-if done?
end:number <- find-next s, delim, start
dest:address:address:array:character <- index-address *result, curr-result
*dest <- string-copy s, start, end
start <- add end, 1
curr-result <- add curr-result, 1
loop
}
reply result
]
scenario string-split-1 [
run [
1:address:array:character <- new [a/b]
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
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
]
memory-should-contain [
3 <- 2
10:string <- [a]
20:string <- [b]
]
]
scenario string-split-2 [
run [
1:address:array:character <- new [a/b/c]
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
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
]
memory-should-contain [
3 <- 3
10:string <- [a]
20:string <- [b]
30:string <- [c]
]
]
scenario string-split-missing [
run [
1:address:array:character <- new [abc]
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
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
]
memory-should-contain [
3 <- 1
10:string <- [abc]
]
]
scenario string-split-empty [
run [
1:address:array:character <- new []
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
3:number <- length *2:address:array:address:array:character
]
memory-should-contain [
3 <- 0
]
]
scenario string-split-empty-piece [
run [
1:address:array:character <- new [a/b//c]
2:address:array:address:array:character <- split 1:address:array:character, 47/slash
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
]
memory-should-contain [
3 <- 4
10:string <- [a]
20:string <- [b]
30:string <- []
40:string <- [c]
]
]
recipe split-first [
local-scope
text:address:array:character <- next-ingredient
delim:character <- next-ingredient
len:number <- length *text
{
empty?:boolean <- equal len, 0
break-unless empty?
x:address:array:character <- new []
y:address:array:character <- new []
reply x, y
}
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
]
scenario string-split-first [
run [
1:address:array:character <- new [a/b]
2:address:array:character, 3:address:array:character <- split-first 1:address:array:character, 47/slash
10:array:character <- copy *2:address:array:character
20:array:character <- copy *3:address:array:character
]
memory-should-contain [
10:string <- [a]
20:string <- [b]
]
]
recipe string-copy [
local-scope
buf:address:array:character <- next-ingredient
start:number <- next-ingredient
end:number <- next-ingredient
len:number <- length *buf
end:number <- min len, end
len <- subtract end, start
result:address:array:character <- new character:type, len
src-idx:number <- copy start
dest-idx:number <- copy 0
{
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
loop
}
reply result
]
scenario string-copy-copies-substring [
run [
1:address:array:character <- new [abc]
2:address:array:character <- string-copy 1:address:array:character, 1, 3
3:array:character <- copy *2:address:array:character
]
memory-should-contain [
3:string <- [bc]
]
]
scenario string-copy-out-of-bounds [
run [
1:address:array:character <- new [abc]
2:address:array:character <- string-copy 1:address:array:character, 2, 4
3:array:character <- copy *2:address:array:character
]
memory-should-contain [
3:string <- [c]
]
]
scenario string-copy-out-of-bounds-2 [
run [
1:address:array:character <- new [abc]
2:address:array:character <- string-copy 1:address:array:character, 3, 3
3:array:character <- copy *2:address:array:character
]
memory-should-contain [
3:string <- []
]
]
recipe min [
local-scope
x:number <- next-ingredient
y:number <- next-ingredient
{
return-x?:boolean <- lesser-than x, y
break-if return-x?
reply y
}
reply x
]
recipe max [
local-scope
x:number <- next-ingredient
y:number <- next-ingredient
{
return-x?:boolean <- greater-than x, y
break-if return-x?
reply y
}
reply x
]