PLACE |
spec |
solutions |
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 throw
n, 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 make
s 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)] ] ] rpn end 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) end
As implemented here, the RPN calculator runs in Berkeley Logo (UCBLogo) and MSWLogo. For other dialects it may need to be slightly adapted.
boykobbatgmaildotcom