4. Programming in SwiftForth

SwiftForth is generally compatible with the basic principles of Forth programming described in Forth Programmer’s Handbook. However, since that book is fairly general and documents a number of optional features, this section will discuss particular features in SwiftForth of interest to a Forth programmer.

4.1 Programming Procedures

The overall programming strategy in SwiftForth involves developing components of your application one at a time and testing each thoroughly using the tools described in Section 2.4, as well as the intrinsically interactive nature of Forth itself. This process is sometimes described as incremental development, and is known to be a good way to develop sound, reliable programs quickly.

This section describes tools and procedures for configuring SwiftForth to include the features you need, as well as for managing the incremental development process.

4.1.1 Dictionary Management

When you are working on a particular component of an application, it’s convenient to be able to repeatedly load and test it. In order to avoid conflicts (and an ever-growing dictionary), it is desirable to be able to treat the portion of the program under test as an overlay that will automatically discard previous versions before bringing in a new one.

This section covers two techniques for managing such overlays. To replace the contents of your entire dictionary with a new overlay, we recommend use of the word EMPTY. To create additional levels of overlays within the task dictionary, such that when an overlay is loaded, it will replace its alternate overlay beginning at the appropriate level, we recommend use of dictionary markers MARKER or REMEMBER. This section also discusses the option of allowing an overlay to reset the boundary between system and private definitions.

4.1.1.1 Single-level Overlays

The command EMPTY empties the dictionary to a pre-defined point, sometimes called its golden state. Initially, this is the state of the dictionary following the launch of SwiftForth. Any application definitions that you don’t want discarded with the overlay should be loaded as system options (see Section 2.3.5).

EMPTY discards all definitions and releases all allocated data space. To maintain a one-level overlay structure, therefore, just place EMPTY at the beginning of the file (such as an INCLUDE file that manages the other files in your application) you are repeatedly loading during development.

If you have loaded a set of functions that you believe are sufficiently tested and stable to become a permanent part of your run-time environment, you may add them to the golden dictionary by using the word GILD. This resets the pointers used by EMPTY so that future uses of EMPTY will return to this golden state. To permanently save this reconfigured system as a turnkey image, use PROGRAM (see Section 4.1.2).

EMPTY
( — )

Reset the dictionary to a predefined golden state, discarding all definitions and releasing all allocated data space beyond that state.The initial golden state of the dictionary is that following the launch of SwiftForth; this may be modified using GILD.

GILD
( — )

Set the current state of the dictionary as a golden state such that subsequent uses of EMPTY will restore the dictionary to this state.

4.1.1.2 Multi-level Overlays

As the kinds of objects that programs define and modify become increasingly complex, more care must be given to how such objects can be removed from the dictionary and replaced using program overlays. In earlier Forth systems, the simple words EMPTY and FORGET were used for this purpose; but in this system, it is not possible for these words to know about all the structures and interrelationships that have been created within the dictionary. Typically, problems are encountered if new structures are chained to some other structure, or if a structure is being used as an execution vector for a system definition. Thus, an extensible concept has been developed to satisfy these needs.

A dictionary marker is an extensible structure that contains all the information necessary to restore the system to the state it was in when the structure was added. The words MARKER and REMEMBER create such entries, giving each structure a name which, when executed, will restore the system state automatically. The difference between the words is simply that a MARKER word will remove itself from the dictionary when it is executed, while a REMEMBER word will preserve itself so that it can be used again. MARKER words are most useful for initialization code that will only be executed once, while REMEMBER words are most useful in creating program overlays.

Although these marker words have been written to handle most common application requirements, you may need to extend them to handle the unique needs of your application. For example, if one of your application overlays requires a modification to the behavior of ACCEPT, you will need to extend the dictionary markers so they can restore the original behavior of ACCEPT when that overlay is no longer needed. To do this, you must keep in mind both the compile-time (when the structure is created) and the run-time (when the structure is used) actions of dictionary markers.

In particular, any time you have passed an address to Windows, such as a callback, you must provide a mechanism for un-doing that connection before you discard the code containing this address.

At compile time, when MARKER or REMEMBER is executed, a linked list of routines is executed to create a structure in the dictionary containing the saved context information. The word :REMEMBER adds another routine to the end of the sequence. This routine can use the compiling words , and C, to add context information. Then, when the words defined by MARKER or REMEMBER are executed, another linked list of routines is executed to restore the system pointers to their prior values (a process called pruning). The word :PRUNE adds another routine to the end of the sequence. It will be passed the address within the structure where the :REMEMBER word compiled its information, and it must return the address incremented past that same information.

Thus, :REMEMBER is paired with :PRUNE, and these words should never appear except in pairs. The only exception is when the information being restored by :PRUNE is constant and there is no need for :REMEMBER to add it to the structure.

The linked lists of :REMEMBER or :PRUNE words are executed strictly in the order in which they are defined, starting with the earliest (effectively at the GILD point).

Two additional words are useful when extending these markers. ?PRUNED will return true if the address passed to it is no longer within the dictionary boundaries, and ?PRUNE will return true if the definition in which it occurs is being discarded.

EMPTY and GILD use these dictionary markers. GILD adds a marker to the dictionary that EMPTY can execute to restore the system state; it is never possible to discard below the GILD point.

:REMEMBER
( — )

Add an unnamed definition to the list of functions to be executed when MARKER or REMEMBER is invoked, to save system state data. The content of this definition must record the state information, normally using , or C,. When an associated :PRUNE definition is executed, it will be passed the address where the data was stored by :REMEMBER. Must be used with :PRUNE in the same overlay layer.

:PRUNE
( addr1 — addr2 )

Add an unnamed definition to the list of functions to be executed when MARKER or REMEMBER is invoked, to restore the system to a saved state. When the :PRUNE definition is executed, addr1 provides the address where the data has been stored by a :REMEMBER in the same overlay layer. The address after the stored data must be returned as addr2.

?PRUNE
( — flag )

Return true if the definition in which it is being invoked is being discarded (a MARKER is being executed).

?PRUNED
( addr — flag )

Return true if addr is no longer within the active dictionary (it has been discarded via MARKER or REMEMBER).

REMEMBER <name>
( — )

Create a dictionary entry for name, to be used as a deletion boundary. When name is executed, it will remove all subsequent definitions from the dictionary and execute all :PRUNE definitions (beginning with the earliest) to restore the system to the state it was in when name was defined. Note that name remains in the dictionary, so it can be used repeatedly.

4.1.2 Preparing a Turnkey Image

You may make a bootable binary image of SwiftForth, including additional code you have compiled, by typing:

PROGRAM <filename>[.<ext>] [<icon>]

This records a “snapshot” of the current running system in the current path. The file extension is optional. If it is omitted, .exe will be assumed and a Windows executable program will be generated.

The optional icon specification lets you associate an icon with this executable that is different from the standard SwiftForth icon. The icon must be in Windows ICO format and contain exactly one icon image, which can be a BMP or PNG image. The maximum icon image size supported is 64k. You specify it by giving its filename, which must have the extension .ico.

This feature is not available in the evaluation version of SwiftForth.

Simple use of PROGRAM is adequate to make a customized version of SwiftForth with added options of your choice. To make a standalone Windows application, however, requires more detailed attention to startup issues. These issues are discussed further in Section 8.7.

PROGRAM <filename>[.<ext>] [<icon>]
( — )

Record a “snapshot” of the current running system in the current path (or in the path specified with filename). If the optional file extension is omitted, .exe is assumed and a Windows executable file will be built. If an optional icon is specified, it must be the name of a Windows ICO file that contains exactly one icon. The icon filename must have the extension .ico.

4.2 Compiler Control

This section describes how you can control the SwiftForth compiler using typed commands and (more commonly) program source. In most respects, the command-line user interface in the SwiftForth console is treated identically to program source. Anything you do in source may be done interactively in the command window.

SwiftForth may be programmed using either blocks or files for source. Any source supplied with SwiftForth is provided in text files, the format that fits best with other programs and with the features of a Windows programming environment; this section assumes you are working with text files.

4.2.1 Case-Sensitivity

SwiftForth is normally case-insensitive, although you may set it to be case-sensitive temporarily. All standard Forth words are in upper case in SwiftForth, as are most SwiftForth words. Calls to Windows procedures are in mixed case, as shown in Windows documentation; these calls are case sensitive regardless of the state of the SwiftForth option.

If you need to make SwiftForth case sensitive, you may do so with the command CASE-SENSITIVE; to return to case insensitivity, use CASE-INSENSITIVE. This may be useful for situations such as compiling legacy code from a case-sensitive Forth, however we strongly recommend against operating in this mode in general, as un-intended conflicts may be introduced that are difficult to find and debug.

4.2.2 Detecting Name Conflicts

Because Forth is extremely modular, there are very many words and word names. As shipped, SwiftForth has over 3,500 named words, plus the Windows constants and procedures that are callable by other means. As a result, programmers are understandably concerned about inadvertently burying a name with a new one.

Insofar as possible, SwiftForth factors words that are not intended for general use into separate vocabularies. This leaves about 1,600 words in the Forth vocabulary.

Re-defining a name is harmless, so long as you no longer need to refer to the buried word; it won’t change references that were compiled earlier. Sometimes it is preferable to use a clear, meaningful name in a high level of your application—even if it buries a low-level function you no longer need—than to devise a more obscure or cumbersome name. And there are occasions when you specifically want to redefine a name, such as to add protection or additional features in ways that are transparent to calling programs.

However, it is often useful to have the compiler warn you of renamings. By default, SwiftForth will warn you if the name of a word being defined matches one in the same vocabulary. For example:

: DUP ( x -- x x )   DUP .  DUP ;
DUP isn't unique.

This makes it possible for you to look at the redefined word and make an informed judgement whether you really want to redefine it.

If you are redefining a number of words, you may find the messages annoying (after all, you already know about these instances). So SwiftForth has a flag called WARNING that you can set to control whether the compiler notifies you of redefinitions. You may set its state by using:

WARNING ON
WARNING OFF

You may also use -? just preceding a definition to suppress the warning that one time only, leaving WARNINGs enabled. Global control of this feature is in the dialog box Options > Warnings from the SwiftForth menu.

-? : DUP ( x -- x x )   DUP .  DUP ;
WARNING
( — )

Return the address of the flag that controls compiler redefinition warnings. If set to true, warnings will be issued.

ON
( addr — )

Set the flag at addr to true.

OFF
( addr — )

Set the flag at addr to false.

-?
( — )

Suppress redefinition warning for the next definition only.

4.2.3 Conditional Compilation

[IF], [ELSE], and [THEN] support conditional compilation by allowing the compiler to skip any text found in the unselected branch. These commands can be nested, although you should avoid very complex structures, as they impair the maintainability and readability of the code.

Say, for example, you have defined an option this way:

0 EQU MEM-MAP   \ 1 Enables memory diagnostics

Then in a load file you might find the statement:

MEM-MAP [IF]  INCLUDE memmap  [THEN]   \ Reports memory use

And later, this one:

MEM-MAP [IF]  .ALLOCATED  [THEN]   \ Display data sizes

[DEFINED] and [UNDEFINED] test if the word that follows them is either defined or undefined.

These tests, combined with conditional compilation, prove useful when providing a high-level definition that might be used if a code version of that word has not been defined.

[UNDEFINED] -TRAILING [IF]

: -TRAILING ( c-addr len1 -- c-addr len2 )   \ Strip trailing spaces
   BEGIN  DUP  WHILE
      1-  2DUP + C@ BL - IF  1+ EXIT THEN
   REPEAT ;

[THEN]
[IF]
( flag — )

If flag is true, do nothing. If false, skip ahead to the next instance of [ELSE] or [THEN].

[ELSE]
( — )

Skip ahead to the next instance of [THEN].

[THEN]
( — )

Text token parsed by [iF] and [ELSE]. [THEN] performs no action and just serves as a placeholder.

[DEFINED] <name>
( — flag )

Returns true if name is defined.

[UNDEFINED] <name>
( — flag )

Returns true if name is undefined.

4.3 Input Number Conversions

When the SwiftForth text interpreter encounters numbers in the input stream, they are automatically converted to binary. If the system is in compile mode (i.e., between : and ;), it will compile a reference to the number as a literal; when the word being compiled is subsequently executed, that number will be pushed onto the stack. If the system is interpreting, the number will be pushed onto the data stack directly.

All number conversions in Forth (input and output) are controlled by the user variable BASE. Several words in this section may be used to control BASE. In each case, the requested base will remain in effect until explicitly changed. Punctuation in a number (decimal point, comma, colon, slash, or dash anywhere other than before the leftmost digit) will cause the number to be converted as a double number.

Your current number base can be set by the commands DECIMAL, HEX, OCTAL, and BINARY. Each of these will stay in effect until you specify a different one. DECIMAL is the default.

In addition, input number conversion may be directed to convert an individual number using a particular base specified by a prefix character. Following such a conversion, BASE remains unchanged from its prior value. If the number is to be negative, the minus sign must follow the prefix and precede the most significant digit.

Number conversion prefixes:

PrefixConversion baseExample
%Binary%10101011
&Octal&177
#Decimal#-12345
$Hex$FE00

SwiftForth also provides a more powerful set of words for handling number conversion. For example, you may need to accept numbers with:

  • decimal places
  • angles or times with embedded colons
  • dates with slashes
  • part numbers, telephone numbers, or other numbers with embedded dashes

SwiftForth’s number-conversion words are based on the low-level number conversion word from Standard Forth, >NUMBER (see also “Number Conversions” in Forth Programmer’s Handbook).

The word NUMBER? takes the address and length of a string, and attempts to convert it until either the length expires (in which case it is finished) or it encounters a character that is neither a digit (0 to BASE-1) nor valid punctuation.

NUMBER? interprets any number containing one or more valid embedded punctuation characters as a double-precision integer. Single-precision numbers are recognized by their lack of punctuation. Conversions operate on character strings of the following format:

[ - ]dddd[ punctuation ]dddd ... delimiter

where dddd is one or more valid digits according to the current base (or radix) in effect for the task. A numeric string may be shorter than the length passed to NUMBER? if it is terminated with a blank. If another character is encountered (i.e., a character which is neither a digit according to the base nor punctuation), conversion will end. The leading minus sign, if present, must immediately precede the leftmost digit or punctuation character.

Any of the following punctuation characters may appear in a number (except in floating-point numbers, as described in Section 12:):

,  . +  -  /  :

All punctuation characters are functionally equivalent. A punctuation character causes the digits that follow to be counted. This count may be used later by certain of the conversion words. The punctuation character performs no other function than to set a flag that indicates its presence, and does not affect the resulting converted number. Multiple punctuation characters may be contained in a single number; the following two character strings would convert to the same number:

1234.56
1,23.456

NUMBER? will return one of three possible results:

  • If number conversion failed (i.e., a character was encountered that was neither a digit nor a punctuation character), it returns the value zero.
  • If the number is single precision (i.e., unpunctuated), it returns a 1 on top of the stack, with the converted value beneath.
  • If the number is double-precision (i.e., contained at least one valid punctuation character), it returns a 2 on top of the stack, with the converted value beneath.

Floating-point support described in Section 12: extends the number conversion process to handle floating-point numbers.

The variable DPL is used during the number conversion process to track punctuation. DPL is initialized to a large negative value, and is incremented every time a digit is processed. Whenever a punctuation character is detected, it is set to zero. Thus, the value of DPL immediately following a number conversion contains potentially useful information:

If it is negative, the number was unpunctuated and is single precision.

Zero or a positive non-zero value indicates the presence of a double-precision number, and gives the number of digits to the right of the rightmost punctuation character.

This information may be used to scale a number with a variable number of decimal places. Because DPL doesn’t care which punctuation character was used, it works equally well with American decimal points and European commas to start the fractional part of a number.

The word NUMBER is the high-level input number conversion routine used by SwiftForth. It performs number conversions explicitly from ASCII to binary, using the value in BASE to determine which radix should be used. This word is a superset of NUMBER?.

NUMBER will attempt to convert the string to binary and, if successful, will leave the result on the stack. Its rules for behavior in the conversion are similar to the rules for NUMBER? except that it always returns just the value (single or double). It is most useful in situations in which you know (because of information relating to the application) whether you will be expecting punctuated numbers. If the conversion fails due to illegal characters, a THROW will occur.

If NUMBER’s result is single precision (negative DPL), the high-order part of the working number (normally zero) is saved in the variable NH, and may be recovered to force the number to double precision.

BASE
( — addr )

Return the address of the user variable containing the current radix for number conversions.

DECIMAL
( — )

Set BASE for decimal (base 10) number conversions on input and output. This is the default number base.

HEX
( — )

Set BASE for hexadecimal (base 16) number conversions on input and output.

OCTAL
( — )

Set BASE for octal (base 8) number conversions on input and output.

BINARY
( — )

Set BASE for binary (base 2) number conversions on input and output.

>NUMBER
( ud1 addr1 u1 — ud2 addr2 u2 )

Convert the characters in the string at addr1, whose length is u1, into digits, using the radix in BASE. The first digit is added to ud1. Subsequent digits are added to ud1 after multiplying ud1 by the number in BASE. Conversion continues until a non-convertible character (including an algebraic sign) is encountered or the string is entirely converted; the result is ud2. addr2 is the location of the first unconverted character or, if the entire string was converted, of the first character beyond the string. u2 is the number of unconverted characters in the string.

NUMBER?
( addr u — 0 | n 1 | d 2 )

Attempt to convert the characters in the string at addr, whose length is u, into digits, using the radix in BASE, until the length u expires. If valid punctuation ( , . + – / : ) is found, returns d and 2; if there is no punctuation, returns n and 1; if conversion fails due to a character that is neither a digit nor punctuation, returns 0 (false).

NUMBER
( addr u — n | d )

Attempt to convert the characters in the string at addr, whose length is u, into digits, using the radix in BASE, until the length u expires. If valid punctuation ( , . + – / : ) is found, returns d; if there is no punctuation, returns n; if conversion fails due to a character that is neither a digit nor punctuation, an ABORT will occur.

DPL
( — addr )

Return the address of a variable containing the punctuation state of the number most recently converted by NUMBER? or NUMBER. If the value is negative, the number was unpunctuated. If it is non-negative, it represents the number of digits to the right of the rightmost punctuation character.

NH
( — addr )

Return the address of a variable containing the high-order part of the number most recently converted by NUMBER? or NUMBER.

4.4 Timing Functions

The words in this section support a time-of-day clock and calendar using the system clock/calendar functions.

4.4.1 Date and Time of Day Functions

SwiftForth supports a calendar using the format. Some of the words described below are intended primarily for internal use, whereas others provide convenient ways to enter and display date and time-of-day information.

SwiftForth’s internal format for time information is an unsigned, double number representing seconds since midnight. There are 86,400 seconds in a day.

Dates are represented internally as a modified Julian date (MJD). This is a simple, compact representation that avoids the “Year 2000 problem,” because you can easily do arithmetic on the integer value, while using the words described in this section for input and output in various formats.

The date is encoded as the number of days since 31 December 1899, which was a Sunday. The day of the week can be calculated from this with 7 MOD.

The useful range of dates that can be converted by this algorithm is from 1 March 1900 thru 28 February 2100. Both of these are not leap years and are not handled by this algorithm which is good only for leap years which are divisible by 4 with no remainder.

A date presented in the form mm/dd/yyyy is converted to a double-precision integer on the stack by the standard input number conversion routines. A leading zero is not required on the month number, but is required on day numbers less than 10. Years must be entered with all four digits. A double-precision number entered in this form may be presented to the word M/D/Y, which will convert it to an MJD. For example:

8/03/1940 M/D/Y

will present the double-precision integer 8031940 to M/D/Y, which will convert it to the MJD for August 3, 1940. This takes advantage of the enhanced SwiftForth number conversion that automatically processes punctuated numbers (in this case, containing / characters) as double-precision (see Section 4.3).

Normally, M/D/Y is included in the application user interface command that accepts the date. For example:

: HIRED ( -- u )        \ Gets date of hire
   CR ." Enter date of hire:"   \ User prompt
   PAD 10 ACCEPT                \ Await input to PAD
   PAD SWAP NUMBER              \ Text to number
   M/D/Y ;                      \ Number to MJD

To obtain the day of the week from an MJD, simply take the number modulo 7; a value of zero is Sunday. For example:

8/03/1940 M/D/Y 7 MOD .

gives 6 (Saturday).

An alternative form D-M-Y is also available. It takes the day, month, and year as separate stack items, and combines them to produce an MJD. These two phrases should return the same MJD:

3 8 1940 D-M-Y
08/03/1940 M/D/Y

Output formatting is done by (DATE), which takes an MJD as an unsigned number and returns the address and length of a string that represents this date. The word .DATE will take an MJD and display it in that format.

(DATE) is an execution vector. The following standard behaviors for this word are provided by SwiftForth:

  • (MM/DD/YYYY) provides the SwiftForth default format, e.g., 12/30/2000.
  • (DD-MM-YYYY) provides an alternative date format, e.g., 30-Dec-2000.

In addition, SwiftForth for Windows provides these two behaviors that are based on Windows system settings:

  • (WINLONGDATE) provides a Windows long date, e.g., Wednesday, June 3, 1998.
  • (WINSHORTDATE) provides the Windows short date format, e.g., 6/3/98.

(MM/DD/YYYY) is the default. To change it, assign a new behavior to (DATE):

<xt> IS (DATE)

…where xt is the execution token of the desired behavior. For example:

' (DD-MM-YYYY) IS (DATE)

Time of day in SwiftForth is represented as an unsigned double number of seconds since midnight. Output formatting is done by (TIME), which takes the time as an unsigned double number and returns the address and length of a string that represents the time. The word .TIME will take double unsigned number of seconds since midnight and display it in that format.

Low-level time and date functions

@NOW
( — ud u )

Return the system time as an unsigned, double number ud representing seconds since midnight, and the system date as u days since 01/01/1900.

TIME&DATE
( — +n1 +n2 +n3 +n4 +n5 +n6 )

Return the current time and date. +n1 is the second {0…59}, +n2 is the minute {0…59}, +n3 is the hour {0…23}, +n4 is the day {1…31}, +n5 is the month {1…12} and +n6 is the year (e.g., 1991). This is the Standard Forth portable date/time access word.

Time Functions

@TIME
( — ud )

Return the system time as an unsigned, double number representing seconds since midnight.

(TIME)
( ud — )

Format the time ud as a string with the format hh:mm:ss, returning the address and length of the string.

.TIME
( ud — )

Display the time ud in the format applied by (TIME) above.

TIME
( — )

Display the current system time.

Date Functions

D-M-Y
( u1 u2 u3 — )

Convert day u1, month u2, and year u3 into MJD u4.

M/D/Y
( ud — u )

Accept an unsigned, double-number date which was entered as mm/dd/yyyy, and convert it to MJD.

@DATE
( — u )

Return the current system date as an MJD.

(DATE)
( u1 — addr u2 )

An execution vector that is set to a suitable formatting behavior. The default (MM/DD/YYYY) formats the MJD u1 as a string in the form mm/dd/yyyy, returning the address and length of the string.

Format the MJD u1 as a string with the format mm/dd/yyyy, returning the address and length of the string. This is the default behavior of (DATE).

(MM/DD/YYYY)
( u1 — addr u2 )

Format the MJD u1 as a string with the format mm/dd/yyyy, returning the address and length of the string. This is the default behavior of (DATE).

(DD-MMM-YYYY)
( u1 — addr u2 )

Format the MJD u1 as a string with the format dd-MMM-yyyy, where MMM is a three-letter month abbreviation, returning the address and length of the string. This is the default behavior of (DATE).

(WINLONGDATE)
( u1 — addr u2 )

Format the MJD u1 as a string using the Windows long date format. This is a possible alternative behavior for (DATE).

(WINSHORTDATE)
( u1 — addr u2 )

Format the MJD u1 as a string using the Windows short date format. This is a possible alternative behavior for (DATE).

.DATE
( u — )

Display the MJD u in the format applied by (DATE) above.

DATE
( — )

Display the current system date.

4.4.2 Interval Timing

SwiftForth includes facilities to time events, both in the sense of specifying when something will be done, and of measuring how long something takes. These words are described in the glossary below.

The word MS causes a task to suspend its operations for a specified number of milliseconds, during which time other tasks can run. For example, if an application word SAMPLE records a sample, and you want it to record a specified number of samples, one every 100 ms., you could write a loop like this:

: SAMPLES ( n -- )
   ( n ) 0 DO           \ Record n samples
      SAMPLE  100 MS    \ Take one sample, wait 100 ms
   LOOP ;

Because MS relies on a system timer, the accuracy of the measured interval depends upon the overall system multitasking environment. In general, the error on an interval will be on the order of the duration of a clock tick; however, it can be much longer if a privileged operation is running.

The words COUNTER and TIMER can be used together to measure the elapsed time between two events. For example, if you wanted to measure the overhead caused by other tasks in the system, you could do it this way:

: MEASURE ( -- ) 
   COUNTER              \ Initial value
   1000000 0 DO         \ Total time for a million trials
      PAUSE             \ One lap around the multitasker
   LOOP  TIMER ;        \ Display results.

Following a run of MEASURE, you can divide the time by 1,000,000 to get the time for an average PAUSE. For maximum accuracy, you can run an empty loop (without PAUSE) and measure the measurement overhead itself.

: TARE ( -- ) 
   COUNTER              \ Initial value
   1000000 0 DO         \ Total time for a million trials
   LOOP  TIMER ;        \ Display results.

The pair of words uCOUNTER and uTIMER are analogous to COUNTER and TIMER, but provide microsecond precision.

MS
( n — )

PAUSE the current task for n milliseconds. The accuracy of this interval is one clock tick.

COUNTER
( — u )

Return the current value of the system millisecond timer.

TIMER
( u — )

Repeat COUNTER, then subtract the two values and display the interval between the two in milliseconds.

EXPIRED
( u — flag )

Return true if the current COUNTER reading has passed u. For example, the following word will execute the hypothetical word TEST for u milliseconds:

: TRY ( u -- )          \ Run TEST repeatedly for u ms.
   COUNTER +  BEGIN     \ Calculate end time
      TEST              \ Perform TEST
   DUP EXPIRED UNTIL ;  \ Done when time expires
uCOUNTER
( — d )

Return the current value of the system microsecond timer.

uTIMER
( d — )

Repeat uCOUNTER, then subtract the two values and display the interval between the two in microseconds.

4.5 Specialized Program and Data Structures

The complex needs of the Windows environment have led to the inclusion of the specialized structures described in this section.

4.5.1 String Buffers

ANS Forth provides the word PAD to return the address of a buffer that may be used for temporary data storage. In SwiftForth, PAD starts at 512 bytes above HERE, and its actual size can extend for the balance of unused memory (typically several MB). You can determine the actual size by the phrase:

UNUSED 512 - .

This will change, as will the address of PAD, any time you add definitions, perform ALLOT, or otherwise change the size of the dictionary.

SwiftForth itself uses PAD only for various high-level functions, such as the WORDS browser, cross-reference, etc. It is highly recommended as a place to put strings for temporary processing, such as building a message or command string that will be used immediately, or to keep a search key during a search. It is not appropriate for long-term storage of data; for this purpose you should define a buffer to hold the string.

BUFFER: is a simple way to define a buffer or array whose size is specified in characters. For example:

100 BUFFER: MYSTRING

…defines a 100-character region whose address will be returned by MYSTRING. If you prefer to specify the length in cells, you may use:

50 CELLS BUFFER: MYARRAY

…which defines a buffer 50 cells long whose address is returned by MYARRAY.

There is an important distinction between PAD and buffers defined by BUFFER:. PAD is in a task’s user area, which means that if you have multiple tasks (including a transient task instantiated by a callback) each may have its own PAD. In contrast, a BUFFER: (like all other Forth data objects) is static and global.

PAD
( — addr )

Return the address of a region in the user area suitable for temporary storage of string data. The size of PAD is indefinite, comprising all unused memory in a task’s dictionary space. Beacuse PAD is in the user area, each task or callback has a private version. PAD is defined relative to HERE, and so will move if additional memory is allotted for definitions or data.

BUFFER: <name>
( u — )

Defines a buffer u characters in size. Use of name will return the address of the start of the buffer.

4.5.2 String Data Structures

SwiftForth includes the Standard Forth words S” and C”, and in addition provides several string-defining words that are used similarly. Windows; Linux, and macOS use a C standard string format that is terminated by a null, sometimes referred to as an ASCIIZ string.

In some systems, Unicode strings may be required. In this implementation, these are ASCII characters in the low byte of a 16-bit character, with the high-order byte zero.

All of the string defining words are intended to be used inside definitions or structures such as switches. If you use one of them interpretively, it will return an address in a temporary buffer. This is useful for interactive debugging, but you should not attempt to save such an address, as there is no guarantee how long the string will remain there.

SwiftForth includes a special set of string words with \ in their names, listed in the glossary below, that provide for the inclusion of control characters, quote marks, and other special characters in the string.

Within the string, a backslash indicates that the character following it is to be translated or treated specially, following the rules listed in the Forth Standard. The transformations listed below are supported. Note that characters following the \ are case sensitive.

SequenceSymbolDescriptionASCII
\aBELalert7
\bBSbackspace8
\eESCescape27
\fFFform feed12
\lLFline feed10
\mCR/LFpair13, 10
\nNLimplementation dependent newline,
e.g., CR/LF, CR, LF, LF/CR
\rCRcarriage return13
\tHThorizontal tab9
\vVTvertical tab11
\zNULnull character0
\qdouble quote34
\”double quote34
\x<hexdigit>
<hexdigit>
The resulting character is the conversion of these two hexadecimal digits. An ambiguous conditions exists if \x is not followed by two hexadecimal characters.
\\backslash92

The string-management extensions in SwiftForth are documented below.

Z” <string>”
( — addr )

Compile a null-terminated string, returning its address.

Z\” <string>”
( — addr )

Compile a zero-terminated string, returning its address. The string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed above.

,Z” <string>”
( — )

Compile a null-terminate string with no leading count.

,Z\” <string>”
( — )

Compile a null-terminated string. The string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed above. (Differs from Z\” in that it is used interpretively to compile a string, whereas Z\” belongs inside a definition).

,U” <string>”
( — )

Compile a Unicode string.

,U\” <string>”
( — )

Compile a Unicode string. The string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed above.

S\” <string>”
( — addr len )

Compile a string, returning its address and length. The string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed above.

.\”
( — )

Compile a counted string and print it at run time. The string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed above.

C\” <string>”
( — addr )

Compile a counted string, returning its address. The string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed above.

,” <string>”
( — )

Compile a string in the dictionary starting at HERE, and allocate space for it.

,\” <string>”
( — )

Similar to ,” but the string may contain a \ followed by one or more characters which will be converted into a control or other special character according to the transformations listed in above.

STRING,
( addr u — )

Compile the string at addr, whose length is u, in the dictionary starting at HERE, and allocate space for it.

PLACE
( addr1 u addr2 — )

Put the string at addr1, whose length is u, at addr2, formatting it as a counted string (count in the first byte).

APPEND
( addr1 u addr2 — )

Append the string at addr1, whose length is u, to the counted string already existing at addr2.

ZPLACE
( addr1 u addr2 — )

Put the string at addr1, whose length is u, at addr2 as a null-terminated string.

ZAPPEND
( addr1 u addr2 — )

Append the string at addr1, whose length is u, to the null-terminate string already existing at addr2.

SCAN
( addr1 len1 char — addr2 len2 )

Searches for the first instance of the specified character in a string. Returns the address of the character and the remaining length of the string. len2 = 0 means that the character was not found.

SKIP
( addr1 len1 char — addr2 len2 )

Skips leading instances of the specified character in a string.

4.5.3 Linked Lists

SwiftForth provides for linked lists of items that are of different lengths or that may be separated by intervening objects or code. This is an important underlying implementation technology used by switches as well as other structures. A linked list is controlled by a VARIABLE that contains a relative pointer to the head of the chain. Each link contains a relative pointer to the next, and a zero link marks the end.

In SwiftForth, references to data objects return absolute addresses. To convert these to and from relative addresses, SwiftForth provides the words @REL, !REL, and ,REL. Respectively, these fetch a relative address (converting it to absolute), store an address converted from absolute to relative, and compile a converted relative address. These words are used when constructing linked lists or when referring to the links in them.

Linked lists are built at compile time. The word >LINK inserts a new entry at the top of the chain, updating the controlling variable and compiling a relative pointer to the next link at HERE. Similarly, <LINK inserts a link at the bottom of the chain, replacing the 0 in the previous bottom entry with a pointer to this link, whose link contains zero. Here is a simple example:

VARIABLE MY-LIST
MY-LIST >LINK 123 ,
MY-LIST >LINK 456 ,
MY-LIST >LINK 789 ,

: TRAVERSE ( -- )       \ Display all values in MY-LIST
   MY-LIST BEGIN
      @REL ?DUP WHILE   \ While there are more links...
      DUP CELL+ @ .     \ Display cell following link
   REPEAT ;
@REL
( addr1 — addr2 )

Fetch a relative address from addr1 to the stack, converting it to the absolute address addr2. The offset stored at addr1 is relative to addr1.

!REL
( addr1 addr2 — )

Convert absolute address addr1 to address relative to addr2 and store the result at addr2.

,REL
( addr — )

Compile absolute address addr at HERE, after converting it to a relative address.

>LINK
( addr — )

Add a link starting at HERE to the start of the linked list whose head is at addr (normally a variable). The head is set to point to the new link, which, in turn, is set to point to the previous start link.

<LINK
( addr — )

Add a link starting at HERE to the end of the linked list whose head is at addr. The new link is given a value of zero (indicating the end of the list), and the previous end link is set to point to this one.

CALLS
( addr — )

Traverse a linked list starting at addr, executing the high-level code that follows each link field in the list.

4.5.4 Switches

Switches are used to process Windows messages, Forth THROW codes, and other encoded information for which specific responses are required.

Message switches are defined using this syntax:

[SWITCH <name> <default-word>
   <val1> RUNS <wordname>
   <val2> RUN: <words> ;
   ...
SWITCH]

There are two possible RUN words:

  • RUNS followed by a previously defined word will set that switch entry to execute the word.
  • RUN: starts compiling a nameless colon definition, terminated by ; that will be executed for that value.

This will build a structure whose individual entries have the form:

|link|value|xt|

…where each link is a relative pointer to the next entry. This is necessary because the entries may be anywhere in memory, and each individual list is subject to extension by [+SWITCH. The last link in a list contains zero. The xt will point either to a specified word (if RUNS is used) or to the code fragment following RUN:. Note that the values do not need to be in any particular order, although performance will be improved if the most common values appear early.

When the switch is invoked with a value on the stack, the execution behavior of the switch is to run down the list searching for a matching value. If a match is found, the routine identified by xt will execute. If no match is found, the default word will execute. The data value is not passed to the xts, but is passed to the default word if no match is found.

For example:

[SWITCH TESTING DROP
   1 RUNS WORDS
   2 RUN: HERE 100 DUMP ;
   3 RUNS ABOUT
SWITCH]

You may add cases to a previously defined switch using a similar structure called [+SWITCH, whose syntax is:

[+SWITCH <name>
   <valn+1> RUNS <wordname>
   <valn+2> RUN: <words> ;
   ...
SWITCH]

…where name must refer to a previously defined switch. No new default may be given. The cases valn+1, etc., will be added to the list generated by the original [SWITCH definition for name, plus any previous [+SWITCH additions to it.

[SWITCH
( — switch-sys addr )

Start the definition of a switch structure consisting of a linked list of single-precision numbers and associated behaviors. The switch definition will be terminated by SWITCH], and can be extended by [+SWITCH. See the discussion above for syntax.

switch-sys and addr are used while building the structure; they are discarded by SWITCH].

The behavior of name when invoked is to take one number on the stack, and search the list for a matching value. If a match is found, the corresponding behavior will be executed; if not, the switch’s default behavior will be executed with the value on the stack.

[+SWITCH
( — switch-sys addr )

Open the switch structure name to include additional list entries. The default behavior remains unchanged. The additions, like the original entries, are terminated by SWITCH].

switch-sys and addr are used while building the structure; they are discarded by SWITCH].

SWITCH]
( switch-sys addr — )

Terminate a switch structure (or the latest additions to it) by marking the end of its linked list.

RUNS
( switch-sys addr n — switch-sys addr )

Add an entry to a switch structure whose key value is n and whose associated behavior is the previously defined word. The parameters switch-sys and addr are used internally during construction of the switch.

RUN: ;
( switch-sys addr n — switch-sys addr )

Add an entry to a switch structure whose key value is n and whose associated behavior is one or more previously defined words, ending with ;. The parameters switch-sys and addr are used internally during construction of the switch.

4.5.5 Execution Vectors

An execution vector is a location in which an execution token (or xt) may be stored for later execution. SwiftForth supports several common mechanisms for managing execution vectors.

The word DEFER makes a definition (called a deferred word) which, when invoked, will attempt to execute the execution token stored in its data space. If none has been set, it will abort. To store an xt in such a vector, use the form:

<xt> IS <vector-name>

Note that DEFER is described in Forth Programmer’s Handbook as being set by TO; that is not a valid usage in SwiftForth.

You may also construct execution vectors as tables you can index into. Such a table is described in Forth Programmer’s Handbook. SwiftForth provides a word for executing words from such a vector, called @EXECUTE. This word performs, in effect, @ followed by EXECUTE. However, it also performs the valuable function of checking whether the cell contained a zero; if so, it automatically does nothing. So it is not only safe to initialize an array intended as an execution vector to zeros, it is a useful practice.

DEFER
( — )

Define name as an execution vector. When name is executed, the execution token stored in name’s data area will be retrieved and its behavior performed. An abort will occur if name is executed before it has been initialized.

IS
( xt — )

Store xt in name, where name is a word defined by DEFER.

@EXECUTE
( addr — )

Execute the xt stored in addr. If the contents of addr is zero, do nothing.

4.5.6 Local Variables

SwiftForth implements the Standard Forth optional Locals word set. Local variables are defined and used as follows:

: <name>   ( xn ... x2 x1 -- )
   {: argn ... arg2 arg1 | valn ... val2 val1 :}
   < content of definition > ;

When name executes, its local variables are initialized with values taken from the stack. Note that the order of the local arg names is the same as the stack arguments as shown in the stack comment. The local val names are not initialized from the stack.

The behavior of a local variable, when invoked by name, is to return its value. You may store a value into a local using the form:

<value> TO <name>

or increment it by a value using the form:

<value> +TO <name>

Local variable names are instantiated only within the definition in which they occur. Following the locals declaration, locals are not accessible except by name. During compilation of a definition in which locals have been declared, they will be found first during a dictionary search. Local variable names may be up to 254 characters long, and follow the same case-sensitivity rules as the rest of the system. SwiftForth supports up to 16 local variables in a definition.

Local variables in SwiftForth are instantiated on the return stack. Therefore, although you may perform some operations in a definition before you declare locals, you must not place anything on the return stack (e.g., using >R or DO) before your locals declarations.

Note that because local variables are not available outside the definition in which they are instantiated, use of local variables precludes interpretive execution of phrases in which they appear. In other words, when you decide to use local variables you may simplify stack handling inside the definition, but at some cost in options for testing.

4.6 Convenient Extensions

This section presents a collection of words that have been found generally useful in SwiftForth programming.

++
( addr — )

Increment the value at addr by 1.

@+
( addr1 — addr2 x )

Fetch the value x from addr1, and increment the address by one cell.

!+
( addr1 x — addr2 )

Store the value x at addr, and increment the address by one cell.

3DUP
( x1 x2 x3 — x1 x2 x3 x1 x2 x3 )

Place a copy of the top three stack items onto the stack.

3DROP
( x1 x2 x3 — )

Drop the top three items from the stack.

ZERO
( x — 0 )

Replace the top stack item with the value 0 (zero).

ENUM
( n1 — n2 )

Define name as a constant with value n1, then increment n1 to return n2. Useful for defining a sequential list of constants.

NEXT-WORD
( — addr u )

Get the next word in the input stream—extending the search across line breaks as necessary, until the end-of-file is reached—and return its address and length. Returns a string length of 0 at the end of the file.

GET-XY
( — x y )

Return the current screen cursor position. The converse of the Standard Forth word AT-XY.

/ALLOT
( n — )

Allot n bytes of space in the dictionary and initialize it to zeros (nulls).

4.7 Exceptions and Error Handling

Program exceptions in SwiftForth are handled using the CATCH/THROW mechanism of Standard Forth. This provides a flexible way of managing exceptions at the appropriate level in your program. This approach to error handling is discussed in Forth Programmer’s Handbook.

SwiftForth includes a scheme to provide optional warnings of potentially serious errors. These include redefinitions of Forth words and possible attempts to compile or store absolute addresses, which is often inappropriate; see Section 5.1.4 for a discussion of address management. You may configure SwiftForth to report only certain classes of warnings, or disable warnings altogether.

In addition, you may configure the way in which both warnings and error messages (from THROW) are displayed: either in the command window, in a separate dialog box, or both. (Even if warnings are disabled, error messages will always be displayed.) This configuration is handled by the Options > Warnings menu selection, discussed in Section 2.3.5.

At the top level of SwiftForth, the text interpreter provides a CATCH around the interpretation of each line processed. You may also place a CATCH around any application word that may generate an exception whose management you wish to control. Typically, CATCH is followed by a CASE statement to process possible THROW codes that may be returned; if you are only interested in one or two possible THROW codes at this level, an IF … THEN structure may be more appropriate.

As shipped, SwiftForth handles errors detected by the text interpreter’s CATCH by issuing an error message in a dialog box that must be acknowledged. You may add application-specific error messages if you wish, using the word >THROW. This word associates a string with a THROW code such that SwiftForth’s standard error handler will display that string if it CATCHes its error code. The code is returned to facilitate naming it as a constant. For example:

12345 S” You’ve been bad! ” >THROW CONSTANT BADNESS

Usage would be BADNESS THROW. In combination with ENUM (an incrementing constant, described in Section 4.6), >THROW can be used to define a group of THROW codes in your application. SwiftForth has predefined many internally used THROW codes, using the naming convention IOR_. You may see which are available using the Words dialog box specifying “Words that start with” IOR_; to see them all, set the Vocabulary to All.

To avoid renaming a particular constant, let SwiftForth continue to manage your throw code assignments. The VALUE THROW# records the next available assigned THROW code. So, you could define your codes this way:

THROW#
   S" Unexpected Error"         >THROW ENUM IOR_UNEXPECTED
   S" Selection unknown"        >THROW ENUM IOR_UNKNOWN
   S" Selection not available"  >THROW ENUM IOR_NOTAVAIL
   S" Value too high"           >THROW ENUM IOR_TOOHIGH
   S" Value too low"            >THROW ENUM IOR_TOOLOW
TO THROW#

This defines codes to issue the messages shown. You can use the names defined with ENUM to issue the errors; for example:

GET-SELECTION 0 10 WITHIN NOT IF IOR_UNKNOWN THROW THEN
>THROW
( n addr u — n )

Associate the string addr u with the throw code n such that SwiftForth’s standard error handler will display that string if it catches its error code. Throw code n is returned to facilitate naming it as a constant. See the example above.

SwiftForth’s error-handling facility includes an interface to the system exception handler. When a processor exception is generated, SwiftForth will generate a THROW. If your application catches such an exception, you can handle it like any other SwiftForth exception.

If the exception is generated from a word executed from the command line, SwiftForth will display a register and stack dump to help you debug the cause of the exception.

For example, a memory read from an illegal address will generate an access violation exception. This is the exception dump in SwiftForth-i386 for Windows:

-1 @ 
ACCESS_VIOLATION at 0041520F @
Registers     Dstack    esp+ Rstack
EAX 0000120F            0000 0041C515  (INTERPRETING) +70
EBX FFFFFFFF            0004 0041C627  INTERPRET +28
ECX 0041520F            0008 0041C783  (QUIT) +16
EDX 00414000            000C 004150EB  <<catch>>
ESI 00421678            0054 0041C829  QUIT +38
EDI 00414000
EBP 0019FF70
ESP 0019EF1C
EFL 00210206
EIP 0041520F

This is the exception dump in SwiftForth-x64 for macOS (the Linux one is nearly identical):

-1 ? 
Segmenation fault at 000000000000AEEB  ? 
Registers             Dstack            rsp+ Rstack  
RAX 0000000000008E9B                    0000 000000000000D35F  (INTERPRETING) +84 
RBX FFFFFFFFFFFFFFFF                    0008 000000000000D4B8  INTERPRET +29 
RCX 000000000000AEEB                    0010 000000000000D685  (QUIT) +18 
RDX 0000000000002050                    0018 0000000000003AFD  <<catch>> 
RSI 0000000000014068                    0090 000000000000D759  QUIT +46 
RDI 0000000000002050                    
RBP 00007FF7BFEFF8C0                    
RSP 00007FF7BFEFD830                    
R8  0000000000000053                    
R9  9E3779B97F4A7C55                    
R10 0000000000012413                    
R11 0000000000000246                    
R12 00000000070C33A0                    
R13 00007FF7BFEFFA78                    
R14 0000000000001E00                    
R15 00000000070AF010                    
FL  0000000000010206                    
RIP 000000000000AEEB