cuttle turns Shoddy's own stack into a value a program can
hold. A stack is a pile of values where the last one put on is the first one
taken off. Ordinarily Dup Swap Rot only exist inside a Def,
compiled away before anything runs. This machine gives a mill a
heterogeneous runtime stack — heterogeneous meaning mixed
kinds: numbers, strings, booleans, lists, quoted programs, pairs, money,
matrices, one cell per value — plus the
pure algebra over it: push, pop, dup, drop, swap, over, nip, tuck, rot,
roll(n), pick(n), depth, clear. It knows nothing about tokens or a
dictionary (a lookup table of named words). The interpreter — the program
that reads and runs RPN lines (reverse Polish notation, where 2 3 + means
2 + 3) against those — is
reckoner, built on top of this.
Nothing here aborts. Every popping word answers a value.
An underflow (asking for more cells than the stack holds), a bad level, a
kind mismatch — each comes back as an answer, never as a raised error. A stack
machine that kills the session on SWAP with one cell on it is
unusable for the interactive purpose it exists for.
The stack was invented for exactly the job it does here: holding the
middle of a calculation while the rest of it happens. Alan Turing's 1945
design for the ACE had subroutine calls that would bury a return
address and unbury it afterwards. In 1957 Friedrich Bauer and Klaus
Samelson in Munich patented the Kellerprinzip — the "cellar
principle" — a store where the last thing put in is the first thing
taken out. They invented it so a machine could evaluate a bracketed formula
without reading it twice. The cellar turned out to be the most reusable idea
in the building. Compilers keep their parse on one. Every running program
keeps its calls on one. Whole languages — Forth on two of them, PostScript,
the HP calculators — made the stack the visible surface of the language
rather than its hidden plumbing. Shoddy's runtime dialect is that tradition,
and cuttle is the cellar itself made into a value: not a trick
of the compiler's bookkeeping but a thing a program can hold, copy, and hand
back.
A stack that is only a compiler's bookkeeping cannot be handed to a
caller, saved, undone, or driven one line at a time from a form field. Once
it is a value — CutPush in, a new Stack out — those
jobs get easy. A history is just a list of stacks. Undo is picking an earlier
one. A shell can render the top few levels without knowing anything about how
they got there. cuttle is that value and nothing else: no
tokenizer, no registers, no notion of a word. Everything that turns typed
text into calls against this stack lives one layer up, in
reckoner.
Cuttling is the finishing operation that folds a finished piece of cloth back and forth into a layered pile on the cuttling table — lap over lap, the last fold laid resting on top. The last fold laid is the first one lifted back off. That is a last-in-first-out store, described without reaching for a metaphor from computing at all. (The heritage of the name has the rest of the mill.)
Build a stack from the values it should hold, typed in the order they would be pushed — the last one ends up on top:
Include "cuttle.shoddy"
Def Main()
Let s0 = CutOf({ CutNum(3), CutStr("hi"), CutBool(True) })
Print(CutDepth(s0)) ' 3, True on top
Select Case CutPop(s0)
Case CutTook(v, s1)
Print(CutShow(v, CutFmtStd())) ' True
Case CutTakeBad(why, s1)
Print(why)
Every shuffle word answers a variant — a value that says which case happened — rather than the bare stack. The failing case always carries the stack unchanged. A caller can therefore match on success or refusal without a separate check beforehand:
Select Case CutSwap(CutOf({ CutNum(1) })) ' only one cell — SWAP needs 2
Case CutDone(s1)
Print("swapped")
Case CutOutBad(why, s1)
Print(why) ' "SWAP needs 2, the stack holds 1"
A few things worth remembering:
CutPair, CutMoney and CutMat hand back
a bare Pair, Money or Matrix. This
machine includes seq, money
and matrix so it can name those types. But a
caller who wants to destructure one of those cells — read
Cents(m) off a popped CutMoney, say — needs the
owning machine included too. The cell only hands back the value, and only
money.shoddy knows what a Money is made of.CutRoll(s, 1)
is a no-op, CutRoll(s, 2) is CutSwap,
CutRoll(s, 3) is CutRot; CutPick is
the copying twin of each.=.
Structural means compared by contents, cell by cell. A
CutList is a quotation under the hood, and Shoddy's own
= compares a List by identity — whether it is the
very same object. So CutEqual walks the cells itself rather than
falling back on the builtin.+ - * dispatch on kind. CutAdd,
CutSub and CutMul give numbers ordinary
arithmetic, money exact addition and subtraction, matrices addition and
multiplication (and a number times a matrix scales it), and strings
concatenation under +. Anything else is a CutCalcBad
naming both kinds. This is what one stack-value type buys, and it is the
arithmetic reckoner's own + - * words
run underneath.CutShow renders every
kind under a mode and digit count the caller holds (this machine keeps no
state of its own). Long lists and quotations abbreviate past a fixed column
width rather than eating the screen, and the exact count is always one word
away.Building, shuffling, and showing
| Word | Description |
|---|---|
| CutNew() | The empty stack. |
| CutOf(values) | A stack built from a list of cells, typed order — the last value ends up on top. |
| CutPush(s, c) | c pushed onto s. |
| CutPop(s) | CutTook(v, rest) or, on an empty stack, CutTakeBad(why, s). |
| CutDepth(s) | How many cells s holds. |
| CutClear(s) | The empty stack, whatever s held. |
| Word | Description |
|---|---|
| CutDup(s) | ( x -- x x ) |
| CutDrop(s) | ( x -- ) |
| CutSwap(s) | ( x y -- y x ) |
| CutOver(s) | ( x y -- x y x ) |
| CutNip(s) | ( x y -- y ) |
| CutTuck(s) | ( x y -- y x y ) |
| CutRot(s) | ( x y z -- y z x ) — the third cell down comes to the top. |
| CutRoll(s, n) | Move the cell at level n to the top, 1-based from the top; refuses n below 1 or not a whole number. |
| CutPick(s, n) | Copy the cell at level n to the top. |
| Word | Description |
|---|---|
| CutKindOf(c) | The kind name of a cell — NUMBER STRING BOOLEAN LIST PROGRAM PAIR MONEY MATRIX, or UNKNOWN for a case an older match has not been taught yet. |
| CutFmtStd() | The default display format: mode STD, 0 digits. |
| CutBound() | 76 — the column width past which a list or quotation abbreviates. |
| CutShow(c, f) | c rendered as text under format f — numbers per mode and digits, strings quoted, lists and quotations bracketed and abbreviated past CutBound(), money via MoneyFmt, matrices as dimensions plus rows at small sizes. |
| Word | Description |
|---|---|
| CutEqual(a, b) | Structural equality, all the way down; two cells of different kinds are unequal rather than an error. |
| CutAdd(a, b) | ( a b -- a+b ) — numbers, money, matrices of one shape, or strings joined; else a CutCalcBad naming both kinds. |
| CutSub(a, b) | ( a b -- a-b ) — numbers or money. |
| CutMul(a, b) | ( a b -- a*b ) — numbers, matrices, or a matrix scaled by a number. |
| CutLess(a, b) | Whether a orders before b — numbers, strings and money order; the underlying word for < > <= >=. |
Thirteen files bridge the rest of the standard library into reckoner, the engine built on this stack. Every one of them includes this machine for its value set. None is catalogued in the general machines index: a seed has no bearing on its own, only together with this stack and that engine. See the full seed list on reckoner's own page for what each one bridges.
| User | How | |
|---|---|---|
| reckoner | The whole value set and pure algebra every registered word and combinator runs against. |
Every reckoner seed listed above also includes this machine, for the value set — not repeated here as thirteen identical rows.
| Machine | Why | |
|---|---|---|
| matrix | Matrix is the type CutMat carries; MatAdd, MatMul, Rows and Cols back its arithmetic and display. | |
| money | Money is the type CutMoney carries; MoneyAdd, MoneySub and MoneyFmt back its arithmetic and display. | |
| seq | Pair is the type CutPair carries, and the list plumbing (First, Rest, Prepend, DropN, Taken) implements the stack and its shuffles. | |
| str | Join and the padding words build the abbreviated list and matrix renders. |