/*------------------------->  ANSI C - headerfile  <-------------------------*/
/* Copyright (C) 2000 by K Hopper, University of Waikato, New Zealand        */
/* This file is part of the GNU Sather library. It is free software; you may */
/* redistribute  and/or modify it under the terms of the GNU Library General */
/* Public  License (LGPL)  as published  by the  Free  Software  Foundation; */
/* either version 2 of the license, or (at your option) any later version.   */
/* This  library  is distributed  in the  hope that it will  be  useful, but */
/* WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY */
/* or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details.       */
/* The license text is also available from:  Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                     */
/*------------>  Please email comments to <bug-sather@gnu.org>  <------------*/

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          This file contains references to all of the routines and macros
     used in handling and defining the Sather exception mechanism.  Note
     that there are some components where there are pSather differences.
     
          Version 2.0 Oct 98.  Copyright K Hopper, U of Waikato
          
                          Development History
                          -------------------

        Date           Who By         Detail
        ----           ------         ------

        16 Oct 98        kh       Original from Sather 1.1 & 1.2 distributions.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
#ifndef PSATHER 
#ifndef _EXC_SATHER_H_
#define _EXC_SATHER_H_

#include <stddef.h>
#include <stdlib.h>
#include <sys/utsname.h>
          
          #if defined(V_RISCOS) && defined(R_5_0)
                                     /* yoshida@agusa.nuie.nagoya-u.ac.jp */
               #ifndef SETJMP_H
                    #define SETJMP_H
                    
                    #include <setjmp.h>
               #endif
          #else
               #include <setjmp.h>
          #endif
          
          #include <assert.h>

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          The structure EXCEPT_ELEMENT should NOT be used anywhere in the code

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
struct EXCEPT_ELEMENT  {
 struct EXCEPT_ELEMENT *next ;
                jmp_buf jmp ;
                     OB exception ;
                  short mark ;
                        } ;

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          In order to use this exception code either

         a.  the macro DEFINE_EXCEPTION_STACK must have been defined in ONE
         (only) C file outside any function to declare a global variable, or

          b.   this file must have been included in ONE C file after the macro
          RUNTIME has been defined.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define DEFINE_EXCEPTION_STACK \
                    static struct EXCEPT_ELEMENT exp_bottom ; \
                    struct EXCEPT_ELEMENT *EXCEPTION_STACK = &exp_bottom
                    
          #ifndef RUNTIME
extern struct EXCEPT_ELEMENT *EXCEPTION_STACK ;
          #else
DEFINE_EXCEPTION_STACK ;
          #endif

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          While executing a lock, try or protect statement, an exception stack
     is built.  Specific stack frames may be marked and the stack may be
     unwound to the first mark at any time.  There are sixteen different marks,
     but MARK(0) and MARK(1) are predefined.

          Mark 0 is used to mark the stack at the beginning of a function.

          Mark 1 may be used for loops.
          
      WARNING While ESCAPE unwinds the stack and ends the function,
           JUMP_OUT_OF_LOOP does NOT terminate the loop.  The loop must be
           ended manually by either a break or a jump.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define MARK(n)                    (1 << (n))
          #define MARK_FUNCTION              MARK(0)
          #define MARK_LOOP                  MARK(1)
          #define SET_EXCEPTION_MARK(n)      EXCEPTION_STACK->mark |= (n)
          #define REMOVE_EXCEPTION_MARK(n)   EXCEPTION_STACK->mark &= ~(n)
          #define POP_TO(m) \
                     do { while(EXCEPTION_STACK->mark & (m) == 0) { \
                           EXCEPTION_STACK = EXCEPTION_STACK->next ; \
                           } ; \
                             \
                           REMOVE_EXCEPTION_MARK(m) } while(0)

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          Note that each function using ESCAPE MUST use EXCEPTION_PROLOGUE at
     the beginning, and EXCEPTION_EPILOGUE at the end.  Such a function may
     NOT use return.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define EXCEPTION_PROLOGUE    SET_EXCEPTION_MARK(MARK_FUNCTION) ;
          #define EXCEPTION_EPILOGUE    REMOVE_EXCEPTION_MARK(MARK_FUNCTION) ;
          #define ESCAPE                POP_TO(MARK_FUNCTION) ; return 

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          If it is desired to use mark on the stack to end loops, then
     LOOP_BEGIN must be used before the loop and LOOP_END at its end.
          
          Note that JUMP_OUT_OF_LOOP must be followed by a break or goto, as
     it will not end the loop.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define LOOP_BEGIN            SET_EXCEPTION_MARK(MARK_LOOP) ;
          #define LOOP_END              REMOVE_EXCEPTION_MARK(MARK_LOOP) ;
          #define JUMP_OUT_OF_LOOP      POP_TO(MARK_LOOP)

/* - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          In order to pop some frames from the exception stack, the macro
      POP_EXCEPTION should be used.  This macro can be used instead of
      LOOP_BEGIN and LOOP_END. The only problem is that exactly the number
      of frames to pop must be known when leaving a loop.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define POP_EXCEPTION1  EXCEPTION_STACK = EXCEPTION_STACK->next
          #define POP_EXCEPTION(n) \
                     do { \
                          int i = n ; \
                          while(i-- > 0) POP_EXCEPTION1 ; \
                          } while(0)

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          The macro RAISE(x) raises an exception. The value x (actually
     a pointer) is passed to the exception handling routine, which can use
     it through the macro EXCEPTION.  If this routine needs to propagate
     the exception then it can use RAISE(EXCEPTION).  This macro is somewhat
     tricky since ex could be instantiated with EXCEPTION when propagating
     the exception to the handler above. If so, EXCEPTION is expanded into
     EXCEPTION_STACK->exception, but EXCEPTION_STACK  is changed in the macro
     and a horrible name capture happens. To avoid it, the "ex" argument needs
     to be copied into local "the_ex" immediately and this must be used
     everywhere.

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define UNCAUGHT_EXCEPTION 0

#define RAISE(ex) do { OB the_ex = ex;                                  \
        struct EXCEPT_ELEMENT *PREV_EXCEPTION_STACK=EXCEPTION_STACK;	\
                       EXCEPTION_STACK=PREV_EXCEPTION_STACK->next;     	\
		       if(PREV_EXCEPTION_STACK->next!=NULL) {		\
			       EXCEPTION_STACK->exception=the_ex;      	\
			       longjmp(PREV_EXCEPTION_STACK->jmp,1); 	\
		       } else assert(UNCAUGHT_EXCEPTION);		\
	          } while(0)
#define EXCEPTION 	   		((OB)EXCEPTION_STACK->exception)

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

          To protect some statements A against exception, use
          
          PROTECT_BEGIN
          A ;
          PROTECT_WHEN
          
          if(EXCEPTION ==
              ....
          PROTECT_END
          
          If PROTECT_END is ever executed because the exception handler does
     neither end the program, nor raise another exception or ESCAPE (see above),
     then the exception is automatically propagated back to the next PROTECT
     by a RAISE(EXCEPTION).

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

          #define PROTECT_BEGIN         { \
                                              struct EXCEPT_ELEMENT p_n ; \
                                                \
                                              if(setjmp(p_n.jmp) == 0) { \
                                                   p_n.next = EXCEPTION_STACK ;\
                                                   p_n.mark = 0 ; \
                                                   EXCEPTION_STACK = &p_n ;
             
          #define PROTECT_WHEN          POP_EXCEPTION1 ; \
                                        } \
                                   else {
                                   
          #define PROTECT_END          }  \
                                  }

     #endif
#endif
