From c43164727bfbe192763292e23dad983fbf09d18e Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Wed, 19 Apr 2017 00:39:42 -0700 Subject: 3837 --- html/061text.mu.html | 2473 +++++++++++++++++++++++++------------------------- 1 file changed, 1249 insertions(+), 1224 deletions(-) (limited to 'html/061text.mu.html') diff --git a/html/061text.mu.html b/html/061text.mu.html index bc269d39..ecaab4cc 100644 --- a/html/061text.mu.html +++ b/html/061text.mu.html @@ -209,17 +209,17 @@ if ('onhashchange' in window) { 147 local-scope 148 load-ingredients 149 # double buffer size - 150 olddata:text <- get *buf, data:offset + 150 olddata:&:@:_elem <- get *buf, data:offset 151 oldlen:num <- length *olddata 152 newlen:num <- multiply oldlen, 2 - 153 newdata:text <- new character:type, newlen + 153 newdata:&:@:_elem <- new _elem:type, newlen 154 *buf <- put *buf, data:offset, newdata 155 # copy old contents 156 i:num <- copy 0 157 { 158 ¦ done?:bool <- greater-or-equal i, oldlen 159 ¦ break-if done? - 160 ¦ src:char <- index *olddata, i + 160 ¦ src:_elem <- index *olddata, i 161 ¦ *newdata <- put-index *newdata, i, src 162 ¦ i <- add i, 1 163 ¦ loop @@ -230,618 +230,618 @@ if ('onhashchange' in window) { 168 local-scope 169 load-ingredients 170 len:num <- get *in, length:offset - 171 s:text <- get *in, data:offset + 171 s:&:@:_elem <- get *in, data:offset 172 capacity:num <- length *s 173 result <- greater-or-equal len, capacity 174 ] 175 - 176 # most broadly applicable definition of append to a buffer: just call to-text - 177 def append buf:&:buffer:char, x:_elem -> buf:&:buffer:char [ + 176 # most broadly applicable definition of append to a buffer + 177 def append buf:&:buffer:_elem, x:_elem -> buf:&:buffer:_elem [ 178 local-scope 179 load-ingredients - 180 text:text <- to-text x - 181 len:num <- length *text - 182 i:num <- copy 0 - 183 { - 184 ¦ done?:bool <- greater-or-equal i, len - 185 ¦ break-if done? - 186 ¦ c:char <- index *text, i - 187 ¦ buf <- append buf, c - 188 ¦ i <- add i, 1 - 189 ¦ loop - 190 } + 180 len:num <- get *buf, length:offset + 181 { + 182 ¦ # grow buffer if necessary + 183 ¦ full?:bool <- buffer-full? buf + 184 ¦ break-unless full? + 185 ¦ buf <- grow-buffer buf + 186 } + 187 s:&:@:_elem <- get *buf, data:offset + 188 *s <- put-index *s, len, x + 189 len <- add len, 1 + 190 *buf <- put *buf, length:offset, len 191 ] 192 - 193 def append buf:&:buffer:char, c:char -> buf:&:buffer:char [ - 194 local-scope - 195 load-ingredients - 196 len:num <- get *buf, length:offset - 197 { - 198 ¦ # backspace? just drop last character if it exists and return - 199 ¦ backspace?:bool <- equal c, 8/backspace - 200 ¦ break-unless backspace? - 201 ¦ empty?:bool <- lesser-or-equal len, 0 - 202 ¦ return-if empty? - 203 ¦ len <- subtract len, 1 - 204 ¦ *buf <- put *buf, length:offset, len - 205 ¦ return - 206 } - 207 { - 208 ¦ # grow buffer if necessary - 209 ¦ full?:bool <- buffer-full? buf - 210 ¦ break-unless full? - 211 ¦ buf <- grow-buffer buf - 212 } - 213 s:text <- get *buf, data:offset - 214 *s <- put-index *s, len, c - 215 len <- add len, 1 - 216 *buf <- put *buf, length:offset, len - 217 ] - 218 - 219 def append buf:&:buffer:char, t:text -> buf:&:buffer:char [ - 220 local-scope - 221 load-ingredients - 222 len:num <- length *t - 223 i:num <- copy 0 - 224 { - 225 ¦ done?:bool <- greater-or-equal i, len - 226 ¦ break-if done? - 227 ¦ c:char <- index *t, i - 228 ¦ buf <- append buf, c - 229 ¦ i <- add i, 1 - 230 ¦ loop + 193 # most broadly applicable definition of append to a buffer of characters: just + 194 # call to-text + 195 def append buf:&:buffer:char, x:_elem -> buf:&:buffer:char [ + 196 local-scope + 197 load-ingredients + 198 text:text <- to-text x + 199 len:num <- length *text + 200 i:num <- copy 0 + 201 { + 202 ¦ done?:bool <- greater-or-equal i, len + 203 ¦ break-if done? + 204 ¦ c:char <- index *text, i + 205 ¦ buf <- append buf, c + 206 ¦ i <- add i, 1 + 207 ¦ loop + 208 } + 209 ] + 210 + 211 # specialization for characters that is backspace-aware + 212 def append buf:&:buffer:char, c:char -> buf:&:buffer:char [ + 213 local-scope + 214 load-ingredients + 215 len:num <- get *buf, length:offset + 216 { + 217 ¦ # backspace? just drop last character if it exists and return + 218 ¦ backspace?:bool <- equal c, 8/backspace + 219 ¦ break-unless backspace? + 220 ¦ empty?:bool <- lesser-or-equal len, 0 + 221 ¦ return-if empty? + 222 ¦ len <- subtract len, 1 + 223 ¦ *buf <- put *buf, length:offset, len + 224 ¦ return + 225 } + 226 { + 227 ¦ # grow buffer if necessary + 228 ¦ full?:bool <- buffer-full? buf + 229 ¦ break-unless full? + 230 ¦ buf <- grow-buffer buf 231 } - 232 ] - 233 - 234 scenario append-to-empty-buffer [ - 235 local-scope - 236 x:&:buffer:char <- new-buffer - 237 run [ - 238 ¦ c:char <- copy 97/a - 239 ¦ x <- append x, c - 240 ¦ 10:num/raw <- get *x, length:offset - 241 ¦ s:text <- get *x, data:offset - 242 ¦ 11:char/raw <- index *s, 0 - 243 ¦ 12:char/raw <- index *s, 1 - 244 ] - 245 memory-should-contain [ - 246 ¦ 10 <- 1 # buffer length - 247 ¦ 11 <- 97 # a - 248 ¦ 12 <- 0 # rest of buffer is empty - 249 ] - 250 ] - 251 - 252 scenario append-to-buffer [ - 253 local-scope - 254 x:&:buffer:char <- new-buffer - 255 c:char <- copy 97/a - 256 x <- append x, c - 257 run [ - 258 ¦ c <- copy 98/b - 259 ¦ x <- append x, c - 260 ¦ 10:num/raw <- get *x, length:offset - 261 ¦ s:text <- get *x, data:offset - 262 ¦ 11:char/raw <- index *s, 0 - 263 ¦ 12:char/raw <- index *s, 1 - 264 ¦ 13:char/raw <- index *s, 2 - 265 ] - 266 memory-should-contain [ - 267 ¦ 10 <- 2 # buffer length - 268 ¦ 11 <- 97 # a - 269 ¦ 12 <- 98 # b - 270 ¦ 13 <- 0 # rest of buffer is empty - 271 ] - 272 ] - 273 - 274 scenario append-grows-buffer [ - 275 local-scope - 276 x:&:buffer:char <- new-buffer 3 - 277 s1:text <- get *x, data:offset - 278 x <- append x, [abc] # buffer is now full - 279 s2:text <- get *x, data:offset - 280 run [ - 281 ¦ 10:bool/raw <- equal s1, s2 - 282 ¦ 11:@:char/raw <- copy *s2 - 283 ¦ +buffer-filled - 284 ¦ c:char <- copy 100/d - 285 ¦ x <- append x, c - 286 ¦ s3:text <- get *x, data:offset - 287 ¦ 20:bool/raw <- equal s1, s3 - 288 ¦ 21:num/raw <- get *x, length:offset - 289 ¦ 30:@:char/raw <- copy *s3 + 232 s:text <- get *buf, data:offset + 233 *s <- put-index *s, len, c + 234 len <- add len, 1 + 235 *buf <- put *buf, length:offset, len + 236 ] + 237 + 238 def append buf:&:buffer:char, t:text -> buf:&:buffer:char [ + 239 local-scope + 240 load-ingredients + 241 len:num <- length *t + 242 i:num <- copy 0 + 243 { + 244 ¦ done?:bool <- greater-or-equal i, len + 245 ¦ break-if done? + 246 ¦ c:char <- index *t, i + 247 ¦ buf <- append buf, c + 248 ¦ i <- add i, 1 + 249 ¦ loop + 250 } + 251 ] + 252 + 253 scenario append-to-empty-buffer [ + 254 local-scope + 255 x:&:buffer:char <- new-buffer + 256 run [ + 257 ¦ c:char <- copy 97/a + 258 ¦ x <- append x, c + 259 ¦ 10:num/raw <- get *x, length:offset + 260 ¦ s:text <- get *x, data:offset + 261 ¦ 11:char/raw <- index *s, 0 + 262 ¦ 12:char/raw <- index *s, 1 + 263 ] + 264 memory-should-contain [ + 265 ¦ 10 <- 1 # buffer length + 266 ¦ 11 <- 97 # a + 267 ¦ 12 <- 0 # rest of buffer is empty + 268 ] + 269 ] + 270 + 271 scenario append-to-buffer [ + 272 local-scope + 273 x:&:buffer:char <- new-buffer + 274 c:char <- copy 97/a + 275 x <- append x, c + 276 run [ + 277 ¦ c <- copy 98/b + 278 ¦ x <- append x, c + 279 ¦ 10:num/raw <- get *x, length:offset + 280 ¦ s:text <- get *x, data:offset + 281 ¦ 11:char/raw <- index *s, 0 + 282 ¦ 12:char/raw <- index *s, 1 + 283 ¦ 13:char/raw <- index *s, 2 + 284 ] + 285 memory-should-contain [ + 286 ¦ 10 <- 2 # buffer length + 287 ¦ 11 <- 97 # a + 288 ¦ 12 <- 98 # b + 289 ¦ 13 <- 0 # rest of buffer is empty 290 ] - 291 memory-should-contain [ - 292 ¦ # before +buffer-filled - 293 ¦ 10 <- 1 # no change in data pointer after original append - 294 ¦ 11 <- 3 # size of data - 295 ¦ 12 <- 97 # data - 296 ¦ 13 <- 98 - 297 ¦ 14 <- 99 - 298 ¦ # in the end - 299 ¦ 20 <- 0 # data pointer has grown after second append - 300 ¦ 21 <- 4 # final length - 301 ¦ 30 <- 6 # but data's capacity has doubled - 302 ¦ 31 <- 97 # data - 303 ¦ 32 <- 98 - 304 ¦ 33 <- 99 - 305 ¦ 34 <- 100 - 306 ¦ 35 <- 0 - 307 ¦ 36 <- 0 - 308 ] - 309 ] - 310 - 311 scenario buffer-append-handles-backspace [ - 312 local-scope - 313 x:&:buffer:char <- new-buffer - 314 x <- append x, [ab] - 315 run [ - 316 ¦ c:char <- copy 8/backspace - 317 ¦ x <- append x, c - 318 ¦ s:text <- buffer-to-array x - 319 ¦ 10:@:char/raw <- copy *s - 320 ] - 321 memory-should-contain [ - 322 ¦ 10 <- 1 # length - 323 ¦ 11 <- 97 # contents - 324 ¦ 12 <- 0 - 325 ] - 326 ] - 327 - 328 def buffer-to-array in:&:buffer:_elem -> result:&:@:_elem [ - 329 local-scope - 330 load-ingredients - 331 { - 332 ¦ # propagate null buffer - 333 ¦ break-if in - 334 ¦ return 0 - 335 } - 336 len:num <- get *in, length:offset - 337 s:text <- get *in, data:offset - 338 # we can't just return s because it is usually the wrong length - 339 result <- new character:type, len - 340 i:num <- copy 0 - 341 { - 342 ¦ done?:bool <- greater-or-equal i, len - 343 ¦ break-if done? - 344 ¦ src:char <- index *s, i - 345 ¦ *result <- put-index *result, i, src - 346 ¦ i <- add i, 1 - 347 ¦ loop - 348 } - 349 ] - 350 - 351 # Append any number of texts together. - 352 # A later layer also translates calls to this to implicitly call to-text, so - 353 # append to string becomes effectively dynamically typed. - 354 # - 355 # Beware though: this hack restricts how much 'append' can be overridden. Any - 356 # new variants that match: - 357 # append _:text, ___ - 358 # will never ever get used. - 359 def append first:text -> result:text [ - 360 local-scope - 361 load-ingredients - 362 buf:&:buffer:char <- new-buffer 30 - 363 # append first ingredient - 364 { - 365 ¦ break-unless first - 366 ¦ buf <- append buf, first - 367 } - 368 # append remaining ingredients - 369 { - 370 ¦ arg:text, arg-found?:bool <- next-ingredient - 371 ¦ break-unless arg-found? - 372 ¦ loop-unless arg - 373 ¦ buf <- append buf, arg - 374 ¦ loop - 375 } - 376 result <- buffer-to-array buf - 377 ] - 378 - 379 scenario text-append-1 [ - 380 local-scope - 381 x:text <- new [hello,] - 382 y:text <- new [ world!] - 383 run [ - 384 ¦ z:text <- append x, y - 385 ¦ 10:@:char/raw <- copy *z - 386 ] - 387 memory-should-contain [ - 388 ¦ 10:array:character <- [hello, world!] - 389 ] - 390 ] - 391 - 392 scenario text-append-null [ - 393 local-scope - 394 x:text <- copy 0 - 395 y:text <- new [ world!] - 396 run [ - 397 ¦ z:text <- append x, y - 398 ¦ 10:@:char/raw <- copy *z - 399 ] - 400 memory-should-contain [ - 401 ¦ 10:array:character <- [ world!] - 402 ] - 403 ] - 404 - 405 scenario text-append-null-2 [ - 406 local-scope - 407 x:text <- new [hello,] - 408 y:text <- copy 0 - 409 run [ - 410 ¦ z:text <- append x, y - 411 ¦ 10:@:char/raw <- copy *z - 412 ] - 413 memory-should-contain [ - 414 ¦ 10:array:character <- [hello,] - 415 ] - 416 ] - 417 - 418 scenario text-append-multiary [ - 419 local-scope - 420 x:text <- new [hello, ] - 421 y:text <- new [world] - 422 z:text <- new [!] - 423 run [ - 424 ¦ z:text <- append x, y, z - 425 ¦ 10:@:char/raw <- copy *z - 426 ] - 427 memory-should-contain [ - 428 ¦ 10:array:character <- [hello, world!] - 429 ] - 430 ] - 431 - 432 scenario replace-character-in-text [ - 433 local-scope - 434 x:text <- new [abc] - 435 run [ - 436 ¦ x <- replace x, 98/b, 122/z - 437 ¦ 10:@:char/raw <- copy *x - 438 ] - 439 memory-should-contain [ - 440 ¦ 10:array:character <- [azc] - 441 ] - 442 ] - 443 - 444 def replace s:text, oldc:char, newc:char, from:num/optional -> s:text [ - 445 local-scope - 446 load-ingredients - 447 len:num <- length *s - 448 i:num <- find-next s, oldc, from - 449 done?:bool <- greater-or-equal i, len - 450 return-if done? - 451 *s <- put-index *s, i, newc - 452 i <- add i, 1 - 453 s <- replace s, oldc, newc, i - 454 ] - 455 - 456 scenario replace-character-at-start [ - 457 local-scope - 458 x:text <- new [abc] - 459 run [ - 460 ¦ x <- replace x, 97/a, 122/z - 461 ¦ 10:@:char/raw <- copy *x - 462 ] - 463 memory-should-contain [ - 464 ¦ 10:array:character <- [zbc] - 465 ] - 466 ] - 467 - 468 scenario replace-character-at-end [ - 469 local-scope - 470 x:text <- new [abc] - 471 run [ - 472 ¦ x <- replace x, 99/c, 122/z - 473 ¦ 10:@:char/raw <- copy *x - 474 ] - 475 memory-should-contain [ - 476 ¦ 10:array:character <- [abz] - 477 ] - 478 ] - 479 - 480 scenario replace-character-missing [ - 481 local-scope - 482 x:text <- new [abc] - 483 run [ - 484 ¦ x <- replace x, 100/d, 122/z - 485 ¦ 10:@:char/raw <- copy *x - 486 ] - 487 memory-should-contain [ - 488 ¦ 10:array:character <- [abc] - 489 ] - 490 ] - 491 - 492 scenario replace-all-characters [ - 493 local-scope - 494 x:text <- new [banana] - 495 run [ - 496 ¦ x <- replace x, 97/a, 122/z - 497 ¦ 10:@:char/raw <- copy *x - 498 ] - 499 memory-should-contain [ - 500 ¦ 10:array:character <- [bznznz] - 501 ] - 502 ] - 503 - 504 # replace underscores in first with remaining args - 505 def interpolate template:text -> result:text [ + 291 ] + 292 + 293 scenario append-grows-buffer [ + 294 local-scope + 295 x:&:buffer:char <- new-buffer 3 + 296 s1:text <- get *x, data:offset + 297 x <- append x, [abc] # buffer is now full + 298 s2:text <- get *x, data:offset + 299 run [ + 300 ¦ 10:bool/raw <- equal s1, s2 + 301 ¦ 11:@:char/raw <- copy *s2 + 302 ¦ +buffer-filled + 303 ¦ c:char <- copy 100/d + 304 ¦ x <- append x, c + 305 ¦ s3:text <- get *x, data:offset + 306 ¦ 20:bool/raw <- equal s1, s3 + 307 ¦ 21:num/raw <- get *x, length:offset + 308 ¦ 30:@:char/raw <- copy *s3 + 309 ] + 310 memory-should-contain [ + 311 ¦ # before +buffer-filled + 312 ¦ 10 <- 1 # no change in data pointer after original append + 313 ¦ 11 <- 3 # size of data + 314 ¦ 12 <- 97 # data + 315 ¦ 13 <- 98 + 316 ¦ 14 <- 99 + 317 ¦ # in the end + 318 ¦ 20 <- 0 # data pointer has grown after second append + 319 ¦ 21 <- 4 # final length + 320 ¦ 30 <- 6 # but data's capacity has doubled + 321 ¦ 31 <- 97 # data + 322 ¦ 32 <- 98 + 323 ¦ 33 <- 99 + 324 ¦ 34 <- 100 + 325 ¦ 35 <- 0 + 326 ¦ 36 <- 0 + 327 ] + 328 ] + 329 + 330 scenario buffer-append-handles-backspace [ + 331 local-scope + 332 x:&:buffer:char <- new-buffer + 333 x <- append x, [ab] + 334 run [ + 335 ¦ c:char <- copy 8/backspace + 336 ¦ x <- append x, c + 337 ¦ s:text <- buffer-to-array x + 338 ¦ 10:@:char/raw <- copy *s + 339 ] + 340 memory-should-contain [ + 341 ¦ 10 <- 1 # length + 342 ¦ 11 <- 97 # contents + 343 ¦ 12 <- 0 + 344 ] + 345 ] + 346 + 347 scenario append-to-buffer-of-non-characters [ + 348 local-scope + 349 x:&:buffer:text <- new-buffer 1/capacity + 350 # no errors + 351 ] + 352 + 353 def buffer-to-array in:&:buffer:_elem -> result:&:@:_elem [ + 354 local-scope + 355 load-ingredients + 356 { + 357 ¦ # propagate null buffer + 358 ¦ break-if in + 359 ¦ return 0 + 360 } + 361 len:num <- get *in, length:offset + 362 s:&:@:_elem <- get *in, data:offset + 363 # we can't just return s because it is usually the wrong length + 364 result <- new _elem:type, len + 365 i:num <- copy 0 + 366 { + 367 ¦ done?:bool <- greater-or-equal i, len + 368 ¦ break-if done? + 369 ¦ src:_elem <- index *s, i + 370 ¦ *result <- put-index *result, i, src + 371 ¦ i <- add i, 1 + 372 ¦ loop + 373 } + 374 ] + 375 + 376 # Append any number of texts together. + 377 # A later layer also translates calls to this to implicitly call to-text, so + 378 # append to string becomes effectively dynamically typed. + 379 # + 380 # Beware though: this hack restricts how much 'append' can be overridden. Any + 381 # new variants that match: + 382 # append _:text, ___ + 383 # will never ever get used. + 384 def append first:text -> result:text [ + 385 local-scope + 386 load-ingredients + 387 buf:&:buffer:char <- new-buffer 30 + 388 # append first ingredient + 389 { + 390 ¦ break-unless first + 391 ¦ buf <- append buf, first + 392 } + 393 # append remaining ingredients + 394 { + 395 ¦ arg:text, arg-found?:bool <- next-ingredient + 396 ¦ break-unless arg-found? + 397 ¦ loop-unless arg + 398 ¦ buf <- append buf, arg + 399 ¦ loop + 400 } + 401 result <- buffer-to-array buf + 402 ] + 403 + 404 scenario text-append-1 [ + 405 local-scope + 406 x:text <- new [hello,] + 407 y:text <- new [ world!] + 408 run [ + 409 ¦ z:text <- append x, y + 410 ¦ 10:@:char/raw <- copy *z + 411 ] + 412 memory-should-contain [ + 413 ¦ 10:array:character <- [hello, world!] + 414 ] + 415 ] + 416 + 417 scenario text-append-null [ + 418 local-scope + 419 x:text <- copy 0 + 420 y:text <- new [ world!] + 421 run [ + 422 ¦ z:text <- append x, y + 423 ¦ 10:@:char/raw <- copy *z + 424 ] + 425 memory-should-contain [ + 426 ¦ 10:array:character <- [ world!] + 427 ] + 428 ] + 429 + 430 scenario text-append-null-2 [ + 431 local-scope + 432 x:text <- new [hello,] + 433 y:text <- copy 0 + 434 run [ + 435 ¦ z:text <- append x, y + 436 ¦ 10:@:char/raw <- copy *z + 437 ] + 438 memory-should-contain [ + 439 ¦ 10:array:character <- [hello,] + 440 ] + 441 ] + 442 + 443 scenario text-append-multiary [ + 444 local-scope + 445 x:text <- new [hello, ] + 446 y:text <- new [world] + 447 z:text <- new [!] + 448 run [ + 449 ¦ z:text <- append x, y, z + 450 ¦ 10:@:char/raw <- copy *z + 451 ] + 452 memory-should-contain [ + 453 ¦ 10:array:character <- [hello, world!] + 454 ] + 455 ] + 456 + 457 scenario replace-character-in-text [ + 458 local-scope + 459 x:text <- new [abc] + 460 run [ + 461 ¦ x <- replace x, 98/b, 122/z + 462 ¦ 10:@:char/raw <- copy *x + 463 ] + 464 memory-should-contain [ + 465 ¦ 10:array:character <- [azc] + 466 ] + 467 ] + 468 + 469 def replace s:text, oldc:char, newc:char, from:num/optional -> s:text [ + 470 local-scope + 471 load-ingredients + 472 len:num <- length *s + 473 i:num <- find-next s, oldc, from + 474 done?:bool <- greater-or-equal i, len + 475 return-if done? + 476 *s <- put-index *s, i, newc + 477 i <- add i, 1 + 478 s <- replace s, oldc, newc, i + 479 ] + 480 + 481 scenario replace-character-at-start [ + 482 local-scope + 483 x:text <- new [abc] + 484 run [ + 485 ¦ x <- replace x, 97/a, 122/z + 486 ¦ 10:@:char/raw <- copy *x + 487 ] + 488 memory-should-contain [ + 489 ¦ 10:array:character <- [zbc] + 490 ] + 491 ] + 492 + 493 scenario replace-character-at-end [ + 494 local-scope + 495 x:text <- new [abc] + 496 run [ + 497 ¦ x <- replace x, 99/c, 122/z + 498 ¦ 10:@:char/raw <- copy *x + 499 ] + 500 memory-should-contain [ + 501 ¦ 10:array:character <- [abz] + 502 ] + 503 ] + 504 + 505 scenario replace-character-missing [ 506 local-scope - 507 load-ingredients # consume just the template - 508 # compute result-len, space to allocate for result - 509 tem-len:num <- length *template - 510 result-len:num <- copy tem-len - 511 { - 512 ¦ # while ingredients remain - 513 ¦ a:text, arg-received?:bool <- next-ingredient - 514 ¦ break-unless arg-received? - 515 ¦ # result-len = result-len + arg.length - 1 (for the 'underscore' being replaced) - 516 ¦ a-len:num <- length *a - 517 ¦ result-len <- add result-len, a-len - 518 ¦ result-len <- subtract result-len, 1 - 519 ¦ loop - 520 } - 521 rewind-ingredients - 522 _ <- next-ingredient # skip template - 523 result <- new character:type, result-len - 524 # repeatedly copy sections of template and 'holes' into result - 525 result-idx:num <- copy 0 - 526 i:num <- copy 0 - 527 { - 528 ¦ # while arg received - 529 ¦ a:text, arg-received?:bool <- next-ingredient - 530 ¦ break-unless arg-received? - 531 ¦ # copy template into result until '_' - 532 ¦ { - 533 ¦ ¦ # while i < template.length - 534 ¦ ¦ tem-done?:bool <- greater-or-equal i, tem-len - 535 ¦ ¦ break-if tem-done?, +done - 536 ¦ ¦ # while template[i] != '_' - 537 ¦ ¦ in:char <- index *template, i - 538 ¦ ¦ underscore?:bool <- equal in, 95/_ - 539 ¦ ¦ break-if underscore? - 540 ¦ ¦ # result[result-idx] = template[i] - 541 ¦ ¦ *result <- put-index *result, result-idx, in - 542 ¦ ¦ i <- add i, 1 - 543 ¦ ¦ result-idx <- add result-idx, 1 - 544 ¦ ¦ loop - 545 ¦ } - 546 ¦ # copy 'a' into result - 547 ¦ j:num <- copy 0 - 548 ¦ { - 549 ¦ ¦ # while j < a.length - 550 ¦ ¦ arg-done?:bool <- greater-or-equal j, a-len - 551 ¦ ¦ break-if arg-done? - 552 ¦ ¦ # result[result-idx] = a[j] - 553 ¦ ¦ in:char <- index *a, j - 554 ¦ ¦ *result <- put-index *result, result-idx, in - 555 ¦ ¦ j <- add j, 1 - 556 ¦ ¦ result-idx <- add result-idx, 1 - 557 ¦ ¦ loop - 558 ¦ } - 559 ¦ # skip '_' in template - 560 ¦ i <- add i, 1 - 561 ¦ loop # interpolate next arg - 562 } - 563 +done - 564 # done with holes; copy rest of template directly into result - 565 { - 566 ¦ # while i < template.length - 567 ¦ tem-done?:bool <- greater-or-equal i, tem-len - 568 ¦ break-if tem-done? - 569 ¦ # result[result-idx] = template[i] - 570 ¦ in:char <- index *template, i - 571 ¦ *result <- put-index *result, result-idx, in - 572 ¦ i <- add i, 1 - 573 ¦ result-idx <- add result-idx, 1 - 574 ¦ loop - 575 } - 576 ] - 577 - 578 scenario interpolate-works [ - 579 local-scope - 580 x:text <- new [abc_ghi] - 581 y:text <- new [def] - 582 run [ - 583 ¦ z:text <- interpolate x, y - 584 ¦ 10:@:char/raw <- copy *z - 585 ] - 586 memory-should-contain [ - 587 ¦ 10:array:character <- [abcdefghi] - 588 ] - 589 ] - 590 - 591 scenario interpolate-at-start [ - 592 local-scope - 593 x:text <- new [_, hello!] - 594 y:text <- new [abc] - 595 run [ - 596 ¦ z:text <- interpolate x, y - 597 ¦ 10:@:char/raw <- copy *z - 598 ] - 599 memory-should-contain [ - 600 ¦ 10:array:character <- [abc, hello!] - 601 ¦ 22 <- 0 # out of bounds - 602 ] - 603 ] - 604 - 605 scenario interpolate-at-end [ - 606 local-scope - 607 x:text <- new [hello, _] - 608 y:text <- new [abc] - 609 run [ - 610 ¦ z:text <- interpolate x, y - 611 ¦ 10:@:char/raw <- copy *z - 612 ] - 613 memory-should-contain [ - 614 ¦ 10:array:character <- [hello, abc] - 615 ] - 616 ] - 617 - 618 # result:bool <- space? c:char - 619 def space? c:char -> result:bool [ - 620 local-scope - 621 load-ingredients - 622 # most common case first - 623 result <- equal c, 32/space - 624 return-if result - 625 result <- equal c, 10/newline - 626 return-if result - 627 result <- equal c, 9/tab - 628 return-if result - 629 result <- equal c, 13/carriage-return - 630 return-if result - 631 # remaining uncommon cases in sorted order - 632 # http://unicode.org code-points in unicode-set Z and Pattern_White_Space - 633 result <- equal c, 11/ctrl-k - 634 return-if result - 635 result <- equal c, 12/ctrl-l - 636 return-if result - 637 result <- equal c, 133/ctrl-0085 - 638 return-if result - 639 result <- equal c, 160/no-break-space - 640 return-if result - 641 result <- equal c, 5760/ogham-space-mark - 642 return-if result - 643 result <- equal c, 8192/en-quad - 644 return-if result - 645 result <- equal c, 8193/em-quad - 646 return-if result - 647 result <- equal c, 8194/en-space - 648 return-if result - 649 result <- equal c, 8195/em-space - 650 return-if result - 651 result <- equal c, 8196/three-per-em-space - 652 return-if result - 653 result <- equal c, 8197/four-per-em-space - 654 return-if result - 655 result <- equal c, 8198/six-per-em-space - 656 return-if result - 657 result <- equal c, 8199/figure-space - 658 return-if result - 659 result <- equal c, 8200/punctuation-space - 660 return-if result - 661 result <- equal c, 8201/thin-space - 662 return-if result - 663 result <- equal c, 8202/hair-space - 664 return-if result - 665 result <- equal c, 8206/left-to-right - 666 return-if result - 667 result <- equal c, 8207/right-to-left - 668 return-if result - 669 result <- equal c, 8232/line-separator - 670 return-if result - 671 result <- equal c, 8233/paragraph-separator - 672 return-if result - 673 result <- equal c, 8239/narrow-no-break-space - 674 return-if result - 675 result <- equal c, 8287/medium-mathematical-space - 676 return-if result - 677 result <- equal c, 12288/ideographic-space - 678 ] - 679 - 680 def trim s:text -> result:text [ - 681 local-scope - 682 load-ingredients - 683 len:num <- length *s - 684 # left trim: compute start - 685 start:num <- copy 0 - 686 { - 687 ¦ { - 688 ¦ ¦ at-end?:bool <- greater-or-equal start, len - 689 ¦ ¦ break-unless at-end? - 690 ¦ ¦ result <- new character:type, 0 - 691 ¦ ¦ return - 692 ¦ } - 693 ¦ curr:char <- index *s, start - 694 ¦ whitespace?:bool <- space? curr - 695 ¦ break-unless whitespace? - 696 ¦ start <- add start, 1 - 697 ¦ loop - 698 } - 699 # right trim: compute end - 700 end:num <- subtract len, 1 - 701 { - 702 ¦ not-at-start?:bool <- greater-than end, start - 703 ¦ assert not-at-start?, [end ran up against start] - 704 ¦ curr:char <- index *s, end - 705 ¦ whitespace?:bool <- space? curr - 706 ¦ break-unless whitespace? - 707 ¦ end <- subtract end, 1 - 708 ¦ loop - 709 } - 710 # result = new character[end+1 - start] - 711 new-len:num <- subtract end, start, -1 - 712 result:text <- new character:type, new-len - 713 # copy the untrimmed parts between start and end - 714 i:num <- copy start - 715 j:num <- copy 0 - 716 { - 717 ¦ # while i <= end - 718 ¦ done?:bool <- greater-than i, end - 719 ¦ break-if done? - 720 ¦ # result[j] = s[i] - 721 ¦ src:char <- index *s, i - 722 ¦ *result <- put-index *result, j, src - 723 ¦ i <- add i, 1 - 724 ¦ j <- add j, 1 - 725 ¦ loop - 726 } - 727 ] - 728 - 729 scenario trim-unmodified [ - 730 local-scope - 731 x:text <- new [abc] - 732 run [ - 733 ¦ y:text <- trim x - 734 ¦ 1:@:char/raw <- copy *y - 735 ] - 736 memory-should-contain [ - 737 ¦ 1:array:character <- [abc] - 738 ] - 739 ] - 740 - 741 scenario trim-left [ - 742 local-scope - 743 x:text <- new [ abc] - 744 run [ - 745 ¦ y:text <- trim x - 746 ¦ 1:@:char/raw <- copy *y - 747 ] - 748 memory-should-contain [ - 749 ¦ 1:array:character <- [abc] - 750 ] - 751 ] - 752 - 753 scenario trim-right [ - 754 local-scope - 755 x:text <- new [abc ] - 756 run [ - 757 ¦ y:text <- trim x - 758 ¦ 1:@:char/raw <- copy *y - 759 ] - 760 memory-should-contain [ - 761 ¦ 1:array:character <- [abc] - 762 ] - 763 ] - 764 - 765 scenario trim-left-right [ - 766 local-scope - 767 x:text <- new [ abc ] - 768 run [ - 769 ¦ y:text <- trim x - 770 ¦ 1:@:char/raw <- copy *y - 771 ] - 772 memory-should-contain [ - 773 ¦ 1:array:character <- [abc] - 774 ] - 775 ] - 776 - 777 scenario trim-newline-tab [ - 778 local-scope - 779 x:text <- new [ abc - 780 ] + 507 x:text <- new [abc] + 508 run [ + 509 ¦ x <- replace x, 100/d, 122/z + 510 ¦ 10:@:char/raw <- copy *x + 511 ] + 512 memory-should-contain [ + 513 ¦ 10:array:character <- [abc] + 514 ] + 515 ] + 516 + 517 scenario replace-all-characters [ + 518 local-scope + 519 x:text <- new [banana] + 520 run [ + 521 ¦ x <- replace x, 97/a, 122/z + 522 ¦ 10:@:char/raw <- copy *x + 523 ] + 524 memory-should-contain [ + 525 ¦ 10:array:character <- [bznznz] + 526 ] + 527 ] + 528 + 529 # replace underscores in first with remaining args + 530 def interpolate template:text -> result:text [ + 531 local-scope + 532 load-ingredients # consume just the template + 533 # compute result-len, space to allocate for result + 534 tem-len:num <- length *template + 535 result-len:num <- copy tem-len + 536 { + 537 ¦ # while ingredients remain + 538 ¦ a:text, arg-received?:bool <- next-ingredient + 539 ¦ break-unless arg-received? + 540 ¦ # result-len = result-len + arg.length - 1 (for the 'underscore' being replaced) + 541 ¦ a-len:num <- length *a + 542 ¦ result-len <- add result-len, a-len + 543 ¦ result-len <- subtract result-len, 1 + 544 ¦ loop + 545 } + 546 rewind-ingredients + 547 _ <- next-ingredient # skip template + 548 result <- new character:type, result-len + 549 # repeatedly copy sections of template and 'holes' into result + 550 result-idx:num <- copy 0 + 551 i:num <- copy 0 + 552 { + 553 ¦ # while arg received + 554 ¦ a:text, arg-received?:bool <- next-ingredient + 555 ¦ break-unless arg-received? + 556 ¦ # copy template into result until '_' + 557 ¦ { + 558 ¦ ¦ # while i < template.length + 559 ¦ ¦ tem-done?:bool <- greater-or-equal i, tem-len + 560 ¦ ¦ break-if tem-done?, +done + 561 ¦ ¦ # while template[i] != '_' + 562 ¦ ¦ in:char <- index *template, i + 563 ¦ ¦ underscore?:bool <- equal in, 95/_ + 564 ¦ ¦ break-if underscore? + 565 ¦ ¦ # result[result-idx] = template[i] + 566 ¦ ¦ *result <- put-index *result, result-idx, in + 567 ¦ ¦ i <- add i, 1 + 568 ¦ ¦ result-idx <- add result-idx, 1 + 569 ¦ ¦ loop + 570 ¦ } + 571 ¦ # copy 'a' into result + 572 ¦ j:num <- copy 0 + 573 ¦ { + 574 ¦ ¦ # while j < a.length + 575 ¦ ¦ arg-done?:bool <- greater-or-equal j, a-len + 576 ¦ ¦ break-if arg-done? + 577 ¦ ¦ # result[result-idx] = a[j] + 578 ¦ ¦ in:char <- index *a, j + 579 ¦ ¦ *result <- put-index *result, result-idx, in + 580 ¦ ¦ j <- add j, 1 + 581 ¦ ¦ result-idx <- add result-idx, 1 + 582 ¦ ¦ loop + 583 ¦ } + 584 ¦ # skip '_' in template + 585 ¦ i <- add i, 1 + 586 ¦ loop # interpolate next arg + 587 } + 588 +done + 589 # done with holes; copy rest of template directly into result + 590 { + 591 ¦ # while i < template.length + 592 ¦ tem-done?:bool <- greater-or-equal i, tem-len + 593 ¦ break-if tem-done? + 594 ¦ # result[result-idx] = template[i] + 595 ¦ in:char <- index *template, i + 596 ¦ *result <- put-index *result, result-idx, in + 597 ¦ i <- add i, 1 + 598 ¦ result-idx <- add result-idx, 1 + 599 ¦ loop + 600 } + 601 ] + 602 + 603 scenario interpolate-works [ + 604 local-scope + 605 x:text <- new [abc_ghi] + 606 y:text <- new [def] + 607 run [ + 608 ¦ z:text <- interpolate x, y + 609 ¦ 10:@:char/raw <- copy *z + 610 ] + 611 memory-should-contain [ + 612 ¦ 10:array:character <- [abcdefghi] + 613 ] + 614 ] + 615 + 616 scenario interpolate-at-start [ + 617 local-scope + 618 x:text <- new [_, hello!] + 619 y:text <- new [abc] + 620 run [ + 621 ¦ z:text <- interpolate x, y + 622 ¦ 10:@:char/raw <- copy *z + 623 ] + 624 memory-should-contain [ + 625 ¦ 10:array:character <- [abc, hello!] + 626 ¦ 22 <- 0 # out of bounds + 627 ] + 628 ] + 629 + 630 scenario interpolate-at-end [ + 631 local-scope + 632 x:text <- new [hello, _] + 633 y:text <- new [abc] + 634 run [ + 635 ¦ z:text <- interpolate x, y + 636 ¦ 10:@:char/raw <- copy *z + 637 ] + 638 memory-should-contain [ + 639 ¦ 10:array:character <- [hello, abc] + 640 ] + 641 ] + 642 + 643 # result:bool <- space? c:char + 644 def space? c:char -> result:bool [ + 645 local-scope + 646 load-ingredients + 647 # most common case first + 648 result <- equal c, 32/space + 649 return-if result + 650 result <- equal c, 10/newline + 651 return-if result + 652 result <- equal c, 9/tab + 653 return-if result + 654 result <- equal c, 13/carriage-return + 655 return-if result + 656 # remaining uncommon cases in sorted order + 657 # http://unicode.org code-points in unicode-set Z and Pattern_White_Space + 658 result <- equal c, 11/ctrl-k + 659 return-if result + 660 result <- equal c, 12/ctrl-l + 661 return-if result + 662 result <- equal c, 133/ctrl-0085 + 663 return-if result + 664 result <- equal c, 160/no-break-space + 665 return-if result + 666 result <- equal c, 5760/ogham-space-mark + 667 return-if result + 668 result <- equal c, 8192/en-quad + 669 return-if result + 670 result <- equal c, 8193/em-quad + 671 return-if result + 672 result <- equal c, 8194/en-space + 673 return-if result + 674 result <- equal c, 8195/em-space + 675 return-if result + 676 result <- equal c, 8196/three-per-em-space + 677 return-if result + 678 result <- equal c, 8197/four-per-em-space + 679 return-if result + 680 result <- equal c, 8198/six-per-em-space + 681 return-if result + 682 result <- equal c, 8199/figure-space + 683 return-if result + 684 result <- equal c, 8200/punctuation-space + 685 return-if result + 686 result <- equal c, 8201/thin-space + 687 return-if result + 688 result <- equal c, 8202/hair-space + 689 return-if result + 690 result <- equal c, 8206/left-to-right + 691 return-if result + 692 result <- equal c, 8207/right-to-left + 693 return-if result + 694 result <- equal c, 8232/line-separator + 695 return-if result + 696 result <- equal c, 8233/paragraph-separator + 697 return-if result + 698 result <- equal c, 8239/narrow-no-break-space + 699 return-if result + 700 result <- equal c, 8287/medium-mathematical-space + 701 return-if result + 702 result <- equal c, 12288/ideographic-space + 703 ] + 704 + 705 def trim s:text -> result:text [ + 706 local-scope + 707 load-ingredients + 708 len:num <- length *s + 709 # left trim: compute start + 710 start:num <- copy 0 + 711 { + 712 ¦ { + 713 ¦ ¦ at-end?:bool <- greater-or-equal start, len + 714 ¦ ¦ break-unless at-end? + 715 ¦ ¦ result <- new character:type, 0 + 716 ¦ ¦ return + 717 ¦ } + 718 ¦ curr:char <- index *s, start + 719 ¦ whitespace?:bool <- space? curr + 720 ¦ break-unless whitespace? + 721 ¦ start <- add start, 1 + 722 ¦ loop + 723 } + 724 # right trim: compute end + 725 end:num <- subtract len, 1 + 726 { + 727 ¦ not-at-start?:bool <- greater-than end, start + 728 ¦ assert not-at-start?, [end ran up against start] + 729 ¦ curr:char <- index *s, end + 730 ¦ whitespace?:bool <- space? curr + 731 ¦ break-unless whitespace? + 732 ¦ end <- subtract end, 1 + 733 ¦ loop + 734 } + 735 # result = new character[end+1 - start] + 736 new-len:num <- subtract end, start, -1 + 737 result:text <- new character:type, new-len + 738 # copy the untrimmed parts between start and end + 739 i:num <- copy start + 740 j:num <- copy 0 + 741 { + 742 ¦ # while i <= end + 743 ¦ done?:bool <- greater-than i, end + 744 ¦ break-if done? + 745 ¦ # result[j] = s[i] + 746 ¦ src:char <- index *s, i + 747 ¦ *result <- put-index *result, j, src + 748 ¦ i <- add i, 1 + 749 ¦ j <- add j, 1 + 750 ¦ loop + 751 } + 752 ] + 753 + 754 scenario trim-unmodified [ + 755 local-scope + 756 x:text <- new [abc] + 757 run [ + 758 ¦ y:text <- trim x + 759 ¦ 1:@:char/raw <- copy *y + 760 ] + 761 memory-should-contain [ + 762 ¦ 1:array:character <- [abc] + 763 ] + 764 ] + 765 + 766 scenario trim-left [ + 767 local-scope + 768 x:text <- new [ abc] + 769 run [ + 770 ¦ y:text <- trim x + 771 ¦ 1:@:char/raw <- copy *y + 772 ] + 773 memory-should-contain [ + 774 ¦ 1:array:character <- [abc] + 775 ] + 776 ] + 777 + 778 scenario trim-right [ + 779 local-scope + 780 x:text <- new [abc ] 781 run [ - 782 ¦ y:text <- trim x + 782 ¦ y:text <- trim x 783 ¦ 1:@:char/raw <- copy *y 784 ] 785 memory-should-contain [ @@ -849,635 +849,660 @@ if ('onhashchange' in window) { 787 ] 788 ] 789 - 790 def find-next text:text, pattern:char, idx:num -> next-index:num [ + 790 scenario trim-left-right [ 791 local-scope - 792 load-ingredients - 793 len:num <- length *text - 794 { - 795 ¦ eof?:bool <- greater-or-equal idx, len - 796 ¦ break-if eof? - 797 ¦ curr:char <- index *text, idx - 798 ¦ found?:bool <- equal curr, pattern - 799 ¦ break-if found? - 800 ¦ idx <- add idx, 1 - 801 ¦ loop - 802 } - 803 return idx - 804 ] - 805 - 806 scenario text-find-next [ - 807 local-scope - 808 x:text <- new [a/b] - 809 run [ - 810 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index - 811 ] - 812 memory-should-contain [ - 813 ¦ 10 <- 1 - 814 ] - 815 ] - 816 - 817 scenario text-find-next-empty [ - 818 local-scope - 819 x:text <- new [] - 820 run [ - 821 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index - 822 ] - 823 memory-should-contain [ - 824 ¦ 10 <- 0 - 825 ] - 826 ] - 827 - 828 scenario text-find-next-initial [ - 829 local-scope - 830 x:text <- new [/abc] - 831 run [ - 832 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index - 833 ] - 834 memory-should-contain [ - 835 ¦ 10 <- 0 # prefix match + 792 x:text <- new [ abc ] + 793 run [ + 794 ¦ y:text <- trim x + 795 ¦ 1:@:char/raw <- copy *y + 796 ] + 797 memory-should-contain [ + 798 ¦ 1:array:character <- [abc] + 799 ] + 800 ] + 801 + 802 scenario trim-newline-tab [ + 803 local-scope + 804 x:text <- new [ abc + 805 ] + 806 run [ + 807 ¦ y:text <- trim x + 808 ¦ 1:@:char/raw <- copy *y + 809 ] + 810 memory-should-contain [ + 811 ¦ 1:array:character <- [abc] + 812 ] + 813 ] + 814 + 815 def find-next text:text, pattern:char, idx:num -> next-index:num [ + 816 local-scope + 817 load-ingredients + 818 len:num <- length *text + 819 { + 820 ¦ eof?:bool <- greater-or-equal idx, len + 821 ¦ break-if eof? + 822 ¦ curr:char <- index *text, idx + 823 ¦ found?:bool <- equal curr, pattern + 824 ¦ break-if found? + 825 ¦ idx <- add idx, 1 + 826 ¦ loop + 827 } + 828 return idx + 829 ] + 830 + 831 scenario text-find-next [ + 832 local-scope + 833 x:text <- new [a/b] + 834 run [ + 835 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index 836 ] - 837 ] - 838 - 839 scenario text-find-next-final [ - 840 local-scope - 841 x:text <- new [abc/] - 842 run [ - 843 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index - 844 ] - 845 memory-should-contain [ - 846 ¦ 10 <- 3 # suffix match + 837 memory-should-contain [ + 838 ¦ 10 <- 1 + 839 ] + 840 ] + 841 + 842 scenario text-find-next-empty [ + 843 local-scope + 844 x:text <- new [] + 845 run [ + 846 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index 847 ] - 848 ] - 849 - 850 scenario text-find-next-missing [ - 851 local-scope - 852 x:text <- new [abcd] - 853 run [ - 854 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index - 855 ] - 856 memory-should-contain [ - 857 ¦ 10 <- 4 # no match + 848 memory-should-contain [ + 849 ¦ 10 <- 0 + 850 ] + 851 ] + 852 + 853 scenario text-find-next-initial [ + 854 local-scope + 855 x:text <- new [/abc] + 856 run [ + 857 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index 858 ] - 859 ] - 860 - 861 scenario text-find-next-invalid-index [ - 862 local-scope - 863 x:text <- new [abc] - 864 run [ - 865 ¦ 10:num/raw <- find-next x, 47/slash, 4/start-index - 866 ] - 867 memory-should-contain [ - 868 ¦ 10 <- 4 # no change + 859 memory-should-contain [ + 860 ¦ 10 <- 0 # prefix match + 861 ] + 862 ] + 863 + 864 scenario text-find-next-final [ + 865 local-scope + 866 x:text <- new [abc/] + 867 run [ + 868 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index 869 ] - 870 ] - 871 - 872 scenario text-find-next-first [ - 873 local-scope - 874 x:text <- new [ab/c/] - 875 run [ - 876 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index - 877 ] - 878 memory-should-contain [ - 879 ¦ 10 <- 2 # first '/' of multiple + 870 memory-should-contain [ + 871 ¦ 10 <- 3 # suffix match + 872 ] + 873 ] + 874 + 875 scenario text-find-next-missing [ + 876 local-scope + 877 x:text <- new [abcd] + 878 run [ + 879 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index 880 ] - 881 ] - 882 - 883 scenario text-find-next-second [ - 884 local-scope - 885 x:text <- new [ab/c/] - 886 run [ - 887 ¦ 10:num/raw <- find-next x, 47/slash, 3/start-index - 888 ] - 889 memory-should-contain [ - 890 ¦ 10 <- 4 # second '/' of multiple + 881 memory-should-contain [ + 882 ¦ 10 <- 4 # no match + 883 ] + 884 ] + 885 + 886 scenario text-find-next-invalid-index [ + 887 local-scope + 888 x:text <- new [abc] + 889 run [ + 890 ¦ 10:num/raw <- find-next x, 47/slash, 4/start-index 891 ] - 892 ] - 893 - 894 # search for a pattern of multiple characters - 895 # fairly dumb algorithm - 896 def find-next text:text, pattern:text, idx:num -> next-index:num [ - 897 local-scope - 898 load-ingredients - 899 first:char <- index *pattern, 0 - 900 # repeatedly check for match at current idx - 901 len:num <- length *text - 902 { - 903 ¦ # does some unnecessary work checking even when there isn't enough of text left - 904 ¦ done?:bool <- greater-or-equal idx, len - 905 ¦ break-if done? - 906 ¦ found?:bool <- match-at text, pattern, idx - 907 ¦ break-if found? - 908 ¦ idx <- add idx, 1 - 909 ¦ # optimization: skip past indices that definitely won't match - 910 ¦ idx <- find-next text, first, idx - 911 ¦ loop - 912 } - 913 return idx - 914 ] - 915 - 916 scenario find-next-text-1 [ - 917 local-scope - 918 x:text <- new [abc] - 919 y:text <- new [bc] - 920 run [ - 921 ¦ 10:num/raw <- find-next x, y, 0 - 922 ] - 923 memory-should-contain [ - 924 ¦ 10 <- 1 - 925 ] - 926 ] - 927 - 928 scenario find-next-text-2 [ - 929 local-scope - 930 x:text <- new [abcd] - 931 y:text <- new [bc] - 932 run [ - 933 ¦ 10:num/raw <- find-next x, y, 1 - 934 ] - 935 memory-should-contain [ - 936 ¦ 10 <- 1 - 937 ] - 938 ] - 939 - 940 scenario find-next-no-match [ - 941 local-scope - 942 x:text <- new [abc] - 943 y:text <- new [bd] - 944 run [ - 945 ¦ 10:num/raw <- find-next x, y, 0 - 946 ] - 947 memory-should-contain [ - 948 ¦ 10 <- 3 # not found - 949 ] - 950 ] - 951 - 952 scenario find-next-suffix-match [ - 953 local-scope - 954 x:text <- new [abcd] - 955 y:text <- new [cd] - 956 run [ - 957 ¦ 10:num/raw <- find-next x, y, 0 - 958 ] - 959 memory-should-contain [ - 960 ¦ 10 <- 2 - 961 ] - 962 ] - 963 - 964 scenario find-next-suffix-match-2 [ - 965 local-scope - 966 x:text <- new [abcd] - 967 y:text <- new [cde] - 968 run [ - 969 ¦ 10:num/raw <- find-next x, y, 0 - 970 ] - 971 memory-should-contain [ - 972 ¦ 10 <- 4 # not found - 973 ] - 974 ] - 975 - 976 # checks if pattern matches at index 'idx' - 977 def match-at text:text, pattern:text, idx:num -> result:bool [ + 892 memory-should-contain [ + 893 ¦ 10 <- 4 # no change + 894 ] + 895 ] + 896 + 897 scenario text-find-next-first [ + 898 local-scope + 899 x:text <- new [ab/c/] + 900 run [ + 901 ¦ 10:num/raw <- find-next x, 47/slash, 0/start-index + 902 ] + 903 memory-should-contain [ + 904 ¦ 10 <- 2 # first '/' of multiple + 905 ] + 906 ] + 907 + 908 scenario text-find-next-second [ + 909 local-scope + 910 x:text <- new [ab/c/] + 911 run [ + 912 ¦ 10:num/raw <- find-next x, 47/slash, 3/start-index + 913 ] + 914 memory-should-contain [ + 915 ¦ 10 <- 4 # second '/' of multiple + 916 ] + 917 ] + 918 + 919 # search for a pattern of multiple characters + 920 # fairly dumb algorithm + 921 def find-next text:text, pattern:text, idx:num -> next-index:num [ + 922 local-scope + 923 load-ingredients + 924 first:char <- index *pattern, 0 + 925 # repeatedly check for match at current idx + 926 len:num <- length *text + 927 { + 928 ¦ # does some unnecessary work checking even when there isn't enough of text left + 929 ¦ done?:bool <- greater-or-equal idx, len + 930 ¦ break-if done? + 931 ¦ found?:bool <- match-at text, pattern, idx + 932 ¦ break-if found? + 933 ¦ idx <- add idx, 1 + 934 ¦ # optimization: skip past indices that definitely won't match + 935 ¦ idx <- find-next text, first, idx + 936 ¦ loop + 937 } + 938 return idx + 939 ] + 940 + 941 scenario find-next-text-1 [ + 942 local-scope + 943 x:text <- new [abc] + 944 y:text <- new [bc] + 945 run [ + 946 ¦ 10:num/raw <- find-next x, y, 0 + 947 ] + 948 memory-should-contain [ + 949 ¦ 10 <- 1 + 950 ] + 951 ] + 952 + 953 scenario find-next-text-2 [ + 954 local-scope + 955 x:text <- new [abcd] + 956 y:text <- new [bc] + 957 run [ + 958 ¦ 10:num/raw <- find-next x, y, 1 + 959 ] + 960 memory-should-contain [ + 961 ¦ 10 <- 1 + 962 ] + 963 ] + 964 + 965 scenario find-next-no-match [ + 966 local-scope + 967 x:text <- new [abc] + 968 y:text <- new [bd] + 969 run [ + 970 ¦ 10:num/raw <- find-next x, y, 0 + 971 ] + 972 memory-should-contain [ + 973 ¦ 10 <- 3 # not found + 974 ] + 975 ] + 976 + 977 scenario find-next-suffix-match [ 978 local-scope - 979 load-ingredients - 980 pattern-len:num <- length *pattern - 981 # check that there's space left for the pattern - 982 { - 983 ¦ x:num <- length *text - 984 ¦ x <- subtract x, pattern-len - 985 ¦ enough-room?:bool <- lesser-or-equal idx, x - 986 ¦ break-if enough-room? - 987 ¦ return 0/not-found - 988 } - 989 # check each character of pattern - 990 pattern-idx:num <- copy 0 - 991 { - 992 ¦ done?:bool <- greater-or-equal pattern-idx, pattern-len - 993 ¦ break-if done? - 994 ¦ c:char <- index *text, idx - 995 ¦ exp:char <- index *pattern, pattern-idx - 996 ¦ { - 997 ¦ ¦ match?:bool <- equal c, exp - 998 ¦ ¦ break-if match? - 999 ¦ ¦ return 0/not-found -1000 ¦ } -1001 ¦ idx <- add idx, 1 -1002 ¦ pattern-idx <- add pattern-idx, 1 -1003 ¦ loop -1004 } -1005 return 1/found -1006 ] -1007 -1008 scenario match-at-checks-pattern-at-index [ -1009 local-scope -1010 x:text <- new [abc] -1011 y:text <- new [ab] -1012 run [ -1013 ¦ 10:bool/raw <- match-at x, y, 0 -1014 ] -1015 memory-should-contain [ -1016 ¦ 10 <- 1 # match found -1017 ] -1018 ] -1019 -1020 scenario match-at-reflexive [ -1021 local-scope -1022 x:text <- new [abc] -1023 run [ -1024 ¦ 10:bool/raw <- match-at x, x, 0 -1025 ] -1026 memory-should-contain [ -1027 ¦ 10 <- 1 # match found -1028 ] -1029 ] -1030 -1031 scenario match-at-outside-bounds [ -1032 local-scope -1033 x:text <- new [abc] -1034 y:text <- new [a] -1035 run [ -1036 ¦ 10:bool/raw <- match-at x, y, 4 -1037 ] -1038 memory-should-contain [ -1039 ¦ 10 <- 0 # never matches -1040 ] -1041 ] -1042 -1043 scenario match-at-empty-pattern [ -1044 local-scope -1045 x:text <- new [abc] -1046 y:text <- new [] -1047 run [ -1048 ¦ 10:bool/raw <- match-at x, y, 0 -1049 ] -1050 memory-should-contain [ -1051 ¦ 10 <- 1 # always matches empty pattern given a valid index -1052 ] -1053 ] -1054 -1055 scenario match-at-empty-pattern-outside-bound [ -1056 local-scope -1057 x:text <- new [abc] -1058 y:text <- new [] -1059 run [ -1060 ¦ 10:bool/raw <- match-at x, y, 4 -1061 ] -1062 memory-should-contain [ -1063 ¦ 10 <- 0 # no match -1064 ] -1065 ] -1066 -1067 scenario match-at-empty-text [ -1068 local-scope -1069 x:text <- new [] -1070 y:text <- new [abc] -1071 run [ -1072 ¦ 10:bool/raw <- match-at x, y, 0 -1073 ] -1074 memory-should-contain [ -1075 ¦ 10 <- 0 # no match -1076 ] -1077 ] -1078 -1079 scenario match-at-empty-against-empty [ -1080 local-scope -1081 x:text <- new [] -1082 run [ -1083 ¦ 10:bool/raw <- match-at x, x, 0 -1084 ] -1085 memory-should-contain [ -1086 ¦ 10 <- 1 # matches because pattern is also empty -1087 ] -1088 ] -1089 -1090 scenario match-at-inside-bounds [ -1091 local-scope -1092 x:text <- new [abc] -1093 y:text <- new [bc] -1094 run [ -1095 ¦ 10:bool/raw <- match-at x, y, 1 -1096 ] -1097 memory-should-contain [ -1098 ¦ 10 <- 1 # match -1099 ] -1100 ] -1101 -1102 scenario match-at-inside-bounds-2 [ -1103 local-scope -1104 x:text <- new [abc] -1105 y:text <- new [bc] -1106 run [ -1107 ¦ 10:bool/raw <- match-at x, y, 0 -1108 ] -1109 memory-should-contain [ -1110 ¦ 10 <- 0 # no match -1111 ] -1112 ] -1113 -1114 def split s:text, delim:char -> result:&:@:text [ -1115 local-scope -1116 load-ingredients -1117 # empty text? return empty array -1118 len:num <- length *s -1119 { -1120 ¦ empty?:bool <- equal len, 0 -1121 ¦ break-unless empty? -1122 ¦ result <- new {(address array character): type}, 0 -1123 ¦ return -1124 } -1125 # count #pieces we need room for -1126 count:num <- copy 1 # n delimiters = n+1 pieces -1127 idx:num <- copy 0 -1128 { -1129 ¦ idx <- find-next s, delim, idx -1130 ¦ done?:bool <- greater-or-equal idx, len -1131 ¦ break-if done? -1132 ¦ idx <- add idx, 1 -1133 ¦ count <- add count, 1 -1134 ¦ loop -1135 } -1136 # allocate space -1137 result <- new {(address array character): type}, count -1138 # repeatedly copy slices start..end until delimiter into result[curr-result] -1139 curr-result:num <- copy 0 -1140 start:num <- copy 0 -1141 { -1142 ¦ # while next delim exists -1143 ¦ done?:bool <- greater-or-equal start, len -1144 ¦ break-if done? -1145 ¦ end:num <- find-next s, delim, start -1146 ¦ # copy start..end into result[curr-result] -1147 ¦ dest:text <- copy-range s, start, end -1148 ¦ *result <- put-index *result, curr-result, dest -1149 ¦ # slide over to next slice -1150 ¦ start <- add end, 1 -1151 ¦ curr-result <- add curr-result, 1 -1152 ¦ loop -1153 } -1154 ] -1155 -1156 scenario text-split-1 [ -1157 local-scope -1158 x:text <- new [a/b] -1159 run [ -1160 ¦ y:&:@:text <- split x, 47/slash -1161 ¦ 10:num/raw <- length *y -1162 ¦ a:text <- index *y, 0 -1163 ¦ b:text <- index *y, 1 -1164 ¦ 20:@:char/raw <- copy *a -1165 ¦ 30:@:char/raw <- copy *b -1166 ] -1167 memory-should-contain [ -1168 ¦ 10 <- 2 # length of result -1169 ¦ 20:array:character <- [a] -1170 ¦ 30:array:character <- [b] -1171 ] -1172 ] -1173 -1174 scenario text-split-2 [ -1175 local-scope -1176 x:text <- new [a/b/c] -1177 run [ -1178 ¦ y:&:@:text <- split x, 47/slash -1179 ¦ 10:num/raw <- length *y -1180 ¦ a:text <- index *y, 0 -1181 ¦ b:text <- index *y, 1 -1182 ¦ c:text <- index *y, 2 -1183 ¦ 20:@:char/raw <- copy *a -1184 ¦ 30:@:char/raw <- copy *b -1185 ¦ 40:@:char/raw <- copy *c -1186 ] -1187 memory-should-contain [ -1188 ¦ 10 <- 3 # length of result -1189 ¦ 20:array:character <- [a] -1190 ¦ 30:array:character <- [b] -1191 ¦ 40:array:character <- [c] -1192 ] -1193 ] -1194 -1195 scenario text-split-missing [ -1196 local-scope -1197 x:text <- new [abc] -1198 run [ -1199 ¦ y:&:@:text <- split x, 47/slash -1200 ¦ 10:num/raw <- length *y -1201 ¦ a:text <- index *y, 0 -1202 ¦ 20:@:char/raw <- copy *a -1203 ] -1204 memory-should-contain [ -1205 ¦ 10 <- 1 # length of result -1206 ¦ 20:array:character <- [abc] -1207 ] -1208 ] -1209 -1210 scenario text-split-empty [ -1211 local-scope -1212 x:text <- new [] -1213 run [ -1214 ¦ y:&:@:text <- split x, 47/slash -1215 ¦ 10:num/raw <- length *y -1216 ] -1217 memory-should-contain [ -1218 ¦ 10 <- 0 # empty result -1219 ] -1220 ] -1221 -1222 scenario text-split-empty-piece [ -1223 local-scope -1224 x:text <- new [a/b//c] -1225 run [ -1226 ¦ y:&:@:text <- split x:text, 47/slash -1227 ¦ 10:num/raw <- length *y -1228 ¦ a:text <- index *y, 0 -1229 ¦ b:text <- index *y, 1 -1230 ¦ c:text <- index *y, 2 -1231 ¦ d:text <- index *y, 3 -1232 ¦ 20:@:char/raw <- copy *a -1233 ¦ 30:@:char/raw <- copy *b -1234 ¦ 40:@:char/raw <- copy *c -1235 ¦ 50:@:char/raw <- copy *d -1236 ] -1237 memory-should-contain [ -1238 ¦ 10 <- 4 # length of result -1239 ¦ 20:array:character <- [a] -1240 ¦ 30:array:character <- [b] -1241 ¦ 40:array:character <- [] -1242 ¦ 50:array:character <- [c] -1243 ] -1244 ] -1245 -1246 def split-first text:text, delim:char -> x:text, y:text [ -1247 local-scope -1248 load-ingredients -1249 # empty text? return empty texts -1250 len:num <- length *text -1251 { -1252 ¦ empty?:bool <- equal len, 0 -1253 ¦ break-unless empty? -1254 ¦ x:text <- new [] -1255 ¦ y:text <- new [] -1256 ¦ return -1257 } -1258 idx:num <- find-next text, delim, 0 -1259 x:text <- copy-range text, 0, idx -1260 idx <- add idx, 1 -1261 y:text <- copy-range text, idx, len -1262 ] -1263 -1264 scenario text-split-first [ -1265 local-scope -1266 x:text <- new [a/b] -1267 run [ -1268 ¦ y:text, z:text <- split-first x, 47/slash -1269 ¦ 10:@:char/raw <- copy *y -1270 ¦ 20:@:char/raw <- copy *z -1271 ] -1272 memory-should-contain [ -1273 ¦ 10:array:character <- [a] -1274 ¦ 20:array:character <- [b] -1275 ] -1276 ] -1277 -1278 def copy-range buf:text, start:num, end:num -> result:text [ -1279 local-scope -1280 load-ingredients -1281 # if end is out of bounds, trim it -1282 len:num <- length *buf -1283 end:num <- min len, end -1284 # allocate space for result -1285 len <- subtract end, start -1286 result:text <- new character:type, len -1287 # copy start..end into result[curr-result] -1288 src-idx:num <- copy start -1289 dest-idx:num <- copy 0 -1290 { -1291 ¦ done?:bool <- greater-or-equal src-idx, end -1292 ¦ break-if done? -1293 ¦ src:char <- index *buf, src-idx -1294 ¦ *result <- put-index *result, dest-idx, src -1295 ¦ src-idx <- add src-idx, 1 -1296 ¦ dest-idx <- add dest-idx, 1 -1297 ¦ loop -1298 } -1299 ] -1300 -1301 scenario copy-range-works [ -1302 local-scope -1303 x:text <- new [abc] -1304 run [ -1305 ¦ y:text <- copy-range x, 1, 3 -1306 ¦ 1:@:char/raw <- copy *y -1307 ] -1308 memory-should-contain [ -1309 ¦ 1:array:character <- [bc] -1310 ] -1311 ] -1312 -1313 scenario copy-range-out-of-bounds [ -1314 local-scope -1315 x:text <- new [abc] -1316 run [ -1317 ¦ y:text <- copy-range x, 2, 4 -1318 ¦ 1:@:char/raw <- copy *y -1319 ] -1320 memory-should-contain [ -1321 ¦ 1:array:character <- [c] -1322 ] -1323 ] -1324 -1325 scenario copy-range-out-of-bounds-2 [ -1326 local-scope -1327 x:text <- new [abc] -1328 run [ -1329 ¦ y:text <- copy-range x, 3, 3 -1330 ¦ 1:@:char/raw <- copy *y -1331 ] -1332 memory-should-contain [ -1333 ¦ 1:array:character <- [] -1334 ] -1335 ] -1336 -1337 def parse-whole-number in:text -> out:num, error?:bool [ -1338 local-scope -1339 load-ingredients -1340 out <- copy 0 -1341 result:num <- copy 0 # temporary location -1342 i:num <- copy 0 -1343 len:num <- length *in -1344 { -1345 ¦ done?:bool <- greater-or-equal i, len -1346 ¦ break-if done? -1347 ¦ c:char <- index *in, i -1348 ¦ x:num <- character-to-code c -1349 ¦ digit:num, error?:bool <- character-code-to-digit x -1350 ¦ return-if error? -1351 ¦ result <- multiply result, 10 -1352 ¦ result <- add result, digit -1353 ¦ i <- add i, 1 -1354 ¦ loop -1355 } -1356 # no error; all digits were valid -1357 out <- copy result -1358 ] -1359 -1360 # (contributed by Ella Couch) -1361 recipe character-code-to-digit character-code:number -> result:number, error?:boolean [ -1362 local-scope -1363 load-ingredients -1364 result <- copy 0 -1365 error? <- lesser-than character-code, 48 # '0' -1366 return-if error? -1367 error? <- greater-than character-code, 57 # '9' -1368 return-if error? -1369 result <- subtract character-code, 48 -1370 ] -1371 -1372 scenario character-code-to-digit-contain-only-digit [ -1373 local-scope -1374 a:number <- copy 48 # character code for '0' -1375 run [ -1376 ¦ 10:number/raw, 11:boolean/raw <- character-code-to-digit a -1377 ] -1378 memory-should-contain [ -1379 ¦ 10 <- 0 -1380 ¦ 11 <- 0 # no error -1381 ] -1382 ] -1383 -1384 scenario character-code-to-digit-contain-only-digit-2 [ -1385 local-scope -1386 a:number <- copy 57 # character code for '9' -1387 run [ -1388 ¦ 1:number/raw, 2:boolean/raw <- character-code-to-digit a -1389 ] -1390 memory-should-contain [ -1391 ¦ 1 <- 9 -1392 ¦ 2 <- 0 # no error -1393 ] -1394 ] -1395 -1396 scenario character-code-to-digit-handles-codes-lower-than-zero [ -1397 local-scope -1398 a:number <- copy 47 -1399 run [ -1400 ¦ 10:number/raw, 11:boolean/raw <- character-code-to-digit a -1401 ] -1402 memory-should-contain [ -1403 ¦ 10 <- 0 -1404 ¦ 11 <- 1 # error -1405 ] -1406 ] -1407 -1408 scenario character-code-to-digit-handles-codes-larger-than-nine [ -1409 local-scope -1410 a:number <- copy 58 -1411 run [ -1412 ¦ 10:number/raw, 11:boolean/raw <- character-code-to-digit a -1413 ] -1414 memory-should-contain [ -1415 ¦ 10 <- 0 -1416 ¦ 11 <- 1 # error -1417 ] -1418 ] + 979 x:text <- new [abcd] + 980 y:text <- new [cd] + 981 run [ + 982 ¦ 10:num/raw <- find-next x, y, 0 + 983 ] + 984 memory-should-contain [ + 985 ¦ 10 <- 2 + 986 ] + 987 ] + 988 + 989 scenario find-next-suffix-match-2 [ + 990 local-scope + 991 x:text <- new [abcd] + 992 y:text <- new [cde] + 993 run [ + 994 ¦ 10:num/raw <- find-next x, y, 0 + 995 ] + 996 memory-should-contain [ + 997 ¦ 10 <- 4 # not found + 998 ] + 999 ] +1000 +1001 # checks if pattern matches at index 'idx' +1002 def match-at text:text, pattern:text, idx:num -> result:bool [ +1003 local-scope +1004 load-ingredients +1005 pattern-len:num <- length *pattern +1006 # check that there's space left for the pattern +1007 { +1008 ¦ x:num <- length *text +1009 ¦ x <- subtract x, pattern-len +1010 ¦ enough-room?:bool <- lesser-or-equal idx, x +1011 ¦ break-if enough-room? +1012 ¦ return 0/not-found +1013 } +1014 # check each character of pattern +1015 pattern-idx:num <- copy 0 +1016 { +1017 ¦ done?:bool <- greater-or-equal pattern-idx, pattern-len +1018 ¦ break-if done? +1019 ¦ c:char <- index *text, idx +1020 ¦ exp:char <- index *pattern, pattern-idx +1021 ¦ { +1022 ¦ ¦ match?:bool <- equal c, exp +1023 ¦ ¦ break-if match? +1024 ¦ ¦ return 0/not-found +1025 ¦ } +1026 ¦ idx <- add idx, 1 +1027 ¦ pattern-idx <- add pattern-idx, 1 +1028 ¦ loop +1029 } +1030 return 1/found +1031 ] +1032 +1033 scenario match-at-checks-pattern-at-index [ +1034 local-scope +1035 x:text <- new [abc] +1036 y:text <- new [ab] +1037 run [ +1038 ¦ 10:bool/raw <- match-at x, y, 0 +1039 ] +1040 memory-should-contain [ +1041 ¦ 10 <- 1 # match found +1042 ] +1043 ] +1044 +1045 scenario match-at-reflexive [ +1046 local-scope +1047 x:text <- new [abc] +1048 run [ +1049 ¦ 10:bool/raw <- match-at x, x, 0 +1050 ] +1051 memory-should-contain [ +1052 ¦ 10 <- 1 # match found +1053 ] +1054 ] +1055 +1056 scenario match-at-outside-bounds [ +1057 local-scope +1058 x:text <- new [abc] +1059 y:text <- new [a] +1060 run [ +1061 ¦ 10:bool/raw <- match-at x, y, 4 +1062 ] +1063 memory-should-contain [ +1064 ¦ 10 <- 0 # never matches +1065 ] +1066 ] +1067 +1068 scenario match-at-empty-pattern [ +1069 local-scope +1070 x:text <- new [abc] +1071 y:text <- new [] +1072 run [ +1073 ¦ 10:bool/raw <- match-at x, y, 0 +1074 ] +1075 memory-should-contain [ +1076 ¦ 10 <- 1 # always matches empty pattern given a valid index +1077 ] +1078 ] +1079 +1080 scenario match-at-empty-pattern-outside-bound [ +1081 local-scope +1082 x:text <- new [abc] +1083 y:text <- new [] +1084 run [ +1085 ¦ 10:bool/raw <- match-at x, y, 4 +1086 ] +1087 memory-should-contain [ +1088 ¦ 10 <- 0 # no match +1089 ] +1090 ] +1091 +1092 scenario match-at-empty-text [ +1093 local-scope +1094 x:text <- new [] +1095 y:text <- new [abc] +1096 run [ +1097 ¦ 10:bool/raw <- match-at x, y, 0 +1098 ] +1099 memory-should-contain [ +1100 ¦ 10 <- 0 # no match +1101 ] +1102 ] +1103 +1104 scenario match-at-empty-against-empty [ +1105 local-scope +1106 x:text <- new [] +1107 run [ +1108 ¦ 10:bool/raw <- match-at x, x, 0 +1109 ] +1110 memory-should-contain [ +1111 ¦ 10 <- 1 # matches because pattern is also empty +1112 ] +1113 ] +1114 +1115 scenario match-at-inside-bounds [ +1116 local-scope +1117 x:text <- new [abc] +1118 y:text <- new [bc] +1119 run [ +1120 ¦ 10:bool/raw <- match-at x, y, 1 +1121 ] +1122 memory-should-contain [ +1123 ¦ 10 <- 1 # match +1124 ] +1125 ] +1126 +1127 scenario match-at-inside-bounds-2 [ +1128 local-scope +1129 x:text <- new [abc] +1130 y:text <- new [bc] +1131 run [ +1132 ¦ 10:bool/raw <- match-at x, y, 0 +1133 ] +1134 memory-should-contain [ +1135 ¦ 10 <- 0 # no match +1136 ] +1137 ] +1138 +1139 def split s:text, delim:char -> result:&:@:text [ +1140 local-scope +1141 load-ingredients +1142 # empty text? return empty array +1143 len:num <- length *s +1144 { +1145 ¦ empty?:bool <- equal len, 0 +1146 ¦ break-unless empty? +1147 ¦ result <- new {(address array character): type}, 0 +1148 ¦ return +1149 } +1150 # count #pieces we need room for +1151 count:num <- copy 1 # n delimiters = n+1 pieces +1152 idx:num <- copy 0 +1153 { +1154 ¦ idx <- find-next s, delim, idx +1155 ¦ done?:bool <- greater-or-equal idx, len +1156 ¦ break-if done? +1157 ¦ idx <- add idx, 1 +1158 ¦ count <- add count, 1 +1159 ¦ loop +1160 } +1161 # allocate space +1162 result <- new {(address array character): type}, count +1163 # repeatedly copy slices start..end until delimiter into result[curr-result] +1164 curr-result:num <- copy 0 +1165 start:num <- copy 0 +1166 { +1167 ¦ # while next delim exists +1168 ¦ done?:bool <- greater-or-equal start, len +1169 ¦ break-if done? +1170 ¦ end:num <- find-next s, delim, start +1171 ¦ # copy start..end into result[curr-result] +1172 ¦ dest:text <- copy-range s, start, end +1173 ¦ *result <- put-index *result, curr-result, dest +1174 ¦ # slide over to next slice +1175 ¦ start <- add end, 1 +1176 ¦ curr-result <- add curr-result, 1 +1177 ¦ loop +1178 } +1179 ] +1180 +1181 scenario text-split-1 [ +1182 local-scope +1183 x:text <- new [a/b] +1184 run [ +1185 ¦ y:&:@:text <- split x, 47/slash +1186 ¦ 10:num/raw <- length *y +1187 ¦ a:text <- index *y, 0 +1188 ¦ b:text <- index *y, 1 +1189 ¦ 20:@:char/raw <- copy *a +1190 ¦ 30:@:char/raw <- copy *b +1191 ] +1192 memory-should-contain [ +1193 ¦ 10 <- 2 # length of result +1194 ¦ 20:array:character <- [a] +1195 ¦ 30:array:character <- [b] +1196 ] +1197 ] +1198 +1199 scenario text-split-2 [ +1200 local-scope +1201 x:text <- new [a/b/c] +1202 run [ +1203 ¦ y:&:@:text <- split x, 47/slash +1204 ¦ 10:num/raw <- length *y +1205 ¦ a:text <- index *y, 0 +1206 ¦ b:text <- index *y, 1 +1207 ¦ c:text <- index *y, 2 +1208 ¦ 20:@:char/raw <- copy *a +1209 ¦ 30:@:char/raw <- copy *b +1210 ¦ 40:@:char/raw <- copy *c +1211 ] +1212 memory-should-contain [ +1213 ¦ 10 <- 3 # length of result +1214 ¦ 20:array:character <- [a] +1215 ¦ 30:array:character <- [b] +1216 ¦ 40:array:character <- [c] +1217 ] +1218 ] +1219 +1220 scenario text-split-missing [ +1221 local-scope +1222 x:text <- new [abc] +1223 run [ +1224 ¦ y:&:@:text <- split x, 47/slash +1225 ¦ 10:num/raw <- length *y +1226 ¦ a:text <- index *y, 0 +1227 ¦ 20:@:char/raw <- copy *a +1228 ] +1229 memory-should-contain [ +1230 ¦ 10 <- 1 # length of result +1231 ¦ 20:array:character <- [abc] +1232 ] +1233 ] +1234 +1235 scenario text-split-empty [ +1236 local-scope +1237 x:text <- new [] +1238 run [ +1239 ¦ y:&:@:text <- split x, 47/slash +1240 ¦ 10:num/raw <- length *y +1241 ] +1242 memory-should-contain [ +1243 ¦ 10 <- 0 # empty result +1244 ] +1245 ] +1246 +1247 scenario text-split-empty-piece [ +1248 local-scope +1249 x:text <- new [a/b//c] +1250 run [ +1251 ¦ y:&:@:text <- split x:text, 47/slash +1252 ¦ 10:num/raw <- length *y +1253 ¦ a:text <- index *y, 0 +1254 ¦ b:text <- index *y, 1 +1255 ¦ c:text <- index *y, 2 +1256 ¦ d:text <- index *y, 3 +1257 ¦ 20:@:char/raw <- copy *a +1258 ¦ 30:@:char/raw <- copy *b +1259 ¦ 40:@:char/raw <- copy *c +1260 ¦ 50:@:char/raw <- copy *d +1261 ] +1262 memory-should-contain [ +1263 ¦ 10 <- 4 # length of result +1264 ¦ 20:array:character <- [a] +1265 ¦ 30:array:character <- [b] +1266 ¦ 40:array:character <- [] +1267 ¦ 50:array:character <- [c] +1268 ] +1269 ] +1270 +1271 def split-first text:text, delim:char -> x:text, y:text [ +1272 local-scope +1273 load-ingredients +1274 # empty text? return empty texts +1275 len:num <- length *text +1276 { +1277 ¦ empty?:bool <- equal len, 0 +1278 ¦ break-unless empty? +1279 ¦ x:text <- new [] +1280 ¦ y:text <- new [] +1281 ¦ return +1282 } +1283 idx:num <- find-next text, delim, 0 +1284 x:text <- copy-range text, 0, idx +1285 idx <- add idx, 1 +1286 y:text <- copy-range text, idx, len +1287 ] +1288 +1289 scenario text-split-first [ +1290 local-scope +1291 x:text <- new [a/b] +1292 run [ +1293 ¦ y:text, z:text <- split-first x, 47/slash +1294 ¦ 10:@:char/raw <- copy *y +1295 ¦ 20:@:char/raw <- copy *z +1296 ] +1297 memory-should-contain [ +1298 ¦ 10:array:character <- [a] +1299 ¦ 20:array:character <- [b] +1300 ] +1301 ] +1302 +1303 def copy-range buf:text, start:num, end:num -> result:text [ +1304 local-scope +1305 load-ingredients +1306 # if end is out of bounds, trim it +1307 len:num <- length *buf +1308 end:num <- min len, end +1309 # allocate space for result +1310 len <- subtract end, start +1311 result:text <- new character:type, len +1312 # copy start..end into result[curr-result] +1313 src-idx:num <- copy start +1314 dest-idx:num <- copy 0 +1315 { +1316 ¦ done?:bool <- greater-or-equal src-idx, end +1317 ¦ break-if done? +1318 ¦ src:char <- index *buf, src-idx +1319 ¦ *result <- put-index *result, dest-idx, src +1320 ¦ src-idx <- add src-idx, 1 +1321 ¦ dest-idx <- add dest-idx, 1 +1322 ¦ loop +1323 } +1324 ] +1325 +1326 scenario copy-range-works [ +1327 local-scope +1328 x:text <- new [abc] +1329 run [ +1330 ¦ y:text <- copy-range x, 1, 3 +1331 ¦ 1:@:char/raw <- copy *y +1332 ] +1333 memory-should-contain [ +1334 ¦ 1:array:character <- [bc] +1335 ] +1336 ] +1337 +1338 scenario copy-range-out-of-bounds [ +1339 local-scope +1340 x:text <- new [abc] +1341 run [ +1342 ¦ y:text <- copy-range x, 2, 4 +1343 ¦ 1:@:char/raw <- copy *y +1344 ] +1345 memory-should-contain [ +1346 ¦ 1:array:character <- [c] +1347 ] +1348 ] +1349 +1350 scenario copy-range-out-of-bounds-2 [ +1351 local-scope +1352 x:text <- new [abc] +1353 run [ +1354 ¦ y:text <- copy-range x, 3, 3 +1355 ¦ 1:@:char/raw <- copy *y +1356 ] +1357 memory-should-contain [ +1358 ¦ 1:array:character <- [] +1359 ] +1360 ] +1361 +1362 def parse-whole-number in:text -> out:num, error?:bool [ +1363 local-scope +1364 load-ingredients +1365 out <- copy 0 +1366 result:num <- copy 0 # temporary location +1367 i:num <- copy 0 +1368 len:num <- length *in +1369 { +1370 ¦ done?:bool <- greater-or-equal i, len +1371 ¦ break-if done? +1372 ¦ c:char <- index *in, i +1373 ¦ x:num <- character-to-code c +1374 ¦ digit:num, error?:bool <- character-code-to-digit x +1375 ¦ return-if error? +1376 ¦ result <- multiply result, 10 +1377 ¦ result <- add result, digit +1378 ¦ i <- add i, 1 +1379 ¦ loop +1380 } +1381 # no error; all digits were valid +1382 out <- copy result +1383 ] +1384 +1385 # (contributed by Ella Couch) +1386 recipe character-code-to-digit character-code:number -> result:number, error?:boolean [ +1387 local-scope +1388 load-ingredients +1389 result <- copy 0 +1390 error? <- lesser-than character-code, 48 # '0' +1391 return-if error? +1392 error? <- greater-than character-code, 57 # '9' +1393 return-if error? +1394 result <- subtract character-code, 48 +1395 ] +1396 +1397 scenario character-code-to-digit-contain-only-digit [ +1398 local-scope +1399 a:number <- copy 48 # character code for '0' +1400 run [ +1401 ¦ 10:number/raw, 11:boolean/raw <- character-code-to-digit a +1402 ] +1403 memory-should-contain [ +1404 ¦ 10 <- 0 +1405 ¦ 11 <- 0 # no error +1406 ] +1407 ] +1408 +1409 scenario character-code-to-digit-contain-only-digit-2 [ +1410 local-scope +1411 a:number <- copy 57 # character code for '9' +1412 run [ +1413 ¦ 1:number/raw, 2:boolean/raw <- character-code-to-digit a +1414 ] +1415 memory-should-contain [ +1416 ¦ 1 <- 9 +1417 ¦ 2 <- 0 # no error +1418 ] +1419 ] +1420 +1421 scenario character-code-to-digit-handles-codes-lower-than-zero [ +1422 local-scope +1423 a:number <- copy 47 +1424 run [ +1425 ¦ 10:number/raw, 11:boolean/raw <- character-code-to-digit a +1426 ] +1427 memory-should-contain [ +1428 ¦ 10 <- 0 +1429 ¦ 11 <- 1 # error +1430 ] +1431 ] +1432 +1433 scenario character-code-to-digit-handles-codes-larger-than-nine [ +1434 local-scope +1435 a:number <- copy 58 +1436 run [ +1437 ¦ 10:number/raw, 11:boolean/raw <- character-code-to-digit a +1438 ] +1439 memory-should-contain [ +1440 ¦ 10 <- 0 +1441 ¦ 11 <- 1 # error +1442 ] +1443 ] -- cgit 1.4.1-2-gfad0