Hubbry Logo
RPL (programming language)RPL (programming language)Main
Open search
RPL (programming language)
Community hub
RPL (programming language)
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
RPL (programming language)
RPL (programming language)
from Wikipedia

RPL
ParadigmConcatenative (stack-based),[1] structured
Designed byHewlett-Packard
First appeared1984 (1986)
OSHP calculators
Dialects
System RPL, User RPL
Influenced by
RPN, Forth, Lisp[2]
HP 48G calculator, uses RPL

RPL[5] is a handheld calculator operating system and application programming language used on Hewlett-Packard's scientific graphing RPN (Reverse Polish Notation) calculators of the HP 28, 48, 49 and 50 series, but it is also usable on non-RPN calculators, such as the 38, 39 and 40 series. Internally, it was also utilized by the 17B, 18C, 19B and 27S.[7]

RPL is a structured programming language based on RPN, but equally capable of processing algebraic expressions and formulae, implemented as a threaded interpreter.[8] RPL has many similarities to Forth, both languages being stack-based, as well as the list-based LISP. Contrary to previous HP RPN calculators, which had a fixed four-level stack, the dynamic stack used by RPL is only limited by available RAM, with the calculator displaying an error message when running out of memory rather than silently dropping arguments off the stack as in fixed-sized RPN stacks.[9]

RPL originated from HP's Corvallis, Oregon development facility in 1984 as a replacement for the previous practice of implementing the operating systems of calculators in assembly language.[7] The first calculator utilizing it internally was the HP-18C and the first calculator making it available to users was the HP-28C, both from 1986.[10][7] The last pocket calculator supporting RPL, the HP 50g, was discontinued in 2015.[11][12][13] However, multiple emulators that can emulate HP's RPL calculators exist that run on a range of operating systems, and devices, including iOS and Android smartphones. There are also a number of community projects to recreate and extend RPL on newer calculators, like newRPL[14][15] or DB48X,[16][17] which may add features or improve performance.[18]

Variants

[edit]

The internal low- to medium-level variant of RPL, called System RPL (or SysRPL) is used on some earlier HP calculators as well as the aforementioned ones, as part of their operating system implementation language. In the HP 48 series this variant of RPL is not accessible to the calculator user without the use of external tools, but in the HP 49/50 series there is a compiler built into ROM to use SysRPL. It is possible to cause a serious crash while coding in SysRPL, so caution must be used while using it. The high-level User RPL (or UserRPL) version of the language is available on said graphing calculators for developing textual as well as graphical application programs. All UserRPL programs are internally represented as SysRPL programs, but use only a safe subset of the available SysRPL commands. The error checking that is a part of UserRPL commands, however, makes UserRPL programs noticeably slower than equivalent SysRPL programs. The UserRPL command SYSEVAL tells the calculator to process designated parts of a UserRPL program as SysRPL code.

Control blocks

[edit]

RPL control blocks are not strictly postfix. Although there are some notable exceptions, the control block structures appear as they would in a standard infix language. The calculator manages this by allowing the implementation of these blocks to skip ahead in the program stream as necessary.

Conditional statements

[edit]

IF/THEN/ELSE/END

[edit]

RPL supports basic conditional testing through the IF/THEN/ELSE structure. The basic syntax of this block is:

 IF condition THEN if-true [ELSE if-false] END

The following example tests to see if the number at the bottom of the stack is "1" and, if so, replaces it with "Equal to one":

 « IF 1 == THEN "Equal to one" END »

The IF construct evaluates the condition then tests the bottom of the stack for the result. As a result, RPL can optionally support FORTH-style IF blocks, allowing the condition to be determined before the block. By leaving the condition empty, the IF statement will not make any changes to the stack during the condition execution and will use the existing result at the bottom of the stack for the test:

 « 1 == IF THEN "Equal to one" END »

IFT/IFTE

[edit]

Postfix conditional testing may be accomplished by using the IFT ("if-then") and IFTE ("if-then-else") functions.

IFT and IFTE pop two or three commands off the stack, respectively. The topmost value is evaluated as a Boolean and, if true, the second topmost value is pushed back on the stack. IFTE allows a third "else" value that will be pushed back on the stack if the Boolean is false.

The following example uses the IFT function to pop an object from the bottom of the stack and, if it is equal to 1, replaces it with "One":

 « 1 == "One" IFT »

The following example uses the IFTE function to pop an object from the bottom of the stack and, if it is equal to 1, replaces it with "One". If it does not equal 1, it replaces it with the string "Not one":

 « 1 == "One" "Not one" IFTE »

IFT and IFTE will evaluate a program block given as one of its arguments, allowing a more compact form of conditional logic than an IF/THEN/ELSE/END structure. The following example pops an object from the bottom of the stack, and replaces it with "One", "Less", or "More", depending on whether it is equal to, less than, or greater than 1.

 «
   DUP 1 ==
   « DROP "One" »
   « 1 < "Less" "More" IFTE »
   IFTE
 »

CASE/THEN/END

[edit]

To support more complex conditional logic, RPL provides the CASE/THEN/END structure for handling multiple exclusive tests. Only one of the branches within the CASE statement will be executed. The basic syntax of this block is:

 CASE 
  condition_1 THEN if-condition_1 END 
   ...
  condition_n THEN if-condition_n END
  if-none
 END

The following code illustrates the use of a CASE/THEN/END block. Given a letter at the bottom of the stack, it replaces it with its string equivalent or "Unknown letter":

 « 
   CASE 
      DUP "A" == THEN "Alpha" END
      DUP "B" == THEN "Beta" END
      DUP "G" == THEN "Gamma" END
      "Unknown letter"
   END
   SWAP DROP  @ Get rid of the original letter
 »

This code is identical to the following nested IF/THEN/ELSE/END block equivalent:

 «
    IF DUP "A" ==
    THEN
       "Alpha"
    ELSE 
       IF DUP "B" == THEN
          "Beta"
       ELSE 
          IF DUP "G" == THEN
             "Gamma"
          ELSE
             "Unknown letter"
          END
       END 
    END
    SWAP DROP  @ Get rid of the original letter
 »

Looping statements

[edit]

FOR/NEXT

[edit]

RPL provides a FOR/NEXT statement for looping from one index to another. The index for the loop is stored in a temporary local variable that can be accessed in the loop. The syntax of the FOR/NEXT block is:

index_from index_to FOR variable_name loop_statement NEXT

The following example uses the FOR loop to sum the numbers from 1 to 10. The index variable of the FOR loop is "I":

 « 
    0       @ Start with zero on the stack
    1 10    @ Loop from 1 to 10
    FOR I   @ "I" is the local variable
       I +  @ Add "I" to the running total
    NEXT    @ Repeat...
 »

START/NEXT

[edit]

The START/NEXT block is used for a simple block that runs from a start index to an end index. Unlike the FOR/NEXT loop, the looping variable is not available. The syntax of the START/NEXT block is:

 index_from index_to START loop_statement NEXT

FOR/STEP and START/STEP

[edit]

Both FOR/NEXT and START/NEXT support a user-defined step increment. By replacing the terminating NEXT keyword with an increment and the STEP keyword, the loop variable will be incremented or decremented by a different value than the default of +1. For instance, the following loop steps back from 10 to 2 by decrementing the loop index by 2:

 « 10 2 START -2 STEP »

WHILE/REPEAT/END

[edit]

The WHILE/REPEAT/END block in RPL supports an indefinite loop with the condition test at the start of the loop. The syntax of the WHILE/REPEAT/END block is:

 WHILE condition REPEAT loop_statement END

DO/UNTIL/END

[edit]

The DO/UNTIL/END block in RPL supports an indefinite loop with the condition test at the end of the loop. The syntax of the DO/UNTIL/END block is:

 DO loop_statement UNTIL condition END

See also

[edit]

Notes

[edit]

References

[edit]

Further reading

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
RPL (Reverse Polish Lisp) is a stack-based programming language developed by (HP) for its programmable scientific calculators, integrating (RPN) with Lisp-inspired features for extensible, interactive programming. Introduced in the late 1980s, it first became user-accessible on the HP-28C calculator in 1987 and powered subsequent models like the HP-48 and HP-50g series until 2015. RPL's design emphasizes dynamic allocation, object-oriented data handling, and symbolic mathematics, enabling users to create programs that manipulate complex expressions, lists, and functions on a theoretically unlimited stack constrained only by available . The language exists in two primary variants: User RPL, a safer, keyboard-accessible subset for end-users that includes type checking and supports common object types like numbers, strings, and programs; and System RPL, a more efficient, low-level version used internally by the calculator's operating system and advanced developers, handling all 29 object types but requiring specialized tools for creation and risking errors without proper stack management. Key features include named alphanumeric variables, conditional statements, looping constructs, and integration of algebraic and RPN modes, allowing symbolic computation of integrals, derivatives, and matrix operations directly within programs. RPL's threaded execution model, akin to Forth and , stores command addresses for rapid performance, making it suitable for both simple scripts and sophisticated applications in , , and scientific computing. Despite HP's discontinuation of RPL-based hardware, open-source reimplementations like newRPL continue to preserve and extend its capabilities for modern devices.

History

Origins and Development

RPL, or Reverse Polish Lisp, was developed by engineers at Hewlett-Packard's Corvallis Division beginning in 1984 as a more advanced and language for handheld calculators, surpassing the limitations of traditional (RPN) systems. The project, led by Bill Wickes, aimed to create a unified framework that could handle complex computations while supporting efficient development across varying hardware constraints. Formal development began around 1985, with the language's core design solidified by 1986 to enable sophisticated user programming in resource-limited environments. The primary design motivations centered on merging RPN's postfix notation for streamlined stack-based operations with Lisp's symbolic and list-processing capabilities, allowing for the creation of advanced applications such as equation solvers and graphics routines. Influences included Forth's execution for stack manipulation and Lisp's s-expressions for handling hierarchical data structures, all while incorporating object-oriented principles to promote extensibility without excessive memory demands. This hybrid approach addressed the need for a language that balanced performance for internal HP programming tasks with accessibility for end-user extensions. RPL's initial deployment occurred internally in 1986 within the firmware of the HP-18C business calculator, where it powered core functions but was not accessible to users. By , the language was finalized for broader public release, evolving into user-facing variants such as User RPL and System RPL to differentiate accessible programming from low-level system operations.

Implementation in HP Calculators

The first full user-exposed implementation of RPL appeared in the , released in January , which introduced symbolic manipulation capabilities and graphing functions powered by RPL's stack-based execution model. This was followed by the HP-28S in 1988, which expanded on the by adding directories for organizing programs and custom menus for improved user interaction. RPL saw major adoption in the HP-48 series, beginning with the HP-48SX in March 1990, which featured 32 KB of RAM, expandable memory via ports, and advanced symbolic math including integration and equation solving. The HP-48S followed in 1991 as a more affordable variant without expansion ports but retaining core RPL functionality. The series evolved with the HP-48GX in June 1993, offering 128 KB of RAM, (IR) printing support for direct output to thermal printers, and enhanced graphics capabilities. Later models continued RPL's integration with hardware advancements, such as the HP-49G released in September 1999, which introduced flash ROM for user-upgradable and 1.5 MB total memory (512 KB RAM and 1 MB Flash ROM). The HP-50g, launched in 2006, built on this by adding USB connectivity, support, and dual entry modes—algebraic alongside RPL—while maintaining full RPL compatibility for advanced programming. Firmware updates drove RPL's evolution across these devices; for instance, updates for the HP-48 series provided bug fixes, new mathematical commands, and improved symbolic operations. Similarly, updates for the HP-48G series incorporated an integrated equation for quick access to formulas and introduced soft menus for context-sensitive navigation. RPL's prominence waned after 2010 as HP shifted toward algebraic-focused models like the Prime series, which discontinued native RPL support, with the HP-50g marking the last official RPL calculator produced until its discontinuation in 2015. However, legacy RPL persists through emulators like EMU48 and community ports. The HP-48 series, in particular, profoundly influenced and by enabling custom RPL applications for matrix operations, , and simulations, becoming a staple for students and professionals in the .

Design and Features

Stack-Based Paradigm

RPL employs a stack-based execution model rooted in (RPN), where data and operations are managed through a dynamic stack rather than explicit variables or parentheses. This paradigm facilitates postfix evaluation, allowing commands to process operands sequentially without operator precedence issues. In the calculators, the stack serves as the primary mechanism for data flow, enabling efficient computation by pushing inputs and popping them for operations. The core of RPL's stack is a four-level visible structure labeled X (level 1, bottom), Y (level 2), Z (level 3), and T (level 4, top), though the underlying stack supports unlimited depth limited only by available . When entering , such as a number, the stack lift mechanism automatically shifts existing levels upward, duplicating the T level to preserve during rollover for operations exceeding four levels. For instance, entering 3 ENTER 4 + pushes 3 to X, lifts the stack to place 4 in Y, then the + command pops Y and X, adds them, and pushes the result (7) to X, demonstrating seamless handling without manual stack management. Commands in RPL execute by manipulating the stack directly: immediate commands, entered via the keyboard, apply instantly to stack levels, while stored commands within programs (delimited by << >>) defer execution until invoked, such as via [EVAL](/page/Eval). Arithmetic operators like + or * typically pop arguments from Y and X, perform the operation, and push the result to X, with stack lift re-enabled post-operation unless disabled by flags or specific commands like DROP. The DEPTH command queries the current stack depth, returning the number of objects, which aids in dynamic programming but does not prevent errors. Unique to the stack paradigm, RPL handles errors such as underflow (e.g., "Too Few Arguments" when insufficient levels are present) or overflow (rare due to dynamic sizing but possible with memory exhaustion), often trapped via IFERR to execute alternative code without halting. These conditions arise during command execution if arguments are missing, and recovery involves adjusting the stack manually or resetting levels. The stack integrates tightly with calculator hardware in the HP-48 series: the display shows the X level prominently, with Y, Z, and T visible in stack view mode, while keyboard input pushes directly to X, and I/O ports use stack-based commands like SEND to transfer objects from specific levels. This hardware-software synergy supports real-time interaction, such as pausing programs with INPUT to populate the stack from user entry. Unlike pure RPN in earlier HP models, which treated stack entries as numeric registers with fixed four-level depth and automatic drop-off, RPL extends the model by representing stack objects as typed entities, including symbols and lists, enabling more versatile data handling while retaining postfix efficiency.

Lisp-Inspired Elements

RPL draws significant inspiration from in its treatment of symbolic expressions as first-class objects that can be manipulated without immediate , enabling algebraic computations beyond mere numerical processing. This allows users to enter and store expressions like 'X^2 + 2*X + 1 as storable symbols on the stack, where they can be operated upon using dedicated commands. For instance, the SIMP command simplifies such expressions, transforming 'X^2 + 2*X + 1 into (X+1)^2, while FACTOR decomposes polynomials, such as converting 'X^2 - 1 to (X-1)(X+1). These operations treat expressions as data structures, mirroring Lisp's symbolic computation paradigm where code and data share the same form. List processing in RPL further echoes Lisp's foundational use of heterogeneous lists as a core data structure, where objects can mix disparate types such as numbers, strings, and symbols. Lists are denoted with curly braces, as in { 1 "hello" 'X }, and support functional-style operations that facilitate recursive and transformative programming. The HEAD command extracts the first element of a list (e.g., HEAD { 1 2 3 } yields 1), while TAIL returns the remainder ({ 2 3 }). For applying transformations across elements, MAP executes a program or expression on each item, such as MAP 'X^2' { 1 2 3 } producing { 1 4 9 }. These primitives enable concise, list-centric algorithms akin to Lisp's car, cdr, and mapcar functions. Automatic garbage collection represents another Lisp-derived mechanism in RPL, providing for dynamically created objects in resource-constrained environments like handheld calculators. As programs generate temporary objects—such as lists or symbolic expressions during computation—the system automatically reclaims unused memory from a dedicated temporary area, preventing leaks in extended sessions. Users can invoke explicit cleanup with commands like GARBAGE to force collection or to remove specific objects, ensuring efficient operation without manual deallocation. This feature, inherited directly from Lisp's runtime model, supports robust handling of complex data structures without overwhelming the device's limited RAM. Extensibility in RPL parallels Lisp's macro system by treating user-defined commands as first-class objects, which can be created, stored, and invoked dynamically to extend the language itself. Programs are defined within double angle brackets, such as << DUP SQ + >> for a simple squaring-and-adding routine, and can be named and stored (e.g., 'MYPROG STO) for reuse or further manipulation. This allows , where commands can generate or modify other programs at runtime, much like Lisp macros, fostering a flexible vocabulary that users can augment via RAM extensions. User RPL supports 17 base object types, while System RPL supports 29, with mechanisms to add or remove types, enhancing adaptability for custom applications. RPL's object-oriented traits, influenced by Lisp's uniform treatment of all entities as objects, assign every datum a type that determines its behavior, promoting polymorphism in operations. Core types include reals (%), complexes (C%), strings ($), lists ({ }), and symbols (SYMBOL), each with type-specific tests like TYPEREAL? or TYPESYMB?. Operations such as (+) dispatch based on types—for numbers, it performs arithmetic; for matrices or lists, it concatenates or element-wise adds—enabling seamless handling of diverse data without explicit type checks in many cases. This type-driven execution, via object prologs that define runtime behavior, unifies the system and supports Lisp-like expressiveness in a stack-based context.

Variants

User RPL

User RPL, often abbreviated as URPL, serves as the primary high-level programming interface available to end-users of calculators, such as the , enabling the creation of custom programs through a stack-oriented syntax that builds on principles. Programs in User RPL are defined within angle bracket delimiters, denoted as « » (entered via left-shift minus and right-shift minus, respectively), which encapsulate sequences of commands, expressions, and data objects separated by spaces. This structure allows users to automate repetitive calculations, implement conditional logic, and handle complex data types like lists and matrices without requiring low-level system knowledge. The syntax of User RPL is intentionally restricted to a predefined set of commands accessible via the calculator's keyboard and menus, ensuring safety by prohibiting , hardware manipulation, or execution of undocumented opcodes—features reserved for the lower-level System RPL. These commands include stack operations (e.g., DUP, SWAP), mathematical functions (e.g., SQRT, +), and control structures (e.g., IF, WHILE), all with built-in type checking to prevent runtime errors from invalid inputs. This limitation promotes portability, as User RPL programs can run consistently across compatible HP models without model-specific adjustments. Programs created in User RPL are stored as global named objects in the , accessible through the VAR menu, and can be managed using commands such as to remove them or to protect against accidental deletion during garbage collection. Within program blocks, local variables are defined using the → notation, which temporarily binds stack elements to names for use in subsequent commands; these locals are automatically discarded upon block exit, avoiding namespace pollution. For instance, the following User RPL program computes the roots of a ax2+bx+c=0ax^2 + bx + c = 0 by assigning stack values to local variables a, b, and c (assuming a on bottom, c on top of stack):

« → a b c b 2 ^ 4 a * c * - SQRT b NEG + a 2 * / b 2 ^ 4 a * c * - SQRT b NEG SWAP - a 2 * / »

« → a b c b 2 ^ 4 a * c * - SQRT b NEG + a 2 * / b 2 ^ 4 a * c * - SQRT b NEG SWAP - a 2 * / »

This program pops a, b, and c from the stack, calculates the discriminant, and pushes the two roots (with the -b + √d root first) back onto the stack. Error handling in User RPL is facilitated by built-in constructs like IFERR, which allow programs to branch on errors and manage exceptions such as invalid arguments or , enhancing reliability without halting execution. Alternative constructs like IFERR provide simpler branching on errors, returning the error number via ERRN for further processing. These mechanisms ensure that user programs remain robust and maintainable, particularly in educational or engineering contexts. The advantages of User RPL lie in its accessibility for non-expert programmers, such as students and engineers, who can rapidly develop tools for tasks like symbolic manipulation or using intuitive stack-based flows. It supports extension through user-created libraries, including those for like the UNITS library, which integrates unit conversions and consistency checks into programs. Overall, User RPL balances power and simplicity, making advanced functionality approachable while abstracting away system complexities.

System RPL

System RPL (SysRPL) is a low-level variant of RPL designed for the HP-48 series calculators, functioning as a superset of User RPL that provides direct access to resources and hardware through opcodes and mnemonics. It enables the creation of binary RPL objects for advanced programming, including firmware-level operations, by manipulating the calculator's stack, memory, and peripherals without the safety abstractions of higher-level variants. Originally developed for internal use in HP's calculator operating s, SysRPL allows programmers to invoke ROM calls via instructions like XROM, load bytes with LB, and perform direct stack operations, making it suitable for performance-critical or system-extending code. SysRPL programs are structured as binary objects compiled from mnemonic-based source code, where commands such as GOTO, GOSUB, and control structures like BEGIN...WHILE...REPEAT are represented by hexadecimal opcodes (e.g., #07295h for ISTOPSTO or #071A2h for indefinite loops). These programs are typically written using text files with extensions like .S or .M and compiled on a PC into loadable objects before transfer to the calculator. Mnemonics facilitate human-readable assembly, but the resulting code operates at the machine level, supporting even-address requirements for certain instructions and direct addressing of ROM entry points. Among its key capabilities, SysRPL provides low-level access to hardware registers (e.g., CPU registers and R0-R4), (via commands like DisableIntr at #01115h and AllowIntr at #0110Eh), I/O ports, keyboard scanning, LCD control, and operations on grobs. It also exposes undocumented features, such as status bits for handling and attention flags, enabling custom extensions like key handlers and control routines. This level of control was essential for developing custom ROMs on the HP-48, where SysRPL code could interface with the Saturn processor's architecture for optimized performance. Development in SysRPL relies on PC-based tools for compilation and the calculator's built-in utilities for execution and . Compilers such as RPL.EXE (for mixed SysRPL and Saturn assembly) or the Saturn assembler convert into binary objects, which are then loaded via tools like SLOAD or kermit-based transfers. Disassembly is performed using calculator commands like DUMP or PC tools such as Jazz and RVIEW, allowing inspection of ROM contents or loaded programs. The workflow involves saving and restoring RPL pointers (e.g., with SAVPTR), managing temporary variables, and testing in a controlled environment to avoid system instability. Despite its power, SysRPL carries significant risks due to the absence of argument validation, range checking, or error handling, potentially leading to corruption, stack overflows, or total crashes requiring a hardware reset. Improper use of operations like grob manipulation or disabling can result in "Memory Lost" errors or environment damage, and is incompatible with write-protected ROM cards. Such activities may void warranties, as they involve undocumented features and hardware access not intended for end users. In later models like the HP-50g, SysRPL remains available but operates under a Saturn emulation layer on the processor, imposing additional limitations on direct hardware interaction and custom ROM development. Historically, SysRPL was employed by HP engineers for developing the operating systems of the HP-28 and HP-48 calculators, providing the foundational layer for core functionality. The user community extended its use through tools like the Saturn assembler for creating custom ROMs and libraries on the HP-48, enabling advanced applications such as enhanced graphics utilities and system monitors, though adoption waned with the shift to emulated environments in subsequent models.

Syntax Basics

Commands and Expressions

RPL employs postfix notation, known as (RPN), in which operands are entered before operators, with operations performed on an object-based stack. For instance, the arithmetic expression to compute 2 multiplied by 3 is entered as 2 ENTER 3 *, pushing 2 onto the stack, followed by 3, and then multiplying to yield 6 on the stack. Stack manipulation commands facilitate data handling: DUP duplicates the top stack level, SWAP exchanges the top two levels, and DROP discards the top level. The quoting mechanism in RPL enables the creation of unevaluated objects for later execution or storage. A single quote (') prefixes symbols or expressions to prevent immediate , such as 'SIN X, which represents the unevaluated command SIN applied to variable X. For more complex unevaluated blocks, User RPL uses double angle brackets (<< ... >>) to define program objects that can be pushed onto the stack without running. RPL categorizes commands into built-ins for core operations and input/output functions. Built-in storage commands include STO, which stores the top stack object into a named variable (e.g., 5 'A' STO stores 5 in A), and RCL, which recalls a variable's value onto the stack (e.g., 'A' RCL). Input/output commands encompass printing utilities like PRST to output the stack contents. Expression parsing in RPL relies entirely on stack-based postfix evaluation, eschewing . Within lines or programs, the arrow operator () introduces temporary variables for intermediate computations, such as 3 → X X X * to compute the square of 3 using a . The EVAL command then processes quoted expressions by substituting stack values and executing, as in 'A + B' [EVAL](/page/Eval) to add variables A and B dynamically. Common patterns in RPL involve combining quoting and evaluation for flexible computation, exemplified by building symbolic expressions like 'A + B' followed by EVAL to perform runtime arithmetic using current variable values. For debugging in User RPL, the TRACE command supports step-by-step execution, allowing inspection of stack states during program runs.

Data Types

RPL employs a rich set of data types, collectively referred to as objects, which form the fundamental building blocks of programs and expressions. These objects are uniformly handled on the stack-based architecture, enabling seamless operations across numeric, collection, and symbolic forms. All objects in RPL are represented internally as tagged binary structures, featuring a 5-nibble header () that identifies the type and size, followed by type-specific data; this design facilitates efficient in System RPL implementations on . Numeric types in RPL include real numbers, represented as decimal values like 3.14, and complex numbers, denoted as pairs such as 3.14 2.71 i. Real numbers support standard arithmetic operations, with specialized functions like ABS computing the or magnitude. Complex numbers extend this with operations such as for and CONJ for conjugate, maintaining rectangular or polar forms as needed. Binary integers (BINTs), entered as like #A h, provide exact arithmetic and bitwise operations, while extended precision variants (e.g., %% for reals) offer higher accuracy for demanding computations. Collection types encompass arrays, lists, strings, and matrices, each designed for structured data handling. Arrays are fixed-size, multidimensional structures like [1 2 3], ideal for efficient access in numerical applications, while lists are growable and heterogeneous, such as {1 2 3}, supporting dynamic operations like appending elements. Strings, quoted as "hello", enable text manipulation with functions for and extraction. Matrices, a specialized form like [[1 2] [3 4]], facilitate linear algebra operations including inversion and transposition. These collections can nest and mix types, promoting flexibility in data organization. Symbolic types include algebraic expressions, quoted with an apostrophe like 'X^2', which represent unevaluated formulas for manipulation in computer algebra systems. Programs are executable objects delimited by << and >>, functioning as first-class entities that can be stored, passed, and invoked like any other type. Directories serve as namespaces, organizing variables and subprograms into hierarchical structures for . These types support symbolic evaluation and substitution, bridging numerical and abstract computation. Type checking is performed via the TYPE? command, which returns a binary code identifying the object's type; for example, code 1 indicates a , code 5 a list, and code 9 a symbolic expression. The OBJ→ command converts objects to their representations for display or . These mechanisms ensure robust handling, with RPL providing low-level access to all 29 object types, while User RPL restricts to safer subsets. Special objects include (∞), represented as a for handling limits and divisions by zero, and undefined (Undef), a placeholder for indeterminate results. Errors are managed as typed exceptions, generated by conditions like type mismatches or overflows, and via trapping mechanisms to prevent program crashes. These elements enhance RPL's resilience in numerical and symbolic contexts. Objects can be briefly pushed or popped from the stack for manipulation, preserving their type integrity across operations.

Programming in RPL

Program Definition

In RPL, programs are structured as executable blocks of commands that operate on the calculator's stack, enabling modular and reusable code. These blocks encapsulate sequences of operations, which can range from simple arithmetic to complex algorithms, and are fundamental to the language's stack-based, functional . Programs in User RPL are typically interpreted, while those in System RPL (SysRPL) are compiled for enhanced performance. Anonymous programs are defined using angled bracket delimiters « » to enclose the sequence of commands, creating a temporary program object that resides on the stack until executed or stored. For example, the block « + » defines an anonymous program that adds the top two elements on the stack. These blocks can be entered directly in command line mode and executed immediately by pressing , which evaluates the program and replaces it with the result on the stack. Alternatively, the program object can be pushed onto the stack first and then evaluated using the command. Named programs extend this by associating a global identifier with the block for persistent storage and easy recall, typically using the syntax NAME « commands » followed by ENTER to store it as a , or 'NAME' « commands » STO for directory-specific storage. Once named, such as FACT « commands », the program can be executed directly by entering its name and pressing ENTER, or by selecting it from the VAR menu and executing. This allows programs to be called recursively or from other blocks without redefining the code each time. Named programs are stored in the calculator's memory or ports, facilitating reuse across sessions. RPL supports nesting of programs within blocks, where inner blocks « inner » are treated as objects that can be executed via when needed, allowing hierarchical code organization limited only by available stack and heap memory. For instance, an outer block might contain « « DUP 2 * » [EVAL](/page/Eval) 3 + », executing the inner squaring operation before adding 3. This nesting depth enables complex, subroutine-like structures without fixed limits beyond hardware constraints. For modular code organization, particularly in the HP-48 series, binary libraries can be created to bundle SysRPL programs, which are then to the using the ATTACH command (e.g., LIBID ATTACH) to add their commands to the name resolution path, or detached with DETACH for cleanup. These libraries, often type 16 objects, store compiled code and metadata like titles, enabling efficient extension of the calculator's functionality without cluttering the main . Libraries are created using specialized tools or commands like CRLIB in SysRPL environments and stored in ROM or ports. User RPL programs remain interpreted bytecode executed at runtime for accessibility, whereas SysRPL programs are compiled into binary opcodes using assemblers like MASD, resulting in faster execution but requiring lower-level knowledge and tools for creation. This compilation process converts high-level commands into machine-efficient code, often stored as type 25 objects, and is essential for performance-critical applications like built-in calculator functions. A representative example is a named factorial program in User RPL:

FACT « DUP 1 > IF DUP 1 - FACT * THEN DROP 1 »

FACT « DUP 1 > IF DUP 1 - FACT * THEN DROP 1 »

This defines FACT to compute the of the top stack : if greater than 1, it recursively calls itself on n-1 and multiplies by n; otherwise, it drops the input and returns 1. Execution involves entering a number (e.g., 5), then FACT, yielding 120.

Variables and Scope

In RPL, global variables provide persistent storage for data and programs, accessible across the calculator's execution contexts unless restricted by directories or protection mechanisms. They are created and managed using the STO (store) command to assign values from the stack to named objects in the current directory, such as , with recall via RCL. For example, entering 5 'X' STO stores the number 5 in the 'X', which remains available until manually removed, and can be retrieved with RCL 'X'. These variables appear in the VARS menu for easy inspection and are resolved by searching the current directory path upward to the . Local variables, in contrast, offer temporary storage scoped exclusively to the execution of a program block or subroutine, promoting encapsulation and reducing pollution. They are defined using the → notation at the start of a program, where stack objects are assigned to named that are automatically deallocated upon block completion. A representative example is the program « 3 4 → A B A^2 B^2 + SQRT », which uses A and B to compute the of the (hypotenuse of a 3-4-5 ) without affecting global state; here, A receives 3 and B receives 4 from the stack. Local variables can also be created with the LOCAL command for named bindings or unnamed via access (e.g., 1GETLAM for the first local), and they support nesting within subprograms. Scope rules in RPL ensure that local variables shadow any global variables of the same name within their defining block, allowing inner scopes to override outer ones without altering persistent data; upon exiting the block, locals are unbound, restoring access to globals. Nested local scopes can reference variables from enclosing blocks, supporting recursive or modular program design, limited only by available memory and hardware constraints. Variable resolution prioritizes locals in the current execution frame before falling back to globals in the directory hierarchy. RPL organizes global variables into hierarchical directories functioning as namespaces, enabling structured storage such as in a subdirectory like MYLIB with access to MYLIB/MYVAR via explicit path or current context. The PATH command returns the list of directories from to the current one, while →GIL (global indirection list) facilitates indirect referencing of globals by name without direct recall, useful for dynamic variable manipulation. Directories are created with CRDIR and navigated using commands like UPDIR or PGDIR. Management of variables includes purging for cleanup and protection against automatic removal. The PURGE command deletes global variables (e.g., 'X' PURGE), freeing memory and requiring confirmation unless disabled by flags; it cannot remove in-use objects or non-empty directories, and locals are auto-purged on scope exit. To safeguard globals from garbage collection during low-memory conditions, the command marks them for preservation in non-volatile areas, with restoration via unarchiving; PRESERVE can temporarily protect objects during operations. Heap management in RPL is largely automatic, with local variables allocated dynamically from available RAM upon creation and reclaimed post-execution to prevent leaks. The system performs garbage collection as needed, prioritizing archived or preserved objects. usage, including heap allocation for locals and globals, can be monitored with the MEM? command, which reports free bytes, total RAM, and object counts (e.g., MEM? displays a summary like available/used/total). This aids in optimizing programs by identifying high-memory variables.

Control Structures

Conditional Constructs

RPL provides several constructs for conditional branching, enabling programs to execute different code paths based on conditions evaluated from the stack. The primary mechanism is the IF-THEN-ELSE-END structure, which tests a condition and selectively executes associated program blocks. In this four-part construct, a sequence of commands forming the condition is placed between IF and THEN, leaving a value (1 for true, 0 for false) on stack level 1; non-zero values are treated as true. If the condition evaluates to true, the commands between THEN and ELSE (or END if no ELSE is present) are executed; otherwise, the ELSE block is executed if specified, or execution skips to after END. The is consumed regardless, ensuring automatic stack cleanup. This structure supports nesting within program delimiters « », allowing complex decision trees without explicit depth limits beyond general stack constraints. For concise branching without ELSE, the IFT command serves as a two-block shortcut. It requires the condition on stack level 2 and the true-branch program on level 1; if true, the program executes and both items are removed, while false skips execution without altering the stack beyond the condition. Similarly, IFTE handles if-then-else in a compact form, taking the condition on level 3, true program on level 2, and false program on level 1; it executes the appropriate program and cleans up the stack by removing the condition and the unused program. These shortcuts integrate seamlessly with Reverse Polish Notation, avoiding the need for explicit END in simple cases. Boolean handling remains consistent, with logical operators like AND, OR, and NOT available to combine conditions prior to these commands. Errors such as stack underflow or invalid boolean types (e.g., non-numeric) trigger standard program halts, potentially recoverable via IFERR. Multi-way branching is facilitated by the CASE-THEN-END construct, which evaluates sequential conditions until one succeeds. The syntax begins with CASE followed by a condition (often an equality test like value case ==), then THEN and its block ended by END; additional condition-THEN-END pairs follow, optionally concluding with a default block before the final END. The first true condition (non-zero) executes its block, skipping the rest and consuming the test; if none match, the default executes. This enables efficient switch-like behavior, with equality matching typically via the == operator for value comparisons. Stack behavior mirrors single IF, consuming each test as evaluated. Nesting CASE within other conditionals is supported, though excessive depth may approach stack limits. For conditional program execution, XEQ can invoke a named program object after a condition, often paired with IFT for brevity. An illustrative example is finding the smallest of three numbers a, b, c (assuming pushed onto the stack before execution):

« → a b c IF a b < a c < AND THEN a ELSE IF b c < THEN b ELSE c END END »

« → a b c IF a b < a c < AND THEN a ELSE IF b c < THEN b ELSE c END END »

This evaluates conditions to compare and select the minimum, demonstrating nested IF usage with logical AND for combined tests.

Looping Mechanisms

RPL provides several looping mechanisms to facilitate iteration over sequences or conditional repetition, primarily through index-based for-loops and condition-based while-loops. These constructs operate within programs defined using double-angle brackets << >>, leveraging the stack-based nature of RPL for variable management and computation. All loops are designed to execute commands until a termination condition is met, with no native support for decrementing loops; programmers must adjust indices accordingly for reverse iteration. The FOR/NEXT construct implements an index-based loop with an accessible loop variable. Its syntax is start end FOR index body NEXT, where index is initialized to start before the first iteration and incremented by 1 after each body execution until it exceeds end. The index remains available on the stack or in local variables during the loop for use in computations. For example, to compute the sum of integers from 1 to 10, the program << 0 → sum 1 10 FOR I sum I + → sum NEXT sum >> accumulates the result in sum, yielding 55 upon completion. This mechanism ensures predictable iteration counts when the range is known in advance. In contrast, the START/NEXT variant omits the loop index for cases where explicit access is unnecessary. The syntax start end START body NEXT uses an internal counter starting at start and incrementing by 1 until surpassing end, but programmers must manage any custom tracking variables manually within the body. An example summing squares from 1 to 5 uses << 0 → s 1 → k 1 5 START k^2 s + → s k 1 + → k NEXT s >>, where k is a initialized to the start value before the loop. This form is useful for simple repetitions without index dependency. Both FOR and START support custom increments via STEP. The syntax extends to start end FOR index body step STEP or start end START body step STEP, where step defines the increment (positive for forward, negative for reverse iteration). The loop terminates when the index passes end in the direction of step. For instance, to sum even numbers from 0 to 10, << 0 → sum 0 10 FOR I sum I + → sum 2 STEP NEXT sum >> produces 30. This flexibility allows efficient traversal of arithmetic sequences without manual index adjustment. For indefinite iteration, RPL offers the WHILE/REPEAT/END construct, which checks a condition before each execution. The syntax is WHILE condition REPEAT body END, where condition is evaluated at the loop's start and after each body; if true (non-zero), the body executes, otherwise the loop exits. This pre-condition testing ensures the body may not run at all if initially false. A practical example sums numbers from 1 downward until reaching zero: << 0 → sum 10 → n WHILE n REPEAT sum n + → sum n 1 - → n END sum >>, resulting in 55. Programmers must update variables in the body to avoid infinite loops. The DO/UNTIL/END provides post-condition control, executing the body at least once before checking. Its syntax DO body UNTIL condition END runs the body, then evaluates condition; repetition continues until condition is true (non-zero). This is ideal for scenarios requiring initial execution regardless of state. For the same sum as above but ensuring at least one iteration, << 0 → sum 10 → n DO sum n + → sum n 1 - → n UNTIL n 0 = END sum >> also yields 55, with the condition n 0 = triggering exit. Like WHILE, infinite loops are possible without proper variable modification. RPL lacks built-in break or continue statements for early termination or skipping iterations. Instead, programmers employ the EXIT command to abort the entire program or use stack-based flags (e.g., a conditional pushing a termination marker checked via ) for simulated . For example, within a loop, an IF clause can invoke EXIT if a flag is set, though this ends the program rather than just the loop. More granular control often involves restructuring with nested conditionals or error-handling via IFERR to drop out early.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.