evalrpn procedure in the following program is implemented as a method of
List, the prototypical object of Io's lists. An already tokenized RPN expression, in the form of a list of tokens, is evaluated by
evalrpn recursively. Each call of
evalrpn modifies the list so that the corresponding (sub)expression is replaced by its result. For a correctly constructed RPN expression this will eventually result in a single number left in the list.
evalrpn works as follows. An item
tk is popped off the list's end, and checked if it denotes an operator. If it does, the method calls itself twice, popping the results off the list. The first result is stored as
y so that, together with
tk, it is sent in a
perform message to the second obtained result – this is how the corresponding arithmetic operation is computed. A list item which is not an operator is analyzed whether it reads as a number: a possible arithmetic sign is stored as a ±1 factor and removed, then the remaining string is scanned to ensure that it contains at least one digit, at most one decimal dot, and nothing else. If a string fails that test, an exception is raised. On success,
tk is converted to a number using
asNumber, and the arithmetic sign is taken account of. (
asNumber itself cannot be relied upon for parsing a numeric token as it happens to not signal trailing characters not belonging to the number.)
Note that the items that are added to the list in
evalrpn are numbers, not strings, but they are pulled back off immediately and never get parsed like the rest of the items do. These numbers only join the list as a way to represent a result of a partial evaluation and the respective remaining part of the list as a single value.
The main part of the program makes use of the
readLine methods to read lines until the latter method returns
nil. Each line
s is split into tokens and the possible empty ones of them (appearing whenever the input line contains successive blanks) are filtered out. The resulting list of tokens, if not empty, is sent the
evalrpn message, and if a one-item list is returned, that item is the result of the evaluation which gets printed by letting it perform the
println method on itself.
Both the evaluation and the final checking if there are not unused tokens left is done in the context of the
try method. Any syntax incorrectness results in signalling an exception, in which case
try returns that exception. For a correctly parsed and computed RPN expression,
nil is returned. The outcome of
try is sent the
catch method which reacts to an exception by printing an error message while not paying attention to a
As Io has no character literals, at two places we use single-element strings and refer to their content by means of a subscript (
at(0)). We could have used ASCII codes instead, e.g.
45 asCharacter for the minus sign, but this would have obscured the text.
List evalrpn := method ( tk := pop push ( c := tk at(0) if (tk size == 1 and "+-*/" contains(c) , y := evalrpn pop evalrpn pop perform(tk asString,y) , sgn := 1 if ("+-" contains(c) , if ("-" at(0) == c, sgn := -1) tk := tk slice(1)) nd := np := 0 tk foreach(c, if (c isDigit, nd := nd+1 , if (c == "." at(0), np := np+1, np:=2))) if (nd==0 or np>1, Error raise("")) tk asNumber * sgn ) ) ) while (s := File standardInput readLine, s := s split select (isEmpty not) (s isEmpty) ifFalse ( try ( r := s evalrpn if (r size > 1, Error raise("")) r first println ) catch(Exception,"error" println) ) )