!!!FROM PASCAL TO FORTH Leonard Morgenstern

Presented at Asilomar, November 1989. Slightly modified.

[{TableOfContents }]


!!ABSTRACT

Fundamental differences in viewpoint between Pascal
and Forth are more important than certain obvious dissimilarities in
syntax.  Pascal is conventional, with a mode of thinking that
derives from algebraic notation. Forth is "organic," growing out of
the processor and operating system, and permits the stepwise
development of complex programming features.


!!INTRODUCTION

The stimulus for writing this article was my recent
purchase of Sedgwick's Algorithms with the aim of translating them
from the  authors Pascal into Forth. I quickly discovered that
certain obvious and large-seeming differences between the two
languages are mere syntactical variations whose effect is one of
inconvenience, while less obvious features that derive from a
divergence in viewpoint present more of a problem.

An example is array limits: Forth conventionally
starts with 0, whereas Sedgwick usually uses 1. Translation is
straightforward, although it may not always be easy to decide
whether a 1 is the index of the first element of an array and can be
changed to 0; or whether it is the quantity 1  that must not be
altered. Less evident are profound dissimilarities between Forth and
Pascal arrays, discussed in detail below. Similar comments apply to
reverse Polish Notation, do loop limits, and Forths stacks.

Pascal, C, Fortran, and BASIC, are conventional
languages one might say that they are all one language, ultimately
derived from algebraic notation. C in particular was designed to
conform to an abstract plan. Forth is genuinely different. It is
organic, in the sense that it grows out of its environment (the
microprocessor and operating system) in response to the practical
and aesthetic needs of the programmer. Forth can be programmed so
that it becomes a language specially written for your application. This fact, of which
all Forth programmers are aware, even if subliminally, is a prime
source of difficulty in establishing a Forth standard. Among major
differences between Forth and Pascal are the following:

1. Traditional languages have three levels:
microprocessor, language, and application. These distinctions
disappear in Forth. Although it is convenient to refer to words
written in assembler as primitive,  or low level, there is no real
difference between them and high level words, once they have been
defined. A new word becomes part of Forth, the equal of all other
words, and the user may ignore its supposed level.

2. Flexibility: Defining words can create new
classes of words as needed. An example of how rather simple Forth
can provide for actions difficult or impossible in Pascal will be
given.

3. Structure: Pascals structures are rigid. Once
incorporated in the language, they are violated only with great
difficulty.

4. Abstractness: As noted, Pascal is designed to an
abstract plan, whereas Forth's features are developed by the
programmer to resolve practical necessities (although it can be made
as abstract as one likes.)


!!STRUCTURE IN FORTH AND PASCAL

A good example of the philosophical differences
between Forth and Pascal is programming structure,  the canons of
which were established by Wirth, Dykstra and others in response to
the spaghetti code that resulted from the indiscriminate use of
goto. The success of the scheme is its justification: individual
programmers could now write segments independently and have them fit
together on the first try. But, structure can be a hindrance. Even
though it can be proved mathematically that a few simple structures
can do anything, it sometimes requires more effort than it is worth
merely to support a principle, when simple controlled violations are
easily defined in Forth. The most familiar instance is  breaking out
of a DO loop with LEAVE.

Exception handling is an area where strict
conformity to the canons of structure presents a formidable
challenge, whereas unstructured methods are easy to apply. Error
traps are, in effect, gotos (more accurately,  go_back_tos) and are
inherently unstructured. ABORT is the ultimate example, exiting the
program and restarting Forth, but less drastic action can be
achieved by various published schemes.

But Pascals avoidance of goto is not merely
aesthetic. Pascals stack is invisible to the programmer, and to jump
out of a structure may have unpredictable side effects. For this
reason, many Pascals do not even have goto, and those that do often
warn users (correctly) not to use it. Forths stacks are always open
to view, and undesirable consequences can be anticipated and evaded.


!!USING AND AVOIDING THE STACK

In Pascal, every quantity has a name, and memory
must be allocated for it; thus, a := b+c uses three names.
Temporaries may also be required during the evaluation of complex
expressions. In Forth, the parameter stack is commonly the best
place to put quantitiesin the above example, if b and c are already
there, + will add them; no variables being required; memory is saved
and speed is improved. The return stack is available for
temporaries. Beginners in Forth do not employ this approach enough,
and overuse named variables; but experienced programmers may go the
other way; superfluous ROT, SWAP, R> and >R commands are a signal
that this has happened. Local variables, now provided by most
Forths, alleviate the situation.

Therefore, when translating a complicated algorithm
to Forth, do not be premature in using the stack. Use ancillary
variables and locals liberally in the first draft. In later
versions, eliminate the unnecessary ones.


!!ARRAYS

There is no standard array in Forth. This used to
annoy me, until I read a comment by Charles Moore to the effect that
it is so easy to write an array to suit your needs, that a standard
is pointless. In Pascal, an array returns its contained quantity,
and can be said to be VALUE-like, but the usual array in Forth
returns an address and can be said to be  VARIABLE-like. As a
result, it is often possible to use primitives such as CMOVE to do
fast shifting, instead of looping slowly with an index. (Some
Pascals permit this.)

The idea of a Forth array is easily extended.
DEFER-like arrays (execution vectors) are familiar; VALUE-like
arrays and string arrays are easily written. The contents of any
kind of array can be located in the dictionary, in a file (virtual
memory), or in extended memory. Mixed arrays in Pascal (arrays of
records) are easily duplicated In Forth, and can include DEFER-like
elements, not possible in Pascal The principle will be illustrated
below.


!!MIXED ARRAYS (ARRAYS OF RECORDS)

In Pascal, an array can have elements of any desired
type, including mixed types (array of records), and any element or
part of one can be accessed. The same can be done in Forth, with the
added power that a mixed array can incorporate actions.  As an
example, verbs will be defined for an adventure game. The reader is
invited to try to duplicate each feature in Pascal step by step.

1. Defining a Forth array. The following simple
definer sets up an array composed of elements of any specified
length. The array is variable-like, returning an address, and is
situated in the dictionary. Range checking and multidimensionality
can be added if desired.
{{{
: -BYTE-ARRAY ( n l -- ) ( i -- adr)
	CREATE DUP , *  ALLOT  DOES> DUP @ ROT * + 2+ ;
}}}

2. Defining offsets. In Pascal, it is possible to
point to any desired part of an array element. For example, if the
elements of an array a[]  consist of an identification number and a
string name, one can define the record so that  a.number[2] and
a.name[2]  point to these quantities for the second individual. In
Forth, one would use offsets.
{{{
: OFFSET ( n -- ) ( adr -- adr')  CREATE , DOES> @ + ;
}}}

3. Defining verbs. Each verb will have two fields,
one for its action and another for its status, used to modify the
action. A dot convention provides a Pascal-like syntax.
{{{
	20  4 -BYTE-ARRAY  VERB-ARRAY
	0 OFFSET .ACTION
	2 OFFSET .STATUS
}}}

Now, 1 VERB-ARRAY .STATUS puts the address of the
status of verb number 1 on the stack, and the action of the verb can
be executed by 1 VERB-ARRAY .ACTION PERFORM  Note that a blank must
appear between the name of the array and its offset.

4. Naming verbs: Verb numbers are clumsy; names should
be used. In Forth, it is easy to define a new class, VERB, that
associates a verb number with a name.
{{{
VARIABLE V#		V# OFF
:  @INCR  ( adr -- n ) DUP @ DUP 1+ ROT ! ;
:  VERB  CREATE V# @INCR ,  DOES> @ VERB-ARRAY ;

VERB GO		(GO becomes verb number 0)
VERB TAKE	(TAKE becomes verb number 1)
}}}

TAKE becomes the equivalent of 1 VERB-ARRAY, and we
can write TAKE .ACTION PERFORM for 1 VERB-ARRAY .ACTION PERFORM.


!!!THE USEFULNESS OF MIXED ARRAYS: MODULES

Forth makes it possible to incorporate some advanced
programing features at a rather elementary level. An example is a
module, which as used here, will refer to a program feature that
forms a conceptual unit, but which may affect the program in many
different places. Let us add a watchdog to our adventure game. The
watchdog is moved from room to room by a random process. When it is
in the same room as the adventurer, it stops him from taking
anything. Programming this entails changing the action of two verbs
	 
GO, which moves the adventurer from room to room,
must also move the dog and set or reset a bit in TAKE .STATUS.
	 
TAKE must examine its status and act appropriately.

Forth provides a smooth way to make these multiple
changes without actually rewriting each definition every time. This
is done by including in the module a new definition that extracts
the old verb action, combines it with the new, and inserts the blend
back into the .ACTION field of the verb. Note how brackets suppress
compilation during extraction. F-PC has a word DEFERS that works
along similar lines. (The word MOVE-DOG is not actually defined in
the illustration.)

{{{
\ Module: Watchdog

: MOVE-DOG ...; \ Make random move, set/reset bit 0 of TAKE .STATUS

: NEW-GO  [ GO .ACTION @ , ]  MOVE-DOG ;
NEW-GO  GO .ACTION !	\ Add MOVE-DOG to GO and put it back

: NEW-TAKE TAKE .STATUS @ 1 AND		\ Insert old action of GO
	 IF [ TAKE .ACTION @ , ] 	\  into IF  THEN phrase
	ELSE ." You can't do that!" THEN ;
	 NEW-TAKE  TAKE .ACTION ! 	\ Put it back
}}}

!!FORTH DO LOOPS AND A WORD ON FACTORING

Forths rule for DO loop limits may look capricious,
but can be justified in two ways: 1) It facilitates factoring, that
is, breaking a definition into fragments, for reasons that may range
from indispensable (saving memory or improving read- ability) to
mere whim; and 2) It follows the Forth convention of starting things
with zero. Factoring in Pascal is limited to writing a new
subroutine. In this familiar example,

{{{
: SPACES ( n -- ) 0 DO SPACE LOOP ;
}}}

factoring has detached the starting value and the loop limit from
each other, a freedom not available in Pascal Clean syntax is the
result, but the effect is like splitting an infinitive: frowned upon
by purists, but useful, nevertheless.

Although this usage is fairly intuitive, making a
formal rule of it becomes convoluted, especially when negative
increments are employed. The Forth 83 stand- ard says that a loop
terminates when the index crosses the barrier between the limit and
limit-1, a formulation that translates easily into assembler on many
microprocessors. A good mnemonic is that forward loops never
attain the limit, while back- ward loops never pass  it. A
consequence is that a loop will execute 216 times if the starting
value and limit are equal. To execute zero times in this case, use
?DO instead of DO.

BASIC-style loops (Pascal, too) can be converted to
Forth-style by a simple transformation. The following works in the
usual case, in which the starting value and the limt are both
positive.
{{{
: BASIC-STYLE ( n1 n2 --  n1 n2)
			2DUP < IF SWAP THEN 1+ SWAP ;
}}}
The usage is:  {{{3 5 BASIC-STYLE DO ................ LOOP}}}

An occasionally useful feature of Forth loops is that the
increment can be changed during execution.


!!RECURSION

Forth supports recursive words. The overhead is the same as
any other word. Recursion can be removed from any algorithm by
employing a stack, always available in Forth. As a result, Forth
programmers have little occasion to use recursion Whether to write
a word in recursive or non-recursive fashion is often merely a
matter of choosing to use the return stack instead the parameter
stack, or vice versa. On the other hand, a stack in Pascal needs to
be user-written, and entails a good deal of overhead. As a result,
recursive methods are often preferred.

Nevertheless, there are routines that are natural
for recursion, both in Forth and Pascal, for example, traversing a
binary tree. It would be a real challenge to make this
non-recursive!

{{{
DEFER DO-NODE	( node -- )
DEFER RB		( node -- node') \node > right branch
DEFER LB		( node -- node') \node > left branch
DEFER NIL		( -- nil )

: TRAVERSE  ( node -- )  RECURSIVE
	DUP NIL =	IF DROP
			ELSE DUP LB TRAVERSE  DUP DO-NODE
				RB TRAVERSE
			THEN ;
}}}


!!!APPENDIX: PRACTICAL HINTS CONVERTING AN ALGEBRAIC EXPRESSION TO RPN

Converting an expression from algebraic to reverse
Polish notation is mechanical. It can and has been programmed, but a
pencil-and-paper (carbon based) algorithm will do just as well for
ordinary purposes.

1. Find a binary operation, put brackets around it, and rearrange
it, putting the operator at the end. Remove parentheses and replace
them with brackets as appropriate. Continue until done.

{{{
(A  + 3 * (B+C) ) * SQRT(D+8)

( A  + [ 3  (B+C)  * ] ) * SQRT(D+8)

[ A [ 3  (B+C)  * ] +] * SQRT(D+8)

[ [ A [ 3  (B+C) * ] + ] SQRT(D+8) * ]

[ [ A [ 3  [ B C + ]  * ] +] SQRT(D+8) * ]

[ [ A [ 3  [ B C + ]  * ] +] SQRT [ D 8 + ] * ]
}}}

2. Reverse unary operations, in this case SQRT.
{{{
	[ [ A [ 3  [ B C + ]  * ] +] [ D 8 + ] SQRT *
}}}
3. Remove brackets.
{{{
 	 A   3    B  C +    *   +    D 8 +   SQRT *
}}}
4. Append @ to those symbols that represent variables.


!!Insertion Sort

The insertion sort will be used as an example of how
to convert an algorithm from Pascal to Forth. The insertion  sort is
the way most people arrange a hand of cards. The already-sorted
cards are on the left and the unsorted are on the right. Look at the
first unsorted card (gray), determine where it is to go, then remove
it temporarily. The cards in between are moved up 1 space, and the
gray card is slipped into place.

Although the insertion sort is not theoretically one
of the best, it is actually rather good in Forth for arrays that are
not too large, and is surprisingly efficient for arrays that are
almost in order. It is almost as simple to program as the bubble
sort, which it resembles. The version given compiles to 182 bytes,
including headers. The reasons are:

1.  Because a Forth array returns an address, one
can move elements in blocks by means of code primitives such as
CMOVE>; many Pascals need inefficient loops.

2.  For large random arrays, binary search  makes
insertion approach O(nlogn).

3. If an array is almost in order, insertion sort
with backwards linear search is O(n)  very efficient indeed!
Sedgwick recommends it for finishing up quicksort, as it is faster
than the latter in the late stages, when the array has been chopped
up into segments of a few elements each.

!Pascal Version of Insertion Sort (Sedgwick)
{{{
Procedure insertion;
		var i,j,v: integer;
		begin
		for i:=2  to N  do
			begin
			v:=a[i];  j:=i;
			while a[j-1]>v  do
				begin a[j]:=a[j-1]; j:=j-1
				end
			a[j]:=v		    /* Put value into place */
			end
		end  ;
}}}
	
This looks simple enough, but Sedgwick admits that
it will not work as written, and, despite his praise of it, he does
not give a working version! In Pascal, a counted loop cannot be used
for the inner loop, because there is no way to break out. Note that
the comparison operator is > which, in Pascal, permits comparisons
more complex than mere numerical inequality.

In the Forth version, comparisons are made by a
deferred word COMPARATOR which compares data at 2 addresses and
leaves a sign, an integer only the sign of which is important, the
magnitude being irrelevant. The result should be positive if the
record at a2 is greater than (comes after) a1. A VALUE named RL
record length is defined as an ancillary. (Note that this version
does not confine itself to the artificially simple situation where
the records are single- precision integers, but can sort records of
any desired length.)

The outer loop starts with 1, the index of the second element. The current element is compared with the 0th element of the whole array. If less, then this is the insertion point;
otherwise the sorted part of the array is scanned backwards until a
greater-or-equal comparison is found, and unstructured exit from the
loop occurs. This always will occur, and it corrects the unworkable
part of Sedgwicks version. Finally, the item to be moved is compared
with its left neighbor; if greater, then it is already in place,
otherwise the element is moved to PAD for temporary storage, shift
is performed by CMOVE>, and the item is put in place. The last
comparison is not essential, but it will prevent unnecessary
calculation of 0-length moves, which are frequent when the array is
almost in order.

!INSERTION SORT WITH BACKWARDS LINEAR SEARCH
Leonard Morgenstern
{{{
DEFER COMPARATOR ( a1 a2 -- s )
0 VALUE RL	\ Record length can be any size, not just 16-bits
:  ISORT	\ a0 n rl (adr, number of items, record length)

IS RL		\ a0 n	
OVER RL + SWAP	\ a0 a0+rl n
RL * BOUNDS	\ a0 lim1 lim2 (adr, limits for outer loop)
DO		\ a0 I OVER		 \ a0 i1 a0
 COMPARATOR	 \ a0 s	      (adr, "sign")	
 0<=		 \ a0 (True signifies that trade will be with a0)
 IF		 \ a0 
  I OVER	  \ a0 i1 a0 (Set up to trade with a0)
 ELSE		 \ a0
  I 2DUP	  \ a0 i1 a0 i1
  RL -		  \ a0 i1 a0 i1-rl (Set up for inner loop)
  DO		  \ a0 i1
   DUP I	   \ a0 i1 i1 i
   COMPARATOR 0>   \ a0 i1 f (Compare  inner loop with target)
   IF		   \ a0 i1 (TRUE indicates this is the place)
    I RL +	    \ a0 i1 i+rl (Set up for unstructured LEAVE)
    LEAVE
   THEN
   RL NEGATE +LOOP \ End of inner loop.
 THEN		   \ a0 i1 ax
 2DUP =		  \ a0 i1 ax f (True=nothing to move)
 IF
  2DROP		 \ a0 (Just clear stack)
 ELSE
  OVER PAD RL CMOVE \ a0 i1 ax (Move i1 to pad)
  2DUP -	   \ a0 i1 ax d (d = number of bytes to move)
  OVER DUP RL +    \ a0 i1 ax d ax ax+rl (Getting ready)
  ROT CMOVE>	   \ a0 i1 ax (Move)
  PAD SWAP RL CMOVE  \ a0 i1 (Insert in place)
  DROP		   \ a0 (Clean up stack)
 THEN
 RL +LOOP	 \ a0 (End outer loop)
DROP		\ Clean up stack & end.
;

}}}