mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-23 11:42:49 -06:00
initial import of the C runtime
This commit is contained in:
195
src/runtime/c/gu/exn.h
Normal file
195
src/runtime/c/gu/exn.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef GU_EXN_H_
|
||||
#define GU_EXN_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* @defgroup GuExn Exceptions
|
||||
* Defined in <gu/exn.h>.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// An exception frame.
|
||||
typedef struct GuExn GuExn;
|
||||
|
||||
/// @private
|
||||
typedef enum {
|
||||
GU_EXN_RAISED,
|
||||
GU_EXN_OK,
|
||||
GU_EXN_BLOCKED
|
||||
} GuExnState;
|
||||
|
||||
typedef struct GuExnData GuExnData;
|
||||
|
||||
/// A structure for storing exception values.
|
||||
struct GuExnData
|
||||
/**
|
||||
* When an exception is raised, if there is an associated value, it
|
||||
* must be allocated from a pool that still exists when control
|
||||
* returns to the handler of that exception. This structure is used to
|
||||
* communicate the exception from the raiser to the handler: the
|
||||
* handler sets #pool when setting up the exception frame, and the
|
||||
* raiser uses that pool to allocate the value and stores that in
|
||||
* #data. When control returns to the handler, it reads the value from
|
||||
* there.
|
||||
*/
|
||||
{
|
||||
|
||||
/// The pool that the exception value should be allocated from.
|
||||
GuPool* const pool;
|
||||
|
||||
/// The exception value.
|
||||
const void* data;
|
||||
};
|
||||
|
||||
struct GuExn {
|
||||
/// @privatesection
|
||||
GuExnState state;
|
||||
GuExn* parent;
|
||||
GuKind* catch;
|
||||
GuType* caught;
|
||||
GuExnData data;
|
||||
};
|
||||
|
||||
|
||||
/// @name Creating exception frames
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate a new local exception frame.
|
||||
#define gu_exn(parent_, catch_, pool_) &(GuExn){ \
|
||||
.state = GU_EXN_OK, \
|
||||
.parent = parent_, \
|
||||
.catch = gu_kind(catch_), \
|
||||
.caught = NULL, \
|
||||
.data.pool = pool_, \
|
||||
.data.data = NULL \
|
||||
}
|
||||
|
||||
|
||||
/// Allocate a new exception frame.
|
||||
GuExn*
|
||||
gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);
|
||||
|
||||
|
||||
|
||||
static inline bool
|
||||
gu_exn_is_raised(GuExn* err) {
|
||||
return err && (err->state == GU_EXN_RAISED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_exn_clear(GuExn* err) {
|
||||
err->caught = NULL;
|
||||
err->state = GU_EXN_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuType*
|
||||
gu_exn_caught(GuExn* err);
|
||||
|
||||
const void*
|
||||
gu_exn_caught_data(GuExn* err);
|
||||
|
||||
/// Temporarily block a raised exception.
|
||||
void
|
||||
gu_exn_block(GuExn* err);
|
||||
|
||||
/// Show again a blocked exception.
|
||||
void
|
||||
gu_exn_unblock(GuExn* err);
|
||||
|
||||
//@private
|
||||
GuExnData*
|
||||
gu_exn_raise_(GuExn* err, GuType* type);
|
||||
|
||||
//@private
|
||||
GuExnData*
|
||||
gu_exn_raise_debug_(GuExn* err, GuType* type,
|
||||
const char* filename, const char* func, int lineno);
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define gu_exn_raise(err_, type_) \
|
||||
gu_exn_raise_(err_, type_)
|
||||
#else
|
||||
#define gu_exn_raise(err_, type_) \
|
||||
gu_exn_raise_debug_(err_, type_, \
|
||||
__FILE__, __func__, __LINE__)
|
||||
#endif
|
||||
|
||||
/// Raise an exception.
|
||||
#define gu_raise(exn, T) \
|
||||
gu_exn_raise(exn, gu_type(T))
|
||||
/**<
|
||||
* @param exn The current exception frame.
|
||||
*
|
||||
* @param T The C type of the exception to raise.
|
||||
*
|
||||
* @return A #GuExnData object that can be used to store the exception value, or
|
||||
* \c NULL if no value is required.
|
||||
*
|
||||
* @note The associated #GuType object for type \p T must be visible.
|
||||
*/
|
||||
|
||||
#define gu_raise_new(error_, t_, pool_, expr_) \
|
||||
GU_BEGIN \
|
||||
GuExnData* gu_raise_err_ = gu_raise(error_, t_); \
|
||||
if (gu_raise_err_) { \
|
||||
GuPool* pool_ = gu_raise_err_->pool; \
|
||||
gu_raise_err_->data = expr_; \
|
||||
} \
|
||||
GU_END
|
||||
|
||||
#define gu_raise_i(error_, t_, ...) \
|
||||
gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))
|
||||
|
||||
|
||||
/// Check the status of the current exception frame
|
||||
static inline bool
|
||||
gu_ok(GuExn* exn) {
|
||||
return !GU_UNLIKELY(gu_exn_is_raised(exn));
|
||||
}
|
||||
/**<
|
||||
* @return \c false if an exception has been raised in the frame \p exn
|
||||
* and it has not been blocked, \c true otherwise.
|
||||
*/
|
||||
|
||||
|
||||
/// Return from current function if an exception has been raised.
|
||||
#define gu_return_on_exn(exn_, retval_) \
|
||||
GU_BEGIN \
|
||||
if (gu_exn_is_raised(exn_)) return retval_; \
|
||||
GU_END
|
||||
/**<
|
||||
* @showinitializer
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
typedef int GuErrno;
|
||||
|
||||
extern GU_DECLARE_TYPE(GuErrno, signed);
|
||||
|
||||
|
||||
|
||||
#define gu_raise_errno(error_) \
|
||||
gu_raise_i(error_, GuErrno, errno)
|
||||
|
||||
#if 0
|
||||
|
||||
typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);
|
||||
|
||||
extern GuTypeTable gu_exn_default_printer;
|
||||
|
||||
void
|
||||
gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // GU_EXN_H_
|
||||
Reference in New Issue
Block a user