PLACE
spec
solutions
Perl

RPN Calculator in Perl

In a typical Perl fashion, the following program uses search and replace in order to evaluate the input expression by gradually transforming it to simpler ones. The inner loop searches for sequences of the kind number-number-operator and replaces each one by a single number until there are no more such sequences. At that time, the input string must have been reduced to a single number (not counting any possible leading and trailing spaces), otherwise it is syntactically incorrect. This is checked in a conditional expression – the last but one line of the program – where either the resulting number is extracted, or an error message is produced. Whatever the outcome, it eventually gets printed.

The outer loop reads in lines from the standard input and stores each one in the special variable $_. Both the input source and $_ are implicit. Also implicit is the use of $_ in the match and match-and-substitute actions that take place throughout the program.

Before going to evaluation, an input line is tested for non-emptiness. If it contains nothing but possible blanks, it is not further processed.

All string searching is done by regular expressions (regexes). Some basic ones are defined as string values stored in the variables $pre, $post, $num, and $op. The last two match a number and an operator, respectively, while $pre matches either a sequence of blanks or the empty space at the start of a string, and similarly $post matches either blanks or the empty space at the end of a string. These variables are combined, together with other regular expressions, to form the matcher for RPN subexpressions used in the inner loop of the program.

The compound regex for an RPN subexpression is so constructed that, when a match occurs, its essential parts – the two numbers and the operator – are captured by the special variables $1, $2, and $3. These three items are reordered and concatenated into a string which is evaluated as a Perl arithmetic expression. Similarly to other implementations, we have to separate a possible minus operator from the minus of a negative number so as to prevent occurence of ‘--’, treated as a decrement operator. Finally, the result of eval is enclosed in a pair of blanks and in this form replaces the matched RPN subexpression whose value it represents.

It should be noted that there are two levels of Perl code evaluation in the above substitution process. One is what eval does, namely computing a number from the value of the string expression $1.$3.' '.$2, representing a Perl arithmetic expression. For eval itself to be invoked, however, including first computing its argument, as well as for performing the subsequent concatenations on its result, another level of evaluation is needed. Without it, the replacement part of the substitution operator in the inner loop – the phrase
        ' '.eval($1.$3.' '.$2).' '
– would have been treated as ordinary text instead of executable code. That higher level of evaluation is enforced by the e key at the end of the substitution operator (after its last ‘/’).

$pre = '(?:^|\s+)';
$post = '(?:\s+|$)';
$num = '([+-]?(?:\.\d+|\d+(?:\.\d*)?))';
$op = '([-+*/])';
while (<>) {
  if (/^\s*$/) {next;}
  while (s/$pre$num\s+$num\s+$op$post/' '.eval($1.$3.' '.$2).' '/e) {}
  print (s/^\s*$num\s*$/$1/ ? $_ : "error", "\n");
}

The implementation of the calculator in PHP follows closely the one presented here.

boykobbatgmaildotcom