############################################################################
#
#	File:     calc.icn
#
#	Subject:  Program to simulate desk calculator
#
#	Author:   Ralph E. Griswold
#
#	Date:     January 3, 1993
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This is a simple Polish "desk calculator".  It accepts as values Icon
#  integers, reals, csets, and strings (as they would appear in an Icon
#  program) as well as an empty line for the null value.
#
#  Other lines of input are interpreted as operations. These may be Icon
#  operators, functions, or the commands listed below.
#
#  In the case of operator symbols, such as +, that correspond to both unary
#  and binary operations, the binary one is used.  Thus, the unary operation
#  is not available.
#
#  In case of Icon functions like write() that take an arbitrary number of
#  arguments, one argument is used.
#
#  The commands are:
#
#	clear	remove all values from the calculator's stack
#	dump	write out the contents of the stack
#	quit	exit from the calculator
#
#  Example: the input lines
#
#	"abc"
#	3
#	repl
#	write
#
#  writes abcabcabc and leaves this as the top value on the stack.
#
#  Failure and most errors are detected, but in these cases, arguments are
#  consumed and not restored to the stack.
#
############################################################################
#
#  Links: ivalue, usage
#
############################################################################

invocable all

link ivalue, usage

global stack

procedure main()
   local line

   stack := []

   while line := read() do
      (operation | value | command)(line) |
         Error("erroneous input ",  image(line))

end

procedure command(line)

   case line of {
      "clear":	stack := []
      "dump":	every write(image(!stack))
      "quit": 	exit()
      default:	fail
      }      

   return

end

procedure operation(line)
   local p, n, arglist

   if p := proc(line, 2 | 1 | 3) then {	# function or operation?
      n := abs(args(p))
      arglist := stack[-n : *stack + 1] | {
         Error("too few arguments")
         fail
         }
      stack := stack[1 : -n]
      &error := 1			# anticipate possible error
      put(stack, p ! arglist) | {	# invoke
         if &error = 0 then
            Error("error ", &errornumber, " evaluating ", image(line))
         else
            Error("failure evaluating ", image(line))
         stack |||:= arglist		# restore unused arguments
         }
      &error := 0
      return
      }

   else fail

end

procedure value(line)

   put(stack,ivalue(line)) | fail

   return

end
