The Technical Committee has had a problem with locals. It has been argued forcefully that ANS Forth should say nothing about locals since:
It has also been argued, it would seem equally forcefully, that the lack of any standard approach to locals is precisely the reason for this lack of accepted practice since locals are at best non-trivial to implement in a portable and useful way. It has been further argued that users who have elected to become dependent on locals tend to be locked into a single vendor and have little motivation to join the group that it is hoped will broadly accept ANS Forth unless the Standard addresses their problems.
Since the Technical Committee has been unable to reach a strong consensus on either leaving locals out or on adopting any particular vendor's syntax, it has sought some way to deal with an issue that it has been unable to simply dismiss. Realizing that no single mechanism or syntax can simultaneously meet the desires expressed in all the locals proposals that have been received, it has simplified the problem statement to be to define a locals mechanism that:
This appears to the Technical Committee to be what most of those who actively use locals are trying to achieve with them, and it is at present the consensus of the Technical Committee that if ANS Forth has anything to say on the subject this is an acceptable thing for it to say.
This approach, defining (LOCAL), is proposed as one that can be used with a small amount of user coding to implement some, but not all, of the locals schemes in use. The following coding examples illustrate how it can be used to implement two syntaxes.
: LOCALS| ( "name...name |" -- ) BEGIN BL WORD COUNT OVER C@ [CHAR] | - OVER 1 - OR WHILE (LOCAL) REPEAT 2DROP 0 0 (LOCAL) ; IMMEDIATE : EXAMPLE ( n -- n**2 n**3 ) LOCALS| N | N DUP N * DUP N * ;
: LOCAL ( "name" -- ) BL WORD COUNT (LOCAL) ; IMMEDIATE : END-LOCALS ( -- ) 0 0 (LOCAL) ; IMMEDIATE : EXAMPLE ( n -- n n**2 n**3 ) LOCAL N END-LOCALS N DUP N * DUP N * ;
Other syntaxes can be implemented, although some will admittedly require considerably greater effort or in some cases program conversion. Yet other approaches to locals are completely incompatible due to gross differences in usage rules and in some cases even scope identifiers. For example, the complete local scheme in use at Johns Hopkins had elaborate semantics that cannot be duplicated in terms of this model.
To reinforce the intent of section 13, here are two examples of actual use of locals. The first illustrates correct usage:
a)
: { ( "name ... }" - ) BEGIN BL WORD COUNT OVER C@ [CHAR] } - OVER 1 - OR WHILE (LOCAL) REPEAT 2DROP 0 0 (LOCAL) ; IMMEDIATE
b)
: JOE ( a b c -- n ) >R 2* R> 2DUP + 0 { ANS 2B+C C 2B A } 2 0 DO 1 ANS + I + TO ANS ANS . CR LOOP ANS . 2B+C . C . 2B . A . CR ANS ;c)
100 300 10 JOE .
The word { at a) defines a local declaration syntax that surrounds the list of locals with braces. It doesn't do anything fancy, such as reordering locals or providing initial values for some of them, so locals are initialized from the stack in the default order. The definition of JOE at b) illustrates a use of this syntax. Note that work is performed at execution time in that definition before locals are declared. It's OK to use the return stack as long as whatever is placed there is removed before the declarations begin.
Note that before declaring locals, B is doubled, a subexpression (2B+C) is computed, and an initial value (zero) for ANS is provided. After locals have been declared, JOE proceeds to use them. Note that locals may be accessed and updated within do-loops. The effect of interpreting line c) is to display the following values:
1 (ANS the first time through the loop),
3 (ANS the second time),
3 (ANS), 610 (2B+C), 10 (C), 600 (2B), 100 (A),
and 3 (ANS left on the stack by JOE).
The names of the locals vanish after JOE has been compiled. The storage and meaning of locals appear when JOE's locals are declared and vanish as JOE returns to its caller at ; (semicolon).
A second set of examples illustrates various things that break the rules. We assume that the definitions of LOCAL and END-LOCALS above are present, along with { from the preceding example.
d)
: ZERO 0 POSTPONE LITERAL POSTPONE LOCAL ; IMMEDIATE
e)
: MOE ( a b ) ZERO TEMP LOCAL B 1+ LOCAL A+ ZERO ANSWER ;
f)
: BOB ( a b c d ) { D C } { B A } ;
Here are two definitions with various violations of rule 13.3.3.2a. In e) the declaration of TEMP is legal and creates a local whose initial value is zero. It's OK because the executable code that ZERO generates precedes the first use of (LOCAL) in the definition. However, the 1+ preceding the declaration of A+ is illegal. Likewise the use of ZERO to define ANSWER is illegal because it generates executable code between uses of (LOCAL). Finally, MOE terminates illegally (no END-LOCALS). BOB inf) violates the rule against declaring two sets of locals.
g)
: ANN ( a b -- b ) DUP >R DUP IF { B A } THEN R> ;
h)
: JANE ( a b -- n ) { B A } A B + >R A B - R> / ;
ANN in g) violates two rules. The IF ... THEN around the declaration of its locals violates 13.3.3.2b, and the copy of B left on the return stack before declaring locals violates 13.3.3.2c. JANE in h) violates 13.3.3.2d by accessing locals after placing the sum of A and B on the return stack without first removing that sum.
i)
: CHRIS ( a b) { B A } ['] A EXECUTE 5 ['] B >BODY ! [ ' A ] LITERAL LEE ;
CHRIS in i) illustrates three violations of 13.3.3.2e. The attempt to EXECUTE the local called A is inconsistent with some implementations. The store into B via >BODY is likely to cause tragic results with many implementations; moreover, if locals are in registers they can't be addressed as memory no matter what is written.
The third violation, in which an execution token for a definition's local is passed as an argument to the word LEE, would, if allowed, have the unpleasant implication that LEE could EXECUTE the token and obtain a value for A from the particular execution of CHRIS that called LEE this time.
Rule 13.3.3.2d could be relaxed without affecting the integrity of the rest of this structure. 13.3.3.2c could not be.
13.3.3.2b forbids the use of the data stack for local storage because no usage rules have been articulated for programmer users in such a case. Of course, if the data stack is somehow employed in such a way that there are no usage rules, then the locals are invisible to the programmer, are logically not on the stack, and the implementation conforms.
The minimum required number of locals can (and should) be adjusted to minimize the cost of compliance for existing users of locals.
Access to previously declared local variables is prohibited by Section 13.3.3.2d until any data placed onto the return stack by the application has been removed, due to the possible use of the return stack for storage of locals.
Authorization for a Standard Program to manipulate the return stack (e.g., via >R R>) while local variables are active overly constrains implementation possibilities. The consensus of users of locals was that Local facilities represent an effective functional replacement for return stack manipulation, and restriction of standard usage to only one method was reasonable.
Access to Locals within DO..LOOPs is expressly permitted as an additional requirement of conforming systems by Section 13.3.3.2g. Although words, such as (LOCALS), written by a System Implementor, may require inside knowledge of the internal structure of the return stack, such knowledge is not required of a user of compliant Forth systems.
Typical use: x TO name
See: A.6.2.2295 TO.
A possible implementation of this word and an example of usage is given in A.13, above. It is intended as an example only; any implementation yielding the described semantics is acceptable.
The Memory-Allocation word set provides a means for acquiring memory other than the contiguous data space that is allocated by ALLOT. In many operating system environments it is inappropriate for a process to pre-allocate large amounts of contiguous memory (as would be necessary for the use of ALLOT). The Memory-Allocation word set can acquire memory from the system at any time, without knowing in advance the address of the memory that will be acquired.
These words have been in widespread common use since the earliest Forth systems.
Although there are environmental dependencies intrinsic to programs using an assembler, virtually all Forth systems provide such a capability. Insofar as many Forth programs are intended for real-time applications and are intrinsically non-portable for this reason, the Technical Committee believes that providing a standard window into assemblers is a useful contribution to Forth programmers.
Similarly, the programming aids DUMP, etc., are valuable tools even though their specific formats will differ between CPUs and Forth implementations. These words are primarily intended for use by the programmer, and are rarely invoked in programs.
One of the original aims of Forth was to erase the boundary between user and programmer - to give all possible power to anyone who had occasion to use a computer. Nothing in the above labeling or remarks should be construed to mean that this goal has been abandoned.
.S is a debugging convenience found on almost all Forth systems. It is universally mentioned in Forth texts.
SEE acts as an on-line form of documentation of words, allowing modification of words by decompiling and regenerating with appropriate changes.
WORDS is a debugging convenience found on almost all Forth systems. It is universally referred to in Forth texts.
Typical use: : namex ... <create> ... ;CODE ...
where namex is a defining word, and <create> is CREATE or any user defined word that calls CREATE.
Some Forth systems implement the assembly function by adding an ASSEMBLER word list to the search order, using the text interpreter to parse a postfix assembly language with lexical characteristics similar to Forth source code. Typically, in such systems, assembly ends when a word END-CODE is interpreted.
The intent is to reiterate a dest on the control-flow stack so that it can be resolved more than once. For example:
\ Conditionally transfer control to beginning of loop \ This is similar in spirit to C's "continue" statement. : ?REPEAT ( dest -- dest ) \ Compilation ( flag -- ) \ Execution 0 CS-PICK POSTPONE UNTIL ; IMMEDIATE : XX ( -- ) \ Example use of ?REPEAT BEGIN ... flag ?REPEAT ( Go back to BEGIN if flag is false ) ... flag ?REPEAT ( Go back to BEGIN if flag is false ) ... flag UNTIL ( Go back to BEGIN if flag is false ) ... ;
The intent is to modify the order in which the origs and dests on the control-flow stack are to be resolved by subsequent control-flow words. For example, WHILE could be implemented in terms of IF and CS-ROLL, as follows:
: WHILE ( dest -- orig dest ) POSTPONE IF 1 CS-ROLL ; IMMEDIATE
Typical use: ... FORGET name ...
FORGET assumes that all the information needed to restore the dictionary to its previous state is inferable somehow from the forgotten word. While this may be true in simple linear dictionary models, it is difficult to implement in other Forth systems; e.g., those with multiple address spaces. For example, if Forth is embedded in ROM, how does FORGET know how much RAM to recover when an array is forgotten? A general and preferred solution is provided by MARKER.
Typical use: ... flag [IF] ... [ELSE] ... [THEN] ...
Typical use: ... flag [IF] ... [ELSE] ... [THEN] ...
Typical use: ... flag [IF] ... [ELSE] ... [THEN] ...
Software that runs in several system environments often contains some source code that is environmentally dependent. Conditional compilation
Conditional compilation is sometimes done with smart comments - definitions that either skip or do not skip the remainder of the line based on some test. For example:
\ If 16-Bit? contains TRUE, lines preceded by 16BIT\ \ will be skipped. Otherwise, they will not be skipped. VARIABLE 16-BIT? : 16BIT\ ( -- ) 16-BIT? @ IF POSTPONE \ THEN ; IMMEDIATE
This technique works on a line by line basis, and is good for short, isolated variant code sequences.
More complicated conditional compilation problems suggest a nestable method that can encompass more than one source line at a time. The words included in the ANS Forth optional Programming tools extensions word set are useful for this purpose. The implementation given below works with any input source (keyboard, EVALUATE, BLOCK, or text file).
: [ELSE] ( -- ) 1 BEGIN \ level BEGIN BL WORD COUNT DUP WHILE \ level adr len 2DUP S" [IF]" COMPARE 0= IF \ level adr len 2DROP 1+ \ level' ELSE \ level adr len 2DUP S" [ELSE]" COMPARE 0= IF \ level adr len 2DROP 1- DUP IF 1+ THEN \ level' ELSE \ level adr len S" [THEN]" COMPARE 0= IF \ level 1- \ level' THEN THEN THEN ?DUP 0= IF EXIT THEN \ level' REPEAT 2DROP \ level REFILL 0= UNTIL \ level DROP ; IMMEDIATE : [IF] ( flag -- ) 0= IF POSTPONE [ELSE] THEN ; IMMEDIATE : [THEN] ( -- ) ; IMMEDIATE
Table of Contents
Next Section