RPN Calculator in Lua

The implementation is stack-based and rather straightforward. An outer loop reads lines and assigns them to s. An inner one traverses the non-blank tokens tk in s, checks each token for being a number or an operator and advances the computation state accordingly. The stack is implemented as the table tb.

Several library functions are used. tonumber tries to convert a string to a number. If it does not succeed, it returns nil. loadstring interprets the contents of a string as a Lua chunk. In our case this is a single statement – an arithmetic operation on the variables x and y, the result of which is assigned to z. The string formation is done by concatenation, denoted by ... As the call of loadstring actually generates a function which can be invoked to execute the corresponding piece of code, the additional () does precisely that invocation.

The mentioned functions, as well as print, belong to the so called basic library. Such functions have simple names. Other functions are parts of specific libraries and have ‘compound names’. These include io.lines of the I/O library, string.find and string.gmatch of the string library, and table.insert and table.remove of the table library.

The number of elements in a table is determined by the operator #, corresponding to the table.getn function.

Actually, it is not that these functions really have compound names, but each specific library is implemented as a single table, and in order to access a function we have to indicate its name as a member of the corresponding table io, string or table.

io.lines and string.gmatch are what is called iterators in Lua, which makes it possible to use them for controlling the two loops. The former function produces a series of input lines, on at a time, and the second – the sequence of (non-blank) tokens within a line.

for s in io.lines() do
  tb = {}  z = 0
  for tk in string.gmatch(s,'%S+') do
    if string.find(tk,'^[-+*/]$')  then
      if 2>#tb then z = nil break end
      y,x = table.remove(tb),table.remove(tb)
      z = tonumber(tk)  if z==nil then break end
  n = #tb
  if n==1 and z then print(z)
  elseif n>1 or z==nil then print('error') end