Rationale
=========
Problem
-------
Several ANS words, e.g. GET-ORDER and SAVE-INPUT, return a variable
number of stack items. To prevent interference with other items,
these items are then saved on the return stack. Saving several
items to the return stack is tedious, especially where the number
of items is unknown at compile time.
Current practice
----------------
At least SwiftForth, VFX Forth, spForth, and some versions of
Win32Forth provide the words N>R and NR> with the following
or similar specification.
N>R \ xn..x1 +n -- ; R: -- x1 .. xn +n
Transfer N items and count to the return stack.
NR> \ -- xn..x1 +n ; R: x1 .. xn +n --
\ *G Pull n items and count off the return stack.
These words cannot be written without an intimate knowledge of the
underlying Forth.
Approach
--------
At least one system stores items on the return stack in the format
R: -- xn .. x1 n
Because coding of this word is dependent on a number of CPU and Forth
design issues, we do not propose to mandate the order of x1..xn on the
return stack, only to specify that n itself is on the top of the return
stack.
A consequence of this is that N>R and NR> are used in pairs. I have
not yet seen any code that relies on the order of items on the return
stack, but it could be useful. It should also be noted that by defining
the order, the ambiguous condition in the proposal can be removed.
Proposal
========
15.6.2.aaaa N>R n-to-r TOOLS EXT
Interpretation: Interpretation semantics for this word are undefined.
Execution: ( x1..xn +n -- ) ( R: -- xn..x1 +n )
Move n+1 items to the return to the return stack such that n is the
top item on the return stack. The order of the items x1..xn on the
return stack is implementation defined.
15.6.2.bbbb NR> n-r-from TOOLS EXT
Interpretation: Interpretation semantics for this word are undefined.
Execution: ( -- xn..x1 n ) ( R: x1..xn n -- )
Move n+1 items from the return stack to the data stack, leaving n on the
top of the data stack. The order of the items x1..xn on the
return stack is implementation defined.
Ambiguous condition
NR> is used with data not placed on the return stack by N>R.
Reference Implementation
========================
This implementation depends on the return address being on the
return stack.
: N>R \ xn .. x1 N -- ; R: -- x1 .. xn n
\ *G Transfer N items and count to the return stack.
dup \ xn .. x1 N N --
begin
dup
while
rot r> swap >r >r \ xn .. N N -- ; R: .. x1 --
1- \ xn .. N 'N -- ; R: .. x1 --
repeat
drop \ N -- ; R: x1 .. xn --
r> swap >r >r
;
: NR> \ -- xn .. x1 N ; R: x1 .. xn N --
\ *G Pull N items and count off the return stack.
r> r> swap >r dup
begin
dup
while
r> r> swap >r -rot
1-
repeat
drop
;
I think we discussed this already. It's still a great idea, and is
well-supported by many Forths.
Andrew.
What makes it great? To me it looks like a bad idea.
- 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/
They are very useful for their stated purpose, which is stashing the
results of words with variable-length results such as GET-ORDER and
SAVE-INPUT, awful to write in Standard Forth, and easy to write with
carnal knowledge. What's not to like?
Are we just going to have the 4 Feb 2009 discussion again?
Andrew.
> Rationale
> =========
> Problem
> -------
> Several ANS words, e.g. GET-ORDER and SAVE-INPUT, return a variable
> number of stack items. To prevent interference with other items,
> these items are then saved on the return stack. Saving several
> items to the return stack is tedious, especially where the number
> of items is unknown at compile time.
This proposal does not solve the latter problem (how could it?)
Instead it discusses how a *defined* number of items can be
transferred to the R-stack. The former problem: "Saving several
items to the return stack is tedious", is simply not true.
: n>r ( xn..x1 +n -- ) ( R: -- x1 .. xn +n )
POSTPONE DUP
POSTPONE BEGIN POSTPONE DUP
POSTPONE WHILE POSTPONE 1- POSTPONE ROT POSTPONE >R
POSTPONE REPEAT
POSTPONE DROP POSTPONE >R ; IMMEDIATE
: nr> ( -- xn..x1 +n ) ( R: x1 .. xn +n -- )
POSTPONE R@
POSTPONE BEGIN POSTPONE R@
POSTPONE WHILE POSTPONE R> POSTPONE 1-
POSTPONE R> POSTPONE SWAP
POSTPONE >R POSTPONE SWAP
POSTPONE REPEAT
POSTPONE R> POSTPONE DROP ; IMMEDIATE
: test 3 2 1 3 N>R R@ . NR> ; test .S
Doing it myself saves me from the arbitrary restrictions that the future
Standard seems to want to impose.
> These words cannot be written without an intimate knowledge of the
> underlying Forth.
???
> Ambiguous condition
> NR> is used with data not placed on the return stack by N>R.
Why is this an ambiguous condition? Of course, if the numbers on
top of the R-stack don't match, but then it's called a bug.
Using that reasoning, this would be an "ambiguous condition," too.
: test 3 2 1 3 N>R ;
> [implementation]
The coders at MPE are being spoilt by their compiler :-)
-marcel
-- -----------------
$01241700 : test
$0124170A push 3 b#
$0124170C push 2 b#
$0124170E push 1 b#
$01241710 mov rcx, 3 d#
$01241717 mov rbx, 3 d#
$0124171E nop
$0124171F nop
$01241720 cmp rbx, 0 b#
$01241724 push rcx
$01241725 je $01241740 offset NEAR
$0124172B pop rdi
$0124172C pop rax
$0124172D lea rbp, [rbp -8 +] qword
$01241731 mov [rbp 0 +] qword, rax
$01241735 push rdi
$01241736 lea rbx, [rbx -1 +] qword
$0124173A pop rcx
$0124173B jmp $01241720 offset SHORT
$0124173D push rcx
$0124173E push rbx
$0124173F pop rbx
$01241740 pop rbx
$01241741 lea rbp, [rbp -8 +] qword
$01241745 mov [rbp 0 +] qword, rbx
$01241749 push rbx
$0124174A lea rbp, [rbp -8 +] qword
$0124174E mov [rbp 0 +] qword, $0124175B d#
$01241756 jmp .+10 ( $011391C2 ) offset NEAR
$0124175B mov rbx, [rbp 0 +] qword
$0124175F nop
$01241760 mov rdi, [rbp 0 +] qword
$01241764 cmp rdi, 0 b#
$01241768 je $01241792 offset NEAR
$0124176E mov rdi, [rbp 0 +] qword
$01241772 lea rbp, [rbp 8 +] qword
$01241776 mov rax, [rbp 0 +] qword
$0124177A lea rbp, [rbp 8 +] qword
$0124177E lea rdi, [rdi -1 +] qword
$01241782 lea rbp, [rbp -8 +] qword
$01241786 mov [rbp 0 +] qword, rdi
$0124178A mov rcx, rax
$0124178D push rcx
$0124178E jmp $01241760 offset SHORT
$01241790 push rbx
$01241791 pop rbx
$01241792 mov rdi, [rbp 0 +] qword
$01241796 lea rbp, [rbp 8 +] qword
$0124179A push rbx
$0124179B ;
Given the problems I have had with substitute, I decided to post this as
an RfD with a view to taking it to a CfV next week if there is little
discussion.
I did however make one changed based on the previous discussion. I have
moved the words from CORE EXT to TOOLS EXT.
I also like Leon Wagner suggestions that the signature should be:
N>R ( n*x n -- ) ( R: -- n*x n )
NR> ( -- n*x n ) ( R: n*x n -- )
Thus leaving it clear that the order of the items on the return stack is
system dependent and may differ depending on the CPU.
--
Peter Knaggs
oh,
POSTPONE: n>r ( xn..x1 +n -- ) ( R: -- x1 .. xn +n )
DUP BEGIN
DUP WHILE
1- ROT >R
REPEAT DROP >R ;POSTPONE
I prefer n>r, even if POSTPONE: ;POSTPONE is available. If the
implementation has a quicker way implemented, I want to use that
instead.
> > Rationale
> > ... Saving several
> > items to the return stack is tedious, especially where the number
> > of items is unknown at compile time.
> This proposal does not solve the latter problem (how could it?)
> Instead it discusses how a *defined* number of items can be
> transferred to the R-stack.
Of course it solves "the latter problem" ... the number of items saved
are determined by the top of stack at runtime, which need not be known
at compile time and which can vary from one run to another.
> I prefer n>r, even if POSTPONE: ;POSTPONE is available. If the
> implementation has a quicker way implemented, I want to use that
> instead.
And for what would you use n>r ? We can't access anything
that n>r pushes.
OTOH the source for POSTPONE: ;POSTPONE looks extremely useful.
How did you handle comments, multi-line parsing, literal
and locals handling, [ and ], and other niceties in a portable
way?
-marcel
BTW, your posts are full of "=A0" enhancements.
By only having it do what it does, which is POSTPONE one word after
another until "<char>POSTPONE" is encountered, then execute
"<char>POSTPONE".
So it only looks nice if you lay it out to make it look that way.
E.g., the name is parsed then {\} is executed, so don't put anything
there that isn't a comment. All it does is name a definition and
postpone a sequence, making the definition immediate, based on
POSTPONE{ and ;POSTPONE ("}POSTPONE" is a NOP).
Just a little sugar frosting on strings of POSTPONES.
Your last line jumped out at me. Is this true? Can the order of the items
on the return stack vary with CPU? I can imagine that one system could
have the stack going up and another going down, but that doesn't affect
the order that a program sees.
Are you implementing the switch with MOVE so that the order on the return
stack depends on whether the return stack and data stack grow in
different directions? That's efficient but it means you can't portably do
anything with the data N>R provides except NR> . You can't get them off
the return stack one at a time unless you don't care about the order.
Is the guarantee of efficiency worth that?
There needs to be a guarantee that they'll come off in the same order
they went on. Mostly these commands are used to get stuff (e.g. Windows
call parameters) temporarily off the data stack, and one isn't trying to
access individual items from the return stack. That's the only reason
you'd need to know the order in the saved (return stack) state. Since
there may be a bunch of stuff, efficiency is important.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com
"Forth-based products and Services for real-time
applications since 1973."
==================================================
> Is the guarantee of efficiency worth that?
The efficiency is what the point is ... if you need to get the data
off the return stack in smaller pieces, construct the smaller pieces
and N>R and R>N those pieces.
"vanish", "now reappear". Thats the point of the behavior.
I disagree that efficiency is the only point, doing what you
suggest could be very fiddly, in the extreme doing >R n times.
Adding useful functionality is the most important point IMHO,
let the implementers worry about efficiency.
>
> "vanish", "now reappear". Thats the point of the behavior.
I don't think it should be restricted to this behaviour. I would like
the order items are placed on the R stack mandated for these reasons:
1. I can foresee cases where, after N>R, items would be individually
processed and it would be necessary to know the order of items on
the R stack. Not mandating the order restricts the utility of N>R,
which is useful beyond temporarily saving the results of GET-ORDER
and SAVE-INPUT.
2. If the order on the R stack is not mandated, that is yet another
portability trap, e.g. programs making use of one particular Forth's
order may not work on another Forth. We have enough of these traps
without introducing another.
3. A less important point is that 2>R mandates the order and it is
inconsistent to not do so for N>R which is a generalisation of 2>R.
For 2>R and 2 N>R to possibly leave 2 items on the R stack in
different orders is inconsistent, unnecessary and would look rather
silly.
Gerry
>I don't think it should be restricted to this behaviour. I would like
>the order items are placed on the R stack mandated for these reasons:
>
>1. I can foresee cases where, after N>R, items would be individually
>processed and it would be necessary to know the order of items on
>the R stack. Not mandating the order restricts the utility of N>R,
>which is useful beyond temporarily saving the results of GET-ORDER
>and SAVE-INPUT.
I checked this in several implementations. They differ. The least
pain is thus not to mandate the order. I did my homework before
writing the proposal!
>2. If the order on the R stack is not mandated, that is yet another
>portability trap, e.g. programs making use of one particular Forth's
>order may not work on another Forth. We have enough of these traps
>without introducing another.
We don't have a standard way to access other than TOR and NOR, so
the portability issue is the same as it was.
>3. A less important point is that 2>R mandates the order and it is
>inconsistent to not do so for N>R which is a generalisation of 2>R.
>For 2>R and 2 N>R to possibly leave 2 items on the R stack in
>different orders is inconsistent, unnecessary and would look rather
>silly.
In scanning source trees, we found that N>R and NR> are only used
to handle opaque information, i.e. you don't know what it is.
I have not seen a use of NR> that leads to the returned data being
picked apart, except by words such RESTORE-INPUT that are carnal by
definition.
N>R and NR> are used to get information out of the way for a short
time. The information is either restored or dropped after NR>.
Stephen
--
Stephen Pelc, steph...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads
Yes, it is self-limiting ... but that is the point of having the count
stored on the stack.
> Adding useful functionality is the most important point IMHO,
> let the implementers worry about efficiency.
Various implementer worried about efficiency have done this, and done
it in various ways, likely that were efficient for their systems.
( [n-cells] n -- ) ( R: -- [n-cells] n )
( R: [n-cells] n -- ) ( -- [n-cells] n )
... could not even be taken for granted, since the most efficient way
to store and retrieve might be ( n ) CELLS or the old Rack index or
some other
The support I could see as possibly being warranted to extend the
functionality would be:
NSPLIT ( [n1-cells] n1 n2 -- [n2-cells] n2 [n3-cells] n3 )
where n1>=n2, and n3=(n1-n2)
NJOIN ( [n1-cells] n1 [n2-cells] n2 -- [n3-cells] n3 )
where n3=n1+n2
> > "vanish", "now reappear". Thats the point of the behavior.
> I don't think it should be restricted to this behaviour. I would like
> the order items are placed on the R stack mandated for these reasons:
> 1. I can foresee cases where, after N>R, items would be individually
> processed and it would be necessary to know the order of items on
> the R stack.
In that case, the existing N>R and R>N on various systems cannot be
used. One cannot even have an environment query on order to choose
between two routines, since the top of the rack might hold a count, an
offset, an index, or an address.
> Not mandating the order restricts the utility of N>R,
Yes, but that restriction has already taken place. This
standardization notes that fact and calls a halt on the forking at the
point we have already reached.
> which is useful beyond temporarily saving the results of GET-ORDER
> and SAVE-INPUT.
> 2. If the order on the R stack is not mandated, that is yet another
> portability trap, e.g. programs making use of one particular Forth's
> order may not work on another Forth. We have enough of these traps
> without introducing another.
Its not being introduced, it already exists. Its a deeper portability
trap if it exists without a sign posting. That is, if there is a low
rail overpass, posting a sign "trucks over 8' should not use this
road" does not *create* the hazard.
> 3. A less important point is that 2>R mandates the order and it is
> inconsistent to not do so for N>R which is a generalisation of 2>R.
N>R may seem like a generalization of 2>R, but NR> is not a
generalization of 2R> ... its the code that says how many to get from
the rack with 2R> while its return stack state that says how many to
get from the rack with 2R>.
If you want a generalization of 2R>, it would be:
U>R ( x1 ... xu u -- ) ( R: -- x1 ... xu )
UR> ( u -- x1 ... xu ) ( R: x1 ... xu -- )
> For 2>R and 2 N>R to possibly leave 2 items on the R stack in
> different orders is inconsistent, unnecessary and would look rather
> silly.
Since you cannot do: "2 N>R 2 NR>" as "2>R 2R>", that fact that the
first part of the phrase is not equivalent is neither here nor there.
We can't get what you want from N>R NR> they are already _de facto_
defined otherwise.
U>R and UR> above still has them reversed in 2>R 2R> order, since they
are defined to be equivalent to the more intrinsic order of a "U" long
sequence of ">R" and "R>", resp. But that is the only way that:
"8 U>R X UR> 8 X - UR>"
can be made consistent for all values X={0,8}
===========================
NB. I know that sometimes in discussion of options, it reads as if I
am arguing in favor of *adoption* of an option when I am trying to
think out load about the *existence* of an option.
So for the record, between NSPLIT / NJOIN and U>R UR>, I tentatively
prefer the latter.
N>R NR> permits an implementation to export maximally efficient
temporary storage and recall of a block. Intrinsically, since NR> is
not parametrized on the datastack, the order that the items resides on
the rack is neither here nor there, because there's no reason to
expect that the top of the rack will necessarily hold a cell count.
U>R UR> permits an implementation to export an open block stack move
as efficiently as possible. For some systems, where N>R and NR> act in
a consistent way (which the implementer will of course know), that
could well be:
: U>R ( x1 ... xu u -- ) ( R: -- xu ... x1 ) N>R RDROP ;
: UR> ( R: x1 ... xu -- ) ( u -- x1 ... xu ) >R NR> ;
... for a minimal forth loading support from source, it may be factors
that do one stage set up for an UNTIL loop, eg, a hypothetical minimal
65816 Forth:
CODE U>R-stage ( x1 u -- u-1 FALSE | TRUE=[u=0] ) ( R: -- x1 )
LDA D+2,X
BEQ +
LDY DL+4,X
PHY
DEC A
STA DL+4,X
LDA #0
STA DL+2,X
JMP NEXT
+: INX
INX
LDA #$FFFF
STA DL+2,X
JMP NEXT
;CODE
: U>R POSTPONE BEGIN POSTPONE U>R-stage POSTPONE UNTIL ; IMMEDIATE
CODE U>R-stage ( x1 u -- u-1 FALSE | TRUE=[u=0] ) ( R: -- x1 )
CODE UR>-stage ( u -- x1 u-1 FALSE | TRUE=[u=0] ) ( R: x1 -- )
LDA DL+2,X
BEQ +
PLY
STY DL+2,X
STA DL,X
DEX
DEX
DEX
DEX
LDA #$FFFF
+: EOR #$FFFF
STA DL+2,X
JMP NEXT
;CODE
: UR> POSTPONE BEGIN POSTPONE UR>-stage POSTPONE UNTIL ; IMMEDIATE
Given that you run a commercial organisation I can understand your
position but it seems to me that at times too much notice is taken
of legacy code when trying to improve the language.
>
> >2. If the order on the R stack is not mandated, that is yet another
> >portability trap, e.g. programs making use of one particular Forth's
> >order may not work on another Forth. We have enough of these traps
> >without introducing another.
>
> We don't have a standard way to access other than TOR and NOR, so
> the portability issue is the same as it was.
>
You miss the point. If N>R is accepted into the standard, at some
point a hyperthetical user will process the data from the R stack
using the order provided in the Forth system being used. When that
code is ported to another system it may not work because that
system stacks the data in a different order.
> >3. A less important point is that 2>R mandates the order and it is
> >inconsistent to not do so for N>R which is a generalisation of 2>R.
> >For 2>R and 2 N>R to possibly leave 2 items on the R stack in
> >different orders is inconsistent, unnecessary and would look rather
> >silly.
>
> In scanning source trees, we found that N>R and NR> are only used
> to handle opaque information, i.e. you don't know what it is.
> I have not seen a use of NR> that leads to the returned data being
> picked apart, except by words such RESTORE-INPUT that are carnal by
> definition.
That is not to say that others won't use N>R to move known data to
the R stack in the future if N>R is available, I would say that is
a certainty.
>
> N>R and NR> are used to get information out of the way for a short
> time. The information is either restored or dropped after NR>.
>
If you want to make the whole thing invisible to the user, why specify
that the R stack be used. Why not have 2 alternative words, say N-SAVE
and N-RESTORE that move n words from the stack somewhere. That would
give implementers even more freedom for efficiency, they could use the
R stack if they wish or use some other area of memory.
Gerry
No, you miss the point. Since the user sees the retrieved data, they
will be in the same order as before they were stored.
>>> 3. A less important point is that 2>R mandates the order and it is
>>> inconsistent to not do so for N>R which is a generalisation of 2>R.
>>> For 2>R and 2 N>R to possibly leave 2 items on the R stack in
>>> different orders is inconsistent, unnecessary and would look rather
>>> silly.
>> In scanning source trees, we found that N>R and NR> are only used
>> to handle opaque information, i.e. you don't know what it is.
>> I have not seen a use of NR> that leads to the returned data being
>> picked apart, except by words such RESTORE-INPUT that are carnal by
>> definition.
>
> That is not to say that others won't use N>R to move known data to
> the R stack in the future if N>R is available, I would say that is
> a certainty.
>
>> N>R and NR> are used to get information out of the way for a short
>> time. The information is either restored or dropped after NR>.
>>
>
> If you want to make the whole thing invisible to the user, why specify
> that the R stack be used. Why not have 2 alternative words, say N-SAVE
> and N-RESTORE that move n words from the stack somewhere. That would
> give implementers even more freedom for efficiency, they could use the
> R stack if they wish or use some other area of memory.
Using the return stack imposes constraints that the programmer needs to
know about.
Jerry
--
Why am I in a handbasket? Where are we going?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
>
> > You miss the point. If N>R is accepted into the standard, at some
> > point a hyperthetical user will process the data from the R stack
> > using the order provided in the Forth system being used. When that
> > code is ported to another system it may not work because that
> > system stacks the data in a different order.
>
> No, you miss the point. Since the user sees the retrieved data, they
> will be in the same order as before they were stored.
Given the data stack holds ( 1 2 3 4 4 )
After N>R, system A saves the data on the R stack as ( R: -- 1 2 3 4
4 )
The user's program executes 2R> to get ( -- 4 4 ) ( R: -- 1 2 3 )
System B saves the data on the R stack as ( R: -- 4 3 2 1 4 )
The user's 2R> gets ( -- 1 4 ) ( R: 4 3 2 )
How are these the same?
[...]
> > If you want to make the whole thing invisible to the user, why specify
> > that the R stack be used. Why not have 2 alternative words, say N-SAVE
> > and N-RESTORE that move n words from the stack somewhere. That would
> > give implementers even more freedom for efficiency, they could use the
> > R stack if they wish or use some other area of memory.
>
> Using the return stack imposes constraints that the programmer needs to
> know about.
>
Of course
Gerry
Why are you assuming that the role of the standard process is to
improve the language *in general*, rather than to improve the language
in the way that a communication standard can usefully improve a
language.
The role of the standards process is to ease the task of porting code
from one system to another. Where there are not divergent practices in
what behavior is called by what name, an existing practice can be
standardized to allow more implementations to support that practice
under that word and ease the porting of source code from systems
possessing that behavior under that word.
Where there are divergent practices, the role of the standard is to
set out what actions can be communicated portably and what actions
need special treatment.
========================
What is the burden of proof for arguing that a behavior like this
should be assigned a standard name and standard specification of the
behavior referred to by that name is that there are implementers that
do or will comply with the specification and there are program authors
who do or will use the word as specified.
If it eases the task of *any* code authors porting code between
implementations, and/or if *any* implementers add it and *any* authors
those implementations targeting put it to use, then there is some
benefit, and the burden of proof is whether the *gross* benefit
justifies the time and trouble of the standardization.
========================
What is the burden of proof for arguing that the standards process has
to "fix" something "that some implementation is doing wrong"?
The burden of proof there is much higher. "Breaking existing code" ...
which is to say, forcing implementers to choose between standards
compliance and being able to continue to interpret existing source ...
is a cost, in pursuit of a speculative gain argued by an advocate in
the quasi-formalized standard process.
For the commercial system houses, since customers value code
stability, breaking *their* code is a substantial commercial cost, and
the advocate should not be surprised if they simply reject the
proposal to adopt a standard that they must violate in order to
maintain a stable existing code base.
If it is a genuinely useful advance, demonstrate it in action. If it
is sufficiently compelling that it causes a fork, then they can decide
whether they want to establishing a new product line that implements
the new behavior and allows them to service the capacities of the new
fork.
Here's a useful piece of information to have in a model constant or an
ENVIROMENT? value. Of course, for this, the contents of the return on
the return stack are entirely opaque, all you need to know is how big
it is, and if you can do it at all.
=rstate= ( -- u|TRUE )
if not TRUE, number of cells on the top of the return stack
consumed by an exit from the word, with data below available to the
caller on return.
if TRUE, data cannot be made available to the caller on the return
stack.
\ Implementation (?? uses system):
\ ... after bunches of system identity testing follow by calls to
system prelude files ...
\ ... some of which will have defined =rstate= for their system ...
?? =rstate= 0= uses 0 INVERT CONSTANT =rstate=
> After N>R, system A saves the data on the R stack as ( R: -- 1 2 3 4
> 4 )
> The user's program executes 2R> to get ( -- 4 4 ) ( R: -- 1 2 3 )
> System B saves the data on the R stack as ( R: -- 4 3 2 1 4 )
> The user's 2R> gets ( -- 1 4 ) ( R: 4 3 2 )
> How are these the same?
They are not, and at this point in time they cannot be made the same.
However, on both system A and system B, if the user executes
... N>R ( R: -- x ) ... NR> ( R: -- ) ...
... they get identical results no matter how N>R and NR> are already
implemented or implemented once standardized ... so long as there are
no attempts to access at or below "x".
Further, if an "open" block datastack<->rstack move is defined, it
ought to be an inverted stack. For one thing, hardware-stack
processors without a datastack index cannot necessarily index down
into the datastack to grab the bottom datastack item to push it onto
the return stack, or conversely for retrieving it. For another thing,
that is *internally* consistent for multiple:
x1 U>R x2 U>R x3 U>R x4 U>R
... so long as x1+x2=x3+x4.
They aren't. I think the point being made here is that (as various
folks have observed) N>R etc. is not intended to push data on the Return
Stack for any reason other than just to *get it out of the way*
temporarily. It's not *intended* to be mixed with R>, 2R>, R@, etc.
>>> If you want to make the whole thing invisible to the user, why specify
>>> that the R stack be used. Why not have 2 alternative words, say N-SAVE
>>> and N-RESTORE that move n words from the stack somewhere. That would
>>> give implementers even more freedom for efficiency, they could use the
>>> R stack if they wish or use some other area of memory.
>> Using the return stack imposes constraints that the programmer needs to
>> know about.
Generalizing the names has the advantage of not implying that the saved
items are accessible in any way other than being restored, but it is
certainly important to point out that the Return Stack should be
considered to be blocked, perhaps with language similar to that used for
DO and friends.
When the user wants the data back, he uses NR> . If he intends to
retrieve it in pieces, he better put it on that way. It is sufficient
that the words be fool proof. They needn't be damn-fool proof.
...
Stephen Pelc wrote:
> Gerry <ge...@jackson9000.fsnet.co.uk> wrote:
>>I don't think it should be restricted to this behaviour. I would like
>>the order items are placed on the R stack mandated for these reasons:
>
> I checked this in several implementations. They differ. The least pain
> is thus not to mandate the order. I did my homework before writing the
> proposal!
That is disingenuous.
I like the idea of knowing the order but I don't know whether that should
be specified or not. If it should, and if for most legacy code it doesn't
matter, then that legacy code will not cause pain.
And the standard approach when there is conflicting common practice is to
give new names to the new standard words.
So I'm very glad to hear you did the homework, but your results are to my
way of thinking not arguments to say which way to decide this question.
Here's a potential argument against specifying the order. As I see it, to
make it efficient then either the data stack and return stack must
increment in the same direction, or else they must increment in opposite
directions. If the order is specified then one or the other of those will
be inefficient.
So, what other reasons are there to set those directions? I remember old
Forths where the data stack and return stack were set in opposite
directions and facing each other, so that they wouldn't actually overflow
until their sum overflowed. That was an advantage. Other systems gave the
return stack say 256 items which was way more than enough, and then the
data stack wouldn't overflow until HERE overtook it. And some systems set
a limit for both data and return stacks and threw an error if either of
them overflowed that limit.
Some processors might allow only decrement or only increment? That would
be an argument to make the stacks face the same direction, if you have to
choose. Is there an important reason that sometimes they should face
opposite directions?
I see the argument not to specify the N>R order. It lets you give
*something* to programmers without costing implementors anything. The
tool has a specific use -- stack juggling -- and making it more flexible
might have a cost.
What can we say about that cost in detail?
Also, Gerry's idea that the data could be moved somewhere else than the
return stack is worth some thought. Standard programs would have to
consider the return stack blocked in case some Forth systems did use it.
But given a small return stack, it might be useful to have the N>R stack
elsewhere. If it is elsewhere then it would be possible to guarantee a
minimum size that must be available. If it shares the return stack that
is not possible. However, if the data could be elsewhere then using R@ or
R> to get the result will not work. And NR> does not take an argument, it
always returns the whole thing. So if you allow the data to go anywhere
other than the return stack and you allow users to remove just a part of
that data requires at least one new command, which is a strong argument
not to do both.
I agree that the purpose of the standard is to standardize and not to
improve the language. Where there isn't sufficient common practice,
there may indeed be language improvement, but that's not the goal.
However, I don't agree that the role of the standards process is only
to ease the task of porting code from one system to another. I think
it's also to specify a common understanding of what the language
means. Portablility may be a result of that.
The rest of your posting makes a lot of sense.
Andrew.
Good point! A standard can also ease the task of porting code from one
mind to another.
Let's not. I suggest
: \\ postpone postpone ; immediate
: n>r \ xn .. x1 N -- ; R: -- x1 .. xn n
\\ dup
\\ begin
\\ ?dup \\ while
\\ rot \\ >r \\ 1-
\\ repeat \\ >r ; immediate
: nr> \ -- xn .. x1 N ; R: x1 .. xn N --
\\ r> \\ dup
\\ begin \\ ?dup \\ while
\\ r> \\ rot \\ rot \\ 1-
\\ repeat ; immediate
This is not very much different from Marcel Hendrix's suggestion.
Andrew.
I get the impression that you and Ellizabeth Rather don't understand
my point.
I *know* (because it is obvious when you read it) that the RfD
states that N>R and NR> is intended to just provide temporary storage
and that different systems save data on the R stack in different
orders. My point was:
*Assume* that the order on the R stack was mandated in the RfD
for N>R. Then then a user, who had deliberately placed *known*
data on the R stack with N>R, *could* get at that known data,
item by item (using R>, R@, 2R> and 2R@) and process that data as
required, eventually removing that data from the R stack without
using NR>.
Not mandating the order on the R stack in the RfD disallows that
potentially useful feature, an unnecessary restriction in my view.
Again I repeat, if the data is inaccessible (or at least not
accessible in a meaningful way) to the user why does the RfD
state that implementations have to use the R stack - I thought
that one main driver in the development of the ANS Forth
standard was not to mandate how features should be implemented.
A precedent has been set by LOCALS which may use the R stack.
Why not follow that and have N-SAVE and N-RESTORE that
temporarily save the data somewhere.
The more I think about this RfD the worse it seems.
Gerry
> 15.6.2.aaaa N>R n-to-r TOOLS EXT
>
> Interpretation: Interpretation semantics for this word are undefined.
>
> Execution: ( x1..xn +n -- ) ( R: -- xn..x1 +n )
>
> Move n+1 items to the return to the return stack such that n is the
> top item on the return stack. The order of the items x1..xn on the
> return stack is implementation defined.
>
> 15.6.2.bbbb NR> n-r-from TOOLS EXT
>
> Interpretation: Interpretation semantics for this word are undefined.
>
> Execution: ( -- xn..x1 n ) ( R: x1..xn n -- )
>
> Move n+1 items from the return stack to the data stack, leaving n on the
> top of the data stack. The order of the items x1..xn on the
> return stack is implementation defined.
>
> Ambiguous condition
> NR> is used with data not placed on the return stack by N>R.
N.B. : You don't have to require that this uses the return stack, just
say that the data is put *somewhere*, and that the return stack is
blocked until the items have been removed. This gives systems with a
small return stack a bit of wiggle room, and it makes alternatives
such as allocating a chunk of memory for the data standard.
Andrew.
It is a natural assumption and, regrettably, I suppose you are right
about the Forth 200X process. Unfortunately it leads to people who
want to "improve the language *in general*" getting frustrated with
the process and going away. Forth 200X *should* be more innovative
in my view. Perhaps an experimental section should be added.
>
> The role of the standards process is to ease the task of porting code
^
Insert "Forth 200X" above, it is not true of all standardisation
processes.
> from one system to another. Where there are not divergent practices in
> what behavior is called by what name, an existing practice can be
> standardized to allow more implementations to support that practice
> under that word and ease the porting of source code from systems
> possessing that behavior under that word.
>
> Where there are divergent practices, the role of the standard is to
> set out what actions can be communicated portably and what actions
> need special treatment.
and if they cannot be reconciled to do nothing about it except agree
to disagree, which is unfortunate to say the least. For an example
look at the discussions about directory paths etc where a solution
*is* needed.
>
> ========================
>
> What is the burden of proof for arguing that a behavior like this
> should be assigned a standard name and standard specification of the
> behavior referred to by that name is that there are implementers that
> do or will comply with the specification and there are program authors
> who do or will use the word as specified.
What an awful sentence (or is it a question?). I think I get the gist
but am unsure - I hope your lectures to students are clearer:-)
[...]
Gerry
Well said
Gerry
>The more I think about this RfD the worse it seems.
What you forget is:
a) that N>R and NR> are already common practice,
b) they were designed to fulfill a need,
c) they were implemented independently with the same names,
d) they were adopted by other Forths,
e) beauty is for tailors.
While we have words such as SAVE-INPUT and RESTORE-INPUT,
we need N>R and NR>. Some programming is just ugly - we
have to find a least worst solution, and this is a solution
in common use.
It doesn't seem to be in GForth, BigForth or the version
of Win32 Forth I use.
> b) they were designed to fulfill a need,
Sure
> c) they were implemented independently with the same names,
Naturally enough when that's what they do, particularly if
point d) is true and the others copied you.
> d) they were adopted by other Forths,
Hence points a) and c)
> e) beauty is for tailors.
I don't recollect saying anything about beauty
>
> While we have words such as SAVE-INPUT and RESTORE-INPUT,
> we need N>R and NR>.
Surely not. The need is to temporarily store N cells of
data somewhere, and to retrieve it later, not to push it
onto the return stack. Better to meet the need with N-SAVE
and N-RESTORE (or whatever you like to call them) and let
the implementer choose where. That wouldn't break existing
code which could carry on using N>R. N-SAVE and N-RESTORE
would just be synonyms in your systems.
> Some programming is just ugly - we
> have to find a least worst solution, and this is a solution
> in common use.
And a solution that goes against the ethos of the ANS Forth
standard i.e. not restricting implementation choices. E.g.
it allows locals to use the return stack but doesn't insist
on it. So why do this with N>R and NR>? What about processors
with a small return stack?
Gerry
]] ... [[ is more versatile, and you can find the source in
macros.fs in <http://www.complang.tuwien.ac.at/forth/compat.zip>.
Why is it more versatile? It can do the same as "POSTPONE:":
: N>R ( n*x n -- R: n*x n )
]] DUP BEGIN
DUP WHILE
1- ROT >R
REPEAT DROP >R [[ ; IMMEDIATE
But it can also do things that would be cumbersome with POSTPONE:,
e.g.
: gen-innerproduct ( a[row][*] -- xt )
\ xt is of type ( b[*][column] -- n )
\ this would be a candidate for using ]] ... [[
>r :noname r>
0 ]] literal swap [[
row-size 0 do
]] dup @ [[
dup @ ]] literal * under+ cell+ [[
row-byte-size +
loop
drop
]] drop ; [[ ;
>How did you handle comments,
Enclose in [[ ... ]]
> multi-line parsing
Works
> literal
use [[ <number> ]] literal or shorter [[ <number> ]]L
>and locals handling,
Depends on what you want to do, but basically the only thing that
works is to use locals outside of ]] ... [[ (or inside [[ ... ]]).
> [ and ]
If you want to get back to normal compilation, use [[ ... ]], if you
really want to do something at compile time, use [[ [ ... ] ]].
>and other niceties in a portable
>way?
It's implementing in standard Forth, so it should be portable.
- 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/
> While we have words such as SAVE-INPUT and RESTORE-INPUT, we need N>R
> and NR>. Some programming is just ugly - we have to find a least worst
> solution, and this is a solution in common use.
The problem is SAVE-INPUT .
SAVE-INPUT gives you an indefinite-size chunk of data clogging the data
stack, so you need special stack-juggling to deal with it. You cannot so
anything useful with that clog except RESTORE-INPUT .
It's just bad design. What would Chuck Moore do?
Here's my immediate suggestion.
-----------
MARK-INPUT ( -- iid )
MARK-INPUT returns a one-cell token. It marks the current state of the
input buffer or source file.
NEST-INPUT ( iid -- )
NEST-INPUT consumes a one-cell token. It restores the state of the
current input buffer or source file to what it was when the iid token was
created. Ambiguous result if the input buffer which was current when iid
was created is not the current input buffer.
-----------
Let the Forth system save that information however it likes so long as
programmers can ignore it. It could save into an ALLOCATEd space, into a
BLOCK , into a previously allotted buffer in memory, etc. Whatever is
appropriate for the particular Forth system.
We have seen what happens when this useless information is left in a
useless pile on the data stack. We need N>R and NR> to juggle it onto the
return stack until the time comes to juggle it back.
Similarly with GET-ORDER and SET-ORDER .
98% of the time you are only interested in the top two stack items that
GET-ORDER gives, but you get copies of all of them regardless. If the wid
you want to add is already on the stack you have to juggle it. The new
words for adding one wid and removing one wid are potentially big
improvements. When you want to keep the whole list of wordlists, do you
really want to save that on the return stack in the faith that no
exception will be thrown? This cries out for a data structure.
But wouldn't it be best if it's a system data structure? That's the least
overhead. I'm not clear what functions people need. Save the current
search order so it can be restored later? Restore a search order?
Construct a new search order one wid at a time without adding them to the
current search order? Examine the current or a noncurrent search order
looking for a particular wid or a collection of them?
A bunch of wids on the stack is better than GET-ORDER because at least
there are multiple things you might choose to do with them. But if it
makes you use N>R and NR> then isn't that a sign there's something wrong?
Is N>R NR> really better than PICK and ROLL ?
Yes. The benefit provided by the flexibility you want comes at what
seems to be excessive cost. The programmer must know if the data are to
be processed in chunks smaller than the whole and can store it
accordingly. The new words are intended to stash N cells in one
operation. The implementor should be free to take best advantage of the
machine architecture when writing them.
> Again I repeat, if the data is inaccessible (or at least not
> accessible in a meaningful way) to the user why does the RfD
> state that implementations have to use the R stack - I thought
> that one main driver in the development of the ANS Forth
> standard was not to mandate how features should be implemented.
> A precedent has been set by LOCALS which may use the R stack.
> Why not follow that and have N-SAVE and N-RESTORE that
> temporarily save the data somewhere.
Would you be happier if the RfD stated that the implementation *might*
use the return stack, so that for portability, the return stack should
be considered blocked? That seems reasonable to me.
> The more I think about this RfD the worse it seems.
Your privilege.
Jerry
--
Discovery consists of seeing what everybody has seen, and thinking what
nobody has thought. .. Albert Szent-Gyorgi
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
...
> e) beauty is for tailors.
Julian Noble was fond of reminding us that Boltzmann had said, "Elegance
is for tailors."
...
Jerry
--
Discovery consists of seeing what everybody has seen, and thinking what
nobody has thought. .. Albert Szent-Gyorgi
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
Yes.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
>N.B. : You don't have to require that this uses the return stack, just
>say that the data is put *somewhere*, and that the return stack is
>blocked until the items have been removed. This gives systems with a
>small return stack a bit of wiggle room, and it makes alternatives
>such as allocating a chunk of memory for the data standard.
That's a good solution that doesn't break existing code or require
renaming. How about wordsmithing it yourself?
A very limited purpose, both because these words are rarely used
(especially in a way that requires stashing away the results), and
because the stashing is to the return stack, which limits the stashing
to stuff within a colon definition and also results in restrictions
wrt counted loops and locals.
GET-ORDER and SAVE-INPUT are unidiomatic in having a dynamic stack
depth with a count. My first reaction is that we should not
standardize more words with that kind of interface.
Alternatively, if we want to better support this kind of interface, we
should not start and end with moving to the return stack and back, we
should also have words for allocating and storing such things in
memory, and getting it back from memory. And maybe others. I am not
sure if we should go that way, though.
> awful to write in Standard Forth, and easy to write with
>carnal knowledge.
It's not awful to write in standard Forth, as the reference
implementation demonstrates. And I don't think that an implementation
with carncal knowledge of Gforth (or any other Forth system) will be
significantly shorter.
> What's not to like?
A word that will be rarely used, if at all, with an unidiomatic
interface? Why should I like it? And what makes it a great idea?
>Are we just going to have the 4 Feb 2009 discussion again?
I don't know. Message-Id or URL?
It's hard to argue about how useful stuff is. The system implementors
that already use it find it so, and it seems to me simple and
well-designed. AIUI this word is already in use in applications and
has proved to be approriate.
> Alternatively, if we want to better support this kind of interface, we
> should not start and end with moving to the return stack and back, we
> should also have words for allocating and storing such things in
> memory, and getting it back from memory. And maybe others. I am not
> sure if we should go that way, though.
I'm sure we shouldn't: that would be a case of needless
generalization. This is Forth, where the argument "if was have one
like this, we have to have a full set in order to be orthogonal"
doesn't apply.
>> awful to write in Standard Forth, and easy to write with
>>carnal knowledge.
>
> It's not awful to write in standard Forth, as the reference
> implementation demonstrates.
The RI is not written in standard Forth. It's one of these words that
can be trivial and super-fast with carnal knowledge and rather clumsy
without.
> And I don't think that an implementation with carncal knowledge of
> Gforth (or any other Forth system) will be significantly shorter.
>
>> What's not to like?
>
> A word that will be rarely used, if at all, with an unidiomatic
> interface? Why should I like it? And what makes it a great idea?
>
>>Are we just going to have the 4 Feb 2009 discussion again?
>
> I don't know. Message-Id or URL?
Andrew.
Just a nit about the name. My impression is that the following
is common practice:
: \\ ( -- ) -1 parse 2drop BEGIN refill 0= UNTIL ;
-- David
Maybe an experimental section is a good idea, but I don't
*think* I'm the only one who believes that the best solution to
this problem is to make libraries a prominent part of Forth
culture and practice?
-- David
To perform the task that N>R and NR> are performing? Quite definitely
better, because no deep stack access is required, while if it makes
for a more efficient primitive, it is permitted.
There are two basic cases: the sequence reversing N>R is faster or the
sequence maintaining N>R is faster. No matter which defined-sequence
is selected, implementations that fall in the other case will have a
slower N>R NR> ... and so will implement the alternate but give it a
different name, which is exactly opposite to the desired impact.
For Forth implementations for widely used processors, since the
existing implementations seem to be superior to anything I could ever
do, and most things I do are I/O-bound, all I want from a standard
word is that it is implemented and it works.
When not working, if I ever implement a system it will be in some
context where there is no implementation available that fits my goals.
Hence my apologies that when I think implementation, its always in
terms of fringe or wildly obsolete hardware.
But consider a hardware stack processor without accessible stack
indices and with only a couple of stash/count registers and access to
the top of each stack.
Each individual PICK or ROLL with n above 1 entails shifting all the
items above the target to the return stack, pulling or copying the
target. then pulling all the items back from the return stack, or IOW
"2(n-1)" stack>stack transfers. So a sequence-maintaining:
5 N>R
performed by successively rolling from the bottom is 8+6+4+2=20
additional stack>stack transfers in addition to the 5 stack transfers
of the actual value to where it goes. And the bigger "n", the bigger
the multiple.
Or consider hardware like the 65816, where one or both stacks will be
implemented in software and both stacks are accessible memory, where
using the hardware block move and computing the new offsets when done
will for fairly small "n" be faster than a sequence of (assuming that
the data stack builds down, so "INX" is a normal pop and "DEX" is
reading from a low:
LP: PHA
LDA DS,X
DEX
DEX
CPX TOP
BNE LP
... since the hardware versus software move is over 67% faster. Doing
it the slow way order-reversing is only slightly faster, but doing it
in hardware, order maintaining is faster if the stacks point in the
same direction and order reversing is faster if the stacks point
toward each other.