=head1 Exceptions

This is a basic overview of how to deal with exceptions from PIR.  There
are two main topics: throwing exceptions and catching exceptions.  We'll
start with the first.

=head2 Throwing exceptions

If you're going to be using exceptions, you probably want to start by
including two pasm files that define constants for exception type and
severity.

You create exceptions just like you create any other object, with
C<new>.

You usually want to at least set a descriptive message about what went
wrong.

You also usually want to set a severity and sometimes a type.  You can
find these in L<runtime/parrot/include/except_severity.pasm>.

You actually throw the exception by using the C<throw> op.

Put all together, it looks like this:

=begin PIR_FRAGMENT

    .include 'except_types.pasm'
    .include 'except_severity.pasm'

    .local pmc ex
    ex = new 'Exception'

    ex = "Everything is horrible, dood."

    ex['severity'] = .EXCEPT_DOOMED
    ex['type'] = .CONTROL_ERROR

    throw ex

=end PIR_FRAGMENT

=head2 Catching exceptions

Parrot maintains a stack of exception handlers.  When an exception is
thrown, Parrot iterates through the stack looking for a handler that
can handle the exception.  When it finds a valid exception handler,
the exception handler is invoked with the exception as an argument.
Exception handlers run in the context of the C<throw> that they're
handling.

You create exception handlers just like you create any other object, with
C<new>.

=begin PIR_FRAGMENT

    .local pmc eh
    eh = new 'ExceptionHandler'

=end PIR_FRAGMENT

You set the target of the exception handler (the code that will be
invoked) with the C<set_attr> opcode.  Usually this will be a label.
You manipulate the exception handler stack with the C<push_eh> and
C<pop_eh> opcodes.  This is a fairly standard use of exception handlers:

=begin PIR_FRAGMENT

    .local pmc eh
    eh = new 'ExceptionHandler'
    set_addr eh, handler
    push_eh eh
      # code that might throw an exception
    pop_eh
    .return (1) # Success!
  handler:
    .local pmc ex
    .get_results (ex)
      # code that prints a warning, logs an error, whatever
    .return (0) # Failure!

=end PIR_FRAGMENT

Sometimes you want to be more specific in what you catch.  You can set
filters on the exception handler based on exception type and severity.
The methods you use include C<.min_severity()>, C<.max_severity()>,
C<.handle_types()>, and C<.handle_types_except()>.

Here's an example of a sub that catches only error exceptions and prints
an appropriate log message before exiting.

=begin PIR

    .include 'except_severity.pasm'

    .sub 'dostuff'
        .local pmc eh
        eh = new 'ExceptionHandler'
        set_addr eh, handler
        eh.'min_severity'(.EXCEPT_ERROR)
        push_eh eh
        # ... stuff that might throw an error
        pop_eh
        .return (1)
      handler:
        .local pmc ex
        .local string msg
        .get_results (ex)
        print "There was a fatal error in dostuff: "
        msg = ex
        say ex
        exit 1
    .end

=end PIR

=cut
