diff options
Diffstat (limited to 'doc/primer.txt')
-rw-r--r-- | doc/primer.txt | 1164 |
1 files changed, 0 insertions, 1164 deletions
diff --git a/doc/primer.txt b/doc/primer.txt deleted file mode 100644 index 7de5214dd379..000000000000 --- a/doc/primer.txt +++ /dev/null @@ -1,1164 +0,0 @@ - A Beginner's Guide to Forth - - by - - J.V. Noble - - Contents - - 0. Preliminaries - - - 1. Getting started - - The structure of Forth - - 2. Extending the dictionary - - 3. Stacks and reverse Polish notation (RPN) - 3.1 Manipulating the parameter stack - 3.2 The return stack and its uses - - 4. Fetching and storing - - 5. Comparing and branching - - 6. Documenting and commenting Forth code - - 7. Arithmetic operations - - 8. Looping and structured programming - - 9. CREATE ... DOES> (the pearl of Forth) - 9.1 Defining "defining" words - 9.2 Run-time vs. compile-time actions - 9.3 Dimensioned data (intrinsic units) - 9.4 Advanced uses of the compiler - - 10. Floating point arithmetic - - - - 0. Introduction - - Forth is an unusual computer language that has probably been applied - to more varied projects than any other. It is the obvious choice when - the project is exceptionally demanding in terms of completion sched- - ule, speed of execution, compactness of code, or any combination of - the above. - - It has also been called "...one of the best-kept secrets in the com- - puting world." This is no exaggeration: large corporations have pur- - chased professional Forth development systems from vendors such as - Laboratory Microsystems, Inc., Forth, Inc. or MicroProcessor Engineer- - ing, Ltd. and sworn them to secrecy. - - Some speculate (unkindly) that corporate giants prefer to hide their - shame at using Forth; but I believe they are actually concealing a - secret weapon from their rivals. Whenever Forth has competed directly - with a more conventional language like C it has won hands down, pro- - ducing smaller, faster, more reliable code in far less time. I have - searched for examples with the opposite outcome, but have been unable - to find a single instance. - - - - 1. Getting started - - We will use Win32Forth for these illustrations. Download the file - - w32for35.exe - - and double-click on it to install on any Windows 95-equipped machine. - - - The compressed files will then decompress themselves. They should also - install a program group on your desktop. - - Now start Win32Forth by opening the program group and clicking on the - appropriate icon. - - - It should respond by opening a window and writing something like - - 32bit Forth for Windows 95, and NT - Compiled: July 23rd, 1997, 5:11pm - Version: 3.5 Build: 0008 Release Build - Platform: Windows 95 Version: 4.0 Build: 16384 - 491k bytes free - 2,719 Words in Application dictionary - 1,466 Words in System dictionary - 4,185 Words total in dictionaries - 8,293 Windows Constants available - - Loading Win32For.CFG - - *** DON'T PANIC, Press: F1 NOW! *** - - - Win32Forth is case-insensitive. - - - Now type - - BYE <cr>. - - The Win32Forth window immediately closes. - - - What just happened? Forth is an interactive programming language con- - sisting entirely of subroutines, called "words". - - A word is executed (interactively) by naming it. We have just seen - this happen: BYE is a Forth subroutine meaning "exit to the operating - system". So when we entered BYE it was executed, and the system re- - turned control to Windows. - - - Click on the Win32Forth icon again to re-start Forth. - Now we will try something a little more complicated. Enter - - 2 17 + . <cr> 19 ok - - What happened? Forth is interpretive. An "outer interpreter" continu- - ally loops, waiting for input from the keyboard or mass storage de- - vice. The input is a sequence of text strings separated from each - other by blank spaces --ASCII 32decimal = 20hex-- the standard Forth - delimiter. - - When the outer interpreter encounters text it first looks for it in - the "dictionary" (a linked list of previously defined subroutine - names). If it finds the word, it executes the corresponding code. - - If no dictionary entry exists, the interpreter tries to read the input - as a number. - - If the input text string satisfies the rules defining a number, it is - converted to a number and stored in a special memory location called - "the top of the stack" (TOS). - - - In the above example, Forth interpreted 2 and 17 as numbers, and - pushed them both onto the stack. - - "+" is a pre-defined word as is ".", so they were looked up and exe- - cuted. - - "+" added 2 to 17 and left 19 on the stack. - - The word "." (called "emit") removed 19 from the stack and displayed - it on the standard output device (in this case, CRT). - - - We might also have said - - HEX 0A 14 * . <cr> C8 ok - - (Do you understand this? Hint: DECIMAL means "switch to decimal arith- - metic", whereas HEX stands for "switch to hexadecimal arithmetic".) - - If the incoming text can neither be located in the dictionary nor in- - terpreted as a number, Forth issues an error message. Try it: say X - and see - - X - Error: X is undefined - - or say THING and see - - THING - Error: THING is undefined - - - - 2. Extending the dictionary - - The compiler is one of Forth's most endearing features. Unlike - all other high-level languages, the Forth compiler is part of the - language. (LISP and its dialects also make components of the com- - pilation mechanism available to the programmer.) That is, its com- - ponents are Forth words available to the programmer, that can be - used to solve his problems. - - In this section we discuss how the compiler extends the - dictionary. - - Forth uses special words to create new dictionary entries, i.e., - new words. The most important are ":" ("start a new definition") - and ";" ("terminate the definition"). - - Let's try this out: enter - - : *+ * + ; <cr> ok - - What happened? The word ":" was executed because it was already - in the dictionary. The action of ":" is - - > Create a new dictionary entry named "*+" and switch from - interpret to compile mode. - - > In compile mode, the interpreter looks up words and - --rather than executing them-- installs pointers to - their code. (If the text is a number, rather than - pushing it on the stack, Forth builds the number - into the dictionary space allotted for the new word, - following special code that puts it on the stack - when the word is executed.) - - > The action of "*+" will be to execute sequentially - the previously-defined words "*" and "+". - - > The word ";" is special: when it was defined a bit - was turned on in its dictionary entry to mark it as - IMMEDIATE. Thus, rather than writing down its - address, the compiler executes ";" immediately. The - action of ";" is first, to install the code that - returns control to the next outer level of the - interpreter; and second, to switch back from compile - mode to interpret mode. - - Now try out "*+" : - - DECIMAL 5 6 7 *+ . <cr> 47 ok - - This example illustrated two principles of Forth: adding a new word to - the dictionary, and trying it out as soon as it was defined. - - - - 3. Stacks and reverse Polish notation (RPN) - - We now discuss the stack and the "reverse Polish" or "postfix" arith- - metic based on it. (Anyone who has used a Hewlett-Packard calculator - should be familiar with the concept.) - - Virtually all modern CPU's are designed around stacks. Forth effi- - ciently uses its CPU by reflecting this underlying stack architecture - in its syntax. - - - But what is a stack? As the name implies, a stack is the machine ana- - log of a pile of cards with numbers written on them. Numbers are - always added to the top of the pile, and removed from the top of the - pile. The Forth input line - - 2 5 73 -16 <cr> ok - - leaves the stack in the state - - cell # contents - - - 0 -16 (TOS) - - 1 73 (NOS) - - 2 5 - - 3 2 - - - where TOS stands for "top-of-stack", NOS for "next-on-stack", etc. - - We usually employ zero-based relative numbering in Forth data struct- - ures (such as stacks, arrays, tables, etc.) so TOS is given relative - #0, NOS #1, etc. - - Suppose we followed the above input line with the line - - + - * . <cr> xxx ok - - what would xxx be? The operations would produce the successive stacks - - cell# initial + - * . - - 0 -16 57 -52 -104 - 1 73 5 2 - 2 5 2 - 3 2 empty - stack - - The operation "." (EMIT) displays -104 to the screen, leaving the - stack empty. That is, xxx is -104. - - - 3.1 Manipulating the parameter stack - - Forth systems incorporate (at least) two stacks: the parameter stack - and the return stack. - - A stack-based system must provide ways to put numbers on the stack, to - remove them, and to rearrange their order. Forth includes standard - words for this purpose. - - Putting numbers on the stack is easy: simply type the number (or in- - corporate it in the definition of a Forth word). - - The word DROP removes the number from TOS and moves up all the other - numbers. (Since the stack usually grows downward in memory, DROP mere- - ly increments the pointer to TOS by 1 cell.) - - SWAP exchanges the top 2 numbers. - - DUP duplicates the TOS into NOS. - - ROT rotates the top 3 numbers. - - - These actions are shown below (we show what each word does to the ini- - tial stack) - - cell | initial | DROP SWAP ROT DUP - - 0 | -16 | 73 73 5 -16 - 1 | 73 | 5 -16 -16 -16 - 2 | 5 | 2 5 73 73 - 3 | 2 | 2 2 5 - 4 | | 2 - - - Forth includes the words OVER, TUCK, PICK and ROLL that act as shown - below (note PICK and ROLL must be preceded by an integer that says - where on the stack an element gets PICK'ed or ROLL'ed): - - cell | initial | OVER TUCK 4 PICK 4 ROLL - - 0 | -16 | 73 -16 2 2 - 1 | 73 | -16 73 -16 -16 - 2 | 5 | 73 -16 73 73 - 3 | 2 | 5 5 5 5 - 4 | | 2 2 2 - - Clearly, 1 PICK is the same as DUP, 2 PICK is a synonym for OVER, and - 2 ROLL means SWAP. - - - 3.2 The return stack and its uses - - We have remarked above that compilation establishes links from the - calling word to the previously-defined word being invoked. The linkage - mechanism --during execution-- uses the return stack (rstack): the - address of the next word to be invoked is placed on the rstack, so - that when the current word is done executing, the system knows to jump - to the next word. (This is so in most, but not all Forth implement- - ations. But all have a return stack, whether or not they use them for - linking subroutines.) - - In addition to serving as a reservoir of return addresses (since words - can be nested, the return addresses need a stack to be put on) the - rstack is where the limits of a DO...LOOP construct are placed. - - The user can also store/retrieve to/from the rstack. This is an ex- - ample of using a component for a purpose other than the one it was - designed for. Such use is discouraged for novices since it adds the - spice of danger to programming. See "Note of caution" below. - - To store to the rstack we say >R , and to retrieve we say R> . The - word R@ copies the top of the rstack to the TOS. - - - Why use the rstack when we have a perfectly good parameter stack to - play with? Sometimes it becomes hard to read code that performs com- - plex gymnastics on the stack. The rstack can reduce the complexity. - - Alternatively, VARIABLEs --named locations-- provide a place to store - numbers --such as intermediate results in a calculation-- off the - stack, again reducing the gymnastics. Try this: - - \ YOU DO THIS \ EXPLANATION - - VARIABLE X <cr> ok \ create a named storage location X; - \ X executes by leaving its address - - 3 X ! <cr> ok \ ! ("store") expects a number and - \ an address, and stores the number to - \ that address - - X @ . <cr> 3 ok \ @ ("fetch") expects an address, and - \ places its contents in TOS. - - However, Forth encourages using as few named variables as possible. - The reason: since VARIABLEs are typically global --any subroutine can - access them-- they can cause unwanted interactions among parts of a - large program. - - Although Forth can make variables local to the subroutines that use - them (see "headerless words" in FTR), the rstack can often replace - local variables: - - > The rstack already exists, so it need not be defined anew. - - > When the numbers placed on it are removed, the rstack shrinks, - reclaiming some memory. - - - A note of caution: since the rstack is critical to execution we mess - with it at our peril. If we use the rstack for temporary storage we - must restore it to its initial state. A word that places a number on - the rstack must remove it --using R> or RDROP (if it has been defined) - -- before exiting that word. Since DO...LOOP also uses the rstack, - for each >R folowing DO there must be a corresponding R> or RDROP - preceding LOOP. Neglecting these precautions will probably crash - the system. - - - - - 4. Fetching and storing - - As we just saw, ordinary numbers are fetched from memory to - the stack by @ ("fetch"), and stored by ! (store). - - @ expects an address on the stack and replaces that address by - its contents using, e.g., the phrase X @ - - ! expects a number (NOS) and an address (TOS) to store it in, and - places the number in the memory location referred to by the address, - consuming both arguments in the process, as in the phrase 3 X ! - - Double length numbers can similarly be fetched and stored, by - D@ and D!, if the system has these words. - - Positive numbers smaller than 255 can be placed in single bytes of - memory using C@ and C!. This is convenient for operations with strings - of ASCII text, for example screen and keyboard I/O. - - - - 5. Comparing and branching - - Forth lets you compare two numbers on the stack, using relational - operators ">", "<", "=" . Ths, e.g., the phrase - - 2 3 > <cr> ok - - leaves 0 ("false") on the stack, because 2 (NOS) is not greater than 3 - (TOS). Conversely, the phrase - - 2 3 < <cr> ok - - leaves -1 ("true") because 2 is less than 3. - - Notes: In some Forths "true" is +1 rather than -1. - - Relational operators consume both arguments and leave a "flag" - to show what happened. - - (Many Forths offer unary relational operators "0=", "0>" and "0<". - These, as might be guessed, determine whether the TOS contains an - integer that is 0, positive or negative.) - - The relational words are used for branching and control. For example, - - : TEST CR 0 = NOT IF ." Not zero!" THEN ; - - 0 TEST <cr> ok ( no action) - -14 TEST <cr> - Not zero! ok - - The word CR issues a carriage return (newline). Then TOS is compared - with zero, and the logical NOT operator (this flips "true" and - "false") applied to the resulting flag. Finally, if TOS is non-zero, - IF swallows the flag and executes all the words between itself and the - terminating THEN. If TOS is zero, execution jumps to the word - following THEN. - - The word ELSE is used in the IF...ELSE...THEN statement: a nonzero - value in TOS causes any words between IF and ELSE to be executed, and - words between ELSE and THEN to be skipped. A zero value produces the - opposite behavior. Thus, e.g. - - - : TRUTH CR 0 = IF ." false" ELSE ." true" THEN ; - - 1 TRUTH <cr> - true ok - - 0 TRUTH <cr> - false ok - - Since THEN is used to terminate an IF statement rather than in its - usual sense, some Forth writers prefer the name ENDIF. - - 6. Documenting and commenting Forth code - - Forth is sometimes accused of being a "write-only" language, i.e. some - complain that Forth is cryptic. This is really a complaint against - poor documentation and untelegraphic word names. Unreadability is - equally a flaw of poorly written FORTRAN, PASCAL, C, etc. - - Forth offers programmers who take the trouble tools for producing ex- - ceptionally clear code. - - - 6.1 Parenthesized remarks - - The word ( -- a left parenthesis followed by a space -- says "disre- - gard all following text until the next right parenthesis in the - input stream". Thus we can intersperse explanatory remarks within - colon definitions. - - - 6.2 Stack comments - - A particular form of parenthesized remark describes the effect of a - word on the stack. In the example of a recursive loop (GCD below), - stack comments are really all the documentation necessary. - - Glossaries generally explain the action of a word with a - stack-effect comment. For example, - - ( adr -- n) - - describes the word @ ("fetch"): it says @ expects to find an address - (adr) on the stack, and to leave its contents (n) upon completion. - The corresponding comment for ! would be - - ( n adr -- ) . - - - - 6.3 Drop line (\) - - The word "\" (back-slash followed by space) has recently gained favor - as a method for including longer comments. It simply means "drop ev- - erything in the input stream until the next carriage return". Instruc- - tions to the user, clarifications or usage examples are most naturally - expressed in a block of text with each line set off by "\" . - - - 6.4 Self-documenting code - - By eliminating ungrammatical phrases like CALL or GOSUB, Forth pre- - sents the opportunity --via telegraphic names for words-- to make code - almost as self-documenting and transparent as a readable English or - German sentence. Thus, for example, a robot control program could con- - tain a phrase like - - 2 TIMES LEFT EYE WINK - - which is clear (although it sounds like a stage direction for Brun- - hilde to vamp Siegfried). It would even be possible without much dif- - ficulty to define the words in the program so that the sequence could - be made English-like: WINK LEFT EYE 2 TIMES . - - - - - 7. Arithmetic operations - - The 1979 or 1983 standards require that a conforming Forth system con- - tain a certain minimum set of pre-defined words. These consist of - arithmetic operators + - * / MOD /MOD */ for (usually) 16-bit signed- - integer (-32767 to +32767) arithmetic, and equivalents for unsigned (0 - to 65535), double-length and mixed-mode (16- mixed with 32-bit) arith- - metic. The list will be found in the glossary accompanying your - system, as well as in SF and FTR. - - Try this example of a non-trivial program that uses arithmetic and - branching to compute the greatest common divisor of two integers using - Euclid's algorithm: - - : TUCK ( a b -- b a b) SWAP OVER ; - : GCD ( a b -- gcd) ?DUP IF TUCK MOD GCD THEN ; - - The word ?DUP duplicates TOS if it is not zero, and leaves it alone - otherwise. If the TOS is 0, therefore, GCD consumes it and does - nothing else. However, if TOS is unequal to 0, then GCD TUCKs TOS - under NOS (to save it); then divides NOS by TOS, keeping the remainder - (MOD). There are now two numbers left on the stack, so we again take - the GCD of them. That is, GCD calls itself. However, if you try the - above code it will fail. A dictionary entry cannot be looked up and - found until the terminating ";" has completed it. So in fact we must - use the word RECURSE to achieve self-reference, as in - - : TUCK ( a b -- b a b) SWAP OVER ; - : GCD ( a b -- gcd) ?DUP IF TUCK MOD RECURSE THEN ; - - Now try - - 784 48 GCD . 16 ok - - - 8. Looping and structured programming - - Forth has several ways to loop, including the implicit method of re- - cursion, illustrated above. Recursion has a bad name as a looping - method because in most languages that permit recursion, it imposes - unacceptable running time overhead on the program. Worse, recursion - can --for reasons beyond the scope of this Introduction to Forth-- be - an extremely inefficient method of expressing the problem. In Forth, - there is virtually no excess overhead in recursive calls because Forth - uses the stack directly. So there is no reason not to recurse if that - is the best way to program the algorithm. But for those times when - recursion simply isn't enough, here are some more standard methods. - - 8.1 Indefinite loops - - The construct - - BEGIN xxx ( -- flag) UNTIL - - executes the words represented by xxx, leaving TOS (flag) set to TRUE - --at which point UNTIL terminates the loop-- or to FALSE --at which - point UNTIL jumps back to BEGIN. Try: - - : COUNTDOWN ( n --) - BEGIN CR DUP . 1 - DUP 0 = UNTIL DROP ; - - 5 COUNTDOWN - 5 - 4 - 3 - 2 - 1 ok - - A variant of BEGIN...UNTIL is - - BEGIN xxx ( -- flag) WHILE yyy REPEAT - - Here xxx is executed, WHILE tests the flag and if it is FALSE - leaves the loop; if the flag is TRUE, yyy is executed; REPEAT then - branches back to BEGIN. - - These forms can be used to set up loops that repeat until some - external event (pressing a key at the keyboard, e.g.) sets the - flag to exit the loop. They can also used to make endless loops - (like the outer interpreter of Forth) by forcing the flag - to be FALSE in a definition like - - - : ENDLESS BEGIN xxx FALSE UNTIL ; - - - 8.2 Definite loops - - Most Forths allow indexed loops using DO...LOOP (or +LOOP or /LOOP). - These are permitted only within definitions - - : BY-ONES ( n --) 0 TUCK DO CR DUP . 1 + LOOP DROP ; - - The words CR DUP . 1 + will be executed n times as the lower - limit, 0, increases in unit steps to n-1. - - To step by 2's, we use the phrase 2 +LOOP to replace LOOP, as with - - : BY-TWOS ( n --) 0 TUCK - DO CR DUP . 2 + 2 +LOOP DROP ; - - - 8.4 Structured programming - - N. Wirth invented the Pascal language in reaction to program flow - charts resembling a plate of spaghetti. Such flow diagrams were - often seen in early languages like FORTRAN and assembler. Wirth - intended to eliminate line labels and direct jumps (GOTOs), thereby - forcing control flow to be clear and direct. - - The ideal was subroutines or functions that performed a single - task, with unique entries and exits. Unfortunately, programmers - insisted on GOTOs, so many Pascals and other modern languages now have - them. Worse, the ideal of short subroutines that do one thing only is - unreachable in such languages because the method for calling them and - passing arguments imposes a large overhead. Thus execution speed re- - quires minimizing calls, which in turn means longer, more complex sub- - routines that perform several related tasks. Today structured program- - ming seems to mean little more than writing code with nested IFs in- - dented by a pretty-printer. - - Paradoxically, Forth is the only truly structured language in common - use, although it was not designed with that as its goal. In Forth word - definitions are subroutine calls. The language contains no GOTO's so - it is impossible to write "spaghetti code". Forth also encourages - structure through short definitions. The additional running time - incurred in breaking a long procedure into many small ones (this is - called "factoring") is typically rather small in Forth. Each Forth sub- - routine (word) has one entry and one exit point, and can be written - to perform a single job. - - - - 8.5 "Top-down" design - - "Top-down" programming is a doctrine that one should design the entire - program from the general to the particular: - - > Make an outline, flow chart or whatever, taking a broad overview - of the whole problem. - - > Break the problem into small pieces (decompose it). - - > Then code the individual components. - - The natural programming mode in Forth is "bottom-up" rather than "top- - down" --the most general word appears last, whereas the definitions - must progress from the primitive to the complex. This leads to a some- - what different approach from more familiar languages: - - > In Forth, components are specified roughly, and then as they are - coded they are immediately tested, debugged, redesigned and - improved. - - > The evolution of the components guides the evolution of the outer - levels of the program. - - - - - 9. CREATE ... DOES> (the pearl of FORTH) - - Michael Ham has called the word pair CREATE...DOES>, the "pearl of - Forth". CREATE is a component of the compiler, whose function is to - make a new dictionary entry with a given name (the next name in the - input stream) and nothing else. DOES> assigns a specific run-time ac- - tion to a newly CREATEd word. - - - 9.1 Defining "defining" words - - CREATE finds its most important use in extending the powerful class of - Forth words called "defining" words. The colon compiler ":" is such - a word, as are VARIABLE and CONSTANT. - - The definition of VARIABLE in high-level Forth is simple - - : VARIABLE CREATE 1 CELLS ALLOT ; - - We have already seen how VARIABLE is used in a program. (An altern- - ative definition found in some Forths is - - : VARIABLE CREATE 0 , ; - - --these variables are initialized to 0.) - - Forth lets us define words initialized to contain specific values: for - example, we might want to define the number 17 to be a word. CREATE - and "," ("comma") can do this: - - 17 CREATE SEVENTEEN , <cr> ok - - Now test it via - - SEVENTEEN @ . <cr> 17 ok . - - - Remarks: - - > The word , ("comma") puts TOS into the next cell of the dic- - tionary and increments the dictionary pointer by that number of - bytes. - - > A word "C," ("see-comma") exists also -- it puts a character into - the next character-length slot of the dictionary and increments - the pointer by 1 such slot. (If the character representation is - ASCII the slots are 1 byte--Unicode requires 2 bytes.) - - - 9.2 Run-time vs. compile-time actions - - In the preceding example, we were able to initialize the variable - SEVENTEEN to 17 when we CREATEd it, but we still have to fetch it to - the stack via SEVENTEEN @ whenever we want it. This is not quite what - we had in mind: we would like to find 17 in TOS when SEVENTEEN is - named. The word DOES> gives us the tool to do this. - - The function of DOES> is to specify a run-time action for the "child" - words of a defining word. Consider the defining word CONSTANT , de- - fined in high-level (of course CONSTANT is usually defined in machine - code for speed) Forth by - - : CONSTANT CREATE , DOES> @ ; - - and used as - - 53 CONSTANT PRIME <cr> ok - - Now test it: - - PRIME . <cr> 53 ok . - - - What is happening here? - - > CREATE (hidden in CONSTANT) makes an entry named PRIME (the - first word in the input stream following CONSTANT). Then "," - places the TOS (the number 53) in the next cell of the dic- - tionary. - - > Then DOES> (inside CONSTANT) appends the actions of all words be- - tween it and ";" (the end of the definition) --in this case, "@"-- - to the child word(s) defined by CONSTANT. - - - 9.3 Dimensioned data (intrinsic units) - - Here is an example of the power of defining words and of the distinc- - tion between compile-time and run-time behaviors. - - Physical problems generally involve quantities that have dimensions, - usually expressed as mass (M), length (L) and time (T) or products of - powers of these. Sometimes there is more than one system of units in - common use to describe the same phenomena. - - For example, U.S. or English police reporting accidents might use - inches, feet and yards; while Continental police would use centimeters - and meters. Rather than write different versions of an accident ana- - lysis program it is simpler to write one program and make unit conver- - sions part of the grammar. This is easy in Forth. - - The simplest method is to keep all internal lengths in millimeters, - say, and convert as follows: - - : INCHES 254 10 */ ; - : FEET [ 254 12 * ] LITERAL 10 */ ; - : YARDS [ 254 36 * ] LITERAL 10 */ ; - : CENTIMETERS 10 * ; - : METERS 1000 * ; - - Note: This example is based on integer arithmetic. The word */ - means "multiply the third number on the stack by NOS, keeping - double precision, and divide by TOS". That is, the stack com- - ment for */ is ( a b c -- a*b/c). - - - The usage would be - - 10 FEET . <cr> 3048 ok - - - The word "[" switches from compile mode to interpret mode while com- - piling. (If the system is interpreting it changes nothing.) The word - "]" switches from interpret to compile mode. - - Barring some error-checking, the "definition" of the colon compiler - ":" is just - - : : CREATE ] DOES> doLIST ; - - and that of ";" is just - - : ; next [ ; IMMEDIATE - - Another use for these switches is to perform arithmetic at compile- - time rather than at run-time, both for program clarity and for easy - modification, as we did in the first try at dimensioned data (that is, - phrases such as - - [ 254 12 * ] LITERAL - - and - - [ 254 36 * ] LITERAL - - which allowed us to incorporate in a clear manner the number of - tenths of millimeters in a foot or a yard. - - - The preceding method of dealing with units required unnecessarily many - definitions and generated unnecessary code. A more compact approach - uses a defining word, UNITS : - - : D, ( hi lo --) SWAP , , ; - : D@ ( adr -- hi lo) DUP @ SWAP 2 + @ ; - : UNITS CREATE D, DOES> D@ */ ; - - Then we could make the table - - 254 10 UNITS INCHES - 254 12 * 10 UNITS FEET - 254 36 * 10 UNITS YARDS - 10 1 UNITS CENTIMETERS - 1000 1 UNITS METERS - - \ Usage: - 10 FEET . <cr> 3048 ok - 3 METERS . <cr> 3000 ok - \ ....................... - \ etc. - - This is an improvement, but Forth permits a simple extension that - allows conversion back to the input units, for use in output: - - VARIABLE <AS> 0 <AS> ! - : AS TRUE <AS> ! ; - : ~AS FALSE <AS> ! ; - : UNITS CREATE D, DOES> D@ <AS> @ - IF SWAP THEN - */ ~AS ; - - \ UNIT DEFINITIONS REMAIN THE SAME. - \ Usage: - 10 FEET . <cr> 3048 ok - 3048 AS FEET . <cr> 10 ok - - - - 9.4 Advanced uses of the compiler - - Suppose we have a series of push-buttons numbered 0-3, and a word WHAT - to read them. That is, WHAT waits for input from a keypad: when button - #3 is pushed, for example, WHAT leaves 3 on the stack. - - We would like to define a word BUTTON to perform the action of pushing - the n'th button, so we could just say: - - WHAT BUTTON - - In a conventional language BUTTON would look something like - - : BUTTON DUP 0 = IF RING DROP EXIT THEN - DUP 1 = IF OPEN DROP EXIT THEN - DUP 2 = IF LAUGH DROP EXIT THEN - DUP 3 = IF CRY DROP EXIT THEN - ABORT" WRONG BUTTON!" ; - - That is, we would have to go through two decisions on the average. - - Forth makes possible a much neater algorithm, involving a "jump - table". The mechanism by which Forth executes a subroutine is to - feed its "execution token" (often an address, but not necessarily) - to the word EXECUTE. If we have a table of execution tokens we need - only look up the one corresponding to an index (offset into the table) - fetch it to the stack and say EXECUTE. - - One way to code this is - - CREATE BUTTONS ' RING , ' OPEN , ' LAUGH , ' CRY , - : BUTTON ( nth --) 0 MAX 3 MIN - CELLS BUTTONS + @ EXECUTE ; - - Note how the phrase 0 MAX 3 MIN protects against an out-of-range - index. Although the Forth philosophy is not to slow the code with un- - necessary error checking (because words are checked as they are de- - fined), when programming a user interface some form of error handling - is vital. It is usually easier to prevent errors as we just did, than - to provide for recovery after they are made. - - How does the action-table method work? - - > CREATE BUTTONS makes a dictionary entry BUTTONS. - - > The word ' ("tick") finds the execution token (xt) of the - following word, and the word , ("comma") stores it in the - data field of the new word BUTTONS. This is repeated until - all the subroutines we want to select among have their xt's - stored in the table. - - > The table BUTTONS now contains xt's of the various actions of - BUTTON. - - > CELLS then multiplies the index by the appropriate number of - bytes per cell, to get the offset into the table BUTTONS - of the desired xt. - - > BUTTONS + then adds the base address of BUTTONS to get the abso- - lute address where the xt is stored. - - > @ fetches the xt for EXECUTE to execute. - - > EXECUTE then executes the word corresponding to the button pushed. - Simple! - - If a program needs but one action table the preceding method suffices. - However, more complex programs may require many such. In that case - it may pay to set up a system for defining action tables, including - both error-preventing code and the code that executes the proper - choice. One way to code this is - - : ;CASE ; \ do-nothing word - : CASE: - CREATE HERE -1 >R 0 , \ place for length - BEGIN BL WORD FIND \ get next subroutine - 0= IF CR COUNT TYPE ." not found" ABORT THEN - R> 1+ >R - DUP , ['] ;CASE = - UNTIL R> 1- SWAP ! \ store length - DOES> DUP @ ROT ( -- base_adr len n) - MIN 0 MAX \ truncate index - CELLS + CELL+ @ EXECUTE ; - - Note the two forms of error checking. At compile-time, CASE: - aborts compilation of the new word if we ask it to point to an - undefined subroutine: - - case: test1 DUP SWAP X ;case - X not found - - and we count how many subroutines are in the table (including - the do-nothing one, ;case) so that we can force the index to - lie in the range [0,n]. - - CASE: TEST * / + - ;CASE ok - 15 3 0 TEST . 45 ok - 15 3 1 TEST . 5 ok - 15 3 2 TEST . 18 ok - 15 3 3 TEST . 12 ok - 15 3 4 TEST . . 3 15 ok - - Just for a change of pace, here is another way to do it: - - : jtab: ( Nmax --) \ starts compilation - CREATE \ make a new dictionary entry - 1- , \ store Nmax-1 in its body - ; \ for bounds clipping - - : get_xt ( n base_adr -- xt_addr) - DUP @ ( -- n base_adr Nmax-1) - ROT ( -- base_adr Nmax-1 n) - MIN 0 MAX \ bounds-clip for safety - 1+ CELLS+ ( -- xt_addr = base + 1_cell + offset) - ; - - : | ' , ; \ get an xt and store it in next cell - - : ;jtab DOES> ( n base_adr --) \ ends compilation - get_xt @ EXECUTE \ get token and execute it - ; \ appends table lookup & execute code - - \ Example: - : Snickers ." It's a Snickers Bar!" ; \ stub for test - - \ more stubs - - 5 jtab: CandyMachine - | Snickers - | Payday - | M&Ms - | Hershey - | AlmondJoy - ;jtab - - 3 CandyMachine It's a Hershey Bar! ok - 1 CandyMachine It's a Payday! ok - 7 CandyMachine It's an Almond Joy! ok - 0 CandyMachine It's a Snickers Bar! ok - -1 CandyMachine It's a Snickers Bar! ok - - - - 10. Floating point arithmetic - - Although Forth at one time eschewed floating point arithmetic - (because in the era before math co-processors integer arithmetic - was 3x faster), in recent years a standard set of word names has - been agreed upon. This permits the exchange of programs that will - operate correctly on any computer, as well as the development of - a Scientific Subroutine Library in Forth (FSL). - - Although the ANS Standard does not require a separate stack for - floating point numbers, most programmers who use Forth for numer- - ical analysis employ a separate floating point stack; and most of - the routines in the FSL assume such. We shall do so here as well. - - The floating point operators have the following names and perform - the actions indicated in the accompanying stack comments: - - F@ ( adr --) ( f: -- x) - F! ( adr --) ( f: x --) - F+ ( f: x y -- x+y) - F- ( f: x y -- x-y) - F* ( f: x y -- x*y) - F/ ( f: x y -- x/y) - FEXP ( f: x -- e^x) - FLN ( f: x -- ln[x]) - FSQRT ( f: x -- x^0.5) - - Additional operators, functions, trigonometric functions, etc. can - be found in the FLOATING and FLOATING EXT wordsets. (See dpANS6-- - available in HTML, PostScript and MS Word formats. The HTML version - can be accessed from this homepage.) - - To aid in using floating point arithmetic I have created a simple - FORTRAN-like interface for incorporating formulas into Forth words. - - The file ftest.f (included below) illustrates how ftran111.f - should be used. - -\ Test for ANS FORmula TRANslator - -marker -test -fvariable a -fvariable b -fvariable c -fvariable d -fvariable x -fvariable w - -: test0 f" b+c" cr fe. - f" b-c" cr fe. - f" (b-c)/(b+c)" cr fe. ; - -3.e0 b f! -4.e0 c f! -see test0 -test0 - -: test1 f" a=b*c-3.17e-5/tanh(w)+abs(x)" a f@ cr fe. ; -1.e-3 w f! --2.5e0 x f! -cr cr -see test1 -test1 - -cr cr -: test2 f" c^3.75" cr fe. - f" b^4" cr fe. ; -see test2 -test2 - -\ Baden's test case - -: quadroot c f! b f! a f! - f" d = sqrt(b^2-4*a*c) " - f" (-b+d)/(2*a) " f" (-b-d)/(2*a) " -; -cr cr -see quadroot - -: goldenratio f" max(quad root(1,-1,-1)) " ; -cr cr -see goldenratio -cr cr -goldenratio f. - - - -0 [IF] -Output should look like: - -: test0 - c f@ b f@ f+ cr fe. c f@ fnegate b f@ f+ cr fe. c f@ fnegate b f@ - f+ c f@ b f@ f+ f/ cr fe. ; -7.00000000000000E0 --1.00000000000000E0 --142.857142857143E-3 - - -: test1 - x f@ fabs 3.17000000000000E-5 w f@ ftanh f/ fnegate b f@ c f@ f* f+ - f+ a f! a f@ cr fe. ; -14.4682999894333E0 ok - -: test2 - c f@ noop 3.75000000000000E0 f** cr fe. b f@ f^4 cr fe. ; -181.019335983756E0 -81.0000000000000E0 ok - -: QUADROOT C F! B F! A F! B F@ F^2 flit 4.00000 A F@ - C F@ F* F* F- FSQRT D F! B F@ FNEGATE D - F@ F+ flit 2.00000 A F@ F* F/ B F@ FNEGATE - D F@ F- flit 2.00000 A F@ F* F/ ; - - -: GOLDENRATIO flit 1.00000 flit -1.00000 flit -1.00000 - QUADROOT FMAX ; - -1.61803 ok - -with more or fewer places. - -[THEN] - - - - |