Exception handling
==================

Try statement
-------------

Example:

.. code-block:: nim
  # read the first two lines of a text file that should contain numbers
  # and tries to add them
  var
    f: File
  if open(f, "numbers.txt"):
    try:
      var a = readLine(f)
      var b = readLine(f)
      echo("sum: " & $(parseInt(a) + parseInt(b)))
    except OverflowError:
      echo("overflow!")
    except ValueError:
      echo("could not convert string to integer")
    except IOError:
      echo("IO error!")
    except:
      echo("Unknown exception!")
    finally:
      close(f)


The statements after the ``try`` are executed in sequential order unless
an exception ``e`` is raised. If the exception type of ``e`` matches any
listed in an ``except`` clause the corresponding statements are executed.
The statements following the ``except`` clauses are called
`exception handlers`:idx:.

The empty `except`:idx: clause is executed if there is an exception that is
not listed otherwise. It is similar to an ``else`` clause in ``if`` statements.

If there is a `finally`:idx: clause, it is always executed after the
exception handlers.

The exception is *consumed* in an exception handler. However, an
exception handler may raise another exception. If the exception is not
handled, it is propagated through the call stack. This means that often
the rest of the procedure - that is not within a ``finally`` clause -
is not executed (if an exception occurs).


Except and finally statements
-----------------------------

``except`` and ``finally`` can also be used as a stand-alone statements.
Any statements following them in the current block will be considered to be 
in an implicit try block:

.. code-block:: nim
  var f = open("numbers.txt")
  finally: close(f)
  ...

The ``except`` statement has a limitation in this form: one can't specify the
type of the exception, one has to catch everything. Also, if one wants to use
both ``finally`` and ``except`` one needs to reverse the usual sequence of the
statements. Example:

.. code-block:: nim
  proc test() =
    raise newException(Exception, "Hey ho")
  
  proc tester() =
    finally: echo "3. Finally block"
    except: echo "2. Except block"
    echo "1. Pre exception"
    test()
    echo "4. Post exception"
  # --> 1, 2, 3 is printed, 4 is never reached


Raise statement
---------------

Example:

.. code-block:: nim
  raise newEOS("operating system failed")

Apart from built-in operations like array indexing, memory allocation, etc.
the ``raise`` statement is the only way to raise an exception.

.. XXX document this better!

If no exception name is given, the current exception is `re-raised`:idx:. The
`ReraiseError`:idx: exception is raised if there is no exception to
re-raise. It follows that the ``raise`` statement *always* raises an
exception (unless a raise hook has been provided).


onRaise builtin
---------------

`system.onRaise() <system.html#onRaise>`_ can be used to override the
behaviour of ``raise`` for a single ``try`` statement. ``onRaise`` has to be
called within the ``try`` statement that should be affected.

This allows for a Lisp-like `condition system`:idx:\:

.. code-block:: nim
  var myFile = open("broken.txt", fmWrite)
  try:
    onRaise do (e: ref Exception)-> bool:
      if e of IOError:
        stdout.writeln "ok, writing to stdout instead"
      else:
        # do raise other exceptions:
        result = true
    myFile.writeln "writing to broken file"
  finally:
    myFile.close()

``onRaise`` can only *filter* raised exceptions, it cannot transform one
exception into another. (Nor should ``onRaise`` raise an exception though 
this is currently not enforced.) This restriction keeps the exception tracking
analysis sound.


Exception hierarchy
-------------------

The exception tree is defined in the `system <system.html>`_ module:

.. include:: exception_hierarchy_fragment.txt
