15.6.2.1940 NR> n-r-from TOOLS EXT

Interpretation:

Interpretation semantics for this word are undefined.

Execution:

( -- i * x +n ) ( R: j * x +n -- )

Retrieve the items previously stored by an invocation of N>R. n is the number of items placed on the data stack. It is an ambiguous condition if NR> is used with data not stored by N>R.

See:

Implementation:

This implementation depends on the return address being on the return stack.

: NR> \ -- xn .. x1 N ; R: x1 .. xn N --
\ 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
;

ContributeContributions

EricBlakeavatar of EricBlake [393] Reference implementation should not use -ROTSuggested reference implementation2025-08-04 13:11:17

-rot is not part of the standard; the reference implementation should be written using only other words in the standard. A working replacement, although not necessarily the most efficient, would be rot rot. (A future version of the standard should consider standardizing -rot.)

For that matter, the reference implementations for n>r and nr> use an atypical style of using \ rather than ( ) for giving the comment listing stack effects. And both examples document that they have a dependency on a non-standard use of r> on a value that was not previously placed on the return stack by r> by assuming that the return stack has exactly one cell occupied by the return pointer to get back to the caller of n>r and nr>. It's not entirely wrong to have a reference that is not portable to all implementations since that limitation is documented.

However, it would be possible to rewrite these examples as immediates in order to be entirely portable, although no longer as compact (every caller duplicates code, rather than being a single call into shared code). That said, such a rewrite still matches with the fact that the standard documents only that its reference implementations demonstrate how the word can work, not that they are the most efficient:

VARIABLE nr-scratch \ scratch space for N>R and NR>
: N>R ( xn .. x1 n -- ; R: -- x1 .. xn n )
\ Transfer N items and count to the return stack.
  POSTPONE DUP POSTPONE nr-scratch POSTPONE !
  POSTPONE BEGIN
    POSTPONE DUP
  POSTPONE WHILE
    POSTPONE SWAP POSTPONE >R
    POSTPONE 1-
  POSTPONE REPEAT
  POSTPONE DROP
  POSTPONE nr-scratch POSTPONE @ POSTPONE >R
; IMMEDIATE
: NR> ( -- xn .. x1 n ; R: x1 .. xn n -- )
\ Pull N items and count off the return stack.
  POSTPONE R>
  POSTPONE DUP POSTPONE nr-scratch POSTPONE !
  POSTPONE BEGIN
    POSTPONE DUP
  POSTPONE WHILE
    POSTPONE R> POSTPONE SWAP
    POSTPONE 1-
  POSTPONE REPEAT
  POSTPONE DROP
  POSTPONE nr-scratch POSTPONE @
; IMMEDIATE

Or, we could go the route of documenting the use of a buffer, by depending on the memory-allocation word set; again, using immediate so that the example does not depend on non-portable use of r> on a return value not placed by >r:

: N>R ( xn .. x1 n -- ; R: -- buf-sys n )
\ Transfer N items and count to the return stack.
\ This version throws ior if ALLOCATE fails
  POSTPONE DUP POSTPONE >R
  POSTPONE DUP POSTPONE ALLOCATE POSTPONE THROW ( xn .. x1 n a-addr ; R: n )
  POSTPONE BEGIN
    POSTPONE OVER
  POSTPONE WHILE
    POSTPONE ROT POSTPONE OVER POSTPONE !
    POSTPONE CELL+ POSTPONE SWAP POSTPONE 1- POSTPONE SWAP ( xn .. x2 n-1 a-addr+1 ; R: n )
  POSTPONE REPEAT ( 0 a-addr+n ; R: n )
  POSTPONE R> POSTPONE SWAP POSTPONE 2>R POSTPONE DROP
; IMMEDIATE
: NR> ( -- buf-sys n ; R: x1 .. xn n -- )
\ Pull N items and count off the return stack.
\ This version throws ior if FREE fails
  POSTPONE 2R> POSTPONE OVER POSTPONE >R ( n a-addr+n ; R: n )
  POSTPONE BEGIN
    POSTPONE OVER
  POSTPONE WHILE
    -1 POSTPONE LITERAL POSTPONE CELLS POSTPONE + POSTPONE DUP POSTPONE @ ( n a-addr+n-1 xn )
    POSTPONE ROT POSTPONE 1- POSTPONE ROT ( xn n-1 a-addr+n-1 ; R: n )
  POSTPONE REPEAT ( xn .. x1 0 a-addr ; R: n )
  POSTPONE FREE POSTPONE THROW POSTPONE DROP POSTPONE R>
; IMMEDIATE

EricBlakeavatar of EricBlake

An improved version of my buffer example, producing less code in the body of each caller (again, throwing ior if allocate or free fails), and fixing my last minute change of swap r> r> to 2r> but forgetting to remove the swap. The standard documents an explicit n at the top of R: rather than allowing the implementation-defined data to encode n in some other manner (in my case; I was accidentally storing 'n buf-sys' instead of 'buf-sys n', but the mistake was symmetric); at any rate, this is observable to a standard program using r@, so the testsuite for N>R ought to be enhanced to test that.

: (n>r) ( xn .. x1 n -- buf-sys )
\ Helper for n>r: allocate a buffer and populate it with n items from the stack, returning a buf-sys
  DUP ALLOCATE THROW
  BEGIN
    OVER
  WHILE
    ROT OVER !
    CELL+ SWAP 1- SWAP
  REPEAT
  NIP
;
: (nr>) ( buf-sys n -- xn .. x1 )
\ Helper for nr>: extract n items from buf-sys to the stack, then free the buffer
  SWAP
  BEGIN
    OVER
  WHILE
    -1 CELLS + DUP @
    ROT 1- ROT
  REPEAT
  FREE THROW DROP
;
: N>R ( xn .. x1 n -- ; R: -- buf-sys n )
\ Transfer N items and count to the return stack.
  POSTPONE DUP POSTPONE >R
  POSTPONE (n>r)
  POSTPONE R> POSTPONE 2>R
; IMMEDIATE
: NR> ( -- buf-sys n ; R: x1 .. xn n -- )
\ Pull N items and count off the return stack.
  POSTPONE 2R> POSTPONE DUP POSTPONE >R
  POSTPONE (nr>)
  POSTPONE R>
; IMMEDIATE

In case it was not obvious to the reader, one benefit of moving code into a helper is that clients of n>r and nr> compile smaller; but it only works if the code being factored into a helper is not itself manipulating >r and r> . Thus, I can only refactor the second example with a buffer, and not the first example (the first example depends on inlining >r within the begin/repeat loop into the body of the word being compiled). Of course, if you are implementing things and KNOW how R is affected by calling into a non-immediate word, then you can exploit that knowledge and write your own 2>r back in the style of the original example by using r> on a return value not placed by >r.

ruvavatar of ruv

Or, we could go the route of documenting the use of a buffer,

Note that if you want to avoid memory leaks, you need to store each buffer in the list and correctly free some buffers in catch.

Reply New Version