Post Reply 
System RPL - How to exit indefinite loop?
07-25-2021, 01:57 PM
Post: #5
RE: System RPL - How to exit indefinite loop?
As you are probably already aware, a System RPL BEGIN...AGAIN loop is inherently an infinite loop. It has no default mechanism for early exiting or aborting. It can be done, of course, it simply requires the programmer to directly manipulate both the runstream and the return stack as needed for the given situation.

This is definitely an advanced topic for System RPL programming, and I'm not going to attempt to explain all the underlying principles here. Suffice it to say that it can be a powerful tool, but great care needs to be taken when designing the program flow for these structures.

I'll share one way of breaking out of a BEGIN...AGAIN loop that I typically deploy. Note that for this to work, there's a requirement that the AGAIN statement be immediately followed by a ";" (SEMI), which implies that the return stack already has an entry for the codeword that immediately follows the SEMI.

An indefinite loop matching the above description can be exited through the effective use of case/COLA and runstream commands. I usually deploy something like the following:
Code:
<some computation resulting in a boolean value>
case :: RDROP ; ( assuming that TRUE means "exit" )

Notes:
- The somewhat odd-looking encapsulation of a single command inside a secondary may seem strange at first glance, but the secondary is definitely needed in this situation. It's also common to include other processing steps in that secondary, since an early exit at an arbitrary point may require additional clean-up to take place. That makes the secondary a more natural fit.
- This construct must be at the root level of the loop as written. Encapsulating it into another secondary adds an additional return stack level and would require different steps to ensure that the proper level of code is aborted (eg. 2RDROP).
- It's possible to save a tiny amount of space by using a different structure, but IMHO this version is actually more flexible if changes are needed and provides more clarity.

I'll include some simple examples to show how this technique can be applied. Let's say I want to create a small app to show the internal keycodes used by the O/S. I want to loop this so that I can press an arbitrary number of keys to get their codes, and I want to do leave a string ("All done!") on the stack at completion.

Example 1: The Lazy Approach

This code sample is written as an infinite loop, and will just run indefinitely until you interrupt it with ON-C or a hard reset. This might be perfectly fine for a quick-and-dirty test, but I would never use this for something that would be distributed. Forcing the user to do a soft- or hard-reset isn't exactly a friendly way to design an app:
Code:
::
   CK0NOLASTWD                      ( no arguments expected )
   CLEARLCD                         ( clear the screen )
   "Press any key" DISPROW1         ( prompt for pressing keys )

   BEGIN
      WaitForKey DROP               ( don't care about the keyplane )
      #>$ "Key: " SWAP&$ DISPROW3   ( show the key value on line 3 )
   AGAIN

   "All done!"                      ( leave a string on the stack upon exit )
;

Note that as written above, the "All done!" string never sees the light of day. The BEGIN...AGAIN loop is never exited gracefully, so that follow-up code never gets executed.

Example 2: I Hate It When I Do That

OK, so I realize now that I need some trigger built into the look to force an exit. Let's use a somewhat intuitive mechanism: pressing the ON/CANCEL key (O/S key number 47). Armed with the awareness of my special "early exit" construct, I place a test between the key acquisition and value printing statements:
Code:
::
   CK0NOLASTWD                      ( no arguments expected )
   CLEARLCD                         ( clear the screen )
   "Press any key" DISPROW1         ( prompt for pressing keys )

   BEGIN
      WaitForKey DROP               ( don't care about the keyplane )

      BINT47 #=casedrop :: RDROP ;  ( exit if keycode is 47 )

      #>$ "Key: " SWAP&$ DISPROW3   ( show the key value on line 3 )
   AGAIN

   "All done!"                      ( leave a string on the stack upon exit )
;

So I run the app, and it behaves as expected to show key codes until I press ON/CANCEL. But instead of leaving my "All done!" string on the stack, it simply exits with no further action taken. At this point I'm usually slapping my forehead when I realize that, once again, I forgot a fundamental element with this version. In particular, the encapsulating secondary that ends with the AGAIN statement. Not having that, the forced exit jumped to the next return stack entry as it was designed to do, which in this case was the end of the whole program (not the string to be left on the stack).

Example 3: Final Working Version

Armed with all of the above, this version does what I originally intended:
Code:
::
   CK0NOLASTWD                         ( no arguments expected )
   CLEARLCD                            ( clear the screen )
   "Press any key" DISPROW1            ( prompt for pressing keys )

   ::
      BEGIN
         WaitForKey DROP               ( don't care about the keyplane )

         BINT47 #=casedrop :: RDROP ;  ( exit if keycode is 47 )

         #>$ "Key: " SWAP&$ DISPROW3   ( show the key value on line 3 )
      AGAIN
    ;

   "All done!"                         ( leave a string on the stack upon exit )
;

While I have given an example here of how to exit a BEGIN...AGAIN loop, I (intentionally) haven't tailored it to your specific situation. Creating or modifying a System Outer Loop app is a risky endeavor. I encourage you to play around with Gaak's program, but be aware that there are many, many things that can come up with this kind of program that makes them very fragile and prone to unexpected behaviors. Definitely don't load this kind of program on a real calc until you've done extensive testing.
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
RE: System RPL - How to exit indefinite loop? - DavidM - 07-25-2021 01:57 PM



User(s) browsing this thread: 1 Guest(s)