"Dynamic-Strings extension"

dstrings /* P4_INTO: CURRENT */ /* constants */
* EMPTY$ ( $: -- empty$ )

Push the MSA of a fixed, external representation of the empty
string onto the string stack. "empty-string"

FORTH/DEF dstrings ordinary primitive

* \n$ ( $: -- newline$ )

Push the MSA of a fixed, external string whose body is the
Unix newline character onto the string stack.
"newline-string"

FORTH/DEF dstrings ordinary primitive

/* variables */
DSTRINGS

[] no special info, see general notes

FORTH/DEF dstrings threadstate variable

/* forth string extensions */
* S, ( addr len -- addr' len )

ALLOT room and store the Forth string into data space as an
mstring, leaving data space aligned; and leave the length and
new body address. It is assumed that len is unsigned. An
error is thrown if len is larger than the system parameter
MAX_DATA_STR. "s-comma"

NOTE: MAX_DATA_STR is returned by
   S" /SCOPY" ENVIRONMENT?

Perhaps this restriction should be removed in favor of a
normal data space overflow error.

NOTE: S, is the same as STRING, in Wil Baden's Tool Belt,
except it stores a measured string instead of a counted
string.

FORTH/DEF dstrings ordinary primitive

/* string space */
* 0STRINGS ( -- )

Set all string variables holding bound string values in string
space to the empty string, and clear string space, including
the string buffer, string stack, and string stack frames.
"zero-strings"
NOTE:  If used for under the hood development, this word must

be executed only when string space is in a valid state.

FORTH/DEF dstrings ordinary primitive

$GC-OFF

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$GC-ON

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$UNUSED

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

* COLLECT-$GARBAGE ( -- collected-flag )

If string space is not marked as containing garbage, return
false. If there is garbage, throw an error when garbage
collection is disabled. Otherwise remove the garbage and return
true. Garbage collection is "transparent", so the user would
not normally use this word.
"collect-string-garbage"

FORTH/DEF dstrings ordinary primitive

* $GARBAGE? ( -- flag )

Leave true if there is garbage in the current string space.
Not normally used, since garbage collection is transparent.
"string-garbage-question"

FORTH/DEF dstrings ordinary primitive

* MAKE-$SPACE ( size #frames -- addr )

Allocate and initialize a string space with size bytes
available for the string buffer including the string stack,
and with a string frame stack for frame description entries
holding up to #frames. The size is rounded up to cell
alignment, and the buffer begins and ends with cell alignment.
Return addr, the address of the string space. The standard
word FREE with addr as input can be used to release the space.
"make-string-space"

FORTH/DEF dstrings ordinary primitive

/* string compilation */
(M$:)

[] no special info, see general notes

FORTH/DEF dstrings compiling primitive

$"

[] no special info, see general notes

FORTH/DEF dstrings compiling primitive

* $CONSTANT ( "name" $: a$ -- )

Create a definition for "name" with the execution semantics
"name" execution: ($: -- a$ )
It is assumed that the input string resides as a measured,

unchanging string outside of string space.
"string-constant"
For example:
   $" This is a sample string." $constant sample$

FORTH/DEF dstrings defining primitive

* $VARIABLE ( "name" -- )
   "name" execution:	( -- dfa )

Create an ordinary Forth variable and initialize it to the
address of a fixed, external, measured representation of the
empty string, such as that pushed onto the string stack by
EMPTY$. "string-variable""

FORTH/DEF dstrings ordinary primitive

($:

[] no special info, see general notes

FORTH/DEF dstrings immediate primitive

* ARGS{ ( arg1'$ ... argN'$ "arg1 ... argN <}>" -- )
    compilation: ( -- $: arg1$ ... argN$ )

Immediate and compilation-only.
Copy the argument strings to the string buffer, push them

onto the string stack with "argN" the most accessible, and
make them into the top compile-time string stack frame.
Compile the run-time code to make an argument frame out of
the N most accessible run-time string stack entries. Inform
the system text interpreter that it should compile run-time
code for any white-space delimited argument encountered in
the text of the definition, that concatenates the
corresponding string in the run-time frame. At the semicolon
terminating the definition, drop the compile-time argument
frame and compile code to drop the run-time argument frame.
"args-brace"
Syntax for defining a string macro GEORGE:
	: george  ($: a$ b$ c$ -- cat$ )
	  args{ arg1 arg2 arg3 }
	  cat" This is arg1:  " arg1 cat" ." ENDCAT ;

The blank following the last argument is required. For a
macro with no arguments, ARGS{ } does nothing but add
useless overhead and should be omitted. Two of the
arguments in this example are ignored and could have been
left out. Words intended only as steps in building a macro
would omit ENDCAT, which terminates concatenation and
leaves the concatenated string on the string stack.
Sample syntax using the string macro GEORGE:
    $" bill"  $" sue"  $" marie"  george $.

The resulting display is:
     This is arg1:  bill.

NOTE: Macro argument labels must be distinct from each other
and from any local labels that appear in the same definition,
and there is no check for that.
NOTE: At the moment the semantics of ARGS{ is undefined

before DOES>.

FORTH/DEF dstrings immediate primitive

CAT"

[] no special info, see general notes

FORTH/DEF dstrings compiling primitive

* CAT` ( "ccc" -- )

This word has only compile-time semantics, just like
CAT". It appends run-time semantics to the current
definition that concatenates the back-ticked string according
to the specification for CAT. An error is thrown if the
length of the quoted string is longer than the system
parameter MAX_DATA_STR (see S,).
"cat-back-tick"

FORTH/DEF dstrings compiling primitive

/* string stack */
* $2DROP ( $: a$ b$ -- )

Drop the two topmost string stack entries, marking them as
garbage if appropriate. "string-two-drop"

FORTH/DEF dstrings ordinary primitive

* $2DUP ( $: a$ b$ -- a$ b$ a$ b$ )

Leave copies of the two topmost string stack entries. The string
values are not copied. "string-two-dupe"

FORTH/DEF dstrings ordinary primitive

* $DEPTH ( -- n )

Leave the number of items on the string stack.
"string-depth"

FORTH/DEF dstrings ordinary primitive

* $DROP ( $: a$ -- )

Drop the topmost string stack entry, marking it as garbage if
it is initially bound to the top of the string stack.
"string-drop"

FORTH/DEF dstrings ordinary primitive

* $DUP ( $: a$ -- a$ a$ )

Leave a copy of the topmost string stack entry. The string
value is not copied. "string-dupe"

FORTH/DEF dstrings ordinary primitive

* $NIP ($: a$ b$ -- b$ )

Drop the next to top item from the string stack.
"string-nip"
NOTE:  Because of essential string  space bookkeeping, the

system level implementation can be little more efficient than
the high-level definition:
     	: $NIP  $SWAP $DROP ;

FORTH/DEF dstrings ordinary primitive

* $OVER ( $: a$ b$ -- a$ b$ a$ )

Leave a copy of the next most accessible string stack entry
on top of the string stack. The string value is not copied.
"string-over"

FORTH/DEF dstrings ordinary primitive

* $PICK ( u $: au$ ... a0$ -- au$ ... a0$ au$ )

Copy the u-th string stack entry to the top of the string
stack. The string value is not copied. Throw an error if
the input string stack does not have at least u+1 items.
"string-pick"

FORTH/DEF dstrings ordinary primitive

* $SWAP ( $: a$ b$ -- b$ a$ )

Exchange the two most accessible strings on the string stack.
Throw an error if there are less than two strings on the
stack. Neither string value is copied.
"string-swap"

FORTH/DEF dstrings ordinary primitive

* $S> ( $: a$ -- S: a.str )

Drop a$ from the string stack and leave it as a Forth string
a.str, without copying. "string-s-from"
WARNING:  If a$ is a bound string, it may move or disappear

at the next garbage collection, making a.str invalid. This
can be avoided by sandwiching sections of code where this
could occur between $GC-OFF and $GC-ON.

FORTH/DEF dstrings ordinary primitive

* $S>-COPY ( $: a$ -- S: a.str )

Drop a$ from the string stack, copy it into data space as a
measured string, and leave it as a Forth string a.str. An
error is thrown if the string length is larger than the
system parameter MAX_DATA_STR (see S,).
"string-s-from-copy"

FORTH/DEF dstrings ordinary primitive

* $S@ ( $: a$ -- a$ S: a.str )

Leave the string stack unchanged, and leave the string body
address and length on the data stack.
"string-s-fetch"
NOTE:  In earlier versions this was call $S@S.  The trailing

"S" is superfluous if it is understood that the only string
format that usually appears on the data stack is the Forth
string format.
WARNING:  If a$ is a bound string, it may move at the next

garbage collection, making a.str invalid. This can be
avoided by sandwiching sections of code where this could
occur between $GC-OFF and $GC-ON.

FORTH/DEF dstrings ordinary primitive

* $TUCK ($: a$ b$ -- b$ a$ b$ )

Copy the top string stack item just below the second item. The
string value is not copied. "string-tuck"
NOTE:  Because of essential string  space bookkeeping, the

system level implementation can be little more efficient than
the high-level definition:
 	: $TUCK  $SWAP $OVER ;

FORTH/DEF dstrings ordinary primitive

* >$S-COPY ( a.str -- $: a$ )

Copy the external string value whose body address and count
are on the parameter stack into the string buffer and push it
onto the string stack. Errors are thrown if the count is
larger than MAX_MCOUNT, if there is not enough room in string
space, even after garbage collection, or if there is an
unterminated string concatenation. The input external string
need not exist as a measured string.
"to-string-s-copy"
NOTE:  MAX_MCOUNT is the largest size the count field of a

measured string can hold, e.g., 255, 64K-1, or 4,096M-1. It
is returned by: S" /DYNAMIC-STRING" ENVIRONMENT?
WARNING: This word should not be used when the input string

is a bound string because the copy operation may generate a
garbage collection which invalidates its MSA.

FORTH/DEF dstrings ordinary primitive

* >$S ( a.str -- $: a$ )

Push the external Forth string a.str onto the string stack,
without copying the string value into the string buffer. It
is an unchecked error if the Forth string a.str is not stored
as an external measured string.
"to-string-s"
WARNING: If the string value of a.str is actually in the

string buffer and not external, the push operation may
generate a garbage collection that invalidates its MSA.

FORTH/DEF dstrings ordinary primitive

/* string manipulation */
* $! ( $var.dfa $: a$ -- )

Store the string MSA on the string stack in the variable
whose DFA is on the parameter stack.
"string-store"
NOTES: The only situation in which $! copies the string

value is when it is a bound string already stored in another
variable. In that case, the new copy is the one that is
stored in the variable. In particular, external strings are
not copied.
If the string value held by the string variable on entry is a

bound string that is also referenced deeper on the string
stack, its back link is reset to point to the deepest string
stack reference. If it is a bound string not deeper on the
string stack and not identical to the input string, its back
link is set to zero, making it garbage. If it is an external
string, its MSA in the variable is simply written over by
that popped from the string stack.

FORTH/DEF dstrings ordinary primitive

* $. ( $: a$ -- )

Display the string on the terminal. If the system
implementation of TYPE has its output vectored, $. uses the
same vector. "string-dot"

FORTH/DEF dstrings ordinary primitive

$TYPE

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

* $@ ( $var.pfa -- $: a$ )

Leave the MSA of the string held by the string variable.
"string-fetch"

FORTH/DEF dstrings ordinary primitive

* CAT ($: a$ -- )

Append the string body to the end of the string currently
being concatenated as the last string in the string buffer,
and update its count field. If there is no concatenating
string, start one. An error is thrown if the size of the
combined string would be larger than MAX_MCOUNT or if there
is not enough room in string space even after a garbage
collection.
If garbage collection occurs, a$ remains valid even when

it is in the string buffer.
 
When there is a concatenating string, concatenation is the
only basic string operation that can copy a string into the
string buffer. "cat"
NOTE: It is left to the user to define special concatenating

words like:
    : \n-cat  ( -- )  \n$ cat ;

FORTH/DEF dstrings ordinary primitive

* S-CAT ( a.str -- )

Append the Forth string body to the end of the string
currently being concatenated as the last string in the string
buffer, and update its count field. If there is no
concatenating string, start one. An error is thrown if the
size of the combined string would be larger than MAX_MCOUNT
or if there is not enough room in string space even after a
garbage collection.
S-CAT is most commonly used on external strings, not assumed

to exist as mstrings. In contrast to CAT, garbage
collection could invalidate a.str if it is a dynamic string
in the string buffer. S-CAT can be used in that situation if
garbage collection is turned off with $GC-OFF.
 
When there is a concatenating string, concatenation is the
only basic string operation that can copy a string into the
string buffer. "s-cat"

FORTH/DEF dstrings ordinary primitive

* ENDCAT ( -- $: cat$ | empty$ )

If there is no concatenating string, do nothing but leave the
empty string. If there is, leave it as a string bound to the
top of the string stack, and terminate concatenation,
permitting normal copies into the string buffer.
"end-cat"

FORTH/DEF dstrings ordinary primitive

/* string frames */
* $FRAME ( u -- )

Push the description of a string stack frame starting at the
top of the string stack and containing u entries onto the
string frame stack. Errors are thrown if the frame stack
would overflow or if the depth of the string stack above the
top frame, if there is one, is less than u. The value u = 0
is allowed. "string-frame"
NOTE: The current implementation pushes u and the string

stack pointer onto the frame stack.

FORTH/DEF dstrings ordinary primitive

* DROP-$FRAME ( -- )

Drop the topmost string frame from the string frame stack and
string stack. Errors are thrown if either stack would
underflow or if the string frame does not begin at the top of
the string stack. The case where the frame has zero entries
on the string stack is handled properly.
"drop-string-frame"

FORTH/DEF dstrings ordinary primitive

* FIND-ARG ( s -- i true | false )

Leave true and its index i in the top string frame if the
Forth string matches an element of the frame, else leave
false. The index of the top frame element is zero.
"find-arg"

FORTH/DEF dstrings ordinary primitive

* (DROP-$FRAME) ( -- )

Cleanup code for the end of a definition that uses ARGS{.
;-semicolon should be overloaded to compile it
automatically if dynamic string arguments were in use.

FORTH/DEF dstrings compiling primitive

/* debugging */
/$SPACE

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

/$SPACE-HEADER

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$BREAK

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$BUFFER

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$SP

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$SP0

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

#FRAMES

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

/FRAME-STACK

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$FBREAK

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$FSP

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$FSP0

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

0$SPACE

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

$FRAME-DEPTH

[] no special info, see general notes

FORTH/DEF dstrings ordinary primitive

ENVIRONMENT ENVIRONMENT DSTRINGS-EXT

[] no special info, see general notes

ENVIRONMENT dstrings ordinary constant

ENVIRONMENT /SCOPY

[] no special info, see general notes

ENVIRONMENT dstrings ordinary constant

ENVIRONMENT /DYNAMIC-STRING

[] no special info, see general notes

ENVIRONMENT dstrings ordinary constant

ENVIRONMENT DSTRINGS-LOADED

[] no special info, see general notes

ENVIRONMENT dstrings constructor primitive

EXTENSIONS P4_EXPT ("string count too large" /* -2053 */, P4_ON_SCOUNT_OVERFLOW), P4_EXPT ("string space overflow" /* -2054 */, P4_ON_SSPACE_OVERFLOW), P4_EXPT ("string garbage locked" /* -2055 */, P4_ON_SGARBAGE_LOCK), P4_EXPT ("string stack underflow" /* -2056 */, P4_ON_SSTACK_UNDERFLOW), P4_EXPT ("cat lock preventing string copy" /* -2057 */, P4_ON_SCAT_LOCK), P4_EXPT ("dynamic string count too large" /* .. */, P4_ON_DSCOUNT_OVERFLOW), P4_EXPT ("too many string frames" /* -2059 */, P4_ON_SFRAME_OVERFLOW), P4_EXPT ("not enough strings for frame" /* -2060 */, P4_ON_SFRAME_ITEMS), P4_EXPT ("string frame stack underflow" /* -2061 */, P4_ON_SFRAME_UNDERFLOW), P4_EXPT ("string frame not at top of string stack" /* -2062 */, P4_ON_SFRAME_MISMATCH),