/* -*- Mode: C; c-file-style: "bsd" -*- */

#ifndef YEXCEP_H
#define YEXCEP_H

/*   yes, macros with gotos in them, but in the interests of
 *   avoiding repetition of code, and having less error prone
 *   error handling
 *
 *   EXCEP_DECL - declares the return value and local state variables
 *                needed by the exception macros
 *
 *   THROW( x ) - set return value to x and goto function cleanup
 *                section (CATCH: block).  In the catch block, THROW
 *                does not goto catch label to avoid loops, and instead
 *                falls through to the next statement.
 *
 *   EXCEP_OK   - success return value (=1)
 *
 *   EXCEP_FAIL - failure return value (=0), other user exceptions are 
 *                given negative values (<0)
 *
 *   TRY( x )   - if code returns value <= 0 TRY sets return value to 
 *                that value and goes to function cleanup section 
 *                (CATCH: block).  In the catch block, TRY does not goto
 *                the catch label to avoid loops, and instead
 *                falls through to the next statement.  The
 *                return value is set to the first non success value
 *                returned by a TRY, unless this is overridden by a THROW.
 *
 *   CATCH:     - start of catch block, also switches behavior of 
 *                TRY and THROW to not goto CATCH: inside the catch
 *                block to avoid loops
 *
 *   EXCEP_RET  - return the current return value from the function
 *                equivlanet to return (EXCEPTION)
 *
 *   EXCEPTION  - current return value, is set to EXCEP_OK by EXCEP_DECL
 *
 *   EXCEP_BOOL - convert current return value to EXCEP_OK, or EXCEP_FAIL
 *                (EXCEP_FAIL is anything other than EXCEP_OK)
 *
 */

/* example usage */

/*
 * 
 * #define EXCEP_OK_COMMENT 2
 * #define EXCEP_NULL_PTR -1
 * #define EXCEP_OUT_OF_MEM -2
 * 
 * int bar( char *c )
 * {
 *     EXCEP_DECL;
 * 
 *     if ( !c ) { THROW( EXCEP_NULL_PTR ); }
 *     if ( *c == '\0' ) { THROW( EXCEP_FAIL ); );
 *     if ( *c == '#' ) { SET( EXCEP_COMMENT ); }
 *  CATCH:
 *     EXCEP_RET;
 * }
 * 
 * int foo( char *c )
 * {
 *     EXCEP_DECL;
 *     int *p = NULL;
 * 
 *     if ( !c ) { THROW( EXCEP_NULL_PTR ); }
 *     TRY( bar( c ) );
 *     if ( RETURN == EXCEP_COMMENT ) { print( "comment\n" ); }
 *     p = strdup( c );
 *     if ( !p ) { THROW( EXCEP_OUT_OF_MEM ); }
 * 
 *  CATCH:
 *     if ( p ) { TRY( bar( p ) ); free( p ); }
 *     THROW( EXCEP_BOOL );
 *     if ( EXCEPTION == EXCEP_OK ) { printf( "success\n" ); }
 *     EXCEP_RET;
 * }
 * 
 */

#define EXCEP_FAIL 0
#define EXCEP_OK 1
#define EXCEP_DECL int _thr = 0, _ret2 = 0, _ret = _ret2+EXCEP_OK

#define THROW( x ) \
    do { \
        _ret = (x); \
        if( !_thr ) { goto _catch; } \
    } while ( 0 )

#define TRY( x ) \
    do { \
        _ret2 = (x); \
        if ( _ret > 0 && _ret2 <= 0 ) { THROW( _ret2 ); } \
    } while ( 0 )

#define SET( x ) (_ret = (x))
#define EXCEP_RET return( _ret )
#define EXCEPTION _ret
#define RETURN _ret2
#define CATCH _catch: _thr = 1; if ( 0 ) { goto _foo; } _foo
#define EXCEP_BOOL ( _ret > 0 ? EXCEP_OK : EXCEP_FAIL )

#endif
