RPN Calculator in Logo

This Logo implementation of the calculator consists of two procedures, rpn and eval. Both are recursive. Within the main procedure rpn, rl (short for readlist) reads an input line and automatically converts it to a list of ‘words’ – strings of nonblank characters. We are going to evaluate the expression with a backward recursion on it. To make this easier, we reverse the list, so that it can be processed in start-to-end order. Calling eval does the evaluation, after which rpn calls itself again.

The list of tokens s is a global variable, so there is no need to send it explicitly as an argument to Eval. The latter splits up s into head (first) and tail (bf, short for butfirst) parts. The tail becomes the new value of s. If the head t is an arithmetical operator, eval calls itself twice to obtain the values of the expected arguments, on which it then applies the operator, by means of run. The result is returned as the value of eval, for which op (short for output) is used. When t happens to be a number, that number is the value returned. If t is neither of these two, or the list is empty (while it shouldn't be, within eval), an exception is thrown, signalling a syntax error.

For dealing with exceptions, Logo provides the catch and throw statements. These are similar to what they are in Common Lisp: catch defines a name and a statement, in the dynamic context of which throw can signal an exception with the same name. If that happens, the value of the entire catch statement is the one passed back by throw (in this program, this is the symbol "error). If no exception is raised, the catch's value is that of its second argument.

At several places, make creates and initializes a variable, or assigns a new value to a variable. Put in other words, this is what in other languages is called an assignment statement. In makes and elsewhere, the presence of a double-quote character (") tells the interpreter to regard the word that follows as a literal value – a symbol, instead of something that refers to the value of a variable with that name. In order to have the latter meaning, a name must be prefixed with a colon (:).

As already seen, for many of its statements Logo provides abbreviations along with the full names. Except the ones we mentioned, there is also pr, short for print, used in this program.

The character ~ (tilde) is used where it is convenient to split a line but Logo would not allow us to do this as it may perplex the interpreter. The tilde is a hint to the interpreter to join the current line with the one that follows.

to rpn
  if eof? [stop]
  make "s reverse rl
  if not empty? :s ~
    [ make "r catch "err [eval]
      pr catch "err
           [ ifelse empty? :s [:r] [(throw "err "error)] ] ]

to eval
  (local "x "y "t)
  if not empty? :s ~
    [ make "t first :s  make "s bf :s
      if member? :t [+ - * /] ~
        [ make "y eval  make "x eval
          op run (list :x :t :y) ]
      if number? :t [op :t] ]
  (throw "err "error)

As implemented here, the RPN calculator runs in Berkeley Logo (UCBLogo) and MSWLogo. For other dialects it may need to be slightly adapted.