# 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.

## Internal buffer

Some words need to copy string contents to memory. For this they use an internal
static buffer and yield that buffer as a result. When a word mentions that it
uses the internal buffer, this means that the result must be used (or copied)
straight away before another word from lib/str is used.

## API

Constants:

STR_MAXSZ   Maximum size of strings, including size byte
NULLSTR     An empty string

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.

s, ( str -- )
  Write "str" to here.

strmove ( str dst -- )
  Copy "str"'s content (including length byte) to "dst", which must have enough
  place to accomodate it.

strmove" ( dst -- )
  Read quoted string and copy it to "dst", as a string.

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

[]>str ( a u -- str )
  Copy range "a u" in the internal buffer and return the corresponding string.

str>zstr ( str -- zstr )
  Copy "str" to internal buffer, add a null terminator and yield zstr.

strcat ( s1 s2 -- str )
  Concatenates s1 and s2 in the internal buffer and return the corresponding
  string. "s1" can be the internal buffer (in other words, "strcat" can be
  chained), but not "s2".

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. Each element must be "quoted" with no space (unless you
  want them in the string) in the quotes.
  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: S" 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".

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

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

rmatch ( c range -- f )
  Given a list of character ranges, which is given in the form of a string of
  character pairs, return whether the specified character is in one of the
  ranges.

0-9? ( c -- f )
  Returns whether "c" is a digit.

A-Za-z? ( c -- f )
  Returns whether "c" is an ASCII letter.

alnum? ( c -- f )
  Returns whether "c" is a letter or digit.

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.

intersect[] ( 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.
