# String utilities

This unit lives at lib/str.fs and provides a few strings an range related words.

Most strings in Dusk are regular strings as [doc/usage] describes. However, we
sometimes have to deal with null-terminated strings and this unit also has words
to deal with them. We refer to null-terminated strings as "zstr" (z for zero).

This unit also defines the concept of "string lists". Those are simply strings
following each other in memory, ending with a null string. To iterate a string
list, all we need to do is "skip" the string using its size byte.

## The string pool

Some words need to copy string contents to memory. For this they use an internal
buffer organized like a scratchpad [mem/scratch]. We don't use scratchpads
directly because lib/str is often a "boot unit" and we want to minimize the size
of that list.

Words needing to work on copies of string will thus yield references from buffer
within that pool as a result. Words with a *P* mention use the pool.

That pool is big enough (4kb) for most of your string processing needs, but as
with any scratchpad reference, it cannot be used for permanent storage, or even
semi-permanent storage. For example, if you write a word that needs to hold onto
a string while it calls an arbitrary word, then it can't be a string from the
pool because that arbitrary word could be compiling a big C source file that
will eat through that pool like butter. In that case, you'll want to use str>r
[usage/mem].

That being said, the pool is pretty useful, you might even want to use it for
itself, just to save you the trouble of setting up a static buffer. For this,
you can use str>pool.

## API

Constants:

NULLSTR  An empty string
POOLSZ   Size in bytes of the string pool

Words:

ws? ( c -- f )
  Returns whether character "c" is a whitespace.

s) ( str -- a )
  Skip "str", that is, return the address following its last char.

#[]>str ( a u -- a u )
  Assert that range "a u" fits in a string ("u" < STR_MAXSZ).

poolallot ( n -- a ) *P*
  Allocate n bytes in the string pool and yield the allocated address.

str>pool ( str -- str ) *P*
  Copy "str" to the pool and yield its pool reference.

[]>str ( a u -- str ) *P*
  Copy range "a u" in the pool and return the corresponding string.

str>zstr ( str -- zstr ) *P*
  Copy "str" to pool, add a null terminator and yield zstr.

strcat ( s1 s2 -- str ) *P*
  Concatenates s1 and s2 in the pool and return the corresponding string.

s[]= ( str a u -- f )
  Compares a string to a range.

cmove+ ( src dst u -- dst+ )
  Like "cmove", but also yields "dst+u".

zstrlen ( ztr -- len )
  Returns the length of "zstr".

zmove ( zsrc dst -- dst+ )
  Copies "u" bytes from zsrc where u is zstr's length, including the null byte.
  Yields "dst+u".

[str]? ( str a u -- idx )
  Search for the first occurrence of "str" in range "a u" and return its index.
  Returns -1 if not found.

sfind ( str list -- idx? f )
  Find "str" in "list" and return its index and f=1 if found, f=0 otherwise.

slistiter ( idx list -- str )
  Given a string list "list", iterate to its "idx" element and return the str.

toword ( -- c )
  Read stdin until next non WS and yield it.

expectchar ( c -- )
  Read stdin, discarding whitespaces and expect the next char to be "c"

stringlist ( n "name" "..." -- )
  Create a list of strings (same format as sfind above) with the specified
  number of elements. Elements are accumulated using "wordorquote"
  [doc/usage/io].

  Example: 3 stringlist mylist foo bar "hello world!"

rfor ( a u -- ) i=a j=idx k=u
  An iterator over the range "a u". It iterates "u" time, yielding an increased
  "a" as "i". Additionally, an index starting at 0 is maintained at "j".

  Example: " hello" c@+ rfor i c@ emit j . next --> h0e1l2l3o4

startswith? ( substr str -- f )
  Yields f=1 if "str" begins with "substr".

endswith? ( substr str -- f )
  Yields f=1 if "str" ends with "substr".

upcase ( c -- c )
  If c is between "a" and "z", yield their upcase version. Otherwise, yield c.

## Range API

bounds[] ( a u -- lo hi ) *B*
  Yields the low and high addresses of the bounds represented by range "a u".

cintersect[] ( a1 u1 a2 u2 -- ?a u )
  Compute the intersection between the two given ranges and return the part
  common to both. If there is no intersection, u=0 and a isn't present.

cmove- ( src dst u -- )
  Like "cmove", but operates backwards. You'll want to use this when copying
  overlapping memory area "forward" (dst > src).

rslide+ ( by a u -- )
  Move range "a u" forward in memory by "by" bytes.

rtrimleft ( n a u -- a u )
  Trim "n" characters at the left of range "a u" and return the resulting range.

rtrimright ( n a u -- a u )
  Trim "n" characters at the right of range "a u" and return the resulting
  range.

swap[] ( a u -- ) *B*
  Reverse the order of the elements in the range. For example, a range with
  3 elements "1 2 3" becomes a range with elements "3 2 1".
