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-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-03 19:53:33 +00:00
|
|
|
a:address:array:character <- next-ingredient
|
2015-05-13 17:03:26 +00:00
|
|
|
a-len:number <- length a:address:array:character/deref
|
2015-04-03 19:53:33 +00:00
|
|
|
b:address:array:character <- next-ingredient
|
2015-05-13 17:03:26 +00:00
|
|
|
b-len:number <- length b:address:array:character/deref
|
2015-04-03 19:53:33 +00:00
|
|
|
# compare lengths
|
|
|
|
{
|
2015-04-12 05:24:33 +00:00
|
|
|
trace [string-equal], [comparing lengths]
|
2015-05-13 17:03:26 +00:00
|
|
|
length-equal?:boolean <- equal a-len:number, b-len:number
|
2015-04-03 19:53:33 +00:00
|
|
|
break-if length-equal?:boolean
|
|
|
|
reply 0:literal
|
|
|
|
}
|
|
|
|
# compare each corresponding character
|
2015-04-12 05:24:33 +00:00
|
|
|
trace [string-equal], [comparing characters]
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- copy 0:literal
|
2015-04-03 19:53:33 +00:00
|
|
|
{
|
2015-05-13 17:03:26 +00:00
|
|
|
done?:boolean <- greater-or-equal i:number, a-len:number
|
2015-04-03 19:53:33 +00:00
|
|
|
break-if done?:boolean
|
2015-05-13 17:03:26 +00:00
|
|
|
a2:character <- index a:address:array:character/deref, i:number
|
|
|
|
b2:character <- index b:address:array:character/deref, i:number
|
2015-04-03 19:53:33 +00:00
|
|
|
{
|
|
|
|
chars-match?:boolean <- equal a2:character, b2:character
|
|
|
|
break-if chars-match?:boolean
|
|
|
|
reply 0:literal
|
|
|
|
}
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-03 19:53:33 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
reply 1:literal
|
|
|
|
]
|
2015-04-06 19:02:38 +00:00
|
|
|
|
|
|
|
scenario string-equal-reflexive [
|
|
|
|
run [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-06 19:02:38 +00:00
|
|
|
x:address:array:character <- new [abc]
|
|
|
|
3:boolean/raw <- string-equal x:address:array:character, x:address:array:character
|
|
|
|
]
|
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-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-06 19:02:38 +00:00
|
|
|
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
|
|
|
|
]
|
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-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-06 19:02:38 +00:00
|
|
|
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
|
|
|
|
]
|
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-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-08 07:47:04 +00:00
|
|
|
x:address:array:character <- new []
|
|
|
|
y:address:array:character <- new [abcd]
|
|
|
|
3:boolean/raw <- string-equal x:address:array:character, y:address:array:character
|
|
|
|
]
|
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-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-08 07:47:04 +00:00
|
|
|
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
|
|
|
|
]
|
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
|
|
|
|
]
|
|
|
|
|
|
|
|
recipe init-buffer [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-05-07 22:06:53 +00:00
|
|
|
#? $print default-space:address:array:location, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-04-18 03:36:25 +00:00
|
|
|
result:address:buffer <- new buffer:type
|
2015-05-13 17:03:26 +00:00
|
|
|
len:address:number <- get-address result:address:buffer/deref, length:offset
|
|
|
|
len:address:number/deref <- copy 0:literal
|
2015-04-18 03:36:25 +00:00
|
|
|
s:address:address:array:character <- get-address result:address:buffer/deref, data:offset
|
2015-05-13 17:03:26 +00:00
|
|
|
capacity:number <- next-ingredient
|
|
|
|
s:address:address:array:character/deref <- new character:type, capacity:number
|
2015-05-07 22:06:53 +00:00
|
|
|
#? $print s:address:address:array:character/deref, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-04-18 03:36:25 +00:00
|
|
|
reply result:address:buffer
|
|
|
|
]
|
2015-04-18 04:51:13 +00:00
|
|
|
|
|
|
|
recipe grow-buffer [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
|
|
|
# double buffer size
|
|
|
|
x:address:address:array:character <- get-address in:address:buffer/deref, data:offset
|
2015-05-13 17:03:26 +00:00
|
|
|
oldlen:number <- length x:address:address:array:character/deref/deref
|
|
|
|
newlen:number <- multiply oldlen:number, 2:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
olddata:address:array:character <- copy x:address:address:array:character/deref
|
2015-05-13 17:03:26 +00:00
|
|
|
x:address:address:array:character/deref <- new character:type, newlen:number
|
2015-04-18 04:51:13 +00:00
|
|
|
# copy old contents
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- copy 0:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
{
|
2015-05-13 17:03:26 +00:00
|
|
|
done?:boolean <- greater-or-equal i:number, oldlen:number
|
2015-04-18 04:51:13 +00:00
|
|
|
break-if done?:boolean
|
2015-05-13 17:03:26 +00:00
|
|
|
src:character <- index olddata:address:array:character/deref, i:number
|
|
|
|
dest:address:character <- index-address x:address:address:array:character/deref/deref, i:number
|
2015-04-18 04:51:13 +00:00
|
|
|
dest:address:character/deref <- copy src:character
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
reply in:address:buffer
|
|
|
|
]
|
|
|
|
|
|
|
|
recipe buffer-full? [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
2015-05-13 17:03:26 +00:00
|
|
|
len:number <- get in:address:buffer/deref, length:offset
|
2015-04-18 04:51:13 +00:00
|
|
|
s:address:array:character <- get in:address:buffer/deref, data:offset
|
2015-05-13 17:03:26 +00:00
|
|
|
capacity:number <- length s:address:array:character/deref
|
|
|
|
result:boolean <- greater-or-equal len:number, capacity:number
|
2015-04-18 04:51:13 +00:00
|
|
|
reply result:boolean
|
|
|
|
]
|
|
|
|
|
|
|
|
# in:address:buffer <- buffer-append in:address:buffer, c:character
|
|
|
|
recipe buffer-append [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
in:address:buffer <- next-ingredient
|
|
|
|
c:character <- next-ingredient
|
|
|
|
{
|
|
|
|
# grow buffer if necessary
|
|
|
|
full?:boolean <- buffer-full? in:address:buffer
|
|
|
|
break-unless full?:boolean
|
|
|
|
in:address:buffer <- grow-buffer in:address:buffer
|
|
|
|
}
|
2015-05-13 17:03:26 +00:00
|
|
|
len:address:number <- get-address in:address:buffer/deref, length:offset
|
2015-04-18 04:51:13 +00:00
|
|
|
s:address:array:character <- get in:address:buffer/deref, data:offset
|
2015-05-13 17:03:26 +00:00
|
|
|
dest:address:character <- index-address s:address:array:character/deref, len:address:number/deref
|
2015-04-18 04:51:13 +00:00
|
|
|
dest:address:character/deref <- copy c:character
|
2015-05-13 17:03:26 +00:00
|
|
|
len:address:number/deref <- add len:address:number/deref, 1:literal
|
2015-04-28 19:20:01 +00:00
|
|
|
reply in:address:buffer/same-as-ingredient:0
|
2015-04-18 04:51:13 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
scenario buffer-append-works [
|
|
|
|
run [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-18 04:51:13 +00:00
|
|
|
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 # 'a'
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 98:literal # 'b'
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 99:literal # 'c'
|
|
|
|
s2:address:array:character <- get x:address:buffer/deref, data:offset
|
|
|
|
1:boolean/raw <- equal s1:address:array:character, s2:address:array:character
|
2015-05-07 22:06:53 +00:00
|
|
|
#? $print s2:address:array:character, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-05-13 17:03:26 +00:00
|
|
|
#? $print 1060:number/raw, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-05-13 17:03:26 +00:00
|
|
|
#? $print 1061:number/raw, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-05-13 17:03:26 +00:00
|
|
|
#? $print 1062:number/raw, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-05-13 17:03:26 +00:00
|
|
|
#? $print 1063:number/raw, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-05-13 17:03:26 +00:00
|
|
|
#? $print 1064:number/raw, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
2015-05-13 17:03:26 +00:00
|
|
|
#? $print 1065:number/raw, [
|
2015-04-18 04:51:13 +00:00
|
|
|
#? ]
|
|
|
|
2:array:character/raw <- copy s2:address:array:character/deref
|
2015-04-18 15:34:48 +00:00
|
|
|
+buffer-filled
|
|
|
|
x:address:buffer <- buffer-append x:address:buffer, 100:literal # 'd'
|
|
|
|
s3:address:array:character <- get x:address:buffer/deref, data:offset
|
|
|
|
10:boolean/raw <- equal s1:address:array:character, s3:address:array:character
|
2015-05-13 17:03:26 +00:00
|
|
|
11:number/raw <- get x:address:buffer/deref, length:offset
|
2015-04-18 15:34:48 +00:00
|
|
|
12:array:character/raw <- copy s3:address:array:character/deref
|
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-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-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-05-13 17:03:26 +00:00
|
|
|
n:number <- next-ingredient
|
2015-04-19 03:11:59 +00:00
|
|
|
# is it zero?
|
|
|
|
{
|
2015-05-13 17:03:26 +00:00
|
|
|
break-if n:number
|
2015-04-19 03:11:59 +00:00
|
|
|
result:address:array:character <- new [0]
|
|
|
|
reply result:address:array:character
|
|
|
|
}
|
|
|
|
# save sign
|
|
|
|
negate-result:boolean <- copy 0:literal
|
|
|
|
{
|
2015-05-13 17:03:26 +00:00
|
|
|
negative?:boolean <- lesser-than n:number, 0:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
break-unless negative?:boolean
|
|
|
|
negate-result:boolean <- copy 1:literal
|
2015-05-13 17:03:26 +00:00
|
|
|
n:number <- multiply n:number, -1:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
}
|
|
|
|
# add digits from right to left into intermediate buffer
|
|
|
|
tmp:address:buffer <- init-buffer 30:literal
|
2015-05-13 17:03:26 +00:00
|
|
|
digit-base:number <- copy 48:literal # '0'
|
2015-04-19 03:11:59 +00:00
|
|
|
{
|
2015-05-13 17:03:26 +00:00
|
|
|
done?:boolean <- equal n:number, 0:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
break-if done?:boolean
|
2015-05-13 17:03:26 +00:00
|
|
|
n:number, digit:number <- divide-with-remainder n:number, 10:literal
|
|
|
|
c:character <- add digit-base:number, digit:number
|
2015-04-19 03:11:59 +00:00
|
|
|
tmp:address:buffer <- buffer-append tmp:address:buffer, c:character
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
# add sign
|
|
|
|
{
|
|
|
|
break-unless negate-result:boolean
|
|
|
|
tmp:address:buffer <- buffer-append tmp:address:buffer, 45:literal # '-'
|
|
|
|
}
|
|
|
|
# reverse buffer into string result
|
2015-05-13 17:03:26 +00:00
|
|
|
len:number <- get tmp:address:buffer/deref, length:offset
|
2015-04-28 19:10:30 +00:00
|
|
|
buf:address:array:character <- get tmp:address:buffer/deref, data:offset
|
2015-05-13 17:03:26 +00:00
|
|
|
result:address:array:character <- new character:type, len:number
|
|
|
|
i:number <- subtract len:number, 1:literal
|
|
|
|
j:number <- copy 0:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
{
|
|
|
|
# while i >= 0
|
2015-05-13 17:03:26 +00:00
|
|
|
done?:boolean <- lesser-than i:number, 0:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
break-if done?:boolean
|
|
|
|
# result[j] = tmp[i]
|
2015-05-13 17:03:26 +00:00
|
|
|
src:character <- index buf:address:array:character/deref, i:number
|
|
|
|
dest:address:character <- index-address result:address:array:character/deref, j:number
|
2015-04-19 03:11:59 +00:00
|
|
|
dest:address:character/deref <- copy src:character
|
|
|
|
# ++i
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- subtract i:number, 1:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
# --j
|
2015-05-13 17:03:26 +00:00
|
|
|
j:number <- add j:number, 1:literal
|
2015-04-19 03:11:59 +00:00
|
|
|
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
|
|
|
|
]
|
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 [
|
|
|
|
1:address:array:character/raw <- integer-to-decimal-string 234:literal
|
|
|
|
2:array:character/raw <- copy 1:address:array:character/deref/raw
|
|
|
|
]
|
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 [
|
|
|
|
1:address:array:character/raw <- integer-to-decimal-string -1:literal
|
|
|
|
2:array:character/raw <- copy 1:address:array:character/deref/raw
|
|
|
|
]
|
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
|
|
|
|
|
|
|
recipe string-append [
|
2015-04-19 07:21:24 +00:00
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
# result = new string[a.length + b.length]
|
|
|
|
a:address:array:character <- next-ingredient
|
2015-05-13 17:03:26 +00:00
|
|
|
a-len:number <- length a:address:array:character/deref
|
2015-04-19 07:13:08 +00:00
|
|
|
b:address:array:character <- next-ingredient
|
2015-05-13 17:03:26 +00:00
|
|
|
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
|
2015-04-19 07:13:08 +00:00
|
|
|
# copy a into result
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- copy 0:literal
|
|
|
|
i:number <- copy 0:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
{
|
|
|
|
# while i < a.length
|
2015-05-13 17:03:26 +00:00
|
|
|
a-done?:boolean <- greater-or-equal i:number, a-len:number
|
2015-04-19 07:13:08 +00:00
|
|
|
break-if a-done?:boolean
|
|
|
|
# result[result-idx] = a[i]
|
2015-05-13 17:03:26 +00:00
|
|
|
out:address:character <- index-address result:address:array:character/deref, result-idx:number
|
|
|
|
in:character <- index a:address:array:character/deref, i:number
|
2015-04-19 07:13:08 +00:00
|
|
|
out:address:character/deref <- copy in:character
|
|
|
|
# ++i
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
# ++result-idx
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- add result-idx:number, 1:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# copy b into result
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- copy 0:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
{
|
|
|
|
# while i < b.length
|
2015-05-13 17:03:26 +00:00
|
|
|
b-done?:boolean <- greater-or-equal i:number, b-len:number
|
2015-04-19 07:13:08 +00:00
|
|
|
break-if b-done?:boolean
|
|
|
|
# result[result-idx] = a[i]
|
2015-05-13 17:03:26 +00:00
|
|
|
out:address:character <- index-address result:address:array:character/deref, result-idx:number
|
|
|
|
in:character <- index b:address:array:character/deref, i:number
|
2015-04-19 07:13:08 +00:00
|
|
|
out:address:character/deref <- copy in:character
|
|
|
|
# ++i
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
# ++result-idx
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- add result-idx:number, 1:literal
|
2015-04-19 07:13:08 +00:00
|
|
|
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
|
|
|
|
]
|
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 [
|
|
|
|
default-space:array:address:location <- new location:type, 60:literal
|
|
|
|
template:address:array:character <- next-ingredient
|
|
|
|
# compute result-len, space to allocate for result
|
2015-05-13 17:03:26 +00:00
|
|
|
tem-len:number <- length template:address:array:character/deref
|
|
|
|
result-len:number <- copy tem-len:number
|
2015-04-20 17:25:02 +00:00
|
|
|
{
|
|
|
|
# while arg received
|
|
|
|
a:address:array:character, arg-received?:boolean <- next-ingredient
|
|
|
|
break-unless arg-received?:boolean
|
|
|
|
# result-len = result-len + arg.length - 1 for the 'underscore' being replaced
|
2015-05-13 17:03:26 +00:00
|
|
|
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
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
2015-05-14 19:29:21 +00:00
|
|
|
#? $print tem-len:number, [ ], $result-len:number, [
|
2015-04-20 17:25:02 +00:00
|
|
|
#? ] #? 1
|
|
|
|
rewind-ingredients
|
|
|
|
_ <- next-ingredient # skip template
|
|
|
|
# result = new array:character[result-len]
|
2015-05-13 17:03:26 +00:00
|
|
|
result:address:array:character <- new character:type, result-len:number
|
2015-04-20 17:25:02 +00:00
|
|
|
# repeatedly copy sections of template and 'holes' into result
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- copy 0:literal
|
|
|
|
i:number <- copy 0:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
{
|
|
|
|
# while arg received
|
|
|
|
a:address:array:character, arg-received?:boolean <- next-ingredient
|
|
|
|
break-unless arg-received?:boolean
|
|
|
|
# copy template into result until '_'
|
|
|
|
{
|
|
|
|
# while i < template.length
|
2015-05-13 17:03:26 +00:00
|
|
|
tem-done?:boolean <- greater-or-equal i:number, tem-len:number
|
2015-04-20 17:25:02 +00:00
|
|
|
break-if tem-done?:boolean, 2:blocks
|
|
|
|
# while template[i] != '_'
|
2015-05-13 17:03:26 +00:00
|
|
|
in:character <- index template:address:array:character/deref, i:number
|
2015-04-20 17:25:02 +00:00
|
|
|
underscore?:boolean <- equal in:character, 95:literal # '_'
|
|
|
|
break-if underscore?:boolean
|
|
|
|
# result[result-idx] = template[i]
|
2015-05-13 17:03:26 +00:00
|
|
|
out:address:character <- index-address result:address:array:character/deref, result-idx:number
|
2015-04-20 17:25:02 +00:00
|
|
|
out:address:character/deref <- copy in:character
|
|
|
|
# ++i
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
# ++result-idx
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- add result-idx:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# copy 'a' into result
|
2015-05-13 17:03:26 +00:00
|
|
|
j:number <- copy 0:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
{
|
|
|
|
# while j < a.length
|
2015-05-13 17:03:26 +00:00
|
|
|
arg-done?:boolean <- greater-or-equal j:number, a-len:number
|
2015-04-20 17:25:02 +00:00
|
|
|
break-if arg-done?:boolean
|
|
|
|
# result[result-idx] = a[j]
|
2015-05-13 17:03:26 +00:00
|
|
|
in:character <- index a:address:array:character/deref, j:number
|
|
|
|
out:address:character <- index-address result:address:array:character/deref, result-idx:number
|
2015-04-20 17:25:02 +00:00
|
|
|
out:address:character/deref <- copy in:character
|
|
|
|
# ++j
|
2015-05-13 17:03:26 +00:00
|
|
|
j:number <- add j:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
# ++result-idx
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- add result-idx:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
# skip '_' in template
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
loop # interpolate next arg
|
|
|
|
}
|
|
|
|
# done with holes; copy rest of template directly into result
|
|
|
|
{
|
|
|
|
# while i < template.length
|
2015-05-13 17:03:26 +00:00
|
|
|
tem-done?:boolean <- greater-or-equal i:number, tem-len:number
|
2015-04-20 17:25:02 +00:00
|
|
|
break-if tem-done?:boolean
|
|
|
|
# result[result-idx] = template[i]
|
2015-05-13 17:03:26 +00:00
|
|
|
in:character <- index template:address:array:character/deref, i:number
|
|
|
|
out:address:character <- index-address result:address:array:character/deref, result-idx:number
|
2015-04-20 17:25:02 +00:00
|
|
|
out:address:character/deref <- copy in:character
|
|
|
|
# ++i
|
2015-05-13 17:03:26 +00:00
|
|
|
i:number <- add i:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
# ++result-idx
|
2015-05-13 17:03:26 +00:00
|
|
|
result-idx:number <- add result-idx:number, 1:literal
|
2015-04-20 17:25:02 +00:00
|
|
|
loop
|
|
|
|
}
|
|
|
|
reply result:address:array:character
|
|
|
|
]
|
|
|
|
|
|
|
|
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
|
|
|
|
4:array:character/raw <- copy 3:address:array:character/raw/deref
|
|
|
|
]
|
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
|
|
|
|
4:array:character/raw <- copy 3:address:array:character/raw/deref
|
|
|
|
]
|
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
|
|
|
|
4:array:character/raw <- copy 3:address:array:character/raw/deref
|
|
|
|
]
|
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
|
|
|
]
|
|
|
|
]
|