Enter RPN

A Matter of Style
Login

A Matter of Style

rejig takes inspiration from go fmt, implementing my preferred RPN programming style with no option for customization, on purpose. One may quibble with the choices I have made, but when the rules are fixed and automatically enforced, developer focus tends to shift from bikeshedding to substantive matters.

Identifier Names

The R47 programming language was not intended to be parsed, as such, but to support its round-tripping features, rejig makes several op name changes. There is presently no documentation for all the changes other than what you find in the source code’s internalOpInfo map, starting roughly halfway down the ops file.

Spaces

To avoid a need for quoted identifiers — which brings on a whole pile of other trouble — rejig either removes spaces or replaces them with hyphens. A good example of this is the “a b/c” function on the calculator for entering fraction mode, which rejig spells a-b/c. I do realize that this is ambiguous with respect to subtraction, but it is a common way of expressing a mixed fraction, as with “1-⅔” in US cookbooks.

The biggest single case you need to be aware of is with the numeric tests. What the R47 styles “x≠ ?” is canonically spelled without the space in rejig. For instance, the way to check for inequality between the X and Y registers is to write:

  x≠? Y

Or, in support of its HP-42S family inputs, rejig also supports this combined style:

  x≠y?

Case Sensitivity

rejig is case-insensitive where possible. It isn’t, always.

Op names are case-insensitive when there are at least three ASCII letters, since the chance of collision becomes low enough to tolerate. Without a lower bound on this rule, the HP-32SII r op to retrieve the calculated linear regression coefficient would be ambiguous with the R op to give a random number on that same machine. For a two-letter example, we have the HP-32S BN spelling for the binary input mode, which tout le monde spells BIN; the length limit avoids a confusion with the R47’s Bn op, returning Bernoulli number `B_n`.

The “ASCII” part of that rule is critical: mathematicians have made many case distinctions over the centuries. To pick on the statisticians, they’re big on distinguishing σ from Σ even though both are Greek sigma. This rule prevents CL𝛸 from being allowed as a cutesy way of spelling CL𝑥; that third character is Greek capital chi, preventing us from accepting it as a swooshy mathematical italic X.

A different context where the same basic logic applies is in register naming. Some machines rejig knows about make a distinction between the a and A registers, the first treated as local and the second as global.

And yet, the three-letter rule is not absolute, else rejig could not obey the separate rule that alpha-named variables and labels are case-sensitive in all forty-series machines including the R47 itself. Because LBL ‘ReJiG’ is not accepted as meaning the same thing as LBL ‘rEjIg’, we must conform.

Yes, this is all a confusing mess, but use the above examples to maintain perspective. Even that pinnacle of engineering (hah!) HP could not make up their own minds on this, within their own line of development.

These rules then interplay, adding additional complexities. The “three ASCII letters” minimum requires rejig to make separate accommodation for both the thirty-series on-screen display of the swap op (x<>y) and the forty-series variant X<>Y, doubtless owed to the HP-41C all-uppercase legacy. You can prove to yourself that rejig isn’t doing automatic case-folding by trying x<>Y or X<>y.

One exception occurs when lowering R47 or HP-42S family programs to a lesser form, requiring rejig to map that string to one of the legal letters or numbers on the target machine. In this way, a reference to “LBL ‘a’” can become “LBL A” in HP-15C output, giving the illusion that rejig is case-folding. What it is actually doing is recognizing that the 15C has no alpha labels, but that label “A” hasn’t been used yet, allowing it to provide a straightforward mapping. If that label had been used already, it would move on to B and so forth until it hit E, then start in on the numbered labels allowed on the 15C.

Question Mark

The R47 uses a trailing question mark in operation names for three incompatible purposes:

Worse, it isn’t even consistent. For instance, the R47 sensibly defines both the SSIZE8 mode setter and the corresponding SSIZE8 named system flag, but it then goes and adds the SSIZE? op, where the trailing question mark makes it look like a test that will apply do-if-true logic to the following instruction, when what it actually does is return either 4 or 8 on the stack in X! The proper way to react to the SSIZE4 limit being active is:

FS? ‘SSIZE4’
  GTO ‘fourVer‘

Here the trailing question mark on the FS? does mean we’re looking at a test op, making the following GTO conditional.

Because this ambiguity is inconvenient within rejig, its canonical form realigns these cases to form a consistent scheme:

This leaves test ops unambiguously using the trailing question mark.2

There are aliases to allow you to call the op by the name seen on-screen, except when this creates an ambiguity. To return to our SSIZE example above, rejig will not only accept its rationalized *SSIZE getter spelling but also the on-device SSIZE? spelling even though that makes it look like a test. rejig will canonicalize these ambiguous forms when formatting a program containing them.

Critically, these efforts permit lossless round-tripping in rejig. Ambiguous names are fine on the calculator where it can use the menu path to disambiguate the cases. It is even fine in the WRITEP output, where differing P47 op code bytes distinguish the cases. Where we run into trouble is in our UTF-8 I/O paths, where these disambiguating data are unavailable. For one, the automatic reformatting feature rejig would collapse ambiguous cases down to the same op code if we did not cope somehow.

Comments

rejig recognizes three styles of comments:

When one of those is encountered — outside certain exception cases3 — everything from that point to the end of the line is ignored. The intent is that the default rejig output format (.p47u) be as useful for programmer-to-programmer communication as in getting programs from your computer into an R47.

One of the first things you are likely to notice on studying my triangle solver is all the explanatory comments, one on nearly every line. I’ve always been a big fan of documenting code, and I find comments especially helpful with terse languages like the one backing the R47.

Take the comments showing stack register movements: they aid the reader in understanding the program by documenting how the data moves on the stack at each step. As currently written, these comments assume the simpler SSIZE4 mode even though the program was tested with SSIZE8 mode; my choice was to pick one for clarity or document both options, muddying the presentation. Since the only material difference is that the interactions involving T affect the R47’s D register instead, I took this simpler tack.

Indenting

The rejig indentation scheme is simple: it adds a 2-space indent for each LBL after the first and subtracts a level for each RTN. The op following a do-if-true op gets an extra indent level. An END op brings the indent level back to 1, and .END. zeroes it. Easy.

The sole exception results from a combination of the above: a do-if-true RTN does not reduce the indent level because it is conditionally the end of the subroutine.

This scheme does not always produce neat hierarchies of subroutine calls, but that is an honest reflection of the unstructured nature of RPN programming. If you find yourself questioning the formatting, first ask whether it is a fair expression of the ops as given. Surprising indent levels might be telling you something important about how the program actually works, which you might wish to address.

That is my experience, at any rate. I find that my scheme makes far more sense than the one built into the R47’s PEM mode and its RTF outputs.

Blank Lines

Doubtless because of the limited screen real estate on calculators — even biggish ones like the R47 — it is traditional in RPN to write one line after the next, all the way through, without any blank lines.

Yet, when writing the program out as described above, I do believe we can accept adding one blank line above each obvious section break for readability.

A blank line is added before any group of LBL and REM statements, in any quantity and order, except for the first in a file. This rule is meant to break a long program up using the common pattern of having a REM that comments on each labeled subroutine.

rejig adds these when pretty-printing and strips them back out when reading text-form programs back in.

(You may now wish to return to the rejig home page.)

License

This work is © 2026 by Warren Young and is licensed under CC BY-NC-SA 4.0


  1. ^ My prior accommodation to this inconvenient reality used a “get” prefix to much the same end, but I demoted it to alias status in rejig 0.23.0. One reason is, I didn’t like how long it made the op names. The other is, the WP43 project adopted a leading question mark scheme after I pointed this ambiguity out, but after due consideration, I decided they had the right general aim point but undershot the mark.
  2. ^ Internally, rejig uses a leading question mark to disambiguate the flag setting ops from the named system flag items themselves. A weakness in its internal data model will let you use these false “ops” in a program, causing XEQ on the R47 to complain, “Non-programmable command, please remove”.
  3. ^ These vary based on the file format. For instance, the main HP-15C input module needs to treat the RAN# operation as a whole, not as RAN followed by a shell-style # comment. As a result, that parser requires a space or tab before # to make the distinction.