% !TeX root = forth.tex
\chapter{The optional String word set} % 17
\wordlist{string}

\section{Introduction} % 17.1

\section{Additional terms and notation} % 17.2

None.

\section{Additional usage requirements} % 17.3

None.

\section{Additional documentation requirements} % 17.4
\label{string:document}

\subsection{System documentation}
\subsubsection{Implementation-defined options}
\begin{itemize}
\item no additional options.
\end{itemize}

\subsubsection{Ambiguous conditions}
\begin{itemize}
\item The substitution cannot be created (\word{REPLACES});
\item The name of a substitution contains the `\texttt{\%}' delimiter character (\word{REPLACES});
\item Result of a substitution is too long to fit into the given buffer (\word{SUBSTITUTE} and \word{UNESCAPE});
\item Source and destination buffers for \word{SUBSTITUTE} are the same.
\end{itemize}

\subsubsection{Other system documentation}
\begin{itemize}
\item no additional requirements.
\end{itemize}

\subsection{Program documentation}

\subsubsection{Environmental dependencies}
\begin{itemize}
\item no additional dependencies.
\end{itemize}

\subsubsection{Other program documentation}
\begin{itemize}
\item no additional requirements.
\end{itemize}

\section{Compliance and labeling} % 17.5

\subsection{Forth-\snapshot{} systems} % 17.5.1

The phrase ``Providing the String word set'' shall be appended to
the label of any Standard System that provides all of the String
word set.

The phrase ``Providing \emph{name(s)} from the String Extensions
word set'' shall be appended to the label of any Standard System
that provides portions of the String Extensions word set.

The phrase ``Providing the String Extensions word set'' shall be
appended to the label of any Standard System that provides all of
the String and String Extensions word sets.

\subsection{Forth-\snapshot{} programs} % 17.5.2

The phrase ``Requiring the String word set'' shall be appended to
the label of Standard Programs that require the system to provide
the String word set.

The phrase ``Requiring \emph{name(s)} from the String Extensions
word set'' shall be appended to the label of Standard Programs that
require the system to provide portions of the String Extensions
word set.

The phrase ``Requiring the String Extensions word set'' shall be
appended to the label of Standard Programs that require the system
to provide all of the String and String Extensions word sets.


\section{Glossary} % 17.6

\subsection{String words} % 17.6.1


\begin{worddef}{0170}{-TRAILING}[dash-trailing]
\item \stack{c-addr u_1}{c-addr u_2}

	If \param{u_1} is greater than zero, \param{u_2} is equal to
	\param{u_1} less the number of spaces at the end of the
	character string specified by \param{c-addr u_1}. If \param{u_1}
	is zero or the entire string consists of spaces, \param{u_2} is
	zero.

	\begin{testing}
		\test{\word{:}  s8 \word{Sq} abc  " \word{;}}{} \\
		\test{\word{:}  s9 \word{Sq}      " \word{;}}{} \\
		\test{\word{:} s10 \word{Sq}    a " \word{;}}{}

		\test{ s1 \word{-TRAILING}}{s1}		\tab \word{bs} ``\texttt{abcdefghijklmnopqrstuvwxyz}'' \\
		\test{ s8 \word{-TRAILING}}{s8 2 -} 			\tab[2.5] \word{bs} ``\texttt{abc~~}'' \\
		\test{ s7 \word{-TRAILING}}{s7}					\tab[5.2] \word{bs} ``\texttt{~}'' \\
		\test{ s9 \word{-TRAILING}}{s9 \word{DROP} 0}	\tab[1] \word{bs} ``\texttt{~~~~~}'' \\
		\test{s10 \word{-TRAILING}}{s10 1-}				\tab[2.8] \word{bs} ``\texttt{~~~a~}''
	\end{testing}
\end{worddef}


\begin{worddef}{0245}{/STRING}[slash-string]
\item \stack{c-addr_1 u_1 n}{c-addr_2 u_2}

	Adjust the character string at \param{c-addr_1} by \param{n}
	characters. The resulting character string, specified by
	\param{c-addr_2 u_2}, begins at \param{c-addr_1} plus \param{n}
	characters and is \param{u_1} minus \param{n} characters long.

\see \rref{string:/STRING}{}.

	\begin{rationale} % A.17.6.1.0245 /STRING
		\word{/STRING} is used to remove or add characters relative
		to the current position in the character string. Positive values
		of \param{n} will exclude characters from the string while
		negative values of \param{n} will include characters to the
		left of the string.

		\word{Sq} ABC" 2 \word{/STRING} \word{2DUP} \word{TYPE} \word{bs} outputs ``C'' \\
		-1 \word{/STRING} \word{TYPE} \word{bs} outputs ``BC''
	\end{rationale}

	\begin{testing}
		\test{s1  5 \word{/STRING}}{s1 \word{SWAP} 5 \word{+} \word{SWAP} 5 \word{-}} \\
		\test{s1 10 \word{/STRING} -4 \word{/STRING}}{s1 6 \word{/STRING}} \\
		\test{s1  0 \word{/STRING}}{s1}
	\end{testing}
\end{worddef}


\begin{worddef}{0780}{BLANK}
\item \stack{c-addr u}{}

	If \param{u} is greater than zero, store the character value for
	space in \param{u} consecutive character positions beginning at
	\param{c-addr}.

	\begin{testing} \ttfamily
		\word{:} s13 \word{Sq} aaaaa~~~~~~a" \word{;}						\tab[4.5] \word{bs} \textdf{Six spaces}

		\test{\word{PAD} 25 \word{CHAR} a \word{FILL}}{}					\tab[2.8] \word{bs} \textdf{Fill PAD with 25 'a's} \\
		\test{\word{PAD} 5 \word{CHARS} \word{+} 6 \word{BLANK}}{}	\tab[1] \word{bs} \textdf{Put 6 spaced from character 5} \\
		\test{\word{PAD} 12 s13 \word{COMPARE}}{0}							\tab[2.2] \word{bs} \textdf{PAD Should now be same as s13}
	\end{testing}
\end{worddef}


\begin{worddef}{0910}{CMOVE}[c-move]
\item \stack{c-addr_1 c-addr_2 u}{}

	If \param{u} is greater than zero, copy \param{u} consecutive
	characters from the data space starting at \param{c-addr_1} to
	that starting at \param{c-addr_2}, proceeding character-by-character
	from lower addresses to higher addresses.

\see \wref{string:CMOVEtop}{}, \rref{string:CMOVE}{}.

	\begin{rationale} % A.17.6.1.0910 CMOVE
		If \param{c-addr_2} lies within the source region (i.e., when
		\param{c-addr_2} is not less than \param{c-addr_1} and
		\param{c-addr_2} is less than the quantity \param{c-addr_1 u}
		\word[core]{CHARS} \word[core]{+}), memory propagation occurs.

		Assume a character string at address
		100: ``ABCD''. Then after
		\begin{quote}\ttfamily
			100 \word[core]{DUP} ~ \word[core]{CHAR+} ~ 3 \word{CMOVE}
		\end{quote}
		the string at address 100 is ``AAAA''.

		See \rref{core:MOVE}{}.
	\end{rationale}
\end{worddef}


\begin{worddef}[CMOVEtop]{0920}{CMOVE>}[c-move-up]
\item \stack{c-addr_1 c-addr_2 u}{}

	If \param{u} is greater than zero, copy \param{u} consecutive
	characters from the data space starting at \param{c-addr_1} to
	that starting at \param{c-addr_2}, proceeding character-by-character
	from higher addresses to lower addresses.

\see \wref{string:CMOVE}{}, \rref{string:CMOVEtop}{}.

	\begin{rationale} % A.17.6.1.0920 CMOVE>
		If \param{c-addr_1} lies within the destination region (i.e.,
		when \param{c-addr_1} is greater than or equal to
		\param{c-addr_2} and \param{c-addr_2} is less than the
		quantity \param{c-addr_1 u} \word[core]{CHARS}
		\word[core]{+}), memory propagation	occurs.

		Assume a character string at address
		100: ``ABCD''. Then after
		\begin{quote}\ttfamily
			100 \word[core]{DUP} \word[core]{CHAR+} \word[core]{SWAP}
			3 \word{CMOVEtop}
		\end{quote}
		the string at address 100 is ``DDDD''.

		See \rref{core:MOVE}{}.
	\end{rationale}
\end{worddef}


\begin{worddef}{0935}{COMPARE}
\item \stack{c-addr_1 u_1 c-addr_2 u_2}{n}

	Compare the string specified by \param{c-addr_1 u_1} to the
	string specified by \param{c-addr_2 u_2}. The strings are
	compared, beginning at the given addresses, character by
	character, up to the length of the shorter string or until a
	difference is found. If the two strings are identical, \param{n}
	is zero. If the two strings are identical up to the length of
	the shorter string, \param{n} is minus-one (-1) if \param{u_1}
	is less than \param{u_2} and one (1) otherwise. If the two
	strings are not identical up to the length of the shorter string,
	\param{n} is minus-one (-1) if the first non-matching character
	in the string specified by \param{c-addr_1 u_1} has a lesser
	numeric value than the corresponding character in the string
	specified by \param{c-addr_2 u_2} and one (1) otherwise.

	\begin{testing}\ttfamily
		\test{s1        s1 \word{COMPARE}}{ 0 } \\
		\test{s1  PAD SWAP \word{CMOVE}  }{   } \tab \word{bs} \textdf{Copy s1 to PAD} \\
		\test{s1  PAD OVER \word{COMPARE}}{ 0 } \\
		\test{s1     PAD 6 \word{COMPARE}}{ 1 } \\
		\test{PAD 10    s1 \word{COMPARE}}{-1 } \\
		\test{s1     PAD 0 \word{COMPARE}}{ 1 } \\
		\test{PAD  0    s1 \word{COMPARE}}{-1 } \\
		\test{s1        s6 \word{COMPARE}}{ 1 } \\
		\test{s6        s1 \word{COMPARE}}{-1 }

		\word{:} "abdde"~ \word{Sq} abdde"~ \word{;} \\
		\word{:} "abbde"~ \word{Sq} abbde"~ \word{;} \\
		\word{:} "abcdf"~ \word{Sq} abcdf"~ \word{;} \\
		\word{:} "abcdee" \word{Sq} abcdee" \word{;}

		\test{s1 "abdde"  \word{COMPARE}}{-1} \\
		\test{s1 "abbde"  \word{COMPARE}}{ 1} \\
		\test{s1 "abcdf"  \word{COMPARE}}{-1} \\
		\test{s1 "abcdee" \word{COMPARE}}{ 1}

		\word{:} s11 \word{Sq} 0abc" \word{;} \\
		\word{:} s12 \word{Sq} 0aBc" \word{;}

		\test{s11 s12 \word{COMPARE}}{ 1} \\
		\test{s12 s11 \word{COMPARE}}{-1}
	\end{testing}
\end{worddef}


\begin{worddef}{2191}{SEARCH}
\item \stack{c-addr_1 u_1 c-addr_2 u_2}{c-addr_3 u_3 flag}

	Search the string specified by \param{c-addr_1 u_1} for the
	string specified by \param{c-addr_2 u_2}. If \param{flag} is
	true, a match was found at \param{c-addr_3} with \param{u_3}
	characters remaining. If \param{flag} is false there was no
	match and \param{c-addr_3} is \param{c-addr_1} and \param{u_3}
	is \param{u_1}.

	\begin{testing}
		\test{\word{:} s2 \word{Sq} abc"   \word{;}}{} \\
		\test{\word{:} s3 \word{Sq} jklmn" \word{;}}{} \\
		\test{\word{:} s4 \word{Sq} z"     \word{;}}{} \\
		\test{\word{:} s5 \word{Sq} mnoq"  \word{;}}{} \\
		\test{\word{:} s6 \word{Sq} 12345" \word{;}}{} \\
		\test{\word{:} s7 \word{Sq} "      \word{;}}{}

		\test{s1 s2 \word{SEARCH}}{s1 <TRUE> } \\
		\test{s1 s3 \word{SEARCH}}{s1  9 \word{/STRING} <TRUE> } \\
		\test{s1 s4 \word{SEARCH}}{s1 25 \word{/STRING} <TRUE> } \\
		\test{s1 s5 \word{SEARCH}}{s1 <FALSE>} \\
		\test{s1 s6 \word{SEARCH}}{s1 <FALSE>} \\
		\test{s1 s7 \word{SEARCH}}{s1 <TRUE> } 
	\end{testing}
\end{worddef}


\begin{worddef}{2212}{SLITERAL}
\interpret
	Interpretation semantics for this word are undefined.

\compile
	\stack{c-addr_1 u}{}

	Append the run-time semantics given below to the current
	definition.

\runtime
	\stack{}{c-addr_2 u}

	Return \param{c-addr_2 u} describing a string consisting of
	the characters specified by \param{c-addr_1 u} during
	compilation. A program shall not alter the returned string.

\see \rref{string:SLITERAL}{}.

	\begin{rationale} % A.17.6.1.2212 SLITERAL
		The current functionality of \wref{core:Sq}{S"} may be
		provided by the following definition:
		\setwordlist{core}
		\begin{quote}\ttfamily
			\word{:} \word{Sq} \word{p} "ccc<quote>" -{}- ) \\
			\tab \word{[CHAR]} " \word{PARSE} ~
				\word{POSTPONE} \word[string]{SLITERAL} \\
			\word{;} \word{IMMEDIATE}
		\end{quote}
		\setwordlist{string}
	\end{rationale}

	\begin{testing}
		\test{\word{:} s14 \word{[} s1 \word{]} \word{SLITERAL} \word{;}}{} \\
		\test{s1 s14 \word{COMPARE}}{0} \\
		\test{s1 s14 \word{ROT} \word{=} \word{ROT} \word{ROT} \word{=}}{<TRUE> <FALSE>}
	\end{testing}
\end{worddef}


\subsection{String extension words} % 17.6.2
\extended

\begin{worddef}{2141}{REPLACES}[][X:substitute]
\item \stack{c-addr_1 u_1 c-addr_2 u_2}{}

	Set the string \param{c-addr_1 u_1} as the text to substitute for
	the substitution named by \param{c-addr_2 u_2}.
	If the substitution does not exist it is created.
	The program may then reuse the buffer \param{c-addr_1 u_1} without
	affecting the definition of the substitution.

	Ambiguous conditions occur as follows:
	\begin{itemize}
	\item The substitution cannot be created.
	\item The name of a substitution contains the `\texttt{\%}' delimiter character.
	\end{itemize}

	\word{REPLACES} may allot data space and create a definition.  This breaks
	the contiguity of the current region and is not allowed during compilation
	of a colon definition
  
\see \xref{usage:contiguous},
	\xref{usage:compilation},
	\wref{string:SUBSTITUTE}{}.

	\begin{implement}
		\word{DECIMAL}

		\word[tools]{[UNDEFINED]} place \word[tools]{[IF]} \\
		\tab \word{:} place \tab \word{bs} \ c-addr1 u c-addr2 -{}- \\
		\tab \word{bs} \textdf{Copy the string described by \emph{c-addr$_1$ u} as a counted} \\
		\tab \word{bs} \textdf{string at the memory address described by \emph{c-addr$_2$}.} \\
		\tab[2] \word{2DUP} \word{2toR} \\
		\tab[2] 1 \word{CHARS} \word{+} \word{SWAP} \word{MOVE} \\
		\tab[2] \word{2Rfrom} \word{C!} \\
		\tab \word{;} \\
		\word[tools]{[THEN]}

		\word{:} "/COUNTED-STRING" \word{Sq} /COUNTED-STRING" \word{;} \\
		"/COUNTED-STRING" \word{ENVIRONMENTq} \word{0=} \word[tools]{[IF]} 256 \word[tools]{[THEN]} \\
		\word{CHARS} \word{CONSTANT} string-max

		\word[search]{WORDLIST} \word{CONSTANT} wid-subst \\
		\word{bs} \textdf{Wordlist ID of the wordlist used to hold substitution names and replacement text.}

		\word[tools]{[DEFINED]} VFXforth \word[tools]{[IF]} \word{bs} \textdf{\textbf{VFX Forth}} \\
		\tab \word{:} makeSubst	\word{bs} c-addr len -{}- c-addr \\
		\tab \word{bs} \textdf{Given a name string create a substution and storage space.} \\
		\tab \word{bs} \textdf{Return the address of the buffer for the substitution text.} \\
		\tab \word{bs} \textdf{This word requires system specific knowledge of the host Forth.} \\
		\tab \word{bs} \textdf{Some systems may need to perform case conversion here.} \\
		\tab[2] \word[search]{GET-CURRENT} \word{toR} wid-subst \word[search]{SET-CURRENT} \\
		\tab[2] (\$create) \tab[12.6] \word{bs} \textdf{like \word{CREATE} but takes c-addr/len} \\
		\tab[2] \word{Rfrom} \word[search]{SET-CURRENT} \\
		\tab[2] \word{HERE}  string-max \word{ALLOT}  0 \word{OVER} \word{C!}	\word{bs} \textdf{create buffer space} \\
		\tab \word{;} \\
		\word[tools]{[THEN]}

		\word[tools]{[DEFINED]} (WID-CREATE) \word[tools]{[IF]} \word{bs} \textdf{\textbf{SwiftForth}} \\
		\tab \word{:} makeSubst \word{bs} c-addr len -{}- c-addr \\
		\tab[2] wid-subst (WID-CREATE) \tab[4.8] \word{bs} \textdf{like \word{CREATE} but takes c-addr/len/wid} \\
		\tab[2] LAST \word{@} >CREATE \word{!} \\
		\tab[2] \word{HERE}  string-max \word{ALLOT}  0 \word{OVER} \word{C!} \word{bs} \textdf{create buffer space} \\
		\tab \word{;} \\
		\word[tools]{[THEN]}

		\word{:} findSubst \word{bs} c-addr len -{}- xt flag | 0 \\
		\word{bs} \textdf{Given a name string, find the substitution.} \\
		\word{bs} \textdf{Return \emph{xt} and \emph{flag} if found, or just zero if not found.} \\
		\word{bs} \textdf{Some systems may need to perform case conversion here.} \\
		\tab wid-subst \word[search]{SEARCH-WORDLIST} \\
		\word{;}

		\word{:} \word{REPLACES} \word{bs} text tlen name nlen -{}- \\
		\word{bs} \textdf{Define the string \emph{text/tlen} as the text to substitute for the substitution named \emph{name/nlen}.} \\
		\word{bs} \textdf{If the substitution does not exist it is created.} \\
		\tab \word{2DUP} findSubst \word{IF} \\
		\tab[2] \word{NIP} \word{NIP}  \word{EXECUTE} \tab \word{bs} \textdf{get buffer address} \\
		\tab \word{ELSE} \\
		\tab[2] makeSubst \\
		\tab \word{THEN} \\
		\tab place \tab[8] \word{bs} \textdf{copy as counted string} \\
		\word{;}
	\end{implement}
\end{worddef}

\begin{worddef}{2255}{SUBSTITUTE}[][X:substitute]
\item \stack{c-addr_1 u_1 c-addr_2 u_2}{c-addr_2 u_3 n}

	Perform substitution on the string \param{c-addr_1 u_1} placing
	the result at string \param{c-addr_2 u_3}, where \param{u3} is
	the length of the resulting string.
	An error occurs if the resulting string will not fit into
	\param{c-addr_2 u_2} or if \param{c-addr_2} is the same as \param{c-addr_1}.
	The return value \param{n} is positive or 0 on success and indicates the
	number of substitutions made.
	A negative value for \param{n} indicates that an error occurred, leaving
	\param{c-addr_2 u_3} undefined.
	Negative values of \param{n} are implementation defined except for
	values in table~\xref{table:throw}.

	Substitution occurs left to right from the start of \param{c-addr_1}
	in one pass and is non-recursive.

	When text of a potential substitution name, surrounded by `\texttt{\%}' (ASCII \$25) delimiters
	is encountered by \word{SUBSTITUTE}, the following occurs:

	\begin{enumerate}
	\item If the name is null, a single delimiter character is passed
		to the output, i.e., \texttt{\%\%} is replaced by \texttt{\%}.
		The current number of substitutions is not changed.

	\item If the text is a valid substitution name acceptable to
		\wref{string:REPLACES}{}, the leading and trailing delimiter
		characters and the enclosed substitution name are replaced by
		the substitution text.  The current number of substitutions
		is incremented.

	\item If the text is not a valid substitution name, the name with
		leading	and trailing delimiters is passed unchanged to the
		output.  The current number of substitutions is not changed.

	\item Parsing of the input string resumes after the trailing delimiter.
	\end{enumerate}

	If after processing any pairs of delimiters, the residue of the input string contains
	a single delimiter, the residue is passed unchanged to the output.

\see \wref{string:REPLACES}{}, \wref{string:UNESCAPE}{}, \rref{string:SUBSTITUTE}{}.

	\begin{rationale}
		Many applications need to be able to perform text substitution, for
		example:

		\begin{center}
			 Your balance at \arg{time} on \arg{date} is \arg{currencyvalue}.
		\end{center}

		Translation of a sentence or message from one language to another may
		result in changes to the displayed parameter order.  The example, the
		Afrikaans translation of this sentence requires a different order:

		\begin{center}
			Jou balans op \arg{date} om \arg{time} is \arg{currencyvalue}.
		\end{center}

		The words \word{SUBSTITUTE} and \word{REPLACES} provide for this
		requirements by defining a text substitution facility.  For example,
		we can provide an initial string in the form:

		\begin{center}
			\texttt{Your balance at \%time\% on \%date\% is \%currencyvalue\%.}
		\end{center}

		The \texttt{\%} is used as delimiters for the substitution name.  The
		text ``\texttt{currencyvalue}'', ``\texttt{date}'' and ``\texttt{time}''
		are text substitutions, where the replacement text is defined by
		\word{REPLACES}:

\makeatletter
\newcommand{\ptime}{%
	% Hour
	\@tempcnta = \time
	\divide\@tempcnta by 60
	\ifnum\@tempcnta<10 0\fi%
	\number\@tempcnta%
	:%
	% Minutes
	\multiply\@tempcnta by 60
	\@tempcntb = \time
	\advance\@tempcntb by -\@tempcnta
	\ifnum\@tempcntb < 10 0\fi%
	\number\@tempcntb
}

\newcommand{\pdate}{%
	% Day
	\ifnum\day<10 0\fi%
	\number\day%
	/%
	% Month
	\ifcase\month
	\or Jan\or Feb\or Mar\or Apr\or May\or Jun
	\or Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi%
	/%
	% Year
	\number\year
}
\makeatother

		\begin{quote}\ttfamily
			\begin{tabbing}
				\word{:} date \word{Sq} \pdate" \word{;} \\
				\word{:} time \word{Sq} \ptime" \word{;} \\
				date\ \= \word{Sq} date" \word{REPLACES} \\
				time\ \> \word{Sq} time" \word{REPLACES}
			\end{tabbing}
		\end{quote}

		The substitution name ``date'' is defined to be replaced with the string
		``\pdate'' and ``time'' to be replaced with ``\ptime''.  Thus
		\word{SUBSTITUTE} would produce the string:

		\begin{center}
			\texttt{Your balance at \ptime{} on \pdate{} is \%currencyvalue\%.}
		\end{center}

		As the substitution name ``currencyvalue'' has not been defined, it is
		left unchanged in the resulting string.

		The return value \param{n} is nonnegative on success and indicates the
		number of substitutions made.  In the above example, this would be two.
		A negative value indicates that an error occurred.
		As substitution is not recursive, the return value could be used to
		provide a recursive substitution.

		Implementation of \word{SUBSTITUTE} may be considered as being equivalent
		to a wordlist which is searched.  If the substitution name is found, the
		word is executed, returning a substitution string.
		Such words can be deferred or multiple wordlists can be used.
		The implementation techniques required are similar to those used by
		\word{ENVIRONMENTq}.
		There is no provision for changing the delimiter character, although a
		system may provide system-specific extensions.
	\end{rationale}

	\begin{implement}
		\textdf{Assuming \iref{string:REPLACES}{} has been defined.}

		\word[tools]{[UNDEFINED]} bounds \word[tools]{[IF]} \\
		\tab \word{:} bounds \tab \word{bs} addr len -{}- addr+len addr \\
		\tab[2] \word{OVER} \word{+} \word{SWAP}\\
		\tab \word{;} \\
		\word[tools]{[THEN]}

		\word[tools]{[UNDEFINED]} -rot \word[tools]{[IF]} \\
		\tab \word{:} -rot \tab \word{bs} a b c -{}- c a b \\
		\tab[2] \word{ROT} \word{ROT} \\
		\tab \word{;} \\
		\word[tools]{[THEN]}

		\word{CHAR} \% \word{CONSTANT} delim 
		\tab[1.2] \word{bs} \textdf{Character used as the substitution name delimiter.} \\
%
		string-max \word{BUFFER:} Name	%\word{bs} -{}- addr \\
		\tab[0] \word{bs} \textdf{Holds substitution name as a counted string.} \\
%
		\word{VARIABLE} DestLen % \word{bs} -{}- addr \\
		\tab[4.2] \word{bs} \textdf{Maximum length of the destination buffer.} \\
%
		\word[double]{2VARIABLE} Dest %\word{bs} -{}- addr \\
		\tab[5.4] \word{bs} \textdf{Holds destination string current length and address.} \\
%
		\word{VARIABLE} SubstErr % \word{bs} -{}- addr \\
		\tab[3.6] \word{bs} \textdf{Holds zero or an error code.}

		\word{:} addDest \word{bs} char -{}- \\
		\word{bs} \textdf{Add the character to the destination string.} \\
		\tab Dest \word{@} DestLen \word{@} \word{less} \word{IF} \\
		\tab[2] Dest \word{2@} \word{+} \word{C!}  1 \word{CHARS} Dest \word{+!} \\
		\tab \word{ELSE} \\
		\tab[2] \word{DROP} -1 SubstErr \word{!} \\
		\tab \word{THEN} \\
		\word{;}

		\word{:} formName \word{bs} c-addr len -{}- c-addr' len' \\
		\word{bs} \textdf{Given a source string pointing at a leading delimiter, place the name string in the name buffer.} \\
		\tab 1 \word{/STRING} \word{2DUP} delim scan \word{toR} \word{DROP}				  \word{bs} \textdf{find length of residue} \\
		\tab \word{2DUP} \word{Rfrom} \word{-} \word{DUP} \word{toR} Name place \tab[3] \word{bs} \textdf{save name in buffer} \\
		\tab \word{Rfrom} 1 \word{CHARS} \word{+} \word{/STRING}						\tab[7.2] \word{bs} \textdf{step over name and trailing \texttt{\%}} \\
		\word{;}

		\word{:} >dest \word{bs} c-addr len -{}- \\
		\word{bs} \textdf{Add a string to the output string.} \\
		\tab bounds \word{qDO} \\
%		\tab \word{OVER} \word{+} \word{SWAP} \word{qDO} \\
		\tab[2] \word{I} \word{C@} addDest \\
		\tab 1 \word{CHARS} \word{+LOOP} \\
		\word{;}

		\word{:} processName \word{bs} -{}- flag \\
		\word{bs} \textdf{Process the last substitution name. Return true if found, 0 if not found.} \\
		\tab Name \word{COUNT} findSubst \word{DUP} \word{toR} \word{IF} \\
		\tab[2] \word{EXECUTE} \word{COUNT} >dest \\
		\tab \word{ELSE} \\
		\tab[2] delim addDest  Name \word{COUNT} >dest  delim addDest \\
		\tab \word{THEN} \\
		\tab \word{Rfrom} \\
		\word{;}

		\word{:} \word{SUBSTITUTE} \word{bs} src slen dest dlen -{}- dest dlen' n \\
		\word{bs} \textdf{Expand the source string using substitutions.} \\
		\word{bs} \textdf{Note that this version is simplistic, performs no error checking,} \\
		\word{bs} \textdf{and requires a global buffer and global variables.} \\
		\tab Destlen \word{!}  0 Dest \word{2!}  0 -rot \word{bs} -{}- 0 src slen \\
		\tab 0 SubstErr \word{!} \\
		\tab \word{BEGIN} \\
		\tab[2] \word{DUP} 0 \word{more} \\
		\tab \word{WHILE} \\
		\tab[2] \word{OVER} \word{C@} delim \word{ne} \word{IF}									\tab[6.8] \word{bs} \textdf{character not \texttt{\%}} \\
		\tab[3]	\word{OVER} \word{C@} addDest  1 \word{/STRING} \\
		\tab[2] \word{ELSE} \\
		\tab[3]	\word{OVER} 1 \word{CHARS} \word{+} \word{C@} delim \word{=} \word{IF}	\tab \word{bs} \textdf{\texttt{\%\%} for one output \texttt{\%}} \\
		\tab[4]		delim addDest  2 \word{/STRING}													\tab[2.4] \word{bs} \textdf{add one \texttt{\%} to output} \\
		\tab[3]	\word{ELSE} \\
		\tab[4]		formName processName \word{IF} \\
		\tab[5]			\word{ROT} \word{1+} -rot														\tab[8.6] \word{bs} \textdf{count substitutions} \\
		\tab[4]		\word{THEN} \\
		\tab[3]	\word{THEN} \\
		\tab[2] \word{THEN} \\
		\tab \word{REPEAT} \\
		\tab \word{2DROP} Dest \word{2@}  \word{ROT} SubstErr \word{@} \word{IF} \\
		\tab[2]	\word{DROP}  SubstErr \word{@} \\
		\tab \word{THEN} \\
		\word{;}
	\end{implement}

	\begin{testing}
		\ttfamily
		30 \word{CHARS} \word{BUFFER:} subbuff \word{bs} \textdf{Destination buffer}

		\word{bs} \textdf{Define a few string constants} \\
		\word{:} "hi"    \word{Sq} hi"    \word{;} \\
		\word{:} "wld"   \word{Sq} wld"   \word{;} \\
		\word{:} "hello" \word{Sq} hello" \word{;} \\
		\word{:} "world" \word{Sq} world" \word{;}

		\word{bs} \textdf{Define a few test strings} \\
		\word{:} sub1 \word{Sq} Start:\ \%hi\%,\%wld\%!\ :End" ; \tab[1.5]\word{bs} \textdf{Original string} \\
		\word{:} sub2 \word{Sq} Start:\ hello,world!\ :End" ;    \tab\word{bs} \textdf{First target string} \\
		\word{:} sub3 \word{Sq} Start:\ world,hello!\ :End" ;    \tab\word{bs} \textdf{Second target string}

		\word{bs} \textdf{Define the \texttt{hi} and \texttt{wld} substitutions} \\
		\test{"hello" "hi"  \word{REPLACES}}{} \tab[4] \word{bs} \textdf{Replace ``\texttt{\%hi\%}''  with ``\texttt{hello}''} \\
		\test{"world" "wld" \word{REPLACES}}{} \tab[4] \word{bs} \textdf{Replace ``\texttt{\%wld\%}'' with ``\texttt{world}''}

		\word{bs} \textdf{``\texttt{\%hi\%,\%wld\%}'' changed to ``\texttt{hello,world}''} \\
		\test{sub1 subbuff 30 \word{SUBSTITUTE} \word{ROT} \word{ROT} sub2 \word{COMPARE}}{2 0}

		\word{bs} \textdf{Change the \texttt{hi} and \texttt{wld} substitutions} \\
		\test{"world" "hi"  \word{REPLACES}}{} \\
		\test{"hello" "wld" \word{REPLACES}}{}

		\word{bs} \textdf{Now ``\texttt{\%hi\%,\%wld\%}'' should be changed to ``\texttt{world,hello}''} \\
		\test{sub1 subbuff 30 \word{SUBSTITUTE} \word{ROT} \word{ROT} sub3 \word{COMPARE}}{2 0}

		\word{bs} \textdf{Where the subsitution name is not defined} \\
		\word{:} sub4 \word{Sq} aaa\%bbb\%ccc" ; \\
		\test{sub4 subbuff 30 \word{SUBSTITUTE} \word{ROT} \word{ROT} sub4 \word{COMPARE}}{0 0}

		\word{bs} \textdf{Finally the \texttt{\%} character itself} \\
		\word{:} sub5 \word{Sq} aaa\%\%bbb" \word{;} \\
		\word{:} sub6 \word{Sq} aaa\%bbb" \word{;} \\
		\test{sub5 subbuff 30 \word{SUBSTITUTE} \word{ROT} \word{ROT} sub6 \word{COMPARE}}{0 0} \\
	\end{testing}
\end{worddef}

\begin{worddef}{2375}{UNESCAPE}[][X:substitute]
\item \stack{c-addr_1 u_1 c-addr_2}{c-addr_2 u_2}

	Replace each `\texttt{\%}' character in the input string
	\param{c-addr_1 u_1} by two `\texttt{\%}' characters.  The output
	is represented by \param{c-addr_2 u_2}.  The buffer at \param{c-addr_2}
	shall be big enough to hold the unescaped string.  An ambiguous
	condition occurs if the resulting string will not fit into the
	destination buffer (\param{c-addr_2}).

\see \wref{string:SUBSTITUTE}{}.

	\begin{implement}
		\word{:} \word{UNESCAPE} \word{bs} c-addr1 len1 c-addr2 -{}- c-addr2 len2 \\
		\word{bs} \textdf{Replace each '\texttt{\%}' character in the input string \emph{c-addr$_1$ len$_1$} with two '\texttt{\%}' characters.} \\
		\word{bs} \textdf{The output is represented by \emph{c-addr$_2$ len$_2$}.} \\
		\word{bs} \textdf{If you pass a string through \word{UNESCAPE} and then \word{SUBSTITUTE}, you get the original string.} \\
		\tab \word{DUP} \word{2SWAP}  \word{OVER} \word{+} \word{SWAP} \word{qDO} \\
		\tab[2] \word{I} \word{C@} \word{[CHAR]} \% \word{=} \word{IF} \\
		\tab[3] \word{[CHAR]} \% \word{OVER} \word{C!} \word{1+} \\
		\tab[2] \word{THEN} \\
		\tab[2] \word{I} \word{C@} \word{OVER} \word{C!} \word{1+} \\
		\tab \word{LOOP} \\
		\tab \word{OVER} \word{-} \\
		\word{;}
	\end{implement}

	\begin{testing}
		Using \texttt{subbuff}, \texttt{sub5} and \texttt{sub6} from \tref{string:SUBSTITUTE}{}.

		\test{sub6 subbuff \word{UNESCAPE} sub5 \word{COMPARE}}{0}
	\end{testing}
\end{worddef}
\endinput
