Appendix E

Style Guidelines


Naming Conventions

Forth allows ultimate flexibility in the naming of words. For this reason, it is very important that the programmer be consistent in their naming. Naming conventions can provide a road map to your code. If you ever have to go back and revisit code months, or years, after writing it, you will be thankful if you used systematic naming.

This section will provide some of the naming conventions used in JForth and offer suggestions for your own naming. Since JForth naming is the combination of naming conventions from 3 developers, plus the standard Forth names, you may notice deviation from these suggested standards in JForth. These suggestions will be more rigidly adhered to in the code I wrote since I often take my own advice. Here goes:

Use Prefixes to Mark Related Code

By a 2 or 3 letter prefix to all the words of a given system, you can clearly identify those words when they appear in other code. This also helps eliminate naming conflicts if the prefixes are unique. Examples in JForth are:

GRxxx - for GRaphics words.

WD_xxx - for members of the WinDow structure
DBxxx - for internal Debugger Words
$xxx - for string related words.

Use Signature Characters to indicate a word's function

You can often place characters in the name that indicate what a given word does. Examples are:

4+ - adds 4
CONSOLE! - set pointer in CONSOLE variables.
GR.COLOR! - set graphics color (store in RastPort)

(xxx) - parentheses usually indicate an internal function that is called from a deferred word, or that is compiled for run time action. Sometimes <> and [] are used. Examples are:

(EMIT) - simplest EMIT word.
(($")) - run time action of ".

{ and } are handy for marking words that are balanced. An example is:

DEBUG{ : FOO DUP + ; }DEBUG

Use Separators that indicate the Type of Word

Compound words can use a separator that give you some idea of what the word is. The separators I use are:

- "hyphen" for variables, objects, and other data structures that leave an address on the stack. These will generally be followed by a @ or !. Examples are:

HIGHLIGHT-INPUT    GR-CURWINDOW    MAX-INLINE DL-LINENUM 

_ "underscore" for constants, values, or other words that leave their actual value on the stack. Examples are:

OFFSET_BEGINNING    KH_HISTORY_SIZE    MEMF_CLEAR

. "dot" for action words, verbs. Examples are:

GR.DRAW    DEBUG.START    PIC.LOAD

Use full names for obscure words, short names for common words

Short names are good for common words because no one likes to type long names all the time. For rarely used words, however, you should use long names because you are less likely to remember what they do. There is also less likelihood of naming conflicts with long names. Remember when an application is cloned the names are removed and do not add size to the final image.

Writing Style

Here are some random thoughts on programming style.

Beware of compile time initialization

If your program needs to open files, allocate memory, build jump tables or do anything with addresses, please put that code in a colon definition. And please do not call that word in the file itself. Doing so might seem like a handy little trick but it will bite you if you do a SAVE-FORTH or try to CLONE your program. Opening files, and allocating memory generate addresses that are only valid for that time. If you do a SAVE-FORTH or CLONE you may save an address that will not be valid the next time you use it.

Here is an example of some BAD code in a file:

VARIABLE MYFILE
VARIABLE MYMEM
FOPEN RAM:DATA MYFILE ! \ Bad! Bad programmer!
MEMF_CLEAR 2000 ALLOCBLOCK MYMEM ! \ Very Bad!

Here is an example of doing it right. Notice that we also check for error flags on initialization, and provide for automatic if we forget the code. Using these techniques will save you from many puzzling errors.

VARIABLE MYFILE
VARIABLE MYMEM

: DOITRIGHT ( -- error? , "Good programmer! Here bisquit.")
TRUE \ default error return
" RAM:DATA" $FOPEN ?DUP \ did it work
IF
MYFILE !
MEMF_CLEAR 2000 ALLOCBLOCK ?DUP
IF
MYMEM !
DROP FALSE \ change error?
THEN
THEN
;

: CLEANUP ( -- )
MYFILE FCLOSEVAR
MYMEM FREEVAR
;

IF.FORGOTTEN CLEANUP

Write short words

Chuck Moore first pointed out that short definitions give you the most flexibility. Nothing is worse than a word that does TOO much. If you want part , but not all, of what a word does you cannot use it. It is useless. But if a word does only part of what you need you can add the rest.

Place Stack Diagrams on Every Word

and sometimes inside words. It is nearly impossible to read someone else's code if there are no stack diagrams. I have also seen people trying to write a word when they don't know what its stack diagram should be, another impossible task.

Indent Paired Words to the Same Level

This really helps improve the readability of code and can prevent many common mistake. I have come to believe that IF, ELSE ,THEN, BEGIN, WHILE, REPEAT, UNTIL, CASE and ENDCASE should always be on their own line and cause a change in indentation level. Here is an example of the suggested indentation style.

: FOO ( a -- b , do something )
DUP 21 < ( -- a flag, generate flag on previous line
IF
0 ( -- a 0 )
DO I . CR
LOOP ." All done!" CR
ELSE ( a -- , good place for stack diagram )
100 >
IF ." A really big!" CR
THEN
THEN
;

Use >R R@ R> or Local Variables -

to avoid excessive stack dancing. Too much DUP SWAP DROP will ROT your brain.

Write your Code Backwards using "Top Down" programming

Start by writing the top word in your application using an English like set of words. Once this makes sense, write the next lower level, and so on. Forth is enough like English that your "pseudo-code" can end up being legal Forth by the time you are done. Forth is typically thought of as a "bottom up" language because it is so easy to hack in. This is fine for pure experimentation but big projects demand the discipline of "top down" coding.

Initialization, Action, and Termination

Divide each module of your program into three parts: Initialization, Action, and Termination. This will greatly simplify debugging because you will be able to completely initialize the system and then examine it before taking any action. It also helps to organize your code. Initialization typically consists of things like opening files, allocating memory, opening windows, or creating gadgets.

Transportability Techniques

by Brian Donovan

This appendix describes some techniques to use for writing transportable high level Forth code.

The CELL concept

In Forth programs, you often want to skip over a single precision unit of data, or several single precision units of data, called "cell"s in Forth. Until recently, most Forth programmers were explicitly using the numeric count needed on the machine they happened to be using. This meant that on a 16 bit 8086 Forth they would use a 2+, on a 68000 32 bit Forth, like JForth, they would use 4+ , and on a NOVIX Forth CPU with word addressing, they would use 1+ . For subtraction, multiplication and division by the single precision unit addressing size, the same situation would arise, thus, all programs written that way would be totally non transportable. A very simple and low overhead solution to this problem is to use CELL+ CELLS CELL/ CELL- and CELL instead of 1+ 2+ 4* etc.. whenever the program is working with cells. We have painlessly used the same source for 16 bit and 32 bit computers of many types, and have found that programs written with CELL are much less obscure to read.

In JForth: CELL = 4

One way to automatically assign CELL size in your programs is as follows:

SP@ SP@ - ABS CONSTANT CELL

: CELL+ CELL + ;
: CELL- CELL - ;
: CELLS CELL * ;
: CELL/ CELL / ;

With conditional compilation, you can pick out the optimal speed words for functions like CELLS and CELL/ . In JForth, all these words are machine coded.

INLINE concept

In more advanced Forth programs, programmers often want to create new inline data words (like ." ). With the advent of segment threaded Forth, relative address Forths ( like JForth ) , and other new ways of implementing Forth, The top return stack item does not necessarily point data following the called word ( inline data ). We use a slow version of LIT to illustrate some of the problems.

This is slow LIT the way it would be in absolute addressing, address threaded Forth ( the good old standard unfancy Forth on a Z80 for example.)

: SLOW-LIT  ( --- N )  ( CELL --INLINE-- )
R@ @ R> CELL+ >R ;

SLOW-LIT on a relative addressing Forth ( like JForth) :

: SLOW-LIT   R@ >ABS @ R> CELL+ >R ;

SLOW-LIT on some segment threaded 32 BIT, 8086 Forths:

: SLOW-LIT-SEG   R@  SEG+OFF>ADDR  @
R> SEG+OFF>ADDR CELL+ ADDR>SEG+OFF >R ;

...pretty bad ? You would have to rewrite any section that uses inline data, when you changed Forths. You can avoid this problem by using the INLINE words: INLINE+ INLINE@ >INLINE and INLINE> ( and adding them to other systems, as appropriate. ) you can write fully transportable inline code:

: SLOW-LIT  ( --- N )  INLINE@ @ CELL INLINE+ ;

This definition will work on ALL of the Forths I have over encountered, as long as you add the appropriate 4 inline definitions to the dictionary first. by the way, SLOW-LIT is used as follows:

: SLOW-LITERAL   ( N --- )
COMPILE SLOW-LIT , ; IMMEDIATE
: EXAMPLE [ 5 ] SLOW-LITERAL ;

You can always add these definitions to a system that doesn't have them. The definitions are very implementation dependent.

For an old fig Forth system they would be the simplest:

: INLINE+  ( N --- )  ( ADDR --R-- ADDR+N )
COMPILE R> COMPILE + COMPILE >R ; IMMEDIATE
: INLINE> COMPILE R> ; IMMEDIATE
: >INLINE COMPILE >R ; IMMEDIATE
: INLINE@ COMPILE R ; IMMEDIATE

In JForth the definitions are slightly more complicated, and on some of the 32 bit segment threaded Forths, they are fairly ugly. But once written, all the rest of your code flies!

You may be confused by a word in JForth, INLINE , which has little to do with the inline words above. INLINE sets a flag in a words header, telling the compiler that this word must be compiled inline, it cannot be called ( for instance >R ) .

Don't Use JForth Internal Words

Avoid using JForth internal words unless you can reconstruct them from scratch.

Understand Unique Features Before Using Them

Unfortunately, you also have to avoid using some of the nice JForth utilities unless you can reconstruct them as well. At least we will keep the JForth utilities around. All of the JForth unique programs are copyrighted, but many are available for distribution without charge. If the file does not specifically state, that it may be freely distributed, than you must get our permission to distribute it elsewhere. We encourage you to take the CELL and INLINE concepts and programs anywhere you want. Many of the JForth utilities are available on other Forths as well, and we made every attempt at compatibility with other Forths where this is appropriate. Some programs that you need permission to distribute are: ODE ASM DISM MULTISTANDARD LOCALS

Delta Research is actively helping to establish better standards, particularly for high-end Forth systems.

Conditional Compilation

Use the conditional compilation words, .IF .ELSE .THEN .NEED INCLUDE? , to adapt to different systems automatically. Usually the differences between systems are small enough to allow one source to deal with all target systems. Major sections that are different between targets, can be put into separate files, and conditionally loaded with INCLUDE? . Unfortunately, The Forth community has not standardized the names for the conditional compilation words. JForth uses the same names as LMI Forth. ( The '83 standard include a SUGESTED set of names that we think are unacceptable: IFTRUE OTHERWISE IFEND ). The words we have used are easy to remember, since they work like the familiar IF ELSE THEN words all Forth programmers know so well. The "." sets them apart from the ordinary conditionals as well. We would have preferred [IF] [ELSE] [THEN] to emphasize the fact that these words work in a different state, but we felt it was better to use an existing acceptable set of words.

Multistandards

If you need to compile code that conforms to the FIG, Forth'79, or Forth'83 standard, then INCLUDE the file JU:MULTISTANDARDS.




© 1998-2023 Martin Randall