The evaluator has been separated from the CLI program, which will make it easier to embed in a web page in the future. The CLI program now reads from standard input if no arguments are passed. If standard input is a file or pipe, it slurps the whole input and evaluates it in one go; if standard input is a terminal, it runs interactively, evaluating a line at a time.

Feature-wise, the biggest improvement is that the calculator now supports user-defined functions. The syntax is taken from Forth:

`:`

begins a definition, and `;`

terminates it. Alternative syntax using `def`

to begin a definition and `end`

to terminate is also accepted. The evaluator doesn't (yet?) have much of a concept of state, so in interactive mode, definitions need to be all on one line.Example definitions: imperial unit conversions:

rpn> : m2y 1760 * ; rpn> : y2f 3 * ; rpn> : f2in 12 * ; rpn>

Now for a bit of silliness:

rpn> : au2m 92955887.6 * ; rpn> : au2in au2m m2y y2f f2in ; rpn> 1 au2in . 5889685038336.0 rpn>

Because everyone needs to know how many inches away the sun is.

The calculator has gained some stack manipulation functions, mostly from Forth. In interactive mode, one of the more useful is

`.s`

, which displays the contents of the stack without altering it:rpn> 1 rpn> 2 3 rpn> 4 5 6 rpn> .s 1 2 3 4 5 6 rpn>

A secondary stack has been added. It is intended to be used for temporary storage of data. There are three operations supporting it:

`push`

, which removes an item from the top of the main stack and pushes it onto the secondary stack; `pop`

, which does the opposite; and `xchg`

, which swaps the two stacks. Example:rpn> 1 2 3 4 5 6 rpn> .s 1 2 3 4 5 6 rpn> push rpn> .s 1 2 3 4 5 rpn> push rpn> .s 1 2 3 4 rpn> xchg .s 6 5 rpn> pop .s 6 5 4 rpn> : 3pop pop pop pop ; rpn> xchg .s 1 2 3 rpn> 3pop .s 1 2 3 4 5 6 rpn>

The secondary stack is purely for storage. The only builtin operations that use it are

`push`

, `pop`

and `xchg`

. Arithmetic operators always use the main stack.New arithmetic operations

`sum`

and `product`

take an operand, then add or multiply that many numbers, e.g.:rpn> 1 2 3 4 5 6 rpn> .s 1 2 3 4 5 6 rpn> 4 sum rpn> .s 1 2 18 rpn>

Along with the secondary stack, this enables functions such as

`mean`

to be written:rpn> : mean dup push sum pop / ; rpn> 10 20 30 .s 10 20 30 rpn> 3 mean . 20 rpn> 5.0 6.0 6.0 3 mean . 5.66666666666667 rpn>

There are also

`sumall`

and `prodall`

, which sum or multiply everything on the stack.In addition to the secondary stack, a heap is provided for storage.

`store`

takes a heap address as an operand, then stores the next number on the stack in that address. `load`

takes a heap address as an operand, and pushes the contents of that address onto the stack. Inspired by JVM bytecode, there are shorthand forms `0load`

to `3load`

and `0store`

to `3store`

which operate directly on addresses 0-3.rpn> 1 2 3 .s 1 2 3 rpn> 0store .s 1 2 rpn> 0load 0load .s 1 2 3 3 rpn>

As with the secondary stack, the heap is used only for storage. Operators always take their operands from the main stack.

I wonder if anyone has read this far?

The next task is to get the code tidy enough to show to the world. After that, I'm going to see about adding loops and conditionals to the calculator. I have a rough idea about how to do it; it involves turning the calculator into a state machine. With loops and conditionals, the calculator will be dangerously close to being Turing-complete; not entirely, though, since I'm only going for finite loops, at least to begin with.

I'm having far too much fun with this. Shame I wasn't into stack machines ten years ago, this has "final year project" written all over it!