Forth Inspired Command Language | |
Author: John Sadler (john_sadler@alum.mit.edu) | |
Created: 19 July 1997 | |
Revision 2.0: 14 September 1998 |
Ficl (Forth-inspired command language) is an ANS Forth interpreter written in C. Unlike traditional Forths, this interpreter is designed to be embedded into other systems as a command/macro/development prototype language. |
Where Forths usually view themselves as the center of the system and
expect the rest of the system to be coded in Forth, Ficl acts as a component
of the system. It is easy to export code written in C or ASM to Ficl in
the style of TCL, or to invoke Ficl code from a compiled module. This allows
you to do incremental development in a way that combines the best features
of threaded languages (rapid development, quick code/test/debug cycle,
reasonably fast) with the best features of C (everyone knows it, easier
to support large blocks of code, efficient, type checking). In addition,
Ficl provides a simple object model that can act as an object oriented
adapter for code written in C (or asm, Forth, C++...).
|
Ficl Design goals
|
|
To install ficl on your target system, you need an ANSI C compiler
and its runtime library. Inspect the system dependent macros and functions
in sysdep.h and sysdep.c and edit them to suit your system.
For example, INT16 is a short on some compilers and an
int on others. Check the default CELL alignment controlled
by FICL_ALIGN. If necessary, add new definitions of ficlMalloc,
ficlFree, ficlLockDictionary, and ficlTextOut to work with
your operating system. Finally, use testmain.c as a guide to installing
the ficl system and one or more virtual machines into your code. You do
not need to include testmain.c in your build.
Feel free to stub out the double precision math functions (which are presently implemented as inline assembly because it's so easy on many 32 bit processors) with kludge code that only goes to 32 bit precision. In most applications, you won't notice the difference. If you're doing a lot of number crunching, consider implementing them correctly. Build controlsThe file sysdep.h contains default values for build controls. Most of these are written such that if you define them on the compiler command line, the defaults are overridden. I suggest you take the defaults on everything below the "build controls" section until you're confident of your port. Beware of declaring too small a dictionary, for example. You need about 3200 cells for a full system, about 2000 if you strip out most of the "soft" words.To-Do List (target system dependent words)
|
ficl.h | Declares most public functions and all data structures. Includes sysdep.h and math.h |
sysdep.h | Declares system dependent functions and contains build control macros. Edit this file to port to another system. |
math.h | Declares functions for 64 bit math |
words.c | Exports ficlCompileCore(), the run-time dictionary builder, and contains all primitive words as static functions. |
vm.c | Virtual Machine methods |
stack.c | Stack methods |
ficl.c | System initialization, termination, and ficlExec |
dict.c | Dictionary |
math64.c | Implementation of 64 bit math words (except the two unsigned primitives declared in sysdep.h and implemented in sysdep.c) |
softcore.c | Contains all of the "soft" words - those written in Forth and compiled by Ficl at startup time. Sources for these words are in the softwords directory. The files softcore.bat and softcore.pl generate softcore.c from the .fr sources. |
sysdep.c | Implementation of system dependent functions declared in sysdep.h |
softwords/ | Directory contains sources and translation scripts for the words defined in softcore.c. Softcore.c depends on most of the files in this directory. See softcore.bat for the actual list of files that contribute to softcore.c. |
Local VariablesFicl now includes support for LOCALS and LOCALS EXT words (all three of them!). I've implemented both of the local variable syntaxes suggested in DPANS Appendix A.13. Examples: (By the way, Ficl implements -ROT as : -rot 2 -roll ; )
: -rot ( a b c -- c a b ) locals| c b a | c a b ; \ Using LOCAL END-LOCAL : -rot ( a b c -- c a b ) local c local b local a end-locals c a b ; The default maximum number of local variables is 16. It's controlled by FICL_MAX_LOCALS in sysdep.h. Search OrderFicl implements many of the search order words in terms of two primitives called >SEARCH and SEARCH>. As their names suggest (assuming you're familiar with Forth), they push and pop the search order stack. See the list of Ficl extras for details.The standard does not appear to specify any conditions under which the search order is reset to a sane state. Ficl resets the search order to its default state whenever ABORT happens. This includes stack underflows and overflows. QUIT does not affect the search order. The minimum search order (set by ONLY) is equivalent to FORTH-WORDLIST 1 SET-ORDER There is a default maximum of 16 wordlists in the search order. This can be changed by redefining FICL_DEFAULT_VOCS (declared in sysdep.h). Soft WordsMany words from all the supported wordsets are written in Forth, and stored as a big string that Ficl compiles when it starts. The sources for all of these words are in directory ficl/softwords. There is a .bat file (softcore.bat) and a PERL 5 script (softcore.pl) that convert Forth files into the file softcore.c, so softcore.c is really dependent on the Forth sources. This is not reflected in the Visual C++ project database. For the time being, it's a manual step. You can edit softcore.bat to change the list of files that contribute to softcore.c. |
Ficl is not the first Forth to include Object Oriented extensions.
Ficl's OO syntax owes a debt to the work of John Hayes and Dick Pountain,
among others. OO Ficl is different from other OO Forths in a few ways,
though (some things never change). First, unlike several implementations,
the syntax is documented (below) beyond the source
code. In Ficl's spirit of working with C code, the OO syntax provides means
to adapt existing data structures. I've tried to make Ficl's OO model simple
and safe by unifying classes and objects, providing late binding by default,
and separating namespaces so that methods and regular Forth words are not
easily confused.
Design goals of Ficl OO syntaxFicl's object extensions provide the traditional OO benefits of associating data with the code that manipulates it, and reuse through single inheritance. Ficl also has some unusual capabilities that support interoperation with systems written in C.
|
Ficl Object ModelAll classes in Ficl are derived from the common base class OBJECT. All classes are instances of METACLASS. This means that classes are objects, too. METACLASS implements the methods for messages sent to classes. Class methods create instances and subclasses, and give information about the class. Classes have exactly three elements:
Note for the curious: METACLASS behaves like a class - it responds
to class messages and has the same properties as any other class. If you
want to twist your brain in knots, you can think of METACLASS
as an instance of itself.
|
Ficl OO Syntax TutorialIntroductionFicl objects associate a class with an instance (really the storage for one set of instance variables). This is done explicitly, in that any Ficl object is represented by the cell pair:
Classes are special kinds of objects that store the methods of their instances, the size of an instance's payload, and a parent class pointer. Classes themselves are instances of a special base class called METACLASS, and all classes inherit from class OBJECT. This is confusing at first, but it means that Ficl has a very simple syntax for constructing and using objects. Class methods include subclassing (SUB), creating initialized and uninitialized instances (NEW and INSTANCE), and creating reference instances (REF). Classes also have methods for disassembling their methods (SEE), identifying themselves (ID), and listing their pedigree (PEDIGREE). All objects inherit methods for initializing instances and arrays of instances, for performing array operations, and for getting information about themselves. Methods and messagesMethods are the chunks of code that objects execute in response to messages. A message is a request to an object for a behavior that the object supports. When it receives a message, the target object looks up a method that performs the behavior for its class, and executes it. Any specific message will be bound to different methods in different objects, according to class. This separation of messages and methods allows objects to behave polymorphically. (In Ficl, methods are words defined in the context of a class, and messages are the names of those words.) Ficl classes associate names with methods for their instances. Ficl provides a late-binding operator --> that sends messages to objects at run-time, and an early-binding operator => that compiles a specific class's method. These operators are the only supported way to invoke methods. Regular Forth words are not visible to the method-binding operators, so there's no chance of confusing a message with a regular word of the same name.Tutorial (finally!)Since this is a tutorial, I'm assuming you're following along by typing the examples into ficlWin, the Win32 version of Ficl (or some other build that includes the OO part of softcore.c). I also assume that you're familiar with Forth. If not, please see one of the references, below. Ficl's OOP words are in vocabulary OOP, which is in the search order and is the compile wordlist when you start one of the executables from the release. To get to this state from the default search order (as set by ONLY), type:
4 chars: m_chars : init ( inst class -- ) locals| class inst | 0 inst class --> m_cell1 ! inst class --> m_chars 4 0 fill ." initializing an instance of c_foo at " inst x. cr ; end-class The init method is special for Ficl objects: whenever you create
an initialized instance using new or new-array,
Ficl calls the class's init method for you on that instance. The
default init method supplied by class object clears the
instance, so we didn't really need to override it in this case (see the
source code in ficl/softwords/oo.fr).
foo-instance --> pedigree
or foo-instance --> class --> see init
metaclass => instance --> init ; metaclass --> see new Notice that the early-binding operator requires a class at compile time. For this reason, classes are IMMEDIATE, meaning that they push their signature at compile time or run time. I'd recommend that you avoid early binding until you're very comfortable with Forth, object-oriented programming, and Ficl's OOP syntax. As advertised earlier, Ficl provides ways to objectify existing data
structures without changing them. Instead, you can create a Ficl class
that models the structure, and instantiate a ref from this class,
supplying the address of the structure. After that, the ref instance behaves
as a Ficl object, but its instance variables take on the values in the
existing structure. Example (from ficlclass.fr):
cell: .parent cell: .size cell: .hash : push drop >search ;
: named-wid ( "name" -- )
wordlist postpone c-wordlist --> ref ;
my-wordlist --> set-current order Ficl can make arrays of instances, and aggregate arrays into class descripions. The class methods array and new-array create uninitialized and initialized arrays, respectively, of a class. In order to initialize an array, the class must define a reasonable init method. New-array invokes it on each member of the array in sequence from lowest to highest. Array instances and array members use the object methods index, next, and prev to navigate. Aggregate a member array of objects using array:. The objects are not automatically initialized in this case - your class initializer has to call array-init explicitly if you want this behavior. For further examples of OOP in Ficl, please see the source file ficl/softwords/ficlclass.fr. This file wraps several Ficl internal data structures in objects and gives use examples. Instance Variable GlossaryNote: these words are only visible when creating a subclass! To create a subclass, use the sub method on object or any class derived from it (not metaclass). Source code for Ficl OOP is in ficl/softwords/oo.fr.
c-4byte obj: .nCells 4 c-4byte array: .quad char: .length 79 chars: .name end-class
Class Methods Glossary
object base-class Methods Glossary
Supplied Classes (See classes.fr)
|
ANS Forth System
Providing names from the Core Extensions word set Providing the Locals word set Providing the Locals Extensions word set Providing the Programming-Tools word set Providing names from the Programming-Tools Extensions word set Providing the Search-Order word set Providing the Search-Order Extensions word set Implementation-defined OptionsThe implementation-defined items in the following list represent characteristics and choices left to the discretion of the implementor, provided that the requirements of the Standard are met. A system shall document the values for, or behaviors of, each item.
System dependent. You can change the default address alignment by defining FICL_ALIGN on your compiler's command line. The default value is set to 2 in sysdep.h. This causes dictionary entries and ALIGN and ALIGNED to align on 4 byte boundaries. To align on 2n byte boundaries, set FICL_ALIGN to n. Depends on target system, C runtime library, and your implementation of ficlTextOut(). None implemented in the versions supplied in words.c. Because ficlExec() is supplied a text buffer externally, it's up to your system to define how that buffer will be obtained. Depends on target system and implementation of ficlTextOut() Ficl characters are one byte each. There are no alignment requirements. No special processing is performed on characters beyond case-folding. Therefore, extended characters will not match their unaccented counterparts. Ficl uses the Standard C function isspace() to distinguish space characters. The rest is up to your library vendor. Uses the data stack The maximum supported value of BASE is 36. Ficl will assertion fail in function ltoa of vm.c if the base is found to be larger than 36 or smaller than 2. There will be no effect if NDEBUG is defined, however, other than possibly unexpected behavior. Target system dependent Does ABORT Target system dependent (implementation of outer loop that calls ficlExec) 255 Limited by available memory and the maximum unsigned value that can fit in a CELL (232-1). Ficl stores the first 31 characters of a definition name. Same as maximum definition name length None supported. This is up to the target system None supported. This is up to the target system Target system dependent. Ficl generally supports processors that can address 8 bit quantities, but there is no dependency that I'm aware of. System dependent. Ficl represents a CELL internally as a union that can hold INT32 (a signed 32 bit scalar value), UNS32 (32 bits unsigned), and an untyped pointer. No specific byte ordering is assumed. Assuming a 32 bit implementation, range for signed single-cell values is -231..231-1. Range for unsigned single cell values is 0..232-1. Range for signed double-cell values is -263..263-1. Range for unsigned single cell values is 0..264-1. None Default is 255. Depends on the setting of nPAD in ficl.h. System dependent, generally four. System dependent, generally one. This buffer is supplied by the host program. Ficl imposes no practical limit. Default is 255 characters. Depends on the setting of nPAD in ficl.h. Not presently supported Ficl is not case sensitive "ok>" Symmetric One (no others) System dependent. Ficl makes no special checks for overflow. No. Definitions are unsmudged after ; only, and only then if no control structure matching problems have been detected. Ambiguous ConditionsA system shall document the system action taken upon each of the general or specific ambiguous conditions identified in this Standard. See 3.4.4 Possible actions on an ambiguous condition.The following general ambiguous conditions could occur because of a combination of factors:
Ficl does ABORT and prints the name followed by " not found". Ficl stores the first 31 characters of the definition name, and uses all characters of the name in computing its hash code. The actual length of the name, up to 255 characters, is stored in the definition's length field. No problem: all addresses in ficl are absolute. You can reach any 32 bit address in Ficl's address space. Ficl makes no check for argument type compatibility. Effects of a mismatch vary widely depending on the specific problem and operands. Ficl returns a valid token, but the result of executing that token while interpreting may be undesirable. Results are target procesor dependent. Generally, Ficl makes no check for divide-by-zero. The target processor will probably throw an exception. With FICL_ROBUST (sysdep.h) set >= 2, most parameter stack operations are checked for underflow and overflow. Ficl does not check the return stack. No check - Evil results. Ficl generates an error message if the dictionary is too full to create a definition header. It checks ALLOT as well, but it is possible to make an unchecked allocation request that overflows the dictionary. Ficl protects all ANS Forth words with undefined interpretation semantics from being executed while in interpret state. It is possible to defeat this protection using ' (tick) and EXECUTE, though. Varies depending on the nature of the buffer. The input buffer is supplied by ficl's host function, and may reside in read-only memory. If so, writing the input buffer can ganerate an exception. String literals are stored in the dictionary, and are writable. In the unlikely event you are able to construct a pictured numeric string of more than 255 characters, the system will be corrupted unpredictably. The buffer area that holds pictured numeric output is at the end of the virtual machine. Whatever is mapped after the offending VM in memory will be trashed, along with the heap structures that contain it. Ficl does not copy parsed strings unless asked to. Ordinarily, a string parsed from the input buffer during normal interpretation is left in-place, so there is no possibility of overflow. If you ask to parse a string into the dictionary, as in SLITERAL, you need to have enough room for the string, otherwise bad things may happen. This is not usually a problem. Value will be truncated Most stack underflows are detected and prevented if FICL_ROBUST (sysdep.h) is set to 2 or greater. Otherwise, the stack pointer and size are likely to be trashed. Ficl returns for a new input buffer until a non-empty one is supplied.
Bad Things occur - unpredictable bacause the input buffer is supplied by the host program's outer loop. It finds the address of the definition before DOES> Not implemented This is OK until the cells are overwritten with something else. The dictionary maintains a hash table, and the table must be updated in order to de-allocate words without corruption. Target processor dependent. Consequences include: none (Intel), address error exception (68K). See above on data space read/write alignment Ficl detects a stack underflow and reports it, executing ABORT, as long as FICL_ROBUST is two or larger. Loop initiation words are responsible for checking the stack and guaranteeing that the control parameters are pushed. Any underflows will be detected early if FICL_ROBUST is set to two or greater. Note however that Ficl only checks for return stack underflows at the end of each line of text. No problem. Ficl's version of TO works correctly with VALUEs, CONSTANTs and VARIABLEs. Ficl prints an error message and does ABORT No check. Results vary depending on the specific problem. The word is postponed correctly. Ficl stores the first FICL_STRING_MAX-1 chars in the destination buffer. (The extra character is the trailing space required by the standard. Yuck.) Depends on target process or and C runtime library implementations of the << and >> operators on unsigned values. For I386, the processor appears to shift modulo the number of bits in a cell. words improperly used outside 6.1.0490 <# and 6.1.0040 #> (6.1.0030 #, 6.1.0050 #S, 6.1.1670 HOLD, 6.1.2210 SIGN) Don't. CREATE reserves a field in words it builds for DOES> to fill in. If you use DOES> on a word not made by CREATE, it will overwrite the first cell of its parameter area. That's probably not what you want. Likewise, pictured numeric words assume that there is a string under construction in the VM's scratch buffer. If that's not the case, results may be unpleasant. Locals Implementation-defined options
Default is 16. Change by redefining FICL_MAX_LOCALS, defined in sysdep.h Locals Ambiguous conditions
Locals can be found in interpretation state while in the context of a definition under construction. Under these circumstances, locals behave correctly. Locals are not visible at all outside the scope of a definition. See the CORE ambiguous conditions, above (no change) Programming Tools Implementation-defined options
SEE de-compiles definitions from the dictionary. Because Ficl words are threaded by their header addresses, it is very straightforward to print the name and other characteristics of words in a definition. Primitives are so noted. Colon definitions are decompiled, but branch target labels are not reconstructed. Literals and string literals are so noted, and their contents displayed. Search Order Implementation-defined options
Defaults to 16. Can be changed by redefining FICL_DEFAULT_VOCS, declared in sysdep.h Equivalent to FORTH-WORDLIST 1 SET-ORDER Search Order Ambiguous conditions
Ficl stores a link to the current definition independently of the compile wordlist while it is being defined, and links it into the compile wordlist only after the definition completes successfully. Changing the compile wordlist mid-definition will cause the definition to link into the new compile wordlist. Ficl prints an error message if the search order underflows, and resets the order to its default state. Ficl prints an error message if the search order overflows, and resets the order to its default state. |
Ficl is freeware. Use it in any way that you like, with the understanding
that the code is not supported.
Any third party may reproduce, distribute, or modify the ficl software code or any derivative works thereof without any compensation or license, provided that the original author information and this disclaimer text are retained in the source code files. The ficl software code is provided on an "as is" basis without warranty of any kind, including, without limitation, the implied warranties of merchantability and fitness for a particular purpose and their equivalents under the laws of any jurisdiction. I am interested in hearing from anyone who uses ficl. If you have a problem, a success story, a defect, an enhancement request, or if you would like to contribute to the ficl release (yay!), please send me email at the address above. |