soveran / clac

Command-line, stack-based calculator with postfix notation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Stack operator to roll top three values

fiddlerwoaroof opened this issue · comments

Let's say I have two items on the stack

>  4 3

And I want to square the second number (3) and add that to the product of the two numbers (4,3). There's no obvious way to do this, you do something like this, and hit a wall (where the pipe indicates the division between input and the printed stack).

> 4 3 dup dup * | 4 3 9

There's no obvious way to solve this. If, however, we had a word (e.g. roll) that changes a b c to c a b, we could do this:

> 4 3 dup dup *          | 4 3 9
> 4 3 dup dup * roll     | 9 4 3
> 4 3 dup dup * roll *   | 9 12
> 4 3 dup dup * roll * + | 21  

Indeed it's impossible with the current set of commands! I have a working version of two features that will solve this issue. Currently, there are only two commands for stack manipulation: swap and dup. There's a way to define custom words, but they can only be defined in terms of existing commands. One of the upcoming features is the ability to store values in variables:

> 42 [a] |

The number 42 was consumed from the stack. Then:

> 42 [a] a a | 42 42

Typing a expands to the number 42. There's also a way to undefine variables:

> 42 [a] a a (a) a | 42 42

By typing (a), the variable a becomes undefined. I added an extra a to show that this time it has no effect on the stack (it's just ignored).

With this feature, you would do something like this:

> 4 3 dup dup * [a] * a (a) + | 21

I don't need to undefine a for that snippet to work, but I do it anyway as a good habit.

The other feature is the ability to define words in the REPL, using the same syntax as that for commands defined in the words file. This time, we need to enclose the definition in quotes:

> 'sqrt "0.5 ^"' 9 sqrt | 3

By combining these two features we can quickly test new commands in the REPL. We can also remove swap and dup from the core and define them in the words file. Here's how I would define them:

swap "[a] [b] a b (a) (b)"
dup "[a] a a (a)"

Some additional stack manipulation commands:

rot "[a] [b] [c] b a c (a) (b) (c)"
drop [_]

I'm still playing with it, but I think I can publish the feature as experimental and see how we feel about it. What do you think?

That sounds interesting, and it definitely solves the problem, although it also feels a bit like "cheating" for some reason. I think the issue I have is that these variables would essentially be mutable state, while building up a small vocabulary of primitive words has a more "functional" feel. I think for the purpose of saving intermediate values, you just need an operator that does a roll, the way I'm describing.

You could use a more general operator than the one I'm describing (e.g. the postscript roll operator, described here: http://www.linuxfocus.org/English/July1999/article80.html#lfindex1) and then swap and the others could be user defined in terms of it.

Anyways, I'm not sure what the best idea is here, your idea of variables/user defined words at the REPL has its own sets of advantages, although I'd be inclined to disallow reassignment if you go that way.

You are right! I'll work on it :-)

@fiddlerwoaroof This was implemented in version 0.3.0, which was released earlier today. There's also another way to solve this particular problem using the stash operation (also new in this version). Thanks a lot for the suggestion!