recipe string-equal [
default-space:address:array:location <- new location:type, 30:literal
a:address:array:character <- next-ingredient
a-len:number <- length a:address:array:character/deref
b:address:array:character <- next-ingredient
b-len:number <- length b:address:array:character/deref
{
trace [string-equal], [comparing lengths]
length-equal?:boolean <- equal a-len:number, b-len:number
break-if length-equal?:boolean
reply 0:literal
}
trace [string-equal], [comparing characters]
i:number <- copy 0:literal
{
done?:boolean <- greater-or-equal i:number, a-len:number
break-if done?:boolean
a2:character <- index a:address:array:character/deref, i:number
b2:character <- index b:address:array:character/deref, i:number
{
chars-match?:boolean <- equal a2:character, b2:character
break-if chars-match?:boolean
reply 0:literal
}
i:number <- add i:number, 1:literal
loop
}
reply 1:literal
]
scenario string-equal-reflexive [
run [
default-space:address:array:location <- new location:type, 30:literal
x:address:array:character <- new [abc]
3:boolean/raw <- string-equal x:address:array:character, x:address:array:character
]
memory-should-contain [
3 <- 1
]
]
scenario string-equal-identical [
run [
default-space:address:array:location <- new location:type, 30:literal
x:address:array:character <- new [abc]
y:address:array:character <- new [abc]
3:boolean/raw <- string-equal x:address:array:character, y:address:array:character
]
memory-should-contain [
3 <- 1
]
]
scenario string-equal-distinct-lengths [
run [
default-space:address:array:location <- new location:type, 30:literal
x:address:array:character <- new [abc]
y:address:array:character <- new [abcd]
3:boolean/raw <- string-equal x:address:array:character, y:address:array:character
]
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:literal
x:address:array:character <- new []
y:address:array:character <- new [abcd]
3:boolean/raw <- string-equal x:address:array:character, y:address:array:character
]
memory-should-contain [
3 <- 0
]
]
scenario string-equal-common-lengths-but-distinct [
run [
default-space:address:array:location <- new location:type, 30:literal
x:address:array:character <- new [abc]
y:address:array:character <- new [abd]
3:boolean/raw <- string-equal x:address:array:character, y:address:array:character
]
memory-should-contain [
3 <- 0
]
]
container buffer [
length:number
data:address:array:character
]
recipe init-buffer [
default-space:address:array:location <- new location:type, 30:literal
result:address:buffer <- new buffer:type
len:address:number <- get-address result:address:buffer/deref, length:offset
len:address:number/deref <- copy 0:literal
s:address:address:array:character <- get-address result:address:buffer/deref, data:offset
capacity:number <- next-ingredient
s:address:address:array:character/deref <- new character:type, capacity:number
reply result:address:buffer
]
recipe grow-buffer [
default-space:address:array:location <- new location:type, 30:literal
in:address:buffer <- next-ingredient
x:address:address:array:character <- get-address in:address:buffer/deref, data:offset
oldlen:number <- length x:address:address:array:character/deref/deref
newlen:number <- multiply oldlen:number, 2:literal
olddata:address:array:character <- copy x:address:address:array:character/deref
x:address:address:array:character/deref <- new character:type, newlen:number
i:number <- copy 0:literal
{
done?:boolean <- greater-or-equal i:number, oldlen:number
break-if done?:boolean
src:character <- index olddata:address:array:character/deref, i:number
dest:address:character <- index-address x:address:address:array:character/deref/deref, i:number
dest:address:character/deref <- copy src:character
i:number <- add i:number, 1:literal
loop
}
reply in:address:buffer
]
recipe buffer-full? [
default-space:address:array:location <- new location:type, 30:literal
in:address:buffer <- next-ingredient
len:number <- get in:address:buffer/deref, length:offset
s:address:array:character <- get in:address:buffer/deref, data:offset
capacity:number <- length s:address:array:character/deref
result:boolean <- greater-or-equal len:number, capacity:number
reply result:boolean
]
recipe buffer-append [
default-space:address:array:location <- new location:type, 30:literal
in:address:buffer <- next-ingredient
c:character <- next-ingredient
{
full?:boolean <- buffer-full? in:address:buffer
break-unless full?:boolean
in:address:buffer <- grow-buffer in:address:buffer
}
len:address:number <- get-address in:address:buffer/deref, length:offset
s:address:array:character <- get in:address:buffer/deref, data:offset
dest:address:character <- index-address s:address:array:character/deref, len:address:number/deref
dest:address:character/deref <- copy c:character
len:address:number/deref <- add len:address:number/deref, 1:literal
reply in:address:buffer/same-as-ingredient:0
]
scenario buffer-append-works [
run [
default-space:address:array:location <- new location:type, 30:literal
x:address:buffer <- init-buffer 3:literal
s1:address:array:character <- get x:address:buffer/deref, data:offset
x:address:buffer <- buffer-append x:address:buffer, 97:literal
x:address:buffer <- buffer-append x:address:buffer, 98:literal
x:address:buffer <- buffer-append x:address:buffer, 99:literal
s2:address:array:character <- get x:address:buffer/deref, data:offset
1:boolean/raw <- equal s1:address:array:character, s2:address:array:character
2:array:character/raw <- copy s2:address:array:character/deref
+buffer-filled
x:address:buffer <- buffer-append x:address:buffer, 100:literal
s3:address:array:character <- get x:address:buffer/deref, data:offset
10:boolean/raw <- equal s1:address:array:character, s3:address:array:character
11:number/raw <- get x:address:buffer/deref, length:offset
12:array:character/raw <- copy s3:address:array:character/deref
]
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
]
]
recipe integer-to-decimal-string [
default-space:address:array:location <- new location:type, 30:literal
n:number <- next-ingredient
{
break-if n:number
result:address:array:character <- new [0]
reply result:address:array:character
}
negate-result:boolean <- copy 0:literal
{
negative?:boolean <- lesser-than n:number, 0:literal
break-unless negative?:boolean
negate-result:boolean <- copy 1:literal
n:number <- multiply n:number, -1:literal
}
tmp:address:buffer <- init-buffer 30:literal
digit-base:number <- copy 48:literal
{
done?:boolean <- equal n:number, 0:literal
break-if done?:boolean
n:number, digit:number <- divide-with-remainder n:number, 10:literal
c:character <- add digit-base:number, digit:number
tmp:address:buffer <- buffer-append tmp:address:buffer, c:character
loop
}
{
break-unless negate-result:boolean
tmp:address:buffer <- buffer-append tmp:address:buffer, 45:literal
}
len:number <- get tmp:address:buffer/deref, length:offset
buf:address:array:character <- get tmp:address:buffer/deref, data:offset
result:address:array:character <- new character:type, len:number
i:number <- subtract len:number, 1:literal
j:number <- copy 0:literal
{
done?:boolean <- lesser-than i:number, 0:literal
break-if done?:boolean
src:character <- index buf:address:array:character/deref, i:number
dest:address:character <- index-address result:address:array:character/deref, j:number
dest:address:character/deref <- copy src:character
i:number <- subtract i:number, 1:literal
j:number <- add j:number, 1:literal
loop
}
reply result:address:array:character
]
recipe buffer-to-array [
default-space:address:array:character <- new location:type, 30:literal
in:address:buffer <- next-ingredient
len:number <- get in:address:buffer/deref, length:offset
s:address:array:character <- get in:address:buffer/deref, data:offset
{
break-if s:address:array:character
reply 0:literal
}
result:address:array:character <- new character:type, len:number
i:number <- copy 0:literal
{
done?:boolean <- greater-or-equal i:number, len:number
break-if done?:boolean
src:character <- index s:address:array:character/deref, i:number
dest:address:character <- index-address result:address:array:character/deref, i:number
dest:address:character/deref <- copy src:character
i:number <- add i:number, 1:literal
loop
}
reply result:address:array:character
]
scenario integer-to-decimal-digit-zero [
run [
1:address:array:character/raw <- integer-to-decimal-string 0:literal
2:array:character/raw <- copy 1:address:array:character/deref/raw
]
memory-should-contain [
2:string <- [0]
]
]
scenario integer-to-decimal-digit-positive [
run [
1:address:array:character/raw <- integer-to-decimal-string 234:literal
2:array:character/raw <- copy 1:address:array:character/deref/raw
]
memory-should-contain [
2:string <- [234]
]
]
scenario integer-to-decimal-digit-negative [
run [
1:address:array:character/raw <- integer-to-decimal-string -1:literal
2:array:character/raw <- copy 1:address:array:character/deref/raw
]
memory-should-contain [
2 <- 2
3 <- 45
4 <- 49
]
]
recipe string-append [
default-space:address:array:location <- new location:type, 30:literal
a:address:array:character <- next-ingredient
a-len:number <- length a:address:array:character/deref
b:address:array:character <- next-ingredient
b-len:number <- length b:address:array:character/deref
result-len:number <- add a-len:number, b-len:number
result:address:array:character <- new character:type, result-len:number
result-idx:number <- copy 0:literal
i:number <- copy 0:literal
{
a-done?:boolean <- greater-or-equal i:number, a-len:number
break-if a-done?:boolean
out:address:character <- index-address result:address:array:character/deref, result-idx:number
in:character <- index a:address:array:character/deref, i:number
out:address:character/deref <- copy in:character
i:number <- add i:number, 1:literal
result-idx:number <- add result-idx:number, 1:literal
loop
}
i:number <- copy 0:literal
{
b-done?:boolean <- greater-or-equal i:number, b-len:number
break-if b-done?:boolean
out:address:character <- index-address result:address:array:character/deref, result-idx:number
in:character <- index b:address:array:character/deref, i:number
out:address:character/deref <- copy in:character
i:number <- add i:number, 1:literal
result-idx:number <- add result-idx:number, 1:literal
loop
}
reply result:address:array:character
]
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/deref
]
memory-should-contain [
4:string <- [hello, world!]
]
]
recipe interpolate [
default-space:array:address:location <- new location:type, 60:literal
template:address:array:character <- next-ingredient
tem-len:number <- length template:address:array:character/deref
result-len:number <- copy tem-len:number
{
a:address:array:character, arg-received?:boolean <- next-ingredient
break-unless arg-received?:boolean
a-len:number <- length a:address:array:character/deref
result-len:number <- add result-len:number, a-len:number
result-len:number <- subtract result-len:number, 1:literal
loop
}
rewind-ingredients
_ <- next-ingredient
result:address:array:character <- new character:type, result-len:number
result-idx:number <- copy 0:literal
i:number <- copy 0:literal
{
a:address:array:character, arg-received?:boolean <- next-ingredient
break-unless arg-received?:boolean
{
tem-done?:boolean <- greater-or-equal i:number, tem-len:number
break-if tem-done?:boolean, +done:label
in:character <- index template:address:array:character/deref, i:number
underscore?:boolean <- equal in:character, 95:literal
break-if underscore?:boolean
out:address:character <- index-address result:address:array:character/deref, result-idx:number
out:address:character/deref <- copy in:character
i:number <- add i:number, 1:literal
result-idx:number <- add result-idx:number, 1:literal
loop
}
j:number <- copy 0:literal
{
arg-done?:boolean <- greater-or-equal j:number, a-len:number
break-if arg-done?:boolean
in:character <- index a:address:array:character/deref, j:number
out:address:character <- index-address result:address:array:character/deref, result-idx:number
out:address:character/deref <- copy in:character
j:number <- add j:number, 1:literal
result-idx:number <- add result-idx:number, 1:literal
loop
}
i:number <- add i:number, 1:literal
loop
}
+done
{
tem-done?:boolean <- greater-or-equal i:number, tem-len:number
break-if tem-done?:boolean
in:character <- index template:address:array:character/deref, i:number
out:address:character <- index-address result:address:array:character/deref, result-idx:number
out:address:character/deref <- copy in:character
i:number <- add i:number, 1:literal
result-idx:number <- add result-idx:number, 1:literal
loop
}
reply result:address:array:character
]
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/deref
]
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/deref
]
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/deref
]
memory-should-contain [
4:string <- [hello, abc]
]
]
recipe space? [
default-space:array:address:location <- new location:type, 30:literal
c:character <- next-ingredient
result:boolean <- equal c:character, 32:literal/space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 10:literal/newline
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 9:literal/tab
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 13:literal/carriage-return
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 11:literal/ctrl-k
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 12:literal/ctrl-l
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 133:literal/ctrl-0085
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 160:literal/no-break-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 5760:literal/ogham-space-mark
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8192:literal/en-quad
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8193:literal/em-quad
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8194:literal/en-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8195:literal/em-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8196:literal/three-per-em-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8197:literal/four-per-em-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8198:literal/six-per-em-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8199:literal/figure-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8200:literal/punctuation-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8201:literal/thin-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8202:literal/hair-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8206:literal/left-to-right
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8207:literal/right-to-left
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8232:literal/line-separator
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8233:literal/paragraph-separator
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8239:literal/narrow-no-break-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 8287:literal/medium-mathematical-space
jump-if result:boolean, +reply:label
result:boolean <- equal c:character, 12288:literal/ideographic-space
jump-if result:boolean, +reply:label
+reply
reply result:boolean
]
recipe trim [
default-space:array:address:location <- new location:type, 30:literal
s:address:array:character <- next-ingredient
len:number <- length s:address:array:character/deref
start:number <- copy 0:literal
{
{
at-end?:boolean <- greater-or-equal start:number, len:number
break-unless at-end?:boolean
result:address:array:character <- new character:type, 0:literal
reply result:address:array:character
}
curr:character <- index s:address:array:character/deref, start:number
whitespace?:boolean <- space? curr:character
break-unless whitespace?:boolean
start:number <- add start:number, 1:literal
loop
}
end:number <- subtract len:number, 1:literal
{
not-at-start?:boolean <- greater-than end:number, start:number
assert not-at-start?:boolean [end ran up against start]
curr:character <- index s:address:array:character/deref, end:number
whitespace?:boolean <- space? curr:character
break-unless whitespace?:boolean
end:number <- subtract end:number, 1:literal
loop
}
new-len:number <- subtract end:number, start:number, -1:literal
result:address:array:character <- new character:type, new-len:number
i:number <- copy start:number
j:number <- copy 0:literal
{
done?:boolean <- greater-than i:number, end:number
break-if done?:boolean
src:character <- index s:address:array:character/deref, i:number
dest:address:character <- index-address result:address:array:character/deref, j:number
dest:address:character/deref <- copy src:character
i:number <- add i:number, 1:literal
j:number <- add j:number, 1:literal
loop
}
reply result:address:array:character
]
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/deref
]
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/deref
]
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/deref
]
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/deref
]
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/deref
]
memory-should-contain [
3:string <- [abc]
]
]