Text macro substitution

Usage

VFX Forth implements text macro substitution, where a text macro named FOO my be substituted in a string. When referenced in a string the macro name must be surrounded by % characters. If a % character is needed in a string it must be entered as %%.

Thus if FOO is defined as "c:\apps\vfxforth" then the string


  "Error in file %FOO%\myfile.fth at line "

would be expanded to


  "Error in file c:\apps\vfxforth\myfile.fth at line "

Macros are defined in the Substitutions vocabulary which is searched when the string is expanded. When executed these words return the address of a counted string for the text to substitute.

TextMacro: <name> defines an empty macro with a 255 character buffer in the Substitutions vocabulary.

<string> SETMACRO <name> sets the given string into the required macro <name>. If <name> does not exist in the Substitutions vocabulary an error is reported. SETMACRO may also be used in colon definitions, providing that the macro name already exists. If a colon definition needs to create a new macro name it should use $SETMACRO instead.


TEXTMACRO: FOO
  C" c:\apps\vfxforth" SETMACRO FOO

: BAR           \ --
  C" h:\myapp" SETMACRO FOO
;

$100 buffer: temp

<source> <dest> $EXPAND \ expand source string into destination

Basic words

: substitute    \ src slen dest dlen -- dest dlen' n ; 17.6.2.2255
Expand the source string using text macro substitutions, placing the result in the buffer dest/dlen and returning the destination string dest/dlen' and the number n of substitutions made. If an error occurred, n is negative. Ambiguous conditions occur if the result of a substitution is too long to fit into the given buffer or the source and destination buffers are the same.

Substitution occurs left to right from the start of src/slen in one pass and is non-recursive. When text of a potential substitution name, surrounded by ?%? (ASCII $25) delimiters is encountered by SUBSTITUTE, the following occurs:
a) If the name is null, a single delimiter character is passed to the output, i.e., %% is replaced by %. The current number of substitutions is not changed.
b) If the text is a valid substitution name, the leading and trailing delimiter characters and the enclosed substitution name are replaced by the substitution text. The current number of substitutions is incremented.
c) 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.
d) Parsing of the input string resumes after the trailing delimiter.

: substituteC   \ src slen dest dlen --
Expand the source string using text macro substitutions, placing the result as a counted string at dest/dlen. If an error occurred, the length of the counted string is zero.

: substituteZ   \ src slen dest dlen --
Expand the source string using text macro substitutions, placing the result as a zero terminated string at dest/dlen. If an error occurred, the length of the string is zero.

: replaces      \ text tlen name nlen -- ; 17.6.2.2141
Define the string text/tlen as the text to substitute for the substitution named name/nlen. If the substitution does not exist it is created.

: subsitute-safe  \ c-addr1 len1 c-addr2 len2 -- c-addr2 len3 ior
Replace each '%' character in the input string c-addr1/len1 by two '%' characters. The output buffer is represented by caddr2/len2. The output is caddr2/len3 and ior is zero on success. If you pass a string through SUBSITUTE-SAFE and then SUBSTITUTE, you get the original string.

: unescape      \ caddr1 len1 caddr2 -- caddr2 len2 ; 17.6.2.2375
Replace each '%' character in the input string caddr1/len1 by two '%' characters. The output is represented by caddr2/len2. The buffer at caddr2 shall be big enough to hold the unescaped string. An ambiguous condition occurs if the resulting string will not fit into the destination buffer caddr2.

Utilities

: MacroExists?  \ caddr -- xt nz | 0
If a macro of the given name exists, return its xt and a non-zero flag, otherwise just return zero. The name is a counted string.

: MacroSet?     \ caddr -- flag
If a macro of the given name exists and text has been set for it, return true. Often used to find out if a macro has been set, so that a sensible default can be defined. In the following example, IDIR is the current include directory and MC" is a version of C" that expands macros.


c" GuiLib" MacroSet? 0= [if]
  mc" %IDIR%" SetMacro GuiLib
[then]

: TextMacro:    \ <"name"> --
Builds a new text-macro with an empty macro string.

  TextMacro: Foo

: setMacro      \ string "<name>" --
Reset/Create a text macro. Used in the form:

  C" abcd" SETMACRO <name>

For historical reasons, this word can be used inside a colon definition in the form:

  C" abcd" SETMACRO <name>

If you want to use setMacro as a factor in another word, you probably want the interpretation action, so use:

  ... [INTERP] setMacro ...

rather than

  ... setMacro ...

: $setmacro     \ string name --
This version of SETMACRO takes both the string and macro name as counted strings.

: getTextMacro  \ caddr len -- macro$
Given a macro name, return the address of its text (a counted string). If the name cannot be found the null counted string cNull is returned.

: .macros       \ --
Display all text macros by macro name.

: .macro        \ "<name>" -- ; .MACRO <name>
Display the text for macro <name>.

: ShowMacros    \ --
Display all macro names and text.

: Expand        \ caddr len -- caddr' len'
Macro expand the given string, returning a global buffer containing the expanded string. The string is zero-terminated and has a count byte before caddr'. If len is longer than 254 bytes only the first 254 bytes will be processed.

: $expand       \ $source $dest --
Macro expand a counted string at $source to a counted string at $dest. The returned string is counted and zero terminated.

: $ExpandMacros \ $ -- $'
Macro expand a counted string. Note that the returned string buffer is in a global buffer.

: z$ExpandMacros        \ z$ -- 'z$
Macro expand a 0 terminated string. The returned string buffer is a global buffer.

: ExpandMacro   \ c-addr len buff -- 'buff len'
Perform TextMacro expansion on a string in c-addr u with the result being placed as a counted string at buff. The address and length of the expanded string are returned. The string at buff' is zero terminated.

: M",           \ "text" --
Compile the text string up to the closing quote into the dictionary as a counted string, expanding text macros as M", executes, usually at compile time. The end of the string is aligned.

: MS"           \ Comp: "<quote>" -- ; Run: -- c-addr u
Like S" but expands text macros. Text is taken up to the next double-quotes character. Text macros are expanded at compile time. The address and length of the string are returned. To expand macros at run time, use:

  s" <string>" expand

: MC"           \ Comp: "<quote>" -- ; Run: -- c-addr
Like C" but expands text macros. Text is taken up to the next double-quotes character. Text macros are expanded at compile time. At run-time the address of the counted string is returned. To expand macros at run time, use:

  c" <string>" $ExpandMacros

System Defined Macros

The following text macros are defined by the system and are always available. They are implemented as words in the substitutions vocabulary.

create VfxPath          ( -- c$ )   0 c, $FF allot
The path containing the VFX Forth source code. For most users, this is the root folder of the VFX Forth installation; however for Mission and Ultimate edition users, VfxPath must be set to the Sources folder of the VFX Forth installation. You must set this yourself. It is preserved in the INI file.

create BasePath         ( -- c$ )   0 c, $FF allot
The root folder of the VFX Forth installation. You must set this yourself. It is preserved in the INI file. Do not use BasePath as the root of the VFX source tree.

create DevPath          ( -- c$ )   0 c, $FF allot
The path containing the developer's application source. If selected by setting BuildLevel to -1, the contents of this macro will be prepended to source file names in the SOURCEFILES vocabulary.

create LOCATE_PATH      ( -- c$ )   0 c, $FF allot
The name of the current/last file to be compiled. Used by LOCATE and friends.

create LOCATE_LINE      ( -- c$ )   0 c, $FF allot
The line number of the line in the current file being compiled. Used by LOCATE and friends.

: f locate_path ;
The name of the current/last file to be compiled. A synonym for LOCATE_PATH. Used by LOCATE and friends.

: l locate_line ;
The line number of the line in the current file being compiled. A synonym for LOCATE_LINE. Used by LOCATE and friends.

create LIBRARYDIR       ( -- c$ )   0 c, $FF allot
The pathname of the VFX Forth Lib directory.

: lib LIBRARYDIR ;
A synonym for LIBRARYDIR above, which returns the pathname of the VFX Forth Lib directory.

create LOAD_PATH        ( -- c$ )   0 c, $FF allot
The directory containg the running program's executable.

: bin LOAD_PATH ;
The directory containing the VFX Forth executables. A synonym for LOAD_PATH.

: idir          \ -- c$
The current include directory. This string is '.' if no file is being INCLUDEd and allows a load file to be in the form below. The load file can then be referenced from any other directory.


\ include \dir1\dir2\dir3\loadfile.fth
                              \ in loadfile.fth
 include %idir%\file1.fth     \ file1 in dir3
 include %idir%\file2.fth     \ file2 in dir3
 ...

create wd       \ -- c$
The working directory. Under Windows, DOS, Unices and OS X this is ".". Do not change this macro.

LOAD_PATH constant Forth-Buff   \ -- caddr
Returns the address of a counted string holding the directory from which the application was loaded. This gives programs easy access to the LOAD_PATH macro.

MacOS specifics

The code described here is specific to VFX Forth for MacOS. Do not rely on any of the words documented here being present in any other VFX Forth implementation.

: GetExeNameZ   \ zaddr --
Get the fully qualified name of the executable. It is saved as a 0 terminated string at zaddr. The buffer is assumed to be at least 1024 characters long.

: InitMacros    \ --
Initialises the directory macros. Run at start up.

Editor and LOCATE actions

#256 buffer: editor$    \ -- addr
A buffer holding the path and name of the preferred editor as a counted string, e.g.

  /bin/vi

0 value EditOnError?    \ -- flag
Set true to call the editor on an error.

: editor-is     \ "<editor-name>" --
Set your preferred editor, e.g.

  editor-is /bin/vi
  editor-is /Applications/UltraEdit.app/Contents/MacOS/UltraEdit
  editor-is edit

: .ed           \ --
Display the name of your preferred editor

#256 buffer: locate$    \ -- addr
A buffer holding the macro expansion required to edit a specific line of a file. This information is used by LOCATE. In the example below the macros %f% and %l% will be replaced by the file name and line number.

  %f% -# %l%
  -- "%f%" --lc%l%:1 &
  +%l% "%f%" &

: edit$         \ -- cstring
If the preferred editor has been set, return the program name, otherwise return the default editor string for nano.

0 value DebugLocate?    \ -- x
If non-zero, the string passed to the shell for LOCATE is displayed before the system is called.

: $edit         \ cstring --
Edit the file provided as a counted string.

: edit          \ "<filename>" --
Edit the file whose name follows in the input stream, e.g.

  EDIT release.txt

: (EditOnError) \ -- ; run editor on error
Edit the file at an error, using the contents of the variables 'SOURCEFILE and LINE#.

: SetLocate     \ --
Tells VFX Forth how your editor can be called to go a particular file and line. Use in the form

  SetLocate  <rest of line>

where the text after SetLocate is used to define how parameters are passed to the editor, e.g. for Emacs, use

  SetLocate +%l% "%f%" &

The rest of line following SetLocate is used as the editor configuration string. Within the editor configuration string 'f' will be replaced by the file name and 'l' will be replaced by the line number. If you use file names with spaces, you should put quotation marks around the %f% text macro. You must finish the line with " &" to run the editor detached from VFX Forth - LOCATE adds this for you.