RPN Calculator in Rexx

The program reads input lines in the variable line. Each line is processed by parsing its beginning, so that a token is extracted and the line is shortened correspondingly. This is done by means of the parse statement and goes on repeatedly until the line is finally empty. Each token is processed as described below. A stack of intermediate values s is maintained, with a topmost item indexed by the current value of i. When there are no more input lines to read, the call of the function lines in the outer loop returns 0, treated as ‘false’, so the loop is exited and the program terminates.

After a token is extracted into s.i, it is examined by a call to the function datatype in order to see if it contains a number – that is what the argument 'N' stands for. Failing this check, s.i must be an arithmetic operator, or else there is a syntax error.

If the token is an operator, a string is composed and passed to interpret. That string contains a Rexx statement which performs the arithmetic operation on the top two elements of the stack and modifies the stack accordingly, storing the result as its topmost item. interpret executes the so formed command in the context of the program – it is just as if that command was in the place of the interpret statement. For example, if after parse-ing there was i=4 and s.i='+', the string s.2=s.2 + s.3 would be generated and executed by interpret.

Note that when the current token is neither a number nor an operator, or when an operation cannot be performed because the stack has not enough operands (i<2), the inner loop (processing line) is exited with i=2. Thus, the syntax errors due to encountering a wrong token, or having too many operators, are equated to one of another kind – too few operators – and if any of these errors occurs, the final if will correspondingly output an error message.

do while lines()
  i = 0
  line = linein()
  do while line\=''
    parse var line s.i line
    if datatype(s.i,'N') then  i = i+1
    else  do
      if i<2 | 1<length(s.i) | 0=pos(s.i,'+-*/') then
        do; i = 2; leave; end
      i = i-1
      interpret 's.'||(i-1)'=s.'||(i-1) value('s.'||(i+1)) 's.'i
  if i=1 then  say value('s.'||(i-1))
         else  if i>1 then  say 'error'