?

Log in

No account? Create an account
penrose orange

stephenw32768


/var/log/stephen

cat /var/log/stephen >/dev/eyes


Gaining features
penrose orange
stephenw32768
The RPN calculator has seen some improvements since last week. No code drop yet, it needs a bit of cleaning up before I'm happy for other people to see it...

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!