‹ back to the board

Tuesday, September 22

Mastermulti-constraint & composite logic

The Tithe Clerk's Reckoning Rod

The countinghouse of bitwick keeps its sums on a reckoning rod: a column of beads, worked from a written strip of marks. The tithe clerk has gone to the coast for the season and the strips have not stopped arriving.

reckonStrip(strip) takes one string. Split it on whitespace to get the list of marks; a strip that is empty or all whitespace has no marks at all. Marks are numbered from 0 by their position in the strip.

A legal mark is one of:

  • a number — one or more digits 0-9, optionally preceded by a single -. Pushes that value onto the column.
  • **+, -, *** — pop the top value as b, then pop the next as a, then push a + b, a - b or a * b.
  • `dup` — push another copy of the top value.
  • `drop` — remove the top value.
  • `swap` — exchange the top two values.
  • `{` — pop a value c, then run the marks between this { and its matching } exactly c times.
  • `}` — ends the stretch opened by its matching {.

A lone - is subtraction; -4 is the number minus four.

A { takes its count at the moment it is reached, and not again: the second and later passes re-enter the stretch just after the {. A { that sits inside another stretch is reached afresh on every pass of the outer stretch, and takes a new count each time. A count of 0 skips the stretch entirely. Braces match in the usual nesting way.

The work is done in this order:

1. If any mark is none of the forms above, return the string "bad token at i" for the smallest such i. This check covers the whole strip before anything else is considered. 2. Otherwise, if some { has no matching }, or some } has no matching {, return "bad brace at i" for the smallest index i of an unmatched brace. 3. Otherwise run the marks left to right. If a mark needs more values than the column holds — +, -, * and swap need two; dup, drop and { need one — stop and return "underflow at i", where i is that mark's index. If a { takes a negative count, stop and return "bad count at i".

An index is always the mark's position in the strip, even when the fault happens on a later pass through a stretch. At most one fault is ever reported: the first one reached.

If the run finishes, return the column as a list of integers, bottom of the column first.

Returns: a list of integers, or one of the fault strings described above.

  • tokenizing and evaluating
  • Master

examples

  1. case 1strip of "3 1 3 { dup + }"[3, 8] — the brace takes 3 as its count, leaving 3 and 1 on the column, then doubles the 1 three times
  2. case 2strip of "2 { 0 { 5 } 7 }"[7, 7] — the outer stretch runs twice; each pass the inner brace takes a fresh count of 0 and its body is skipped
  3. case 3strip of "} 5 frobnitz""bad token at 2" — every mark is judged legal before any brace is paired, so the unreadable word is reported and the stray brace at 0 is never considered
  4. case 4strip of " " (whitespace only)[] — no marks, nothing to fault, an empty column
reward
540 XP
signature
reckonStrip
not yet run

TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.

loading the editor…
  • a plain sum and product
  • a strip of nothing but whitespace
  • doubling three times
  • nested brace with a count of zero
  • nested brace takes a fresh count each outer pass
  • minus as operator against minus as sign
  • unreadable word outranks an earlier stray brace
  • stray closing brace
  • two stretches left open
  • column runs dry on the second pass
  • a negative count
  • brace with no count to take
The Tithe Clerk's Reckoning Rod · bitwick