← All examples

rpn.raku

A postfix (RPN) calculator.

A Reverse Polish Notation (postfix) calculator built as a stack machine, in deliberate contrast to the grammar-based calculator.raku. It reads each token left to right, pushes numbers onto an Array used as a stack, and on an operator pops two operands and pushes the result. Arithmetic stays exact, so 1 3 / yields a true 1/3.

The program

Edit it and press Run — it executes in your browser.

#!/usr/bin/env raku
# A Reverse Polish Notation (postfix) calculator driven by an explicit stack.
#
# Postfix needs no parentheses and no precedence rules: read left to right,
# push every number, and when an operator turns up pop its two operands,
# combine them, and push the result back. An ordinary Array used as a stack
# (`push`/`pop`) is the whole machine. Arithmetic stays exact — dividing two
# integers yields a `Rat`, so 15 / 5 is 3 and 1 / 3 is a true one-third.

sub rpn(Str $expr) {
    my @stack;
    for $expr.words -> $tok {
        if $tok ~~ /^ '-'? \d+ ['.' \d+]? $/ {
            @stack.push: $tok.contains('.') ?? $tok.Rat !! $tok.Int;
        }
        else {
            # Operator: pop the right operand first, then the left.
            my $b = @stack.pop;
            my $a = @stack.pop;
            given $tok {
                when '+' { @stack.push: $a + $b }
                when '-' { @stack.push: $a - $b }
                when '*' { @stack.push: $a * $b }
                when '/' { @stack.push: $a / $b }
                default  { die "unknown operator '$tok'" }
            }
        }
    }
    @stack.pop;   # the sole survivor is the answer
}

sub fmt($n) {
    # Show exact fractions as numerator/denominator, integers plainly.
    return ~$n if $n ~~ Int;
    $n.denominator == 1 ?? ~$n.numerator !! $n.nude.join('/');
}

sub MAIN() {
    my @tests =
        '3 4 +',                 # 7
        '3 4 + 5 *',             # 35
        '5 1 2 + 4 * + 3 -',     # 14
        '15 7 1 1 + - / 3 *',    # 9
        '2 3 4 * +',             # 14
        '10 2 /',                # 5
        '1 3 /',                 # exact 1/3, not 0.333...
        '100 5 / 3 -';           # 17

    for @tests -> $expr {
        say sprintf('%-22s = %s', $expr, fmt(rpn($expr)));
    }
}
Output
3 4 +                  = 7
3 4 + 5 *              = 35
5 1 2 + 4 * + 3 -      = 14
15 7 1 1 + - / 3 *     = 9
2 3 4 * +              = 14
10 2 /                 = 5
1 3 /                  = 1/3
100 5 / 3 -            = 17

Feature focus: stack machine, exact Rat.