Reading Forth Dimesions (Vol III, Number VI), page 176. There is an
interesting article (interesting to me, anyway) called "Transportable
Control Structures with Compiler Security" by Marc Perkel.
The code as presented is complete, but the article makes references to
'pairs', however, the code for ?PAIRS was not given. I assume that ?
PAIRS simply compares the two top-most stack values and returns a
boolean?
Inspired (simple things for simple minds) I then set about writing
one. I came up with this:
: ?PAIRS ( n1 n2 -- n1 n2 boolean ) DUP ROT DUP -ROT = ;
As can be seen, it is non-destructive. However, I guess the above is
classic newbie (over complex) Forth. There is a better way:
: ?PAIRS ( n1 n2 -- n1 n2 boolean ) OVER OVER = ;
And probably faster on most systems. (I'm so proud of myself ;-)
However, the feeling didn't last long. (I can almost see Elizabeth
smiling):
: ?PAIRS (n1 n2 -- n1 n2 boolean ) 2DUP = ;
Damn it.
Anyway, my question is about ?PAIRS itself: Do I essentially have the
idea correct? ?PAIRS returns a boolean if the top two stack elements
are the same?
Thanks in advance.
In case anyone was interested in the article, here it is in it's
entirety, since it is quite short:
------------------------------------
This article is an enhancement of the idea presented by Kim Haris at
the Rochester FORTH Conference (from the Conference Proceedings, page
97). Basically, the article proposes a wordset of primitives for
defining control words
such as IF , ELSE , THEN , DO , LOOP , BEGIN , WHILE , REPEAT ,
UNTIL , AGAIN , CASE , etc. Kim points out that these strucures are
either compiling a branch to a location not yet defined (such as IF --
> THEN ) or back to a location previously defined ( BEGIN <-- UNTIL ).
There are two steps in compiling either kind of branch: marking the
first place compiled and then later resolving the branch. This
observation leads to four of Kim's words:
>MARK Marks the source of forward branch and leaves a gap.
>RESOLVE Resolves forward branch and leaves a gap.
<MARK Marks destination of backward branch.
<RESOLVE Resolves backward branch.
I complement Kim at this point for his excellent choice of names.
Here's where compiler security comes in. The word >RESOLVE is filling
a gap left by >MARK . If >RESOLVE were to first check to make sure a
gap was there ( DUP @ 0 ?PAIRS ) it would help ensure that the value
on the stack was indeed left by >MARK . Likewise, if <RESOLVE made
sure that the point where it branches back to does not have a gap
( DUP @ NOT 0 ?PAIRS ) it would guarantee that it was not answering a
>MARK . This method allows some compiler security where it is
important not to carry pairs on the stack.
: >MARK HERE O , ;
: >RESOLVE DUP @ 0 ?PAIRS HERE SWAP ! ;
: <MARK HERE ;
: <RESOLVE DUP B NOT 0 ?PAIRS , ;
: IF C, >MARK ;
: ENDIF >RESOLVE ;
: ELSE C3 IF SWAP ENDIF ;
: BEGIN <MARK ;
: UNTIL C, <RESOLVE ;
: AGAIN C3 UNTIL ;
: WHILE IF ;
: REPEAT SWAP AGAIN ENDIF ;
------------------------------------
There is no definition given (nor mention) of C3 in the article, and I
note it appears twice. I assume this is some old old word? No idea.
As an aside, it was interesting to read the letters page. It seems
even the 80's, it was irresistable to compare the strengths and
weaknesses of Forth against other languages, to the extent that one
individual wrote in to complain about the obsession of writing words
to allow Forth to reproduce features of other languages. It sounded
kind of familiar! Some things never change!
Regards
Mark
> This is an old question (possibly) relating to old style (79?) Forths.
>
> Reading Forth Dimesions (Vol III, Number VI), page 176. There is an
> interesting article (interesting to me, anyway) called "Transportable
> Control Structures with Compiler Security" by Marc Perkel.
>
> The code as presented is complete, but the article makes references to
> 'pairs', however, the code for ?PAIRS was not given. I assume that ?
> PAIRS simply compares the two top-most stack values and returns a
> boolean?
>
> Inspired (simple things for simple minds) I then set about writing
> one. I came up with this:
>
> : ?PAIRS ( n1 n2 -- n1 n2 boolean ) DUP ROT DUP -ROT = ;
>
> As can be seen, it is non-destructive. However, I guess the above is
> classic newbie (over complex) Forth. There is a better way:
>
> : ?PAIRS ( n1 n2 -- n1 n2 boolean ) OVER OVER = ;
>
> And probably faster on most systems. (I'm so proud of myself ;-)
> However, the feeling didn't last long. (I can almost see Elizabeth
> smiling):
>
> : ?PAIRS (n1 n2 -- n1 n2 boolean ) 2DUP = ;
>
> Damn it.
>
> Anyway, my question is about ?PAIRS itself: Do I essentially have the
> idea correct? ?PAIRS returns a boolean if the top two stack elements
> are the same?
Given that there's no documentation, probably the next best thing is to
look at the code and see how ?PAIRS gets used.
If the code is well-written, that might be the best thing even if there
was documentation.
So it looks like they check the address and see whether it contains a
zero. If it does, they figure the zero could have been left by >MARK .
If the value is not zero then they figure it could not have been left by
>MARK .
> : >MARK HERE O , ;
> : >RESOLVE DUP @ 0 ?PAIRS HERE SWAP ! ;
>RESOLVE does not use a boolean left by ?PAIRS . If ?PAIRS leaves a
>boolean, then ! will try to store HERE into address 0 or address -1 (or
>1?) . Not good.
It looks like ?PAIRS doesn't leave any result. >RESOLVE doesn't decide
what to do. So ?PAIRS must decide what to do.
So we get something like:
: ?PAIRS ( value flag -- )
- ABORT" conditionals not paired" ;
> : >RESOLVE ( addr -- ) DUP @ 0 ?PAIRS HERE SWAP ! ;
So >RESOLVE will fail if the value at the address is not zero.
> : <MARK HERE ;
> : <RESOLVE DUP B NOT 0 ?PAIRS , ;
: <RESOLVE ( addr -- )
DUP @ NOT 0 ?PAIRS , ;
NOT clearly is being used the way 0= would today.
> : IF C, >MARK ;
> : ENDIF >RESOLVE ;
> : ELSE C3 IF SWAP ENDIF ;
> : BEGIN <MARK ;
> : UNTIL C, <RESOLVE ;
> : AGAIN C3 UNTIL ;
> : WHILE IF ;
> : REPEAT SWAP AGAIN ENDIF ;
>
> ------------------------------------
>
> There is no definition given (nor mention) of C3 in the article, and I
> note it appears twice. I assume this is some old old word? No idea.
It's probably a hexadecimal value. IF and UNTIL both do C, to store some
number, and so in ELSE and in AGAIN the number that is stored is $C3.
Probably an unconditional jump instruction in whatever system they were
writing for.
There's no sign that any of these words are IMMEDIATE , I wonder how
they arranged for them to execute at the right time.
Hrm. Not quite. Looking at a couple of the Forth systems I have
around, it's not `2DUP =`, but `<> ABORT" control-flow mismatch"`, or
`<> -22 AND THROW`.
--Josh
?PAIRS is from fig-Forth, and it was used for compiler security (one
of the flagship features of fig-Forth).
>I assume that ?
>PAIRS simply compares the two top-most stack values and returns a
>boolean?
Not quite. It compares the two values and gives an error if they
don't match.
An implementation in Forth-94 could look like this:
: ?PAIRS ( n1 n2 -- )
<> -22 and throw ;
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2009: http://www.euroforth.org/ef09/
> Reading Forth Dimesions (Vol III, Number VI), page 176. There is an
> interesting article (interesting to me, anyway) called "Transportable
> Control Structures with Compiler Security" by Marc Perkel.
> The code as presented is complete, but the article makes references to
> 'pairs', however, the code for ?PAIRS was not given. I assume that ?
> PAIRS simply compares the two top-most stack values and returns a
> boolean?
> Inspired (simple things for simple minds) I then set about writing
> one. I came up with this:
> : ?PAIRS ( n1 n2 -- n1 n2 boolean ) DUP ROT DUP -ROT = ;
> As can be seen, it is non-destructive. However, I guess the above is
> classic newbie (over complex) Forth. There is a better way:
> : ?PAIRS ( n1 n2 -- n1 n2 boolean ) OVER OVER = ;
> And probably faster on most systems. (I'm so proud of myself ;-)
> However, the feeling didn't last long. (I can almost see Elizabeth
> smiling):
> : ?PAIRS (n1 n2 -- n1 n2 boolean ) 2DUP = ;
> Damn it.
From the fig-FORTH source:
: error warning @ 0< if ... ( abort)
: ?error ( boolean error-type) swap if error else drop endif ;
: ?pairs ( n n') - 13 ?error ;
Andrew.
<snip>
Others have written about ?PAIRS
The following is for a 8080 (Z80) Forth Assembler
Marc Perkel in: Forth Dimensions III/6 page 176.
C3 is the opcode for an unconditional JMP
>:>MARK HERE O , ;
>:>RESOLVE DUP @ 0 ?PAIRS HERE SWAP ! ;
>: <MARK HERE ;
>: <RESOLVE DUP B NOT 0 ?PAIRS , ;
B must be read as @
NOT can be read as 0= or INVERT, does not matter here.
>: IF C, >MARK ;
>: ENDIF >RESOLVE ;
>: ELSE C3 IF SWAP ENDIF ;
>: BEGIN <MARK ;
>: UNTIL C, <RESOLVE ;
>: AGAIN C3 UNTIL ;
>: WHILE IF ;
>: REPEAT SWAP AGAIN ENDIF ;
--
Coos
CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html
Mark