Version 0.12.0, 2026.02.27
Added support for the RLM-15CX simulator’s file format, implemented as a simple JSON parser/generator and data massager in front of the existing hp11c format handlers.
Added support for the RLM-11CX simulator’s file format, implemented as a partial but hard fork of the existing HP-15C handling due to the significant amount of differences between the two machines.
Added support for several uses of “
I” in HP-15C I/O:- “
I” alon (as an operation) maps to the R47 opCOMPLEX - “
RCL (i)” raises to “RCL →I” in R47 form - “
FIX I” sets the display mode from the register indicated by the contents ofI
- “
Added round-tripping for “
STO/RCL RAN#” on HP-15C. This becomes into a generic named variable at the R47 layer due to lack of a reserved variable of that name having the same effect.Added the HP-15C
DIMop. (MATRIXgenerally not yet working.)REMops found in input become inline comments in--to hp15coutput, and vice versa for--from hp15ccontaining such.Programs read in via
--from hp15cnow include implicitENDand.END.statements so that when writing them out to an R47 format, they show up. This improves round-tripping fidelity in chains that start with R47 formats.Tolerating comma in place of dot for registers in
--from hp15c. They’re normalizd to R.0 and such on output.Numeric labels in HP-15C input no longer become single-character string labels with that digit in R47 output. It maps 0-9 to R47 00-09 and .0-.9 to 10-19, with lossless round-tripping.
File format guessing based on extension now does case-insensitive matches. (Some files in the Manz example collection use
*.15C)Reals starting in a string-coded form (as with the HP-15C input) are kept in that form unless they are long enough to benefit from being translated into the R47’s version of decNumber 34-digit values. This keeps input forms lossless through round-tripping.
Values that look integer-like in DM32 input have a decimal radix appended to force them to be considered reals in R47 output. We do this for the same reason as our HP-15C I/O path always has: the HP-32S line doesn’t have a distinct integer data type, so any value sourced from it must be treated as a real.
DM32 output now includes a more detailed header comment modeled on the style invented for our
--to hp15coutputs.Improved handling of
CMPLXop mapping in DM32 I/O.The
--from/tooptions now override the file type guessing based on extension unconditionally; it guesses only when forced. With “-” for stdin/stdout, it continues to use the existing defaults when these options are not given since guessing is not possible then.Added
.txtas another alias for our pretty-printed version of R47 programs in file extension guessing.Fixed handling of ops like
FIXin DM32 output when the op originated from another format.
Version 0.11.0, 2026.02.21
Added experimental HP-15C support via the Manz simulator’s program format, which gives us interop with four different calculators.
DM32 output now halts if the input includes a short integer in a base it cannot support with the numeric display mode ops.
DM32 output now warns if it sees
ENDwithout anything beyond it other than one last optional.END.op.Special-cased the output for the DM32’s oddball “
∫FN d F” op.
Version 0.10.0, 2026.02.19 (yes, later that same day)
DM32 output can now include date, time, and DMS angle literals from the input, lowered from R47 typed form into untyped HP decimal form. This includes a fix to allow recognition of fractional seconds in DMS angle literals in R47 inputs.
Emulating the
DROP𝑥op on DM32 output as CL𝑥 then add, making use of the additive identity: the prior Y + 0 = Y as the new X.When giving multiple input files (
./rejig tests/r47/*) it now infers the input file type from the file name extension separately for each.Complex decomposition in DM32 output now works for the binary case as well.
Improved handling of LBL conflicts after smashing them down to a single uppercase letter.
Added translations for the DM32 linear regression slope (
m) and Y-intercept (b) ops by making use of the R47’s newLR:a?ops, which expose these values in a generic way, not tied to L.R.Added the new R47 energy conversion ops: erg, foe, joe.
Improved handling of op name aliases in DM32 I/O.
Added the
x⇋yalias. (Old style used on my site before I settled on the R47 style,𝑥⇄𝑦.)Fixed a bug with “
GTO D” type ops in DM32 output.
Version 0.9.0, 2026.02.19
DM32 I/O recognizes
CMPLXops and attempts to map R47 complex literals to the DM32 scheme where the real and imaginary parts take a stack register each.Op names with fewer than 3 ASCII characters are now case-sensitive in lookups to avoid confusions between
Rthe molar gas constant andrthe linear regression correlation coefficient. Three English letters gives 26³=17576 possible op names without adding in all the other characters one could use to make the name unique.And yes, this does mean
ris now supported as an R47 op alias forCORRand as a direct op name in DM32 programs.(Plus the
x̂andŷprediction ops.)Variables and labels can now be multi-lettered and non-alpha in DM32 inputs and outputs. These otherwise illegal referents are mapped to an unused A-Z, which is then remembered in case it reappears, in which case it reuses that mapping. It still tries to use the first letter, capitalized, but a clash will override that.
The DM32
/cop is now transformed on input into aDMXwith the literal preceding it becoming the op arg, and vice versa for output.Sequences of DM32 PSE ops are now counted and divided by 10 to give an approximately equivalent PAUSE op arg, and vice versa on DM32 output, with the PAUSE arg producing an equivalent PSE count.
Whole-line comments in DM32 input now produce
REMops.Fixed round-tripping
INPUTops thru the DM32 chain.Issuing a diagnostic in DM32 output when it encounters a string literal provided as input.
The
--versionflag now pulls its info from VCS info embedded by the Go compiler if present, which it should be if run from a Fossil checkout. (The prior solution required that, too.) The release builds still pass the Taskfile.yaml fileVERSIONvariable in, but for normal builds, the commands are shorter and clearer.
Version 0.8.0, 2026.02.18
- Added experimental DM32 statefile support, both input and output. There is much left to do, but it’s functional within its limitations.
Version 0.7.0, 2026.02.17
Improved automatic blank lines in UTF-8 output. It now works on any number and ordering of
LBLandREM, with the whole group treated as one.Comments now align in UTF-8 output four spaces past the end of the longest line.
Added the
--fix-obsoleteoption to try upgrading>OBSOLETE<ops found in inputs to ones having the same name without the brackets, as long as the argument types are the same.Updated the op names to track R47 firmware 00.109.03.00b2. Changed names become aliases.
Using the canonicalized
tests/AllOps2.*to produce an alternative code coverage report viamake intcov. Ideally, this integration test should run all the code tested by the happy-path unit tests, plus things like file I/O, option parsing, etc. It will not test known error conditions since we want it to run to a successful conclusion every time.Straightened out a confusion between system flags — always named like ‘TDM24’ in UTF-8 output — and the numbered global flags 00-99. There is no such thing as “SF00” but there is “F00”. Without the leading F, it is treated as a numbered global flag: “SF 00”.
System flags do have underlying numeric values, but we see no value in allowing you to specify them as SF00 = ‘TDM24’ given that the R47 doesn’t do this itself.
This sorts out the single biggest remaining unintended difference between the upstream “AllOp’s” program and our local version, which now differs primarily in the added tests it offers.
UTF-8 output of the
SKIPandBACKops now uses zero-padded three-digit formatting, as the R47 does.Fixed handling of 3-digit
MSGargs.Added more op aliases.
Version 0.6.0, 2026.02.15
MILESTONE: This release round-trips the AllOps program.
Well, mostly!
If you
RESETyour R47 and save out a copy of this program to ourtestsdirectory and then run “make test” it will give several bytes of difference. As far as I can tell, these are due to the hand-coded nature of this program viagenerateTestPgmsupstream.Fixing these breaks other known-good tests, but if you instead say:
$ ./rejig .../path/to/AllOps.p47 -o tests/AllOps.p47u $ ./rejig tests/AllOps.p47u -o tests/AllOps.p47...the resulting canonicalized program then round-trips reliably.
I’m calling that success pending proof that this process overlooks something important.
Added support for binary reals by vendoring the C47/R47’s custom fork of decNumber, built with the same options for 100% fidelity.
This also adds support for binary complex numbers.
WARNING: This only works for native builds, which ipso facto excludes five of the six release binaries I cross-compile on the release build machine. To fix that, I would have to maintain six different build boxes, then build a single native binary on each one. (Yes, yes, VMs blah blah, I know. It’s still a PITA I am unwilling to tolerate.) If you need this to work reliably, build from source.
P47 files with string literals marked as real but without a radix mark or “e” notation are normalized by appending a decimal in UTF-8 output so that they are not misinterpreted as long integers by the UTF-8 input parser.
There is no way to cause this to happen in PEM: entering a “0” will cause it to be coded as a long integer, same as in NIM. It only occurs in the C47
src/generateTestPgmswhich emits the programs you get onRESET, because it writes the program bytes directly, allowing it to declare a single-digit value as real.If you then
WRITEPone of these out, it won’t round-trip throughrejigbecause it changes these 1-character string-formatted reals to 2-character values, but with the proper type. This is why we now carry a local copy oftests/ReMp261.p47: to serve as an example of the difference. This one will round-trip because I went into PEM and manually re-entered the “1.” value on line 0006.Obsolete ops are now recognized as such, but it causes an immediate cessation of input processing for the same reason the R47 stops XEQ on encountering one of these.
The case-insensitive op name matching now works for ASCII letters only to avoid various confusions:
- visual: math italic 𝑥 to 𝑋, confusable with Greek χ
- semantic: σx to ΣX, changing its meaning to statisticians
Replaced the method of discovering the Fossil checkin hash used to compile the binary for use in the
--versionoption with one that does not require that you are using a recent trunk version.Fixed UTF-8 I/O of single-letter flags:
SF CString-formatted number literals of the form “1e3” are now considered reals, not integers.
Short integers of base > 10 now use uppercase A-F to match the P47 convention for string-formatted literals.
Fixed a conflict between shell-style comment parsing, the
#(zero) op, and short integer base tags.
Version 0.5.0, 2026.02.14 — the “Valentine’s Gift” release
Added support for date, time-of-day, time interval, and DMS angle string-formatted literals.
Added support for “
×10^” and “*10^” scientific notation in parsed UTF-8 inputs. (Previously only supported “e” notation.) This extends to complex numbers, where we must be careful not to be confused between the optional sign in the exponents vs the+/-separating the real and imaginary parts.Added handling for the shuffle (
⇄) op’s special arg type, which takes a register-mapping string like “TZYX” to mean “reverse the four main stack registers.” (Leave them as-is would be “XYZT”.)Added several missing ops:
CASE,DAY,DECOMP,MVAR,SINC,Δ%x̄₁(delta% mean),ζ(ϕ,𝑚)(Jacobi zeta)Added a large number of op aliases, most of which were needed to allow
PROGRAMS/AllOp's.txtfrom the C47 distribution to parse.In certain cases, it went the other way: our local version of that file has changes to conform to the
rejigconvention.Op name and alias lookup is now case-insensitive.
UTF-8 inputs can now use “bare” values instead of the less ambiguous references we emit on UTF-8 output:
- registers can be 00-99 or .00-.99 (“R” elided)
- system flags can be 00-99 (“SF” elided)
Prior versions already allowed local flags to be referenced as .00-.31, without the leading “F” we emit on UTF-8 output.
The UTF-8 short integer parser now uses
uint64internally for positive numbers so it can get the full 64-bit range. It drops back toint64only for negative numbers.UTF-8 input error messages now include the line number.
Fixed argument handling for several ops:
STO↑,STO↓,RCL↑,RCL↓PGMSLV,SOLVE∫fd
Fixed recognition of the
→INTop in P47 inputs and its name-to-op mapping in UTF-8 inputs.Fixed several cases of indirect access syntax, for both UTF-8 and P47 byte-coded inputs.
Fixed handling of args > 249 for the
SKIPandBACKops. These values normally mark a “special” arg (e.g. indirect access) but these two R47 ops allow the full 8-bit arg range.Removed the
?from theαLENGandαPOSop names. They are not do-if-true ops. They are also not getters, which is why they did not gain a “get” prefix at the same time.Op name parsing no longer succeeds if the arg follows without any separating whitespace. Previously,
PAUSE99would work, which might seem okay to a certain type of person, but it also meant it would try to treat a line likeLocR?as aLocRallocation with value “?” and fail with an obscure message.We have to flag cases like this clearly owing to our decision to drop the trailing “?” and prefix it with “get” to avoid treating these as do-if-true ops.
Bogus op names were being passed through as string literals. They now cause an error meant to aid diagnosis.
Version 0.4.0, 2026.02.12
Added the
--line-numbersoption to include 4-digit line numbers in UTF-8 outputs, as the R47 does in RTF and PEM outputs.Added support for string-formatted complex literals:
3.14-2.5𝒾Short integers in UTF-8 output use Unicode subscripts for the base. On parsing UTF-8 program text as input, it recognizes the new syntax while allowing the plain-ASCII (e.g.
#10) alternative syntax. “173₈” and “173#8” are both valid octal expressions of decimal 123.Improved handling of short integers:
- P47 input: improvements to recognition of binary vs short integer data byte code formats
UTF-8 input:
- use string-formatted short integers for values under 1e6
- use binary (
uint64_tLE) short integers for all else
P47, UTF-8 output: updated to track the above changes
Added special-case handling for the
KEYop, which cuddles the requisiteXEQ/GTOfollowing it on the same line, per the HP-42S.Straightened out a confusion regarding 𝜋:
- Used as an op name in UTF-8 program input text, that math italic character refers to the 🟧-shifted R↓ key press. We also accept as aliases “pi” and the unstyled “π”.
- The mathematical bold italic character “𝝅” refers to the predefined constant backing this, which has a separate op code.
Added several op aliases:
- All predefined CNST ops except 𝝅 lest we break the above. In a few cases, the primary op name changed instead, and what we were using as the name became the alias.
- Added trig aliases to match on-calculator names:
COSforcos(𝑥)and so forth. Also added ones likearccosh(x)foracosh(x) - Added
CEILandFLOORaliases for⌈𝑥⌉and⌊𝑥⌋.
Changed
ViewandTestop names to all-caps to match their display on the calculator.Added “
p47u” as an alias for “utf8” in--from/tooptions, though we continue to prefer the latter as more visually distinct.The
--from/tooptions can now be left off when giving file names using the supported format names as extensions.Extended the blank-before-LBL in UTF-8 output to the common
REM+LBLpairing. (Does not yet handle multipleREM.)No longer doing indent reduction on
RTNif it is itself being indented extra under do-if-true logic.Fixed a bug in the decoding of “
STO M” registers and beyond due to a bounds check error in the range of local registers: they run to R.98 max, not R.99.Fixed UTF-8 output for indirect variables, e.g. “
STO →‘foo’”
Version 0.3.0, 2026.02.11
Added operation name aliases:
- mathematical italics: DROPx = DROP𝑥
- blackboard letters: T->zyx = 𝕋→𝑧𝑦𝑥 (also ASCII arrows!)
- inverses like
cos⁻¹(𝑥)may be given asarccos(x)oracos(x) - Unicode subscripts now allow LaTeX equivalent; e.g.
log_2(x)forlog₂(𝑥) - ops may have multiple spellings, e.g.
lb(x)forlog₂(𝑥) - and more!
This then allows more extensive use of mathematical italics in op names, making the P47U language (such as it is) prettier without being less typeable. Type in plain ASCII, then run your program thru a
rejig -f utf8 -t utf8pass to “upgrade” it to the canonical form of the op names.Added a blank line before labels after the first in pretty-printed output to visually break up routines containing subroutines.
The P47U pass-thru mode now preserves end-of-line comments and transforms comments alone on a line to
REMops. Comment styles are normalized to the assembly language style (;) and are aligned four spaces past the end of the longest line seen in the input, clamped to column 20 as a minimum. This makes it less painful to use rejig to “upgrade” existing text program files.Fixed the extra indent applied after a do-if-true test op: it was being applied to that line instead of the next.
Fixed the definition of the “∥” (is parallel to) op.
Version 0.2.0, 2026.02.10
Added support for unsigned binary short integer literals.
Consequently, shell-style comments now require a space before them to be interpreted as such, else the parser would truncate the base annotation. (e.g.
7f#16) Applied the same rule to assembly style comments (;) just in case; needed for matrix literals?Added handling of “
-” as the input file name to read from stdin. It also allows this for--output/-ofor symmetry, but all this does is make it as if the option was not given, since this is the default behavior.Added code coverage reporting, and expanded the self-tests to bring that metric up to ~90%. The bulk of what’s missing are the known error cases.
Added a
Makefileas a wrapper for the Task build system, for the convenience of those used to it.Implemented several MIA ops.
Fixed parsing of several ops:
- matrix index inc/decrement:
I+,I-,J+,J- - matrix dimensioning:
M.DIMandM.DIMgr
- matrix index inc/decrement:
Fixed UTF-8 parsing for indirect register/variable access using a Unicode arrow (as opposed to the ASCII “->” alternative)
Using the same Unicode character to indicate swaps (⇄) as in the C47 custom font. I mistakenly thought this was a nonstandard custom glyph, but it is merely giving a different appearance to something semantically identical, U+21C4. We can therefore safely use it as-is across fonts.
Tightened up parsing of quoted strings: the closing quote style must match the opening quote. Not only does this mean single vs double, it means if you start a string with a curly quote, you must use the opposite-curled version to close that quote.
Version 0.1.1, 2026.02.08
The
.END.marker in P47 files is now preserved: if present on input, it appears on output, and if not, then not. This has a few side effects:- Removed the diagnostic for a missing “end marker.” It isn’t a
bug in the R47 but simply an indication that the program
happened not to be the last one in memory at
WRITEPtime. - The
ENDop’s hard-coded indent level is now 1, not 0, because.END.must now be hard left-aligned. - Updated all the
../*.p47ufiles with the new UTF-8 output to allow the round-trip testing to pass. These differences are now considered part of the test.
- Removed the diagnostic for a missing “end marker.” It isn’t a
bug in the R47 but simply an indication that the program
happened not to be the last one in memory at
Improved handling of string literals:
- strings as inline program values now work
- length is now preserved; Go’s notion of string length does not match that of the R47, and it is the latter that matters here
Fixed processing of op codes 128-255 on P47 input.
Version 0.1.0, 2026.02.07
Added the
--output/-o filenameoption for writing output to a named file instead of to stdout.Implemented many more op codes:
- 2, 3, 4, and N-register stack ops
- data type checks
- equation solve/plot
- information unit multipliers
- matrix row and column manipulations
- specialized STO-to-register and STO reserved variable variants
- system flag getters
- unit conversions
- week-of-year get/set
- uniform distributions
- vector manipulation
- XFCNS (3-register FMA arithmetic)
...plus a whole range of grab-bag ops
Renamed the xth-root-of-y operation name from
root(x,y)toroot(x)(y)to conform to LaTeX math notation.Renamed all operations and constants with multi-character subscripts to put them in parens per LaTeX notation rules, without which the second and later letters render normally.
Windows console output is now UTF-8 when it is redirected to a pipe instead of using the default code page. Without this, one is most likely to get UTF-16 with a BOM, causing the R47 to fail to read our P47 output as it is expecting UTF-8.
The end markers in P47 files are now optional. A diagnostic is emitted even though we have a report of this happening in a real file. It’s still something the user should be aware of, if only as a hint that they should use “-f p47 -t p47” to clean it up.
REMops are marked as having no “argument” in the R47 code, but of course we must still consume the following label in this program, even if just to discard it lest we interpret it as program bytes.Fixed parsing of ops with “value” typed arguments, which use one of two different (!) 16-bit encodings.
Fixed several problems in the way -t utf8 printed system flags.
Minor fixes to
NOPhandling.Better diagnostics.
Version 0.0.1, 2026.02.06
- Initial release, announced on the SwissMicros forum