#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>

#include <Eina.h>
#include "eolian_database.h"
#include "eolian_priv.h"
#include "eo_lexer.h"

static Eina_Bool
node_error(const Eolian_Object *obj, const char *msg)
{
   eolian_state_log_obj(obj->unit->state, obj, "%s", msg);
   return EINA_FALSE;
}

static void
mask_to_str(int mask, char *buf)
{
#define APPEND_TP(str) \
   { \
      if (append_sep) *(buf++) = '|'; \
      memcpy(buf, str, sizeof(str) - 1); \
      buf += sizeof(str) - 1; \
      append_sep = EINA_TRUE; \
   }

   Eina_Bool append_sep = EINA_FALSE;

   if ((mask & EOLIAN_MASK_SINT) && (mask & EOLIAN_MASK_UINT))
     APPEND_TP("integer")
   else if (mask & EOLIAN_MASK_SINT)
     APPEND_TP("signed integer")
   else if (mask & EOLIAN_MASK_UINT)
     APPEND_TP("unsigned integer")

   if (mask & EOLIAN_MASK_FLOAT)
     APPEND_TP("float")
   if (mask & EOLIAN_MASK_BOOL)
     APPEND_TP("boolean")
   if (mask & EOLIAN_MASK_STRING)
     APPEND_TP("string")
   if (mask & EOLIAN_MASK_CHAR)
     APPEND_TP("char")
   if (mask & EOLIAN_MASK_NULL)
     APPEND_TP("null")

   *buf = '\0';
#undef APPEND_TP
}

static Eina_Bool
expr_type_error(const Eolian_Expression *expr, int type, int mask)
{
   char buf[512];
   char ebuf[256];
   char tbuf[128];
   mask_to_str(mask, ebuf);
   mask_to_str(type, tbuf);
   snprintf(buf, sizeof(buf), "invalid type (given %s, expected %s)",
       tbuf, ebuf);
   return node_error((const Eolian_Object*)expr, buf);
}

static Eina_Bool
expr_error(const Eolian_Expression *expr, const char *msg)
{
   char buf[512];
   snprintf(buf, sizeof(buf), "%s '%s'", msg, expr->value.s);
   return node_error((const Eolian_Object*)expr, buf);
}

static int
expr_type_to_mask(const Eolian_Expression *expr)
{
   assert(expr->type);
   switch (expr->type)
     {
      case EOLIAN_EXPR_ULLONG:
      case EOLIAN_EXPR_ULONG:
      case EOLIAN_EXPR_UINT:
        return EOLIAN_MASK_UINT;
      case EOLIAN_EXPR_LLONG:
      case EOLIAN_EXPR_LONG:
      case EOLIAN_EXPR_INT:
        return EOLIAN_MASK_SINT;
      case EOLIAN_EXPR_DOUBLE:
      case EOLIAN_EXPR_FLOAT:
        return EOLIAN_MASK_FLOAT;
      case EOLIAN_EXPR_BOOL:
        return EOLIAN_MASK_BOOL;
      case EOLIAN_EXPR_STRING:
        return EOLIAN_MASK_STRING;
      case EOLIAN_EXPR_NULL:
        return EOLIAN_MASK_NULL;
      case EOLIAN_EXPR_CHAR:
        return EOLIAN_MASK_CHAR;
      default:
        return 0;
     }
   return 0;
}

static Eina_Bool
expr_type_mismatch_error(const Eolian_Expression *lhs,
                         const Eolian_Expression *rhs)
{
   char buf[512 + 128];
   char tbuf[256];
   char ebuf[256];
   mask_to_str(expr_type_to_mask(lhs), tbuf);
   mask_to_str(expr_type_to_mask(rhs), ebuf);
   snprintf(buf, sizeof(buf), "mismatched types (%s vs %s)", tbuf, ebuf);
   return node_error((const Eolian_Object*)rhs, buf);
}

static Eina_Bool
promote(Eolian_Expression *a, Eolian_Expression *b)
{
#define CONVERT_CASE(id, dtp, expr, field, fnm) \
   case EOLIAN_EXPR_##id: { \
      dtp nv = (dtp)(expr->value.fnm); \
      expr->value.field = nv; \
      break; \
   }

#define CONVERT(dtp, expr, field) \
   switch (expr->type) \
     { \
      CONVERT_CASE(DOUBLE, dtp, expr, field, d  ) \
      CONVERT_CASE(FLOAT , dtp, expr, field, f  ) \
      CONVERT_CASE(ULLONG, dtp, expr, field, ull) \
      CONVERT_CASE(LLONG , dtp, expr, field, ll ) \
      CONVERT_CASE(ULONG , dtp, expr, field, ul ) \
      CONVERT_CASE(LONG  , dtp, expr, field, l  ) \
      CONVERT_CASE(UINT  , dtp, expr, field, u  ) \
      CONVERT_CASE(INT   , dtp, expr, field, i  ) \
      default: \
        break; \
     }

#define PROMOTE(a, b, tp, dtp, field) \
   if (a->type == tp) \
     { \
        CONVERT(dtp, b, field) \
        b->type = a->type; \
        return EINA_TRUE; \
     } \
   else if (b->type == tp) \
     { \
        CONVERT(dtp, a, field) \
        a->type = b->type; \
        return EINA_TRUE; \
     }

   assert(a->type && b->type);
   /* not a number */
   if (a->type >= EOLIAN_EXPR_STRING)
     return expr_type_error(a, expr_type_to_mask(a), EOLIAN_MASK_NUMBER);
   if (b->type >= EOLIAN_EXPR_STRING)
     return expr_type_error(b, expr_type_to_mask(b), EOLIAN_MASK_NUMBER);
   /* no need for promotion */
   if (a->type == b->type) return EINA_TRUE;
   /* if either operand is floating point, everything has to be */
   PROMOTE(a, b, EOLIAN_EXPR_DOUBLE, double, d)
   PROMOTE(a, b, EOLIAN_EXPR_FLOAT,   float, f)
   /* if either operand is unsigned with rank >= the other one, convert to
    * unsigned; if either signed operand can represent all values of the
    * other signed or unsigned operand, convert to the larger one; our
    * ordering of types already guarantees this
    */
   PROMOTE(a, b, EOLIAN_EXPR_ULLONG, unsigned long long, ull)
   PROMOTE(a, b, EOLIAN_EXPR_LLONG,           long long, ll)
   PROMOTE(a, b, EOLIAN_EXPR_ULONG,  unsigned      long, ul)
   PROMOTE(a, b, EOLIAN_EXPR_LONG,                 long, l)
   PROMOTE(a, b, EOLIAN_EXPR_UINT,   unsigned       int, u)
   PROMOTE(a, b, EOLIAN_EXPR_INT,                   int, i)
   /* it never gets here - this is just so static analyzers don't yell at me */
   return EINA_TRUE;

#undef PROMOTE
#undef CONVERT
#undef CONVERT_CASE
}

static Eina_Bool eval_exp(const Eolian_Unit *unit,
                          Eolian_Expression *expr,
                          Eolian_Expression_Mask mask, Eolian_Expression *out,
                          Expr_Obj_Cb cb, void *data);

static Eina_Bool
eval_unary(const Eolian_Unit *unit, Eolian_Expression *expr,
           Eolian_Expression_Mask mask, Eolian_Expression *out,
           Expr_Obj_Cb cb, void *data)
{
   switch (expr->unop)
     {
      case EOLIAN_UNOP_UNP:
        {
           /* no-op, but still typecheck */
           if (!(mask & EOLIAN_MASK_SIGNED))
             return expr_type_error(expr, EOLIAN_MASK_SIGNED, mask);

           return eval_exp(unit, expr->expr, EOLIAN_MASK_SIGNED, out, cb, data);
        }
      case EOLIAN_UNOP_UNM:
        {
           Eolian_Expression exp;

           if (!(mask & EOLIAN_MASK_SIGNED))
             return expr_type_error(expr, EOLIAN_MASK_SIGNED, mask);

           if (!eval_exp(unit, expr->expr, EOLIAN_MASK_SIGNED, &exp, cb, data))
             return EINA_FALSE;

           switch (exp.type)
             {
              case EOLIAN_EXPR_LLONG : exp.value.ll = -(exp.value.ll); break;
              case EOLIAN_EXPR_LONG  : exp.value.l  = -(exp.value.l ); break;
              case EOLIAN_EXPR_INT   : exp.value.i  = -(exp.value.i ); break;
              case EOLIAN_EXPR_FLOAT : exp.value.f  = -(exp.value.f ); break;
              case EOLIAN_EXPR_DOUBLE: exp.value.d  = -(exp.value.d ); break;
              default: return EINA_FALSE;
             }

           *out = exp;
           return EINA_TRUE;
        }
      case EOLIAN_UNOP_NOT:
        {
           Eolian_Expression exp;

           if (!(mask & EOLIAN_MASK_BOOL))
             return expr_type_error(expr, EOLIAN_MASK_BOOL, mask);

           if (!eval_exp(unit, expr->expr, EOLIAN_MASK_NUMBER | EOLIAN_MASK_BOOL, &exp, cb, data))
             return EINA_FALSE;

           exp.value.b = !(exp.value.ull);
           exp.type = EOLIAN_EXPR_BOOL;

           *out = exp;
           return EINA_TRUE;
        }
      case EOLIAN_UNOP_BNOT:
        {
           Eolian_Expression exp;

           if (!(mask & EOLIAN_MASK_INT))
             return expr_type_error(expr, EOLIAN_MASK_INT, mask);

           if (!eval_exp(unit, expr->expr, EOLIAN_MASK_INT, &exp, cb, data))
             return EINA_FALSE;

           switch (exp.type)
             {
              case EOLIAN_EXPR_ULLONG: exp.value.ull  = ~(exp.value.ull); break;
              case EOLIAN_EXPR_LLONG : exp.value.ll   = ~(exp.value.ll ); break;
              case EOLIAN_EXPR_ULONG : exp.value.ul   = ~(exp.value.ul ); break;
              case EOLIAN_EXPR_LONG  : exp.value.l    = ~(exp.value.l  ); break;
              case EOLIAN_EXPR_UINT  : exp.value.u    = ~(exp.value.u  ); break;
              case EOLIAN_EXPR_INT   : exp.value.i    = ~(exp.value.i  ); break;
              default: return EINA_FALSE;
             }

           *out = exp;
           return EINA_TRUE;
        }

      default:
        assert(EINA_FALSE);
        return EINA_FALSE;
     }

   return EINA_TRUE;
}

static Eina_Bool
eval_promote_num(const Eolian_Unit *unit, Eolian_Expression *expr,
                 Eolian_Expression *lhs, Eolian_Expression *rhs, int mask,
                 int emask, Expr_Obj_Cb cb, void *data)
{
   /* make sure the output can be a number */
   if (!(mask & EOLIAN_MASK_NUMBER))
     return expr_type_error(expr, EOLIAN_MASK_NUMBER, mask);

   /* eval into primitive value */
   if (!eval_exp(unit, expr->lhs, emask, lhs, cb, data))
     return EINA_FALSE;

   if (!eval_exp(unit, expr->rhs, emask, rhs, cb, data))
     return EINA_FALSE;

   /* promote so both sides are of the same type */
   if (!promote(lhs, rhs))
     return EINA_FALSE;

   return EINA_TRUE;
}

static Eina_Bool
eval_binary(const Eolian_Unit *unit, Eolian_Expression *expr,
            Eolian_Expression_Mask mask, Eolian_Expression *out,
            Expr_Obj_Cb cb, void *data)
{
#define APPLY_CASE(id, expr, lhs, rhs, fnm, op) \
   case EOLIAN_EXPR_##id: \
     expr->value.fnm = lhs.value.fnm op rhs.value.fnm; break;

#define APPLY_CASE_INT(expr, lhs, rhs, op) \
   APPLY_CASE(ULLONG, expr, lhs, rhs, ull, op) \
   APPLY_CASE(LLONG , expr, lhs, rhs, ll , op) \
   APPLY_CASE(ULONG , expr, lhs, rhs, ul , op) \
   APPLY_CASE(LONG  , expr, lhs, rhs, l  , op) \
   APPLY_CASE(UINT  , expr, lhs, rhs, u  , op) \
   APPLY_CASE(INT   , expr, lhs, rhs, i  , op)

#define APPLY_CASE_FLOAT(expr, lhs, rhs, op) \
   APPLY_CASE(DOUBLE, expr, lhs, rhs, d , op) \
   APPLY_CASE(FLOAT , expr, lhs, rhs, f , op)

#define APPLY_NUM(expr, lhs, rhs, op) \
   expr->type = lhs.type; \
   switch (lhs.type) \
     { \
      APPLY_CASE_INT(  expr, lhs, rhs, op) \
      APPLY_CASE_FLOAT(expr, lhs, rhs, op) \
      default: \
        return EINA_FALSE; \
     }

#define APPLY_INT(expr, lhs, rhs, op) \
   expr->type = lhs.type; \
   switch (lhs.type) \
     { \
      APPLY_CASE_INT(expr, lhs, rhs, op) \
      default: \
        return EINA_FALSE; \
     }

#define CASE_ARITH(id, op) \
   case EOLIAN_BINOP_##id: \
     { \
        Eolian_Expression lhs; \
        Eolian_Expression rhs; \
        int emask = (mask & EOLIAN_MASK_FLOAT) \
                    ? EOLIAN_MASK_NUMBER \
                    : EOLIAN_MASK_INT; \
        if (!eval_promote_num(unit, expr, &lhs, &rhs, mask, emask, cb, data)) \
          return EINA_FALSE; \
        APPLY_NUM(out, lhs, rhs, op) \
        return EINA_TRUE; \
     }

#define CASE_ARITH_INT(id, op) \
   case EOLIAN_BINOP_##id: \
     { \
        Eolian_Expression lhs; \
        Eolian_Expression rhs; \
        if (!eval_promote_num(unit, expr, &lhs, &rhs, mask, EOLIAN_MASK_INT, cb, data)) \
          return EINA_FALSE; \
        APPLY_INT(out, lhs, rhs, op) \
        return EINA_TRUE; \
     }

#define CASE_COMP(id, op, allowed) \
   case EOLIAN_BINOP_##id: \
     { \
        Eolian_Expression lhs; \
        Eolian_Expression rhs; \
        if (!(mask & EOLIAN_MASK_BOOL)) \
          return expr_type_error(expr, EOLIAN_MASK_BOOL, mask); \
        if (!eval_exp(unit, expr->lhs, allowed, &lhs, cb, data)) \
          return EINA_FALSE; \
        if (!eval_exp(unit, expr->rhs, allowed, &rhs, cb, data)) \
          return EINA_FALSE; \
        if (lhs.type >= EOLIAN_EXPR_STRING && rhs.type != lhs.type) \
          return expr_type_mismatch_error(&lhs, &rhs); \
        else if (rhs.type >= EOLIAN_EXPR_STRING && rhs.type != lhs.type) \
          return expr_type_mismatch_error(&lhs, &rhs); \
        out->type    = EOLIAN_EXPR_BOOL; \
        out->value.b = lhs.value.ull op rhs.value.ull; \
        return EINA_TRUE; \
     }

   switch (expr->binop)
     {
      CASE_ARITH(ADD, +)
      CASE_ARITH(SUB, -)
      CASE_ARITH(MUL, *)
      CASE_ARITH(DIV, /)

      CASE_ARITH_INT(MOD, %)

      CASE_COMP(EQ, ==, EOLIAN_MASK_ALL)
      CASE_COMP(NQ, !=, EOLIAN_MASK_ALL)
      CASE_COMP(GT, > , EOLIAN_MASK_NUMBER)
      CASE_COMP(LT, < , EOLIAN_MASK_NUMBER)
      CASE_COMP(GE, >=, EOLIAN_MASK_NUMBER)
      CASE_COMP(LE, <=, EOLIAN_MASK_NUMBER)

      CASE_COMP(AND, &&, EOLIAN_MASK_ALL)
      CASE_COMP(OR,  ||, EOLIAN_MASK_ALL)

      CASE_ARITH_INT(BAND, &)
      CASE_ARITH_INT(BOR , |)
      CASE_ARITH_INT(BXOR, ^)
      CASE_ARITH_INT(LSH , <<)
      CASE_ARITH_INT(RSH , >>)

      default:
        assert(EINA_FALSE);
        return EINA_FALSE;
     }
   return EINA_TRUE;

#undef CASE_ARITH
#undef CASE_ARITH_INT
#undef CASE_COMP
#undef APPLY_NUM
#undef APPLY_INT
#undef APPLY_CASE_FLOAT
#undef APPLY_CASE_INT
#undef APPLY_CASE
}

static Eina_Bool
split_enum_name(const char *str, char **ename, char **memb)
{
   char *fulln  = strdup(str);
   char *memb_s = strrchr(fulln, '.');
   if  (!memb_s)
     {
        free(fulln);
        return EINA_FALSE;
     }
   *(memb_s++) = '\0';
   *ename = fulln;
   *memb  = memb_s;
   return EINA_TRUE;
}

static Eina_Bool
eval_exp(const Eolian_Unit *unit, Eolian_Expression *expr,
         Eolian_Expression_Mask mask, Eolian_Expression *out,
         Expr_Obj_Cb cb, void *data)
{
   switch (expr->type)
     {
      case EOLIAN_EXPR_INT:
      case EOLIAN_EXPR_LONG:
      case EOLIAN_EXPR_LLONG:
        {
           if (!(mask & EOLIAN_MASK_SINT))
             return expr_type_error(expr, EOLIAN_MASK_SINT, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_UINT:
      case EOLIAN_EXPR_ULONG:
      case EOLIAN_EXPR_ULLONG:
        {
           if (!(mask & EOLIAN_MASK_UINT))
             return expr_type_error(expr, EOLIAN_MASK_UINT, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_FLOAT:
      case EOLIAN_EXPR_DOUBLE:
        {
           if (!(mask & EOLIAN_MASK_FLOAT))
             return expr_type_error(expr, EOLIAN_MASK_FLOAT, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_STRING:
        {
           if (!(mask & EOLIAN_MASK_STRING) && !(mask & EOLIAN_MASK_NULL))
             return expr_type_error(expr, EOLIAN_MASK_STRING, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_NULL:
        {
           if (!(mask & EOLIAN_MASK_NULL))
             return expr_type_error(expr, EOLIAN_MASK_NULL, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_CHAR:
        {
           if (!(mask & EOLIAN_MASK_CHAR))
             return expr_type_error(expr, EOLIAN_MASK_CHAR, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_BOOL:
        {
           if (!(mask & EOLIAN_MASK_BOOL))
             return expr_type_error(expr, EOLIAN_MASK_BOOL, mask);
           *out = *expr;
           return EINA_TRUE;
        }
      case EOLIAN_EXPR_NAME:
        {
           if (!unit)
             {
                if (!expr->expr)
                  return expr_error(expr, "undefined value");
                return eval_exp(NULL, expr->expr, mask, out, cb, data);
             }

           const Eolian_Constant *var = eolian_unit_constant_by_name_get
             (unit, expr->value.s);
           Eolian_Expression *exp = NULL;

           if (!var)
             {
                const Eolian_Typedecl *etpd;
                const Eolian_Enum_Type_Field *fl;

                /* try aliases, hoping it'll be enum */
                char *fulln = NULL, *memb = NULL;

                if (!split_enum_name(expr->value.s, &fulln, &memb))
                  return expr_error(expr, "undefined variable");

                /* assert int here, as we're clearly dealing with enum */
                if (!(mask & EOLIAN_MASK_INT))
                  {
                     free(fulln);
                     return expr_type_error(expr, EOLIAN_MASK_INT, mask);
                  }

                etpd = eolian_unit_alias_by_name_get(unit, fulln);
                if (etpd && cb) cb((const Eolian_Object *)etpd, data);
                while (etpd && etpd->type == EOLIAN_TYPEDECL_ALIAS)
                  {
                     const Eolian_Type *etp = eolian_typedecl_base_type_get(etpd);
                     if (!etp || etp->type != EOLIAN_TYPE_REGULAR)
                       break;
                     etpd = database_type_decl_find(unit, etp);
                  }

                if (!etpd)
                  {
                     etpd = eolian_unit_enum_by_name_get(unit, fulln);
                     if (etpd && cb) cb((const Eolian_Object *)etpd, data);
                  }
                if (!etpd || etpd->type != EOLIAN_TYPEDECL_ENUM)
                  {
                     free(fulln);
                     return expr_error(expr, "undefined variable");
                  }

                fl = eolian_typedecl_enum_field_get(etpd, memb);
                if (fl) exp = (Eolian_Expression *)eolian_typedecl_enum_field_value_get(fl, EINA_TRUE);
                free(fulln);

                if (!exp)
                  return expr_error(expr, "invalid enum field");
             }
           else
             {
                exp = (Eolian_Expression *)var->value;
                if (cb) cb((const Eolian_Object *)var, data);
             }

           if (!exp)
             return expr_error(expr, "undefined variable");

           expr->expr = exp;
           return eval_exp(unit, exp, mask, out, cb, data);
        }
      case EOLIAN_EXPR_UNARY:
        return eval_unary(unit, expr, mask, out, cb, data);
      case EOLIAN_EXPR_BINARY:
        return eval_binary(unit, expr, mask, out, cb, data);
      default:
        assert(EINA_FALSE);
        return EINA_FALSE;
     }

   return EINA_TRUE;
}

Eolian_Value
database_expr_eval(const Eolian_Unit *unit, Eolian_Expression *expr,
                   Eolian_Expression_Mask mask, Expr_Obj_Cb cb, void *data)
{
   Eolian_Expression out;
   Eolian_Value ret;
   ret.type = EOLIAN_EXPR_UNKNOWN;
   if (!mask)
     return ret;
   if (!eval_exp(unit, expr, mask, &out, cb, data))
     return ret;
   ret.type = out.type;
   ret.value = out.value;
   return ret;
}

Eolian_Value
database_expr_eval_type(const Eolian_Unit *unit, Eolian_Expression *expr,
                        const Eolian_Type *type, Expr_Obj_Cb cb, void *data)
{
    Eolian_Value err;
    err.type = EOLIAN_EXPR_UNKNOWN;
    if (!type)
      return err;
    switch (type->type)
      {
        case EOLIAN_TYPE_CLASS:
          return database_expr_eval(unit, expr, EOLIAN_MASK_NULL, cb, data);
        case EOLIAN_TYPE_REGULAR:
          {
              if (database_type_is_ownable(unit, type, EINA_FALSE, NULL))
                 return database_expr_eval(unit, expr, EOLIAN_MASK_NULL, cb, data);
              int  kw = eo_lexer_keyword_str_to_id(type->base.name);
              if (!kw || kw < KW_byte || kw >= KW_void)
                 {
                     const Eolian_Typedecl *base = database_type_decl_find(unit, type);
                     if (!base)
                        return err;
                     if (base->type == EOLIAN_TYPEDECL_ALIAS)
                        return database_expr_eval_type(unit, expr, eolian_typedecl_base_type_get(base), cb, data);
                     else if (base->type == EOLIAN_TYPEDECL_ENUM)
                        return database_expr_eval(unit, expr, EOLIAN_MASK_INT, cb, data);
                     return err;
                 }
              switch (kw)
                 {
                  case KW_byte:
                  case KW_short:
                  case KW_int:
                  case KW_long:
                  case KW_llong:
                  case KW_int8:
                  case KW_int16:
                  case KW_int32:
                  case KW_int64:
                  case KW_int128:
                  case KW_ssize:
                  case KW_intptr:
                  case KW_ptrdiff:
                     return database_expr_eval(unit, expr, EOLIAN_MASK_SINT, cb, data);
                  case KW_ubyte:
                  case KW_ushort:
                  case KW_uint:
                  case KW_ulong:
                  case KW_ullong:
                  case KW_uint8:
                  case KW_uint16:
                  case KW_uint32:
                  case KW_uint64:
                  case KW_uint128:
                  case KW_size:
                  case KW_uintptr:
                  case KW_time:
                     return database_expr_eval(unit, expr, EOLIAN_MASK_UINT, cb, data);
                  case KW_float:
                  case KW_double:
                     return database_expr_eval(unit, expr, EOLIAN_MASK_FLOAT, cb, data);
                  case KW_bool:
                     return database_expr_eval(unit, expr, EOLIAN_MASK_BOOL, cb, data);
                  case KW_char:
                     return database_expr_eval(unit, expr, EOLIAN_MASK_CHAR, cb, data);
                  default:
                     return err;
                 }
          }
        default:
          return err;
      }
}

void
database_expr_del(Eolian_Expression *expr)
{
   if (!expr) return;
   if (expr->base.file) eina_stringshare_del(expr->base.file);
   if (expr->type == EOLIAN_EXPR_BINARY)
     {
        if (!expr->weak_lhs) database_expr_del(expr->lhs);
        if (!expr->weak_rhs) database_expr_del(expr->rhs);
     }
   else if (expr->type == EOLIAN_EXPR_UNARY)
     {
        if (!expr->weak_lhs) database_expr_del(expr->expr);
     }
   else if (expr->type == EOLIAN_EXPR_STRING)
     {
        eina_stringshare_del(expr->value.s);
     }
   free(expr);
}
