# String utilities This unit lives at [lib/str.fs] and provides a few strings 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 (a pointer to a byte containing a 0). To iterate a string list, all we need to do is "skip" the string using its size byte. ## String pool usage Some words need to copy string contents to memory. For this they use the string pool [doc/usage/lit]. Words needing to work on copies of string will yield references from buffer within that pool as a result. Words with a *P* mention use the pool. ## API ### Constants NULLSTR An empty string 32SPCS ( -- a u ) Just a range with 32 spaces ### Words string ( "name" -- ) Shortcut for "create STR_MAXSZ allot0" str< ( -- ?c f ) Read from in< and apply string literal escapes. Yield char as c and f=1. If we've reached the end of string with an unescaped double quote, f=0 and c is absent. " ( -- str ) *IP* Repeatedly call str< and accumulate it into a string in the pool. Then yield that string. ," ( -- ) Repeatedly call str< and write contents to here. n" ( -- n ) *I* Repeatedly call str< and accumulate characters into a 32-bit number, pushing accumulation left by 8 bits each time. Examples: n"DUSK" --> $4455534b n"USK" --> $0055534b n"XDUSK" --> $4455534b newstr ( -- a ) *P* Begin building a new string. See [doc/usage/lit]. endstr ( a -- str ) Finalize string. See [doc/usage/lit]. strallot ( n -- a ) *P* Allocate n bytes in the string pool and yield it. []>pool ( a u -- a u ) *P* Copy range "a u" to the pool and yield its pool reference. 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* char* strcat(char *s2, char *s1); Concatenates s1 and s2 in the pool and return the corresponding string. s[]= ( str a u -- f ) Compares a string to a range. strmove ( str dst -- ) Copy "str"'s content (including length byte) to "dst", which must have enough place to accomodate it. []strmove ( a u dst -- ) Copy range "a u" to "dst as a string. cmove+ ( src dst u -- dst+ ) Like "cmove", but also yields "dst+u". cmove" ( dst "x" -- a u ) Copy bytes "x" within quotes to address "dst" and yield range "a u" of the copied contents. s, ( str -- ) Write "str" to here. zstrlen ( ztr -- len ) Returns the length of "zstr". z[] ( zstr -- a u ) Yields range for zstr. c[] ( c -- a u ) Write char in 1b static buffer and yield its range. zmove ( zsrc dst -- dst+ ) Copies "u" bytes from zsrc where u is zstr's length, including the null byte. Yields "dst+u". nspcs ( n -- ) Emit "n" spaces. rtypeleft ( umin a u -- ) Run "rtype" on "a u". If "a u" is smaller than "umin", pad that string with aligning it left. rtyperight ( umin a u -- ) Same as "rtypeleft", but align right. capture ( xt -- a u ) Executes "xt" while hijacking RTYPE so that it "holds it in" and accumulates the result in a static buffer of STR_MAXSZ bytes in size. This word *can* be nested but the total length of the accumulated buffer in the whole nesting operation cannot exceed STR_MAXSZ. When a nested capture yields, it rewinds the buffer back to where it was when it started. Aborts if buffer capacity is exceeded. This happens in a memory safe manner, that is, such an abort doesn't mean the system is broken. consume[] ( a u n -- a u ) Add "n" to "a" and subtract "n" to "u". If "n" is greater than u, resulting u will be negative, which means a broken range. Note that this word ideally belongs to [mem/range] but lives here, for now, for bootstrapping purposes. do[] ( a u -- ) *I* Equivalent of "over + swap do", that is, loop over addresses of the range. Example: : foo c@+ do[] i c@ emit loop ; "hello" foo \ Prints "hello" [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. If "str" is a null string, yields idx=0. ws? ( c -- f ) Returns whether character "c" is a whitespace. s) ( str -- a ) *A* Skip "str", that is, return the address following its last char. 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. slistlen ( list -- len ) Yields the number of elements in a string list. stringlist ( "name" "..." -- ) Create a list of strings (same format as sfind above) from reading the rest of line of the input stream. See [doc/usage/io]. Example: stringlist mylist foo bar "hello world!" strings< ( "..." -- ... n ) Read rest of line with "wordorquote" and keep resulting strings on PS, with "n" being the number of strings following this number. Example: strings< foo bar baz . spc> stype spc> stype spc> stype \ prints "3 baz bar foo" startswith? ( substr str -- f ) Yields f=1 if "str" begins with "substr". Yields f=1 when substr is a null string. endswith? ( substr str -- f ) Yields f=1 if "str" ends with "substr". Yields f=1 when substr is a null string. upcase ( c -- c ) If c is between "a" and "z", yield their upcase version. Otherwise, yield c. lowcase ( c -- c ) Opposite of "upcase". upstr ( str -- str ) *P* Copy str to pool, then make all its characters upper case. lowstr ( str -- str ) *P* Opposite of "upstr".