In this chapter, we discard some of the already familiar control structures and get acquainted with a non-standard, but commonly-running, CASE source. (Solution of CASE FORTH under the name of Charles Eaker came to immortality.) It is recommended to read this chapter to those who want to get intimate acquaintance with translating words or control structures. Those who do not, they can enter the description of the operation of the CASE (see 17.5.1). you can use CASE keywords whenever you need them without full understanding.
17.1.
Running
words
The controls work with a conditional and unconditional branch.
Both are behind you, in the
parser, looking for the branch address (this is the field field parameter).
Branching addresses are relative
to the address of the field parameter (so say how many bytes of the parameter
should jump). This is good
because the jumping parameterization does not depend on the title of the
jump at the memory.
The two words:
BRANCH | (---) |
unconditional
jump to the (relative) address as field parameter;
|
0BRANCH | (---) | if the received flag is false, it will skip to the (relative) address as field parameter. |
An example makes it easier to understand. Let's say, so we define a word:
: PELDA BEGIN A KETTO AGIN;
(here,
ONE and KETTO are defined, not immediate words).
Let's say the PELDA parter starts at 2000.
At:
BRANCH
must go back to the 2000 absolute address.
In the field parameter, this
address is relative to the parameter address (2006), so the field parameter
value will be 2000-2006 = -6.
After the BRANCH, the value of the parameter will be -6 even if the EXAMPLE
parameter does not start at 2000 but at any other address.
Therefore, relative addressing
is good.
The parser is closed by the code field of the word S (in this case at the
2008 address); this will finish
the title interpreter to interpret the word.
Let's look at a parody of a known word:
Source #S:
: ERROR SWAP IF ERROR ELSE DROP ENDIF;
paramezõje:
In addition to the parameters of BRANCH and 0BRANCH, it is also worth noting that some words (BEGIN, ENDIF) do not include any code field addresses in the parsing, their task is only to indicate where to jump from UNTIL or ELSE.
17.2.
Checks
The check words are called "ERROR", which interrupts all QUITs in error (see
section
13.3 ).
In addition to the error indicator,
the ERROR should also specify a fault number that reflects the nature of
the error. There are the
corresponding error messages in the ranks of the reader's 4th queue.
The word COMP checks whether the control structures are actually used in
translation mode.
source:
:? COMP (---)
STATE @ 0 = 17? ERROR;
To match the keywords correctly, make sure that the opening keyword (say BEGIN) puts a number on the stack, your business name (BEGIN eg 1), and AGAIN is only willing to compile if the vermen finds 1 . Pairing Word:
:? PAIRS (n ---)
- 19? ERROR;
The translation words at the end of the translation must be left in the same way as they found it. To control this, a separate system variable, CSP, is used. The instantaneous value of the pointer is a
! CSP (---)
so we can do it in the variable; the
? CSP (---)
so you can check that the stack is the same as the CSP position. The source of the two words:
: CSP SP @ CSP! ; :? CSP SP @ CSP @ - 20? ERROR;
The! CSP : the? CSP ; call it; at CASE we will see that they may be useful at other times.
17.3.
BEGIN, AGAIN, and
UNTIL
A BEGIN do not translate anything into the parsing;
it is his job to put the words
he paired with him into where to jump.
Thus, the essence of BEGIN's
action: when translating it, it makes the value of the word puncture on the
stack; this is the title where
the first word of the cycle nucleus will be translated, so it will have to
jump back. The stack is still
worth 1, this is BEGIN's business card.
: BEGIN
? COMP
HERE
1
; IMMEDIATE
(
if you do not use it while translating)
(BEGIN "skips")
(jumper address)
(contact)
The relative address of the jumpback is entered by the word BACK:
: BACK
HERE
-
,
;
(skip title ---)
(this is the parameter)
(deducted from the address on the vermin , so)
(we get a relative address)
(the received address is stored in the parameter)
So AGAIN:
: AGAIN
1? PAIRS
COMPILE BRANCH
BACK
; IMMEDIATE
(jumper contact ---) (translation)
(was BEGIN?)
(translating the jumping word)
(skip title translation)
UNTIL differs from AGAIN only in that return jump is conditional.
: UNTIL
1? PAIRS
COMPILE 0BRANCH
BACK
; IMMEDIATE
(jumper contact ---) (translation)
(was BEGIN?)
(translating the jumping word)
(skip title translation)
17.4.
IF, ELSE, and
ENDIF
IF translates the word 0BRANCH into the parser, which jumps to the ELSE branch
or ENDIF after the false signal.
However, when IF is compiling,
you still do not know where the ELSE branch or ENDIF will be, the 0BRANCH
parameter will be entered later.
At the IF moment, we can only
help ourselves to note the vermin where it will have to go to the jump
title:
: IF
(--- parameter address contact) (when translating)
COMPILE 0BRANCH (conditional jump) HERE
0,
2
; IMMEDIATE(parameter address)
(leave the parameter location)
(contact)
If there is no ELSE, ENDIF places the relative bounce address in the specified parameters:
: ENDIF
? COMP
2? PAIRS
HERE
OVER -
SWAP!
; IMMEDIATE
(--- parameter address contact) (at translation)
(this is the absolute jump address)
(now relative to)
(we have reset the parameter)
If ELSE is present, the situation is more complicated:
In the IF then the ELSE will enter the jump address, "literally" in the same way as ENDIF. And ENDIF receives the address from the ELSE where the jump address should be.
: ENDIF
2? PAIRS(par address1 contact --- par address2 contacts)
(translation)COMPILE BRANCH (end of true branch) HERE
0,(this address is given further)
(location of the jump address)(now shows the word counter for the beginning of the false branch) SWAP 2 (the worm: par.cim2 par. title 1 2) [COMPILE] ENDIF (Address Relative to Parent Address 1) 2
; IMMEDIATE(contact)
17.5. A non-standard structure: CASE
17.5.1.
How to use it?
The CASE structure is to handle different things depending on a value (called
this branch value). The word
CASE waits for the grove to run the truncation value at which the action
depends. Each branch is described
between words OF and ENDOF;
such OF branches may be any.
When running, the OF at the
worm is given to determine the branching value of the given OF branch.
If the branch value assigned
to CASE does not deliver the program to any of the OF branches, then the
last ENDOF and the ENDCASE ending the end of the structure will be executed.
The splitting value is dropped
by the real OF from the stack, so if the other part runs, it is on top of
the stack and ENDCASE will remove it.
For example, if German-speaking users are G, English is E, French is indicated
by letter F, this may be a saying:
:
CASE
71 OF (letter G)
"auf Wiedersehen"
ENDOF
69 OF (letter E)
"good bye"
ENDOF
70 OF (letter F)
"au revoir"
ENDOF
(other)
. " what to do with you? "
ENDCASE
;
17.5.2.
Source of
Keywords
As far as we are concerned, the novelty is that the CASE structure has an
ever-repeating element, the OF ... ENDOF branch.
After each ENDOF you have to
go to ENDCASE, but of course (when translating) you do not even know where
it is. Thus ENDCASE should
enter the jump addresses on the stack of parameter addresses;
how many places you need to
call the CSP variable to calculate it.
Endcases and ENDOFs postpone
jump addresses for ENDOFs and ENDOFs.
the OFs generated jumps.
Since this post-entry is done
by ENDIF, if you get the parameter address and a 2-denominate contact, ENDCASE
and ENDOF will mobilize it for this task.
: CASE
? COMP
CSP @
! CSP
4
; IMMEDIATE
(
for example, CSP
is corrupted , fits) (preserve the eedeti value)
(CSP is the current value of the pointer)
(contact): OF
4? PAIRS
(contact name --- parameter address contact)
(when translating)
(was CASE?)COMPILE OVER (comparing the branch value to)
COMPILE = (with the value)
COMPILE 0BRANCH (if not = after ENDOF)HERE
0,
(here comes the jump address)
(location of the jump address)COMPILE DROP (if this is the true OF, the branching)
5
; IMMEDIATE
(No longer required)
(About OF): ENDOF
5? PAIRS(par. address1 contact1 --- par.cím2 contacts2)
(translation)
(was OF?)COMPILE BRANCH (end of OF branch to ENDCASE)
HERE
0,
SWAP 2(we go)
(par address2)
(parameter location)
(preparing for ENDIF)[COMPILE] ENDIF 4
; IMMEDIATE(contact2)
If the last OF branch is not real, then the program will go after the last ENDOF. There is no further question here: what is there, it is accomplished. During the branching value the vermin will be forgotten - ENDCASE will be discarded.
: ENDCASE
4? PAIRSCOMPILE DROP (this will get you running at a)
BEGIN
SP @ CSP
@ = 0 =
WHILE
2(program if none of the OFs was the real one)
(and left the branching value)
(ENDOF jumpers)
(until ENDOFs had been sold)
(the stack on the stack)
(contact with ENDIF )[COMPILE] ENDIF REPEAT
CSP!
; IMMEDIATE
(so we kept the CSP on the bug)
(original value)