OpenVMS runs on both DEC VAX and DEC Alpha computer systems. VAX systems typically use VAX-11C and Alpha systems use DEC-C. There are enough differences in the two compilers that separate DCL command files are supplied for each. The files COMPILE.COM and LINK.COM build Until using VAX-C. COMPILEA.COM and LINKA.COM build Until with DEC-C. Use the appropriate command files for your configuration.
COMPILE.COM compiles the modules in Until and a separate command file, LINK.COM, links the compiled object files. Type @COMPILE to compile the source. After the compile process finishes, type @LINK to run the linker and create the Until executable image. LINK.COM produces UNTIL_V.EXE and LINKA.COM creates UNTIL_A.EXE.
Most of the configuration of the Until executable is done via symbols in COMPILER.H. The configuration symbols are:
Additionally, symbols that specify the compiler are set. #define the symbol to cause code to be included in Until or #undef the symbol to ignore it.
A second approach is no local variables and either directly manipulate the pstack[] array, which eliminates both local variables and the calls to popsp() and pushsp(). This is more efficient, but much less readable.
A third approach in many cases is a nested pushsp() call with the entire function coded in a single line. This also uses no local variables.
The original approach to Until code was to make all of the source code as readable as possible, at the expense of efficiency. The time has now come to start optimizing Until. I am told that some C compilers do not create a stack frame if no local variables are used, which should result in even faster code. #define READABLE to compile the 'readable' version of the code. Otherwise, more efficient code will be substituted. There are only a few code optimizations in this version of Until. Expect more and more to be added with each release.
This section gives some hints for program development with Until. As I use Until more and more, the more I believe that Until is a unique and very powerful approach to program development. There are several possible approaches for developing Until applications. These include:
Because of the completely different Until development options, System development must be approached from a slightly unconventional approach. (Does this surprise you?) The first decision is which approach to follow. Is application is going to be written directly in Until or to use Until as a macro language only. If you choose to write code in C, the question of coding style arises. You can make the C code look a lot like Forth. After all, dup(), over(), swap(), etc are all available as C functions. Or do you manipulate the data structures directly. Someone who knows Forth and not C will probably choose to use Until as merely another Forth compiler and the fluent C programmer will probably code large portions in C.
The absolute minimum needed to run Until is until.exe. Running an application written in Until requires until.exe and an application file; applic.app by default. The application file can be specified on the command line or sealed into until.bin.
An application is generally kept in a .APP file during development then sealed into a .BIN file for distribution. The advantages of sealing an application include:
The process of sealing an application is relatively simple. First, run Until. At the initial program prompt, type:
seal applic.app
applic.bin is created from applic.app.
The bibliography identifies several good Forth resources, including books. Forth is a simple stack oriented, interactive programming language. It uses an RPN syntax ala HP calculators. General practice includes writing small words that do only one function, such as open a file or comparing a string. Test each word individually. In other words, write a few lines of code, then test it. That way, Forth words become building blocks that construct your application. This is an incredibly productive approach once you get past the learning curve.
Until is integrated with the OS from the Forth point of view. Program development can be done entirely within Until provided you are writing Forth code. Source can be edited, compiled, and tested without exiting back to the native OS as long as you don't crash the system. The following commands are available for development:
See the section on Operating System Words for more OS specific words.
Most Forth compilers includes a word, DOES> that is used to create new classes of words. For example:
create array 4 * allot does> 4 * @ + ;
builds a new defining word, array, that defines an array of integers. An array is the defined as:
10 array vector
40 bytes of memory would be allocated in the dictionary at compile time. At run time, the array entry number is converted to the address of the nth element and returned to the stack. Thus,
2 vector
returns the address of the third element in the array. (The array is zero based in the example.)
The magic of Forth's create ... does> is the creation of new word types or classes. Words that use create ... does> compile a new word that has separate compile-time (from the create to the does>) and run-time (from the does> to the end of the definition).
In Until, the approach is writing a C function for the compile-time action and a separate function for run-time. The compile-time function, something like Compile_array() is bound to array as an immediate word by calling build_iprim() in USER.C. The run-time C function is bound to the Until word via build_prim(), also in USER.C.
Until's equivalent to DOES> is not quite as convenient traditional Forth's, but it gets the job done!
Writing applications from the C prospective can lead to innovative applications. This approach allows those more comfortable with C than Forth to effectively utilize some of Forth's interactive nature. The bulk of your application code is coded as normal C functions using the proscribed interface to Until. (See Chapter 10 of [SMITH1] for more detailed discussions about adding new C primitives to Until.) pushsp() and popsp() are used to pass parameters from Forth to C functions.
Until is designed from the ground up to be embedded in other application programs. A particularly good use is letting Until be the ``macro'' language for an application. If conditionals and looping are not required, much of the internal Until code can be eliminated. The user could build what are logically equivalent to display or execution list macros. These macros are really Forth words, but the RPN nature of Forth can easily be hidden from the user. One application I built had only about a dozen primitives available to the user, but the whole application was driven by user written macros. It was an elegant and simple solution. The final executable file was only 37k after removing unused internal Until code.
The mechanics of embedding Until in an application is covered in [SMITH2] and therefore only mentioned in passing here.
The process is almost as simple as it sounds. However, please keep the application code completely separate from Until code. Simply providing a couple of interface points is all that is really necessary. The next major release of Until will re-arrange the files slightly to make this a natural thing to do. The files LOCAL.H and LOCAL.C are the first step in this direction. There are several reasons for separating the code. These include:
When writing small applications it is easy to add a couple of variables to LOCAL.H and a function or two to LOCAL.C. I did this on the first large application that I built up on top of Until. I made a special effort to go back and clearly separate the Until code from the application code. That way, I could supply an Until object library instead of source code to the customer. Besides, it is just good programming practice.
The ease with which new primitives can be added to Until is and will probably remain one of the primary advantages that it has over any of its rivals; a new primitive can be added in just a couple of minutes excluding C compile time.
#### UPDATE ####
This section contains the second cut at detailed instructions for embedding Until in another application. The problem is that with every release, things evolve. So, while the details published with the previous release may not be wrong, they are not 100% up to date at this point.
One big change since V2.2 is the addition of a hook at the end of the Outer Interpreter to call a user supplied function. This provides a convenient place to perform initialization after an ABORT.
Note that each call to outer() does a warm start, which clears the parameter stack. Until's main() is set up this way. Refer to it as an example.
Application development style is mentioned in the main description for this chapter. This section attempts to address style is some detail. Until is unique and takes some additional thought to take fullest advantage of what we have here for developing applications.
The picture in my mind is going to be hard to describe. When writing a Forth program, the programmer sits at the computer with the vendor supplied Forth manual and maybe a copy of Starting Forth for reference when coding.
A C programmer also has reference material close at hand too, probably a copy of The C Programming Language and the function library manual when coding. When coding in either language deciding on program partitioning is a matter of figuring out what module a word or function logically belongs in.
Developing an Until application that takes fullest advantage of the marriage between Forth and C has an additional level of development decisions. Should this operation be a Forth word or a C function? How are the C and Forth pieces going to interface? What primitives should the user be allowed to see? Conscious effort is required for language choice on a function by function basis, instead of once for the whole application. I believe this is a very powerful combination with a great deal of potential, but it is an art for the time being.
Part of the ``art'' is deciding what Forth words the user can access to build application macros. Some applications require a relatively complete language but, many can get by with only a handful. One system I built around Until contained only eight to ten words. The system was customizable, quite powerful, and simple from the user prospective.
Since Until based applications are really C programs with user macros (at least that is what the customer thinks...), calling the user level word set a ``language'' has always had bad connotations in my mind. First, I don't want the customer asking me what the macro language is. Secondly, calling it an application language will probably confuse more users than help describe what it is.
While rereading Thinking Forth [BRODIE1], I ran across the term ``lexicon''. Brodie uses lexicon to describe the application specific set of words the user has access to. In my mind, it describes exactly the situation of Until being embedded in an application. The user has a lexicon of application specific commands to construct macros with. Learning a few simple commands is perceived as much easier than learning a new language by users.
The point of this whole rambling dissertation is that designing the user lexicon is an important part of building an application around Until. In turn, I believe this is where much of the potential power of Until as an application development tool comes from.