RPN Calculator in Icon

Expression orientation and goal-directed computation being characteristic features of Icon, they are intensively used in our calculator implementation. The program shown below is of the stack-based kind and works as follows.

The main loop (re)sets the stack stk to an empty array and attempts reading a line from the standard input. If an end of input is encountered, read fails, the loop breaks, and the program terminates. If read succeeds, its result is enclosed in a pair of blank characters for ease and uniformity of the parsing that follows.

The main loop's body has only two statements, of which the first one is a ‘scan’ operator (?) that does most of the work. It sets line as the string being parsed and another while loop – the right argument of ? – as the parsing action.

The condition part of that while extracts non-blank substrings s from line, so that s can be parsed in the loop's body. Here, many returns the index of the first non-blank character, starting from the current position in line. Similarly, upto returns the index of the first blank after that. In both cases we need to call tab so as to advance the current scan position within line and return the string between the old and the new current positions: that is how we obtain the current token in s.

Note that the calls to many and upto do not mention line explicitly as their argument. They do not have to, because we use them in the context of the ? operator, where the subject string is implied.

The body of the parsing while is a single but complex expression. It calls real on s, thus attempting to parse it as a number, and if that is successful, the number is pushed (put) on the stack. Failing to be a number, s might be an operator, so we check whether its length (*s) is 1 and whether it is a substring of "+-*/". We also try pulling off the stack twice, and if all this is successful, we finally invoke the procedure corresponding to the arithmetic operation denoted by s – that procedure's name is the string s itself. The result is pushed onto the stack.

If s is neither a number nor an operator, the last alternative of the expression is in turn: it prints an error message and does break next to exit the current loop and jump to the next iteration of the enclosing one, which is reading a new line. Note how all the components of the above described long expression are connected to each other with | and & to ensure the proper succession of the actions taken according to the logic of the computation.

When the parsing is complete and no error occured so far, it remains to see whether there is exactly one number left on the stack; if so, we print it. More than one entries means that the input expression was incorrect (having too few operators), so in this case we print "error". If line was initially empty, the stack is empty as well, therefore the call of pull fails. Then so does the if, and thus nothing at all is printed, as it has to be.

procedure main()
  ws := ' \t'
  while stk := [] & line := " " || read() || " " do {
    line ? while tab(many(ws)) & s:=tab(upto(ws)) do
             put(stk,real(s)) |
             (1=*s & find(s,"+-*/") & y:=pull(stk) & put(stk,s(pull(stk),y))) |
             (write("error") & break next)
    if y:=pull(stk) then write((0=*stk & y) | "error")