The following words allow the use of
[IF] ... [ELSE] ... [THEN]
blocks to control which
pieces of code are compiled/executed and which are not. These
words behave in the same manner as compiled definitions of
IF ... ELSE ... THEN
structures but take immediate
effect even outside definitions. Nesting is supported.
variable cc-level \ -- addr
A variable used for error checking during compilation.
It holds the number of [IF] ... [THEN]
blocks we
currently inside. Cleared at cold start.
VOCABULARY Compilation? \ --
The COMPILATON?
vocabulary holds the control code for
passover operations during a conditional block.
: PassOver \ n --
Skip n levels of nested conditional code.
: have \ "<name>" -- flag
Look to see if the word exists in the CONTEXT
search order
and return flag true if found.
: [defined] \ "<name>" -- flag Forth200x
Look to see if the word exists in the CONTEXT
search
order and return flag TRUE if the word exists.
This is an immediate version of HAVE
.
: [undefined] \ "<name>" -- flag Forth200x
The inverse of [DEFINED]
. Return TRUE if <name>
does not exist.
: [ELSE] \ -- 15.6.2.2531
Marks the start of the ELSE clause of a conditional compilation
block.
: [IF] \ flag -- 15.6.2.2532
Marks the start of a conditional compilation clause. If
flag is TRUE compile/execute the following code, otherwise
ignore all up to the next [ELSE]
or [THEN]
.
Note that the parser for [IF]
and [ELSE]
is
really dumb. You cannot comment out an [ELSE]
or
or THEN
. However they are nestable.
: [THEN] \ -- 15.6.2.2533
Marks the end of a conditional compilation clause.
: [ENDIF] \ --
Marks the end of a conditional compilation clause.
The following definitions exist in the Compilation?
vocabulary.
: [IF] 1+ 1 cc-level +! ;
[IF]
increments the number of levels to skip.
: [THEN] 1- -1 cc-level +! ;
[THEN]
decrements the number of levels to skip.
: [ENDIF] 1- -1 cc-level +! ;
[ENDIF]
decrements the number of levels to skip.
: [ELSE] 1- dup if 1+ then ;
[ELSE]
switches the redirection level.
The following words provide useful diagnostic routines and/or general purpose functions in the spirit of the ANS Forth TOOLS and TOOLS EXT wordsets.
: .tabword \ addr$ --
Displays tabbed string, CRing if required. Variable
TABWORDSTOP
contains the size of a tab.
: .tabwordN \ addr$ --
Displays tabbed *NAME*, CRing if required. Variable
TABWORDSTOP
contains the size of a tab.
0 value PauseConsole \ -- device
Some tools, e.g. WORDS
and DUMP
will pause
periodically if PauseConsole
returns the same value
as the output device in OP-HANDLE
. Any interactive
console can select this behaviour with:
op-handle @ to PauseConsole
You can stop any pausing with:
0 to PauseConsole
: flushKeys \ --
Flush any pending input that might be returned by KEY
.
: HALT? \ -- flag
Used in listed displays. This word will check the keyboard for a
pause key (<space> or <lf> or <cr>). If a pause key is pressed it will
then wait for another key. The return flag is TRUE if the second
key is not a pause key. If the first key is not a pause key TRUE
is returned and no key wait occurs. Line Feed characters are
ignored.
: DUMP \ addr u -- 15.6.1.1280
Display an arbitrary block of memory in a 'hex-dump' fashion
which displays in both HEX and printable ASCII.
: LDUMP \ addr len -- ; dump 32 bit long words
Display (dump) len bytes of memory starting at addr
as 32 bit words.
: .S \ -- 15.6.1.0220
Display to the console the current contents of the data
stack. If the number base is not HEX
than a dump is
also made in HEX.
: .rs \ --
Display to the console the current contents of the return
stack. Where possible a word name is also displayed with
the data value.
: ? \ a-addr -- 15.6.1.0600
Display the contents of a memory location. It has the same
effect as @ .
.
: WORDS \ --
Display the names of all definitions in the wordlist at the
top of the search order.
: .FREE \ --
Text display of size of unused dictionary area in Kbytes
: mat \ -- ; MAT <wildcardpattern>
Search the current search-order for all definitions whose name
matches the wild-carded expression supplied. Expressions can
contain either an asterix '*' to match 0 or more characters, or can be
a query '?' to mark any single character.
: similars { | temp[ MAX_PATH ] -- }
A slightly faster version of MAT
with a limited range. The
definitions listed will contain <pattern> within their name.
The <pattern> can only contain printable ASCII characters.
: sim \ -- ; SIM <pattern>
A slightly faster version of MAT
with a limited range. The
definitions listed will contain <pattern> within their name.
<pattern> can only contain printable ASCII characters.
A synonym for SIMILARS
.
A group of simple primitive words to work with 0 terminated ASCII strings.
: caddr>zaddr \ caddr zaddr --
Copy a counted string to a 0 terminated string.
: .z$ \ zaddr --
TYPE
a zero terminated string.
: z$. \ zaddr --
TYPE
a zero terminated string. Depending on the output
device, this may be a bit more efficient than .z$
.
: .z$EXPANDED \ zaddr --
TYPE
a zero terminated string after macro expansion.
: z$, \ c-addr u --
Lay the given string in the dictionary as a zero terminated string.
The end of the string is not aligned.
: $>z, \ addr --
Lay a zero terminated string in the dictionary, given a counted string.
The end of the string is not aligned.
: z", \ "cc<quote>" --
"comma" in a zero terminated string from the following text.
The end of the string is not aligned.
: $>ASCIIZ \ caddr -- zaddr
Convert a counted string to a zero terminated string. The
converted string is in a thread-local buffer of limited
lifetime
: asciiz>$ \ zaddr -- caddr
Convert a zero terminated string to a counted string. The
conversion happens in place.
The data structure words implement records, fields, field types, subrecords and variant records.
The following syntax is used:
STRUCT <name>
n FIELD <field1>
m FIELD <field2>
SUBRECORD <subrec1>
a FIELD <sf1>
b FIELD <sf2>
END-SUBRECORD
END-STRUCT
A structure may contain multiple subrecords, and subrecords may be nested.
A field adds its base offset to the given address [that of the record or subrecord]. A record returns its length, and so can be used as an input to field.
len FIELD <name>
n len ARRAY-OF <name>
Subrecords are checked for stack depth, like branch structures. They may be nested as required.
Variant records describe an alternative view of the current record or subrecord from the start to the current point. The variant need not be of the same length, but the larger is taken
SUBRECORD <name>
----
VARIANT <name2> .............. END-VARIANT
END-SUBRECORD
use
<structure> BUFFER: <name>
to create a new instance of a previously defined structure.
The VFX structures package has also been enhanced to handle areas of overlapping data called "UNIONS". Consider the example:
struct test
int a
int b
union
int c
int d
part
1 field e1
1 field e2
part
int f
subrecord jim
float jim1
int jim2
end-subrecord
end-union
20 field g
end-struct
Each part of a union is overlapped, but fields within a part are treated as individual items. So, in the above example, c and f refer to the same cell, but c and d refer to different cells.
: struct \ -- addr 0 ; -- size
Begin definition of a new structure. Use in the form
STRUCT <name>
. At run time <name>
returns the
size of the structure.
: end-struct \ addr n --
Terminate definition of a structure.
: field \ n <"name"> -- ; Exec: addr -- 'addr
Create a new field within a structure definition of size n bytes.
: int \ <"name"> -- ; Exec: addr -- 'addr
Create a new field within a structure definition of size one cell.
: array-of \ n #entries size -- n+(#entries*size)
Create a new field within a structure definition of size
#entries*size.
: subrecord \ n -- n csp 0 [parent]
Begin definition of a subrecord.
: end-subrecord \ n csp len -- n+len
End definition of a subrecord.
: variant \ n -- n csp 0
Currently an alias for subrecord
. Begin a variant.
: end-variant \ n csp m -- n|m
Terminate a variant clause.
: union \ currentOffset -- csp 0 currentOffset currentOffset
Begin UNION
definition block.
: part \ max base last -- max base start
Begin definition of alternative data description within a
UNION
.
: end-union \ csp maxLength baseOffset lastOffset -- next-offset
Mark end of a UNION
definition block.
: field-type \ n --
Define a new field type of size n bytes. Use in the form
<size> FIELD-TYPE <name>
. When <name>
executes
used in the form <name> <name2>
a field <name2>
is created of size n bytes.
The Forth200x standards effort has adopted s notation that is compatible with VFX Forth, but changes some names.
: begin-structure \ -- addr 0 ; -- size
Begin definition of a new structure. Use in the form
BEGIN-STRUCTURE <name>
. At run time <name>
returns the size of the structure. The Forth200x version
of the MPE word struct
.
: end-structure \ addr n --
Terminate definition of a structure.
The Forth200x version of the MPE word end-struct
.
: +FIELD \ n <"name"> -- ; Exec: addr -- 'addr
Create a new field of size n bytes within a structure
definition. The Forth200x version of the MPE word field
.
: cfield: \ n1 <"name"> -- n2 ; Exec: addr -- 'addr
Create a new field of size 1 CHARS
within a structure
definition,
: field: \ n1 <"name"> -- n2 ; Exec: addr -- 'addr
Create a new field of size 1 CELLS
within a structure
definition. The field is ALIGNED
.
The ENVIRONMENT system was defined by ANS Forth to enable you to find out about the underlying Forth system. The needs of modern portable libraries have proven the ENVIRONMENT system to be inadequate and so it is little used. The ENVIRONMENT system may be removed in a future standard.
You use the system through the word ENVIRONMENT?
caddr len -- false | i*x true
where caddr/len represents the name of a query. If the system does not know this query, it just returns false (0). If it does know the query, it return the relevant value with true (-1) on top of the stack.
In VFX Forth, ENVIRONMENT?
is implemented by searching
a vocabulary called ENVIRONMENT
. If the query is found,
it is executed.
The words in this section are defined in the ENVIRONMENT
vocabulary.
#255 constant /COUNTED-STRING \ -- n
Maximum length of a counted string.
picnumsize constant /HOLD \ -- n
Maximum size of HOLD area.
padsize constant /PAD \ -- n
Maximum size of PAD.
8 constant ADDRESS-UNIT-BITS \ -- n
Number of bits in an address unit (byte in this system).
true constant CORE \ -- TRUE
The full CORE wordset is present.
true constant CORE-EXT \ -- TRUE
The full CORE-EXT wordset is present.
false constant FLOORED \ -- flag
The standard division operators use symmetric (normal)
division.
#255 constant MAX-CHAR \ -- u ; max value of char
Characters are 8 bit units.
: MAX-D \ -- d
Maximum positive value of a double number.
: MAX-N \ -- n
Maximum positive value of a single signed number.
: MAX-U \ -- u ; max size unsigned number
Maximum value of a single unsigned number.
: MAX-UD \ -- u ; max size unsigned double
Maximum value of a double unsigned number.
: MAX-D \ -- d
Maximum positive value of a double number.
: MAX-N \ -- n
Maximum positive value of a single signed number.
: MAX-U \ -- u ; max size unsigned number
Maximum value of a single unsigned number.
: MAX-UD \ -- u ; max size unsigned double
Maximum value of a double unsigned number.
rp-size cell / constant RETURN-STACK-CELLS \ -- n
Maximum size of the return stack (in cells).
sp-size cell / constant STACK-CELLS \ -- n
Maximum size of the data stack (in cells).
true constant EXCEPTION \ -- TRUE
EXCEPTION word-set is present.
true constant EXCEPTION-EXT \ -- TRUE
EXCEPTION EXT word-set is present.
' environment >body @ constant environment-wordlist \ -- wid
The wid used by ENVIRONMENT?
for look ups. You can add
your own queries to this wordlist.
: ENVIRONMENT? \ c-addr u -- false | i*x true 6.1.1345
The text string c-addr/u is of a keyword from ANS 3.2.6
Environmental queries or the optional word sets to be checked
for correspondence with an attribute of the present environment.
If the system treats the attribute as unknown, the returned flag
is false; otherwise, the flag is true and the i*x returned is of
the type specified in the table for the attribute queried.
: [environment?] \ "string" -- false | i*x true
As ENVIRONMENT?
but is IMMEDIATE and takes the string
from the input stream.
: .environment \ --
Display a list of queries.
The build numbering system allows you to generate a string in the system which can be used for displaying version information.
The system relies on a file (normally called BUILD.NO)
which holds the complete build version string. The string
can consist of any characters, e.g "Version 1.00.0034". The
contents of the file can be placed as a counted string in
the dictionary by BUILD$,
. After successful
compilation of your application, UPDATE-BUILD
will
update the build number file by treating all the digits
in the build string as a single number to be incremented. )
: Make-Build \ buffer --
Read the contents of the build number file and place as a counted
string in the application defined buffer for later use.
: Build$, \ --
Read the contents of the build number file and place as a
counted string at HERE
. ALLOT
the required space.
: Date$, \ --
Compile date as counted string.
: Time$, \ --
Compile time as counted string
: DateTime$, \ --
Compile date and time as counted string
: Set-BuildFile \ c-addr u --
Set the build number file.
: BuildFile \ -- ; Buildfile <filename>
Use GetPathSpec to parse a filename from the input stream,
and make it the current build number file.
: Update-Build \ --
Update the contents of the build number file ready for the next
build.
The following example, defines which file to use, loads
the text into a buffer, and finally updates the build text.
By placing Update-Build
last in your load file, your
build number file will only be updated for each successful
build.
s" MyBuild.no" Set-Buildfile \ set file to use
#256 buffer: MyVersion$ \ -- caddr
MyVersion$ make-build \ load version string
...
update-build \ put this last in load file
MPE documentation is produced by using DocGen to produce an indexed PDF file. The PDF help system parses the index file produced by pdftex to display the relevant page of the PDF manual. To display a particular line in a PDF file requires the following incantation for Adobe Reader v7 and beyond:
<reader> /A "page=n=OpenActions" "<pdffile>"
The page number is the PDF file page number, not the page number in the document section. For example, to display page 10 on a Windows PC, use:
"C:\Program Files\Adobe\Reader 8.0\Reader\AcroRd32.exe"
/A "page=10=OpenActions" "%h%.pdf"
"C:\Products\VfxCommunity\Sources\Manual\PDFs\VfxWin.pdf"
For VFX Forth for Windows, you can use the menu item Option -> Set PDF help ... for the configuration. For all versions, when setting the base of the PDF file name, do not add the file extension. This is because the PDF file and the index file share the same base name.
The page number is extracted from the index file VfxWin.vix, from which this example comes:
\initial {A}
\entry {\code {abell}}{11}
\entry {\code {abl}}{12}
\entry {\code {abort}}{15, 200}
\entry {\code {abort"}}{200}
The file is parsed for the entry containing the word name, the page number is extracted, and the file page is displayed. The index file is derived from the .fns file produced by pdftex.
The source code is in Lib\PDFhelp.fth.
TextMacro: p \ -- $text
Defines the page number macro p.
TextMacro: h \ -- $text
Define the help file macro h.
#256 constant /Help$ \ -- n
Size of the command and base string buffers.
/Help$ buffer: HelpCmd$ \ -- addr
Holds the pathname and command line of the PDF viewer as a
counted string. In the command line, the page number is
supplied by the text macro %p%, and the base help file
path/name with no extension is supplied by the text macro
%h%. This string may include other macros.
For Acrobat Reader under Windows, we must use the full reader pathname; we cannot use an association. The default string is
"<reader>" /A \qpage=%p%=OpenActions\q %h%.pdf
where <reader> is
"C:\Program Files\Adobe\Reader 8.0\Reader\AcroRd32.exe"
If you change the settings, use S\"
as you may need to
have double-quotes characters in the string around path names
that include spaces.
One alternative is the free PDF-XChange Viewer from
http://www.tracker-software.com/product/pdf-xchange-viewer
"<path>\PDFXCview.exe" /A "page=%p%" "%h%.pdf"
Another alternative is the free Foxit Reader from
http://www.foxitsoftware.com/
The required command strings for Foxit Reader are
"<path>\Foxit Reader.exe" /A "page=%p%" "%h%.pdf"
The rules for the Foxit Reader command line need to be checked with every new major release!
Users have also suggested Nitro, Qpdfview and Sumatra among many others. MPE has no position on this - it's a matter of personal preference. Just check the viewer's manual for the command line incantation!
For some Linux systems, e.g. Ubuntu, xpdf is installed by default. The VFX defaults are
s\" xpdf %h%.pdf %p% &" HelpCmd$ place
s" ~/VfxLin<ver>/Doc/VfxLin" HelpBase$ place
#17 HelpPage0 !
If you used the default installer script, replace <ver> by by one of Eval, Standard or Pro, e.g. VfxLinStandard.
Some Linux distributions, e.g. Debian, require shared documentation files to be compressed. Type:
sudo find / -name "VfxLin.pdf"
To see where VfxLin.pdf has been installed, and to see what extension, e.g. .pdf.gz is in use.
For OS X, the default PDF viewer is Preview. It can be run from the command line using:
open -a Preview filename.pdf
However, going to a page number is undocumented. The best solution we have found is to install the Skim package from:
http://skim-app.sourceforge.net/
Skim can be run using the supplied executable script Bin/skimpage.scpt. This is run in the form:
skimpage.scpt "<file>" <pageno>
After copying the script file to a suitable directory such as /usr/bin (done by the install script), a suitable setup is:
s\" skimpage.scpt \q%h%.pdf\q %p%" HelpCmd$ place
s" ~/VfxForth/Doc/VfxOsx" HelpBase$ place
#14 HelpPage0 !
/Help$ buffer: HelpBase$ \ -- addr
Contains a full path to the directory containing the help and
index files, plus the base file name with no extension. This
string may include macros.
A counted string, e.g for Windows
%LOAD_PATH%\..\doc\VfxMan
and for Linux
%LOAD_PATH%/../doc/VfxLin
and for OS X
%LOAD_PATH%/../doc/VfxOsx
variable HelpPage0 \ -- addr
Holds the offset to be added to the index page number to
convert it to a PDF page number. This value may change
according to the manual version, so it is extracted from
the index file.
0 value DebugHelp? \ -- flag
Set this non-zero if you are having trouble setting up the
help system. The command line will be displayed.
: $Help \ caddr len -- ior ; 0=success
Run the help file system using caddr/len as the search key.
: Help \ "<word>" -- ; e.g. HELP dup
Get help on the given word name, e.g.
help locate
Unlike LOCATE
, HELP
does not require the word to
be present in the Forth dictionary and current search order,
it only requires that a word have an index entry in the PDF
manual.
: PDFLoadCfg \ --
Load the PDF help configuration from the INI file.
Linux and OS X only.
: PDFSaveCfg \ --
Load the PDF help configuration from the INI file.
Linux and OS X only.
If you are upgrading from a system installed before March 2012, you may/will need to change how your application specifies its INI files.
The INI file mechanism used by Windows is also available for other operating systems. This allows us to use the same configuration file mechanism for all operating systems that support shared libraries (not in VFX Forth for DOS yet).
The code accesses a derivative of the iniParser v3.0b shared library published by Nicholas Devillard at http://ndevilla.free.fr/iniparser/, where the latest version may be found. Note that the MPE versions differ from this version, but are upward compatible. We have submitted our changes to the author. A binary copy of the library can be found in the Bin folder. You are free to release this with your applications.
The full sources for iniParser as used by MPE are in the directories <VFX>\Tools\iniparser3.0b\src and <VFX>\Tools\iniparser3.0b\src.win. The relevant shared library (.so or .dll) is in the Bin folder and is copied to the Windows\System32 or /user/lib directory during installation.
The private profile file mechanism used by VFX Forth for
Windows before version 4.20 may be found in <VFX>Lib\Win32\Profile.fth.
The old mechanism is not portable between operating systems
and is much slower. We strongly recommend that you convert
any existing code that uses the PROFILE::xxx
words to
use the new mechanism.
The following example shows how INI files are used. A later section describes the words in detail.
: SaveSome \ --
s" %IniDir%\UserIde.ini" Ini.Open 0= if
S" Options" Ini.Section
s" FontSet" FontSet? Ini.WriteInt
s" LogFont" lf[ LOGFONT Ini.WriteMem
s" Editor" szEditor zcount Ini.WriteStr
Ini.Close
endif
;
: LoadSome \ --
s" %IniDir%\UserIde.ini" Ini.Open 0= if
S" Options" Ini.Section
s" FontSet" 0 Ini.ReadInt dup -> FontSet? if
s" LogFont" lf[ LOGFONT Ini.ReadMem
lf[ CreateFontIndirect -> hConsoleFont
endif
s" Editor" szEditor MAX_PATH zNull zcount Ini.ReadZStr
Ini.Close
endif
;
The main things to note are:
[name]
. Sections contain key/value pairs in the form
key=value
. The value portion is represented as text.
Library: libmpeparser64.so.0
The library reference.
: IniLib \ -- addr|0
Used to determine if the library is available and to isolate the
host-dependent library name from the rest of the code.
Library: libmpeparser.so.0
The library reference.
: IniLib \ -- addr|0
Used to determine if the library is available and to isolate the
host-dependent library name from the rest of the code.
Library: libarmmpeparser.so.0
The library reference.
: IniLib \ -- addr|0
Used to determine if the library is available and to isolate the
host-dependent library name from the rest of the code.
Extern: int iniparser_getnsec( void * dict );
This function returns the number of sections found in a dictionary.
The test to recognize sections is done on the string stored in the
dictionary: a section name is given as "section" whereas a key is
stored as "section:key", thus the test looks for entries that do not
contain a colon.
This function returns -1 in case of error.
Extern: char * iniparser_getsecname( void * dict, int n );
This function locates the n-th section in a dictionary and returns
its name as a pointer to a string statically allocated inside the
dictionary. Do not free or modify the returned string!
This function returns NULL in case of error.
Extern: void iniparser_dump_ini( void * dict, void * file );
This function dumps a given dictionary into a loadable ini file.
It is Ok to specify stderr or stdout as output files.
Extern: void iniparser_dump( void * dict, void * file );
This function prints out the contents of a dictionary, one element by
line, onto the provided file pointer. It is OK to specify stderr
or stdout as output files. This function is meant for debugging
purposes mostly.
Extern: char * iniparser_getstring( void * dict, const char * key, char * def );
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the pointer passed as 'def' is returned.
The returned char pointer is pointing to a string allocated in
the dictionary, do not free or modify it.
Extern: int iniparser_getint( void * dict, const char * key, int notfound );
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
Supported values for integers include the usual C notation
so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
are supported. Examples:
- "42" -> 42
- "042" -> 34 (octal -> decimal)
- "0x42" -> 66 (hexa -> decimal)
Warning: the conversion may overflow in various ways. Conversion is totally outsourced to strtol(), see the associated man page for overflow handling.
Extern: int iniparser_getboolean( void * dict, const char * key, int notfound );
This function queries a dictionary for a key. A key as read from an
ini file is given as "section:key". If the key cannot be found,
the notfound value is returned.
A true boolean is found if one of the following is matched:
- A string starting with 'y'
- A string starting with 'Y'
- A string starting with 't'
- A string starting with 'T'
- A string starting with '1'
A false boolean is found if one of the following is matched:
- A string starting with 'n'
- A string starting with 'N'
- A string starting with 'f'
- A string starting with 'F'
- A string starting with '0'
The notfound value returned if no boolean is identified, does not necessarily have to be 0 or 1.
Extern: int iniparser_set( void * dict, char * entry, char * val );
If the given entry can be found in the dictionary, it is modified to
contain the provided value. If it cannot be found, -1 is returned.
It is Ok to set val to NULL.
Extern: void iniparser_unset( void * dict, char * entry );
If the given entry can be found, it is deleted from the dictionary.
Extern: int iniparser_find_entry( void * dict, char * entry );
Finds out if a given entry exists in the dictionary. Since sections
are stored as keys with NULL associated values, this is the only way
of querying for the presence of sections in a dictionary.
Extern: void * iniparser_load( const char * ininame );
This is the parser for ini files. This function is called,
providing the name of the file to be read. It returns an ini
dictionary object that should not be accessed directly, but
through accessor functions instead. The returned dictionary
must be freed using iniparser_freedict().
Extern: int iniparser_save( void * d, char * ininame );
Saves a dictionary object. This is just a wrapper around
iniparser_dump_ini() to provide insulation between the caller
and the file system for languages and operating systems which
do not expose the libc library. The returned error code is 0
for a successful operation. You still need to call
iniparser_freedict
below.
Extern: void iniparser_freedict( void * dict );
Free all memory associated to an ini dictionary.
It is mandatory to call this function before the dictionary object
gets out of the current context.
These tools are in the SYSTEM
vocabulary and may change
from version to version. If all you are interested in is using
the MPE parser interface API, skip this section.
0 value IniSrcFile \ -- addr
Holds the currently loaded INI source file pathname as a
zero-terminated string.
0 value IniDestFile \ -- addr
Holds the currently loaded INI destination file pathname as a
zero-terminated string.
0 value IniSection \ -- addr
Holds the current section name as a zero-terminated string.
0 value IniKey \ -- addr
Holds the current key as a zero-terminated string.
0 value IniData \ -- addr
Holds the current write data as a zero-terminated string,
0 value IniDefault \ -- addr
Holds the current default as a zero-terminated string
0 value IniScratch \ -- addr
Holds the current scratch buffer for processing quote marks.
0 value IniDict \ -- addr
Holds the current dictionary pointer.
: IniAlloc \ ptr -- ior
Allocate /IniBuff
bytes and place the buffer address
at ptr. The first bye of ptr is set to zero.
: IniFree \ ptr --
Free buffer memory allocated by us.
: InitIniBuffs \ -- ior
Initialise all the buffers and pointers.
: TermIniBuffs \ --
Free all the buffers and clear pointer.
: +DoubleQ \ z$1 -- z$2
Convert double quote characters to pairs of double quote
characters.
: -DoubleQ \ z$1 -- z$2
Convert pairs of double quote characters to single double quote
characters.
: >IniName \ caddr len dest --
Copy name to zero terminated string.
: >IniString \ caddr len dest --
Copy string to zero terminated string. If the last character
is a '\', add a dummy comment " ; x".
: WriteIniFile \ --
Write the current INI dictionary to the INI file.
: FormIniKey \ caddr u --
Form the key string from the current section name and the
given key. The key string is of the form "<section:<key>
".
: setIniString \ dict entry val --
Calls iniparser_set() and marks the INI file as changed.
: IniExists \ caddr len --
If the file does not exist create an empty one.
: czplace \ caddr len dest
Store the string caddr/len as a counted and zero-terminated string
at dest. The strings must not overlap.
: nib>hex \ 4b -- char
Convert a nibble to a hex character.
: Mem>Hex \ caddr len zdest --
Generate an ASCII hex representation of the memory block as
a zero terminated string at zdest. The length len
of the memory block must be less than 128 bytes.
: Hex>Nib \ char -- 4b
Convert a hex character to a nibble.
: Hex>Mem \ zsrc caddr len --
Convert the zero-terminated ASCII HEX string at zsrc to
its memory representation in the buffer caddr/len.
The code here is not thread safe, you so may need a semaphore from open to close. Some data is held in global variables/buffers.
: Ini.Open \ caddr len -- ior
Define and load the Ini file, returning zero on success.
Sets the destination file to be the same as the loaded file.
Macros in the file name are expanded.
: Ini.Dest \ caddr len --
Set the destination file. This must be done after
Ini.Open
and before Ini.Close
.
: Ini.Close \ --
If the dictionary has been changed, write it out to the
destination file.
: Ini.Section \ caddr len --
Set the current section name.
: Ini.Section? \ caddr u -- flag
Given a section name, make it current, and return true
if it exists.
: Ini.WriteSection \ caddr u --
Make the given section current, and write it to the
dictionary.
: Ini.ReadStr \ c-addr1 u1 c-addr2 u2 c-addr3 u3 --
Read a string value under key c-addr1/u1 and return it in
the result buffer specified by c-addr2/u2. If the key
couldn't be read then the default string c-addr3/u3 is
placed in the result buffer.
Note that the returned string is placed in the result buffer
as a zero terminated and counted string.
: Ini.ReadZStr \ c-addr1 u1 c-addr2 u2 c-addr3 u3 --
Read a string value under key c-addr1/u1 and return it in
the result buffer specified by c-addr2/u2. If the key
couldn't be read then the default string c-addr3/u3 is
placed in the result buffer.
Note that the returned string is placed in the result buffer
as a zero terminated string.
: Ini.ReadInt \ c-addr1 u1 default -- value
Attempt to read an integer value from key c-addr1/u1
and return it. If the key couldn't be read then the default
is returned.
: Ini.ReadBool \ c-addr1 u1 defbool -- bool
Attempt to read an boolean value from key c-addr1/u1
and return it. If the key couldn't be read then the default
is returned.
: Ini.ReadMem \ c-addr1 u1 c-addr2 u2 --
Read a memory block with key c-addr1/u1 and return it in
the result buffer specified by c-addr2/u2. If the key
couldn't be read then the result buffer is filled with zero
bytes. u2 must be less than 128 bytes.
: Ini.WriteStr \ c-addr1 u1 c-addr2 u2 --
Write a string value attached to a key, into the currently named
section of the current INI file. C-addr1/u1 describes
the name of the key to place the entry under, and C-addr2/u2
the string to write. If the key already exists it is updated.
: Ini.WriteZStr \ c-addr1 u1 caddrz --
Write a zero-terminated string value attached to a key, into the
current section of the INI file. C-addr1/u1 describes
the name of the key to place the entry under, and caddr\
the string to write. If the key already exists it is updated.
: Ini.WriteInt \ c-addr1 u1 u2 --
Write a string value corresponding to decimal text for u2
to the key C-addr1/u1 in the current section of the
current dictionary.
: Ini.WriteBool \ c-addr1 u1 bool --
Write a string value corresponding to decimal text for bool
to the key C-addr1/u1 in the current section of the
current dictionary.
: Ini.WriteMem \ c-addr1 u1 c-addr2 u2 --
Write a memory block attached to a key, into the currently named
section of the current INI file. C-addr1/u1 describes
the name of the key to place the entry under, and C-addr2/u2
the memory block to write. If the key already exists it is updated.
: Ini.DeleteKey \ c-addr1 u1 --
Delete a key entry completely from the current section.
C-addr1/u1 is the name of the key.
#256 buffer: IniFile$ \ -- addr
A 256 byte buffer for the expanded INI file path name, which
may include macros. The path name is stored as a counted string.
This buffer is initialised at startup from the three
components below.
#256 buffer: IniDir$ \ -- addr
A 256 byte buffer for the expanded INI file directory name,
which may include macros. The path is stored as a counted
string. This buffer is initialised at startup
#256 buffer: AppSupp$ \ -- addr
The directory where application support files go, held as
a counted string This directory must already exist. May
contain macros.
You can change this for your own application.
#256 buffer: AppSuppDir$ \ -- addr
The directory in AppSupp$
in which the INI file is
placed, held as a counted string. The directory will be
created if it does not already exist. This string may be
null, or may define one or two levels of directory.
You can change this for your own application.
64 buffer: AppSuppIni$ \ -- addr
The name of the INI file in AppSuppDir$
, held as a
counted string. By default, the file is VfxForth.ini.
You can change this for your own application.
256 buffer: PrevIni$ \ -- addr
Holds the full pathname of a default file copied to the INI
file if it does not exist. May contain macros.
You can change this for your own application.
-1 value GenINI? \ -- flag ; true to generate INI files
If this VALUE
is set true (the default condition) .INI files
will be generated for VFX Forth and applications when the
application performs BYE
. Such files will also be loaded
when VFX Forth or an application is executed.
defer CheckSysIni \ --
This word is run at cold start before any INI file is loaded.
It should provide an INI file if one is needed and does not
exist.
1 value IniParserModes \ -- modes
If you need only one INI file that could be in the directory
from which the application is loaded, set this to zero.
TextMacro: IniFile
A text macro that returns the INI file name after macro
expansion at startup.
TextMacro: IniDir
A text macro that returns the INI file directory after macro
expansion at startup.
: -ini-exec \ --
When used on the command line in lower case, -ini-exec
causes the INI file to be loaded from PrevIni$
, which
is usually in the executable directory.
: (CheckSysIni) \ --
Creates the INI file directory if required and sets up the
INI file macros. This is the default action of CheckSysIni
.
: (CheckSysIni) \ --
Creates the INI file directory if required and sets up the
INI file macros. This is the default action of CheckSysIni
.
Setting the INI files has changed. You must now set up four
strings rather than two, and IniFile$
is set at startup,
and not by you. The defaults are shown below for three
operating systems.
These changes were required by changes in the Windows
security system, the desire to fit in "well" with
Unix-derived systems, and the requirement for multiple
application-specific data files. See the IniDir
macro in particular.
By default, the INI file is %$AppLocal%\MPE\VfxForth\VfxForth.ini. If the directory or file does not exist, as may happen after installation, the directory is created and/or a default file is copied.
s" %$AppLocal%" AppSupp$ place \ system dir s" MPE\VfxForth" AppSuppDir$ place \ our dir s" VfxForth.ini" AppSuppIni$ place \ file s" %load_path%\VfxForth.ini" PrevIni$ place \ default/previous
By default, the INI file is %$home%/Library/Application Support/VfxForth/VfxForth.ini. If the directory or file does not exist, as may happen after installation, the directory is created and/or a default file is copied.
s" %$home%/Library/Application Support" AppSupp$ place s" VfxForth" AppSuppDir$ place s" VfxForth.ini" AppSuppIni$ place s" %$home%/.VfxForth.ini" PrevIni$ place
s" %$home%/Library/Application Support" AppSupp$ place s" VfxForth" AppSuppDir$ place s" VfxForth64.ini" AppSuppIni$ place s" %$home%/.VfxForth64.ini" PrevIni$ place
By default, the INI file is %$home%/.VfxForth/VfxForth.ini. If the directory or file does not exist, as may happen after installation, the directory is created and/or a default file is copied.
s" %$home%" AppSupp$ place s" .VfxForth" AppSuppDir$ place s" VfxForth.ini" AppSuppIni$ place s" %$home%/.VfxForth.ini" PrevIni$ place
s" %$home%" AppSupp$ place s" .VfxForth" AppSuppDir$ place s" VfxForth64.ini" AppSuppIni$ place s" %$home%/.VfxForth64.ini" PrevIni$ place
These chains are used for configurations options which are preserved when VFX Forth closes down, and are reloaded when it starts. Note that because of the position in the cold and exit chains, you must be careful that you still have the configuration data. For example, if a window is closed before configuration save, its position may have to be saved in a buffer rather than reading it directly from the window. Similarly, when the configuration information is restored, the window may/will not be open yet.
If you want to read and write directly from live information, it is more reliable to provide full handlers in your application code. However, there will be a time penalty because the INI file is repeatedly opened and closed.
variable IniLoadChain \ -- addr
The anchor for the initialisation load sequence.
variable IniSaveChain \ -- addr
The anchor for the initialisation save sequence.
: AtIniLoad \ xt --
The given word is run during the INI load sequence.
: AtIniSave \ xt --
The given word is run during the INI save sequence.
The words run must have no net stack action, and behave
according to the rules of ExecChain
.
: LoadSysIni \ --
Open the INI file specified by iniFile$
, run the
IniLoadChain
, and close the file. Run in the
cold chain. No action is taken if GenINI?
is zero.
: SaveSysIni \ --
Open the INI file specified by iniFile$
, run the
IniSaveChain
, and close/save the file. Run in the
exit chain. No action is taken if GenINI?
is zero.
By design, there is an almost one to one correspondence between the words in the profile and INI mechanisms. There are two major differences.
DROP
ped them.must
use Ini.Close
after you have finished
with the INI file. The data is in memory, and is only
flushed at close.For examples of use, see LoadUserIde
and SaveUserIde
in Studio\XTB.FTH for the Windows versions.
Switch chains provide a mechanism for generating extensible chains similar to the CASE ... OF ... ENDOF ... ENDCASE control structure, except that the user may extend the chain at any time. These chains are of particular use when defining winprocs whose action may need to be adjusted or extended after the chain itself has been defined.
The following example shows how to define a simple chain that translates numbers to text. At a later date, translations in Italian are added.
Define some words which will be executed by the chain.
: one ." one" ;
: two ." two" ;
: three ." three" ;
: many . ." more" ;
The following definition defines a switch called NUMBERS which executes ONE when 1 is the selector, TWO if 2 is the selector, or MANY if any other number is the selector. Note that MANY must consume the selector. The word RUNS associates a word with the given selector.
[switch numbers many
1 runs one
2 runs two
switch]
cr 1 numbers
cr 5 numbers
The next piece of code extends the NUMBERS switch chain, and demonstrates the use of RUN: to define an action without giving it a name.
[+switch numbers
3 runs three
4 run: ." four" ;
5 run: ." five" ;
switch]
cr 1 numbers
cr 5 numbers
cr 8 numbers
The following portion of this example demonstrates how selectors are overridden by the last action defined. Although an action has already been defined for selectors 1 and 2, if another action is defined, it will be found before the old ones, and so the action will be performed.
[+switch numbers
1 run: ." uno" ;
2 run: ." due" ;
switch]
cr 1 numbers
cr 2 numbers
cr 3 numbers
cr 5 numbers
cr 8 numbers
code switch \ i*x id switchhead -- j*x
Given an id and the head of a switch chain, SWITCH
will perform the action of the given id if it is found,
otherwise it will perform the default action, passing id
to that action.
: [switch \ "default" -- head ; i*x id -- j*x
Builds a new named switch list with a default action. Use in
the form: [SWITCH <name> <default.action> where <default.action>
must consume the selector id.
: [+switch \ "head" -- head ; to extend an existing switch
Used in the form [+SWITCH <switch>
to extend an existing
switch chain.
: run: \ head id -- head ; add nameless action to switch
Used in the form <id> RUN: <words> ;
to define a nameless
action in a switch chain.
: runs \ head id "word" -- head
Used in the form <id> RUNS <word>
to define a named
action in a switch chain.
: switch] \ head -- ; finishes a switch chain or extension
Used to finish a [SWITCH <name> <default>
or [+SWITCH <name>
chain definition.
: .switches \ -- ; lists defined switches
Lists all the defined switch chains.
: InSwitch? \ id xt -- flag
Returns true if the id is in the switch chain given by
its xt.
VFX Forth contains a set of words for managing character (8 bit ) queues. These queues are allocated from the system heap.
STRUCT FIFO \ -- len
A structure which defines the internal format of the fifo. To
create a new FIFO you must create an instance of the structure.
The instance pointer (address of structure) is then used as an
identifier for subsequent operations.
: InitialiseFIFO \ *FIFO size -- ior
Initialise a FIFO with a maximum buffer 'size' bytes long.
the IOR is 0 for success and non-zero for memory allocation failed.
: FreeFIFO \ *FIFO -- ior
Destroy FIFO. Memory is released back into the heap.
: resetFIFO \ *fifo --
Discard any characters in the FIFO.
: FIFO? \ *FIFO -- n ; Return # bytes used in fifo
Return the number of storage bytes in use within a fifo.
: >FIFO(b) \ BYTE *FIFO -- ior
Add a byte to the FIFO queue. IOR is 0 for success.
: FIFO>(b) \ *FIFO -- byte ior
Remove next byte from a FIFO. IOR is 0 for success.
The random number system in VFX Forth is based around
the DEFER
red word RANDOM
(see below). We have
found that a single random number generator (RNG) is not adequate
for all applications. Some applications need a particular
degree of randomness, others require more speed. If you do
not like the default RNG, you can install your own.
The implementation uses a seed in the variable *\fo{RandSeed),
which is set to some time value at start up.
The default implementation is in the SYSTEM
vocabulary.
defer RANDOM \ -- u
Generate a random number.
: CHOOSE \ n1 -- n2
Generate a random number *\i{n2) in the range 0..n1-1. The algorithm is
from Paul Mennen, 1991 .
: +randDigits \ buff$ -- ; 64 bit version
Add a 32 bit random number as 8 hex digits at the end
of the counted string in buff. Used for generating
random names for things like semaphores.
In order to extract very long strings from the source code, the word PARSE/L is provided. Support is also provided for counted strings with a 16 bit count. These words allow long strings such as those required for internationalisation to be generated without the restrictions of counted strings that use a character-sized count.
The contents of this section are subject to change until the ANS Forth committee reaches a conclusion about internationalisation issues. An implementation of the system descibed in the paper on the MPE web site may be found in the file %LIB%\INTERNATIONAL.FTH.
: parse/l \ char -- c-addr len ; like PARSE over lines
Parse the next token from the terminal input buffer using <char> as
the delimiter. The text up to the delimiter is returned as a
c-addr u string. PARSE/L does not skip leading delimiters. In
order to support long strings, PARSE/L can operate over multiple
lines of input and line terminators are not included in the text.
The string returned by PARSE/L remains in a single global buffer
until the next invocation of PARSE/L. PARSE/L is designed for use
at compile time and is not thread-safe or winproc-safe.
: wcount \ addr1 -- addr2 len
Given the address of a word-counted string in memory WCOUNT will
return the address of the first character and the length in
characters of the string.
: (W") \ -- waddr u ; step over caller's in line string
Returns the address and length of inline 16-bit word-counted string.
Steps over inline text.
: ((W")) \ -- waddr u ; dangerous factor!
A factor provided for the generation of long string actions that
have to step over an inline string. For example, to define W."
which uses a long string, you might compile (W.") and then use
W", to compile the inline string. The definition of (W.") then
might be:
: (W.") \ --
((W")) type
;
: wAppend \ c-addr u $dest --
Add string c-addr u to the end of word-counted string $dest.
: (w$+) \ c-addr u $dest -- ; SFP001
Add string c-addr u to the end of word-counted string $dest.
: w$+ \ $src $dest -- ; add $SRC to end of $DEST
Add word-counted string $src to the end of word-counted string $dest.
: w$, \ caddr len --
Lay a 16 bit string string into the dictionary at HERE
, reserve
space for it and ALIGN
the dictionary. The inline string string
has a 16 bit count and 16 bit zero termination.
: w", \ -- ; compile a word counted string
multiline version of ",.
Interprets multiline text and lays down inline string string with 16 bit
count and 16 bit zero termination.
: ls" \ c: -- ; i: -- caddr len
A version of S"
that can extend over several lines.
Line separators are ignored.
: zls" \ c: -- ; i: -- zaddr
A version of Z"
that can extend over several lines.
Line separators are ignored. The returned address is that
of the start of the zero-terminated string.
VFX Forth includes code for handling the OS X command line.
You can access the command line using Forth versions of
ARGV[]
and ARGC
as in C. At start up, VFX Forth
attempts to recreate the original command line using argc
and the argv[] strings so that the command line can be
treated as a sequence of Forth commands.
The attempt to recreate the command line is only partially successful because the C parser uses double-quotes characters to denote literal strings. These are passed to the application as a single string with the double-quotes characters removed. For example:
vfxosx s" foo bar" type
will give a command line error. You can escape the double-quotes characters in the usual C manner, but this will not preserve the spaces.
vfxosx s\" foo bar\" type
Most OS X system shells, including Bash, allow you to use single-quotes characters for literal strings.
vfxosx 's" foo bar" type'
: argc \ -- u
The number of defined arguments.
: argv[ \ n -- pointer|0
Given an index of 0..argc-1 return a pointer to the command
line token's zero-terminated string. 0 argv[
returns the
executable's name. If the argument does not exist, the
pointer is zero.
: CommandLine \ -- c-addr len
Return the system command line. This is recreated from the
data passed to VFX Forth and may not be exact.
VFX Forth supports a few "helper" definitions to aid the parsing of "C" header files. See VFXBase/GenTools.fth.
These are especially helpful in the parsing of Windows resource scripts which are based on the Microsoft RC Language for C, and for cutting and pasting from C header files.
: #define \ <spaces"NAME"> <eol"value-def"> -- ; Exec: -- value
A simple version of C's #define preprocessor command.
Any text between the definition name and the end of the line is
EVALUATE
d when <NAME>
is invoked.
: // \ --
An implementation of the C++ single line comment.
: /* \ --
A simple implementation of the C "/* ... */" comment.
: enum \ --
Process an enum of the form:
enum <name> { a, b, c=10, d };
<name> is placed in the *\fo{NamedEnums) vocabulary. The elements appear as Forth constants in the *\fo{CURRENT) wordlist/vocabulary. The definition may extend over many lines. C comments may occur after the ',' separator, e.g.
JIM = 25, // comment about this line
: enum{ \ --
Process an enum of the form:
enum{ a, b, c=10, d };
: .NamedEnums \ --
List all the named ENUMs.
These words preserve the stack pointers, put dummy items on the stack, and restore the stack later. Use these words inside a colon definition for protection of (say) a text interpreter. See VFXBase/GenTools.fth.
[GuardSP ... GuardSP]
8 cells constant /guard \ -- n
The amount by which the stack is guarded.
: [GuardSP \ -- ; R: -- oldS0 oldSP
Reserves guard space on the data stack and resets the data
stack pointers as if the stack was empty.
: GuardSP] \ -- ; R: oldS0 oldSP --
Restore the stack condition saved by [GuardSP
.
There are occasions when we want to run words only once or twice in order to initialise existing data. These words occupy space. The code here allows you to build words in a transient region allocated from the heap. When the region is released, all the words in it are removed from the dictionary and the memory is freed. The regions are nestable. See VFXBase/GenTools.fth.
[tr
: foo .... ;
foo
tr]
0 value anchorTR \ -- addr|0
Anchors the linked list of transient areas.
struct /transient \ -- len
Defines the head of the transient region.
: [TR \ --
Allocate a new transient area
: TR] \ --
End the current transient area and remove it
There are four things that you can do with a word:
EXECUTE
.The four words here provide these functions explicitly. See VFXBase/GenTools.fth.
: exec-interp \ xt --
Execute the interpretation action as if on the console.
: exec-comp \ xt --
Execute the compilation behaviour of the word.
: comp-interp \ xt --
Compile the interpretation behaviour of a word.
: comp-comp \ xt --
Compile the compilation behaviour of a word.
: (ndcs,) \ i*x xt -- j*x
Like (COMPILE,)
but executes the NDCS action for a word
and may parse and/or have a stack effect during compilation.
: compile-word \ i*x xt -- j*x
Process an XT for compilation.