Search code examples
forthgforth

Catching exceptions from a loop


I'm trying to write a Forth word which will drop all items on the stack. I'm using a pretty dumb method, by running an infinite loop with 'drop' and catching the error when 'drop' fails because the stack is empty.

My words are defined as such:

( Infinite drop loop)
: droploop begin drop true while drop repeat ;

( Experimental catch with a single drop)
: dropcatcher ['] drop catch drop ;

( Like dropcatcher, but with 'droploop' instead of 'drop')
: dropall  ['] droploop catch drop ;

When I run droploop, I get an error like I'd expect and after execution, the stack is empty. When I run dropcatcher, it drops if the stack isn't empty, and if the stack is empty it reports nothing. When I run dropall, I get all sorts of stuff leftover on the stack.

It looks like this:

2 3 4 5 6 7 8 9 10 dropall .s <9> -1 3 -1 5 -1 7 -1 9 -1 ok

I'd expect that dropall would simply clear the stack without complaint because droploop and dropcatcher seem to work right by themselves, but alas, I don't understand why dropall isn't working.

Why does dropall seem to work differently than droploop and dropcatcher? Or, what I'm I doing wrong here?


Solution

  • When droploop throws an error, it will probably call ABORT or -1 THROW, which by definition clears the data stack. So it may well be that droploop doesn't drop the right number of stack items before the error is detected, but the system exception handler does it for you.

    When I run droploop myself, it either crashes or hangs. This would seem to confirm my theory that there is a severe stack underflow.

    As for the state of the stack after dropall, my guess would be that gforth maybe doesn't detect the stack underflow at the right time, and what you're seeing is some semi-random garbage. Hence this is what droploop leaves on the stack before calling ABORT.

    Side note: Your droploop drops two stack items every iteration, which may or may not be what you intended. Also, use again for an endless loop instead of true while. I.e. : droploop begin drop again;

    Of course, a better way to clear the stack would be

    : clear   depth 0 ?do drop loop ;
    

    or

    : clear   begin depth while drop repeat ;
    

    or

    : clear   sp0 sp! ;