/* Automatically generated. Do not edit. */
/* Copyright (c) 2003, David Leonard. All rights reserved. */
/* $Id: try.h.in 1316 2008-01-13 02:21:42Z d $ */

#ifndef _SEE_h_try_
#define _SEE_h_try_

/*
 * Exception handling (try/catch)
 *
 * Usage example:
 *
 *	SEE_try_context_t c;
 *
 *	SEE_TRY(interp, c) {
 *		-- do code here that may call SEE_THROW()
 *		-- the try block breaks as soon as SEE_THROW happens
 *		-- NEVER 'return' or 'break' from inside a SEE_TRY!
 *		-- use TRY_BREAK or 'continue' to exit the SEE_TRY block.
 *	}
 *	-- 'finally' code gets run here always
 *	if (SEE_CAUGHT(c)) {
 *		-- this code run if a SEE_THROW was called during SEE_TRY 
 *		-- the exception thrown is SEE_CAUGHT(c)
 *		-- do a SEE_THROW(interp, SEE_CAUGHT(c)) if you can't handle it
 *	}
 *
 * If you only want a finally, and don't want to catch anything, use:
 *
 *	SEE_TRY(interp, c) {
 *		...
 *	}
 *	-- 'finally' code goes here
 *	SEE_DEFAULT_CATCH(interp, c);
 *
 * The signatures for the macros are:
 *
 *      struct SEE_value *SEE_CAUGHT(SEE_try_context_t);
 *      void SEE_THROW(struct SEE_interpreter *, struct SEE_value *);
 *      void SEE_DEFAULT_CATCH(SEE_interpreter *, SEE_try_context_t);
 *
 */

#include <see/type.h>
#include <see/value.h>

/*
 * Determine which setjmp/longjmp interface to use
 */

#if 1/*HAVE__LONGJMP*/
# include <setjmp.h>
# define _SEE_LONGJMP(buf, val)	_longjmp(buf, val)
# define _SEE_SETJMP(buf)	_setjmp(buf)
# define _SEE_JMPBUF		jmp_buf
#else
# if 1/*HAVE_LONGJMP*/
#  include <setjmp.h>
#  define _SEE_LONGJMP(buf, val)	longjmp(buf, val)
#  define _SEE_SETJMP(buf)	setjmp(buf)
#  define _SEE_JMPBUF		jmp_buf
# else
 # error "exception handling features requires longjmp/setjmp"
# endif
#endif

/*
 * Exception handling macros
 *
 * _SEE_TRY_INIT(interp, c);
 * if (_SEE_TRY_COND(interp, c))
 */

/* Sets up a try context */
#define _SEE_TRY_INIT(interp, c) 				\
    	 (c).previous = (interp)->try_context,			\
	 (interp)->try_context = &(c),				\
	 (c).interpreter = (interp),				\
	 SEE_SET_NULL(&(c).thrown),				\
	 (c).traceback = 0,					\
	 (c).saved_traceback = (c).interpreter->traceback

/* A setjmp-like function that calls FINI and returns true on catch */
#define _SEE_TRY_SETJMP(interp, c)				\
	 (_SEE_SETJMP(((struct SEE_try_context *)&(c))->env)	\
	   ? (/* longjmp caught */				\
	      (c).traceback = (c).interpreter->traceback,	\
	      _SEE_TRY_FINI(interp, c),				\
	      1 						\
	     )			   				\
	   : /* longjmp not caught */				\
	      0)

/* Called when the try block completes normally */
#define _SEE_TRY_FINI(interp, c)				\
	 (c).interpreter->try_context = (c).previous,		\
         (c).interpreter->traceback = (c).saved_traceback

/* A for-like macro that runs the body and finalizes exactly once */
#define SEE_TRY(interp, c) 					\
    for ((c).done = 0, _SEE_TRY_INIT(interp, c);		\
         !(c).done && !_SEE_TRY_SETJMP(interp, c);		\
         _SEE_TRY_FINI(interp, c), (c).done = 1)

/* Breaks out of a SEE_TRY() loop */
#define SEE_TRY_BREAK						\
	    continue

#define SEE_CAUGHT(c)						\
	    ((struct SEE_value *)((c).done ? 0 : &(c).thrown))

#define SEE_THROW(interp, v) SEE__THROW(interp, v, __FILE__, __LINE__)

#define SEE__THROW(interp, v, _file, _line)			\
	    do {						\
		if (!(interp)->try_context)			\
			SEE_throw_abort(interp, v, _file, 	\
				_line);	\
		SEE_VALUE_COPY((struct SEE_value *)		\
			&(interp)->try_context->thrown, 	\
			(struct SEE_value *)v);			\
		(interp)->try_context->throw_file = _file;	\
		(interp)->try_context->throw_line = _line;	\
		SEE_throw();	/* debugger hook */		\
		_SEE_LONGJMP(((struct SEE_try_context *)	\
		      (interp)->try_context)->env, 1);		\
		/* NOTREACHED */				\
	    } while (0)

#define SEE_RETHROW(interp, c)					\
	    do {						\
		/* SEE_ASSERT(!(c).done); */			\
		(interp)->traceback = (c).traceback;		\
		SEE__THROW(interp, 				\
			(struct SEE_value *)&(c).thrown,	\
			(c).throw_file, (c).throw_line);	\
	    } while (0)

/* convenience macro */
#define SEE_DEFAULT_CATCH(interp, c)				\
	    do {						\
		if (SEE_CAUGHT(c))				\
		    SEE_RETHROW(interp, c);			\
	    } while (0)

/*------------------------------------------------------------
 * private functions and externs used by the above macros
 */

struct SEE_throw_location {
	struct SEE_string *filename;		/* source location */
	int lineno;
};

struct SEE_try_context {
	struct SEE_interpreter *interpreter;
	volatile struct SEE_try_context *previous; /* try chain */
	struct SEE_value thrown;		/* value thrown during try */
	int done;				/* true if try completed */
	_SEE_JMPBUF env;			/* setjmp storage */
	const char *throw_file;			/* (debugging) */
	int throw_line;				/* (debugging) */
	struct SEE_traceback *saved_traceback;	/* traceback at try start */
	struct SEE_traceback *traceback;	/* traceback at throw time */
};

typedef struct SEE_try_context volatile SEE_try_context_t; 

struct SEE_interpreter;
void	SEE_throw_abort(struct SEE_interpreter *, const struct SEE_value *,
		const char *, int) SEE_dead;
struct SEE_string *SEE_location_string(struct SEE_interpreter *i,
		struct SEE_throw_location *loc);

/* Debugger hook for exceptions */
#ifndef NDEBUG
void	SEE_throw(void);
#else
# define SEE_throw() /* nothing */
#endif

#endif /* _SEE_h_try_ */
