#ifndef UIRX_H
#define UIRX_H

#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <altmalloc.h>

#ifdef UIRX_WC_T
typedef UIRX_WC_T uirx_wc_t;
#else
typedef unsigned int uirx_wc_t;
#endif

#ifndef UIRX_WC_MAX
#define UIRX_WC_MAX (UINT_MAX)
#endif

typedef enum {
  uirx_alpha_is_e,
  uirx_alpha_is_c,
  uirx_alpha_is_v,
} uirx_alpha_type_t;

typedef struct uirx_alpha_range_t {
  uirx_wc_t beg, end;
} uirx_alpha_range_t;

typedef void (*uirx_special_callback_t)(uirx_wc_t, void *);

typedef struct uirx_posv_st {
  ptrdiff_t *v;
  ptrdiff_t n;
} uirx_posv_t;

typedef struct {
  int type;
  union {
    uirx_alpha_range_t c;
    struct {
      uirx_alpha_range_t *cs;
      ptrdiff_t n;
    } v;
  } a;
  uirx_special_callback_t cb_func;
  union {
    unsigned char *map;
    uirx_posv_t posv;
  } followpos;
} uirx_alpha_t;

enum {
  uirx_expr_is_alpha,
  uirx_expr_is_cat,
  uirx_expr_is_or,
  uirx_expr_is_star,
  uirx_expr_is_plus,
  uirx_expr_is_0or1,
};

typedef unsigned char uirx_expr_type_t;

typedef struct uirx_expr_st {
  uirx_expr_type_t type;
  unsigned char nullable;
  unsigned char *firstpos;
  unsigned char *lastpos;
  union {
    ptrdiff_t one;
    ptrdiff_t two[2];
  } data;
} uirx_expr_t;

typedef struct uirx_exprv_st {
  uirx_expr_t *v;
  ptrdiff_t n, n_max;
  unsigned char **mv;
  ptrdiff_t mn, mn_max;
} uirx_exprv_t;

typedef struct uirx_nfa_st {
  uirx_alpha_t *v;
  ptrdiff_t n, n_max, n_bytes;
  unsigned char *posflag;
  uirx_posv_t first, stack[2];
  ptrdiff_t from;
} uirx_nfa_t;

typedef struct uirx_parse_stack_st {
  struct uirx_parse_stack_st *sup;
  ptrdiff_t last;
  uirx_exprv_t *exprv;
  uirx_nfa_t *nfa;
} uirx_parse_stack_t;

extern uirx_alpha_t *uirx_new_alpha(uirx_nfa_t *nfa);
extern uirx_expr_t *uirx_parse_alpha(uirx_parse_stack_t *sp, uirx_alpha_t *alpha);
extern uirx_nfa_t *uirx_parse_start(uirx_parse_stack_t *sp, uirx_parse_stack_t *sup, uirx_alpha_t *alpha);
extern uirx_expr_t *uirx_parse_end(uirx_parse_stack_t *sp, uirx_alpha_t *alpha);
extern uirx_expr_t *uirx_parse_or(uirx_parse_stack_t *sp);
extern uirx_expr_t *uirx_parse_postfix(uirx_parse_stack_t *sp, uirx_expr_type_t t);
extern uirx_nfa_t *uirx_complete_nfa(uirx_parse_stack_t *sp);
extern void uirx_match_start(uirx_nfa_t *nfa);
extern ptrdiff_t uirx_match(uirx_nfa_t *nfa, void *cb_arg, uirx_wc_t wc);
extern void uirx_match_end(uirx_nfa_t *nfa, void *cb_arg);
extern void uirx_free_exprv(uirx_exprv_t *exprv);

#endif
