2014-08-06 08:30:42 -07:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <Eina.h>
|
|
|
|
#include "eolian_database.h"
|
|
|
|
|
|
|
|
static Eina_Bool
|
2014-08-07 07:15:07 -07:00
|
|
|
node_error(const Eolian_Object *obj, const char *msg)
|
2014-08-06 08:30:42 -07:00
|
|
|
{
|
|
|
|
eina_log_print(_eolian_log_dom, EINA_LOG_LEVEL_ERR, obj->file, "",
|
|
|
|
obj->line, "%s at column %d", msg, obj->column);
|
|
|
|
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;
|
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if ((mask & EOLIAN_MASK_SINT) && (mask & EOLIAN_MASK_UINT))
|
2014-08-06 08:30:42 -07:00
|
|
|
APPEND_TP("integer")
|
2014-08-07 03:13:40 -07:00
|
|
|
else if (mask & EOLIAN_MASK_SINT)
|
2014-08-06 08:30:42 -07:00
|
|
|
APPEND_TP("signed integer")
|
2014-08-07 08:29:03 -07:00
|
|
|
else if (mask & EOLIAN_MASK_UINT)
|
|
|
|
APPEND_TP("unsigned integer")
|
2014-08-06 08:30:42 -07:00
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (mask & EOLIAN_MASK_FLOAT)
|
2014-08-06 08:30:42 -07:00
|
|
|
APPEND_TP("float")
|
2014-08-07 03:13:40 -07:00
|
|
|
if (mask & EOLIAN_MASK_BOOL)
|
2014-08-06 08:30:42 -07:00
|
|
|
APPEND_TP("boolean")
|
2014-08-07 03:13:40 -07:00
|
|
|
if (mask & EOLIAN_MASK_STRING)
|
2014-08-06 08:30:42 -07:00
|
|
|
APPEND_TP("string")
|
2014-08-07 07:15:07 -07:00
|
|
|
if (mask & EOLIAN_MASK_CHAR)
|
|
|
|
APPEND_TP("char")
|
|
|
|
if (mask & EOLIAN_MASK_NULL)
|
|
|
|
APPEND_TP("null")
|
2014-08-06 08:30:42 -07:00
|
|
|
|
|
|
|
*buf = '\0';
|
|
|
|
#undef APPEND_TP
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
2014-08-07 07:15:07 -07:00
|
|
|
expr_type_error(const Eolian_Expression *expr, int type, int mask)
|
2014-08-06 08:30:42 -07:00
|
|
|
{
|
|
|
|
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);
|
2014-08-07 07:15:07 -07:00
|
|
|
return node_error((const Eolian_Object*)expr, buf);
|
2014-08-06 08:30:42 -07:00
|
|
|
}
|
|
|
|
|
2014-08-12 06:25:53 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-08-06 08:30:42 -07:00
|
|
|
static int
|
2014-08-07 07:15:07 -07:00
|
|
|
expr_type_to_mask(const Eolian_Expression *expr)
|
2014-08-06 08:30:42 -07:00
|
|
|
{
|
|
|
|
assert(expr->type);
|
|
|
|
switch (expr->type)
|
|
|
|
{
|
|
|
|
case EOLIAN_EXPR_ULLONG:
|
|
|
|
case EOLIAN_EXPR_ULONG:
|
|
|
|
case EOLIAN_EXPR_UINT:
|
2014-08-07 03:13:40 -07:00
|
|
|
return EOLIAN_MASK_UINT;
|
2014-08-06 08:30:42 -07:00
|
|
|
case EOLIAN_EXPR_LLONG:
|
|
|
|
case EOLIAN_EXPR_LONG:
|
|
|
|
case EOLIAN_EXPR_INT:
|
2014-08-07 03:13:40 -07:00
|
|
|
return EOLIAN_MASK_SINT;
|
2014-08-06 08:30:42 -07:00
|
|
|
case EOLIAN_EXPR_LDOUBLE:
|
|
|
|
case EOLIAN_EXPR_DOUBLE:
|
|
|
|
case EOLIAN_EXPR_FLOAT:
|
2014-08-07 03:13:40 -07:00
|
|
|
return EOLIAN_MASK_FLOAT;
|
2014-08-06 08:30:42 -07:00
|
|
|
case EOLIAN_EXPR_BOOL:
|
2014-08-07 03:13:40 -07:00
|
|
|
return EOLIAN_MASK_BOOL;
|
2014-08-06 08:30:42 -07:00
|
|
|
case EOLIAN_EXPR_STRING:
|
2014-08-07 03:13:40 -07:00
|
|
|
return EOLIAN_MASK_STRING;
|
2014-08-07 07:15:07 -07:00
|
|
|
case EOLIAN_EXPR_NULL:
|
|
|
|
return EOLIAN_MASK_NULL;
|
|
|
|
case EOLIAN_EXPR_CHAR:
|
|
|
|
return EOLIAN_MASK_CHAR;
|
2014-08-06 08:30:42 -07:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
2014-08-07 07:15:07 -07:00
|
|
|
expr_type_mismatch_error(const Eolian_Expression *lhs,
|
|
|
|
const Eolian_Expression *rhs)
|
2014-08-06 08:30:42 -07:00
|
|
|
{
|
|
|
|
char buf[512];
|
|
|
|
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);
|
2014-08-07 07:15:07 -07:00
|
|
|
return node_error((const Eolian_Object*)rhs, buf);
|
2014-08-06 08:30:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
promote(Eolian_Expression *a, Eolian_Expression *b)
|
|
|
|
{
|
|
|
|
#define CONVERT_CASE(id, dtp, expr, field, fnm) \
|
|
|
|
case EOLIAN_EXPR_##id: \
|
|
|
|
expr->value.field = (dtp)(expr->value.fnm); break;
|
|
|
|
|
|
|
|
#define CONVERT(dtp, expr, field) \
|
|
|
|
switch (expr->type) \
|
|
|
|
{ \
|
|
|
|
CONVERT_CASE(LDOUBLE, dtp, expr, field, ld ) \
|
|
|
|
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)
|
2014-08-07 03:13:40 -07:00
|
|
|
return expr_type_error(a, expr_type_to_mask(a), EOLIAN_MASK_NUMBER);
|
2014-08-06 08:30:42 -07:00
|
|
|
if (b->type >= EOLIAN_EXPR_STRING)
|
2014-08-07 03:13:40 -07:00
|
|
|
return expr_type_error(b, expr_type_to_mask(b), EOLIAN_MASK_NUMBER);
|
2014-08-06 08:30:42 -07:00
|
|
|
/* 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_LDOUBLE, long double, ld)
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-08-07 07:15:07 -07:00
|
|
|
static Eina_Bool eval_exp(const Eolian_Expression *expr, Eolian_Expression_Mask mask, Eolian_Expression *out);
|
2014-08-06 08:30:42 -07:00
|
|
|
|
|
|
|
static Eina_Bool
|
2014-08-07 07:15:07 -07:00
|
|
|
eval_unary(const Eolian_Expression *expr, Eolian_Expression_Mask mask,
|
2014-08-06 08:30:42 -07:00
|
|
|
Eolian_Expression *out)
|
|
|
|
{
|
|
|
|
switch (expr->unop)
|
|
|
|
{
|
|
|
|
case EOLIAN_UNOP_UNP:
|
|
|
|
{
|
|
|
|
/* no-op, but still typecheck */
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_SINT))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_SINT, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
return eval_exp(expr->expr, EOLIAN_MASK_SINT, out);
|
2014-08-06 08:30:42 -07:00
|
|
|
}
|
|
|
|
case EOLIAN_UNOP_UNM:
|
|
|
|
{
|
|
|
|
Eolian_Expression exp;
|
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_SINT))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_SINT, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr->expr, EOLIAN_MASK_SINT, &exp))
|
2014-08-06 08:30:42 -07:00
|
|
|
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;
|
|
|
|
default: return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out = exp;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
case EOLIAN_UNOP_NOT:
|
|
|
|
{
|
|
|
|
Eolian_Expression exp;
|
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_BOOL))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_BOOL, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr->expr, EOLIAN_MASK_NUMBER | EOLIAN_MASK_BOOL, &exp))
|
2014-08-06 08:30:42 -07:00
|
|
|
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;
|
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_INT))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_INT, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr->expr, EOLIAN_MASK_INT, &exp))
|
2014-08-06 08:30:42 -07:00
|
|
|
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;
|
|
|
|
}
|
2014-08-07 07:15:07 -07:00
|
|
|
|
|
|
|
default:
|
|
|
|
assert(EINA_FALSE);
|
|
|
|
return EINA_FALSE;
|
2014-08-06 08:30:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
2014-08-07 07:15:07 -07:00
|
|
|
eval_promote_num(const Eolian_Expression *expr, Eolian_Expression *lhs,
|
2014-08-06 08:30:42 -07:00
|
|
|
Eolian_Expression *rhs, int mask, int emask)
|
|
|
|
{
|
|
|
|
/* make sure the output can be a number */
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_NUMBER))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_NUMBER, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
|
|
|
|
/* eval into primitive value */
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr->lhs, emask, lhs))
|
2014-08-06 08:30:42 -07:00
|
|
|
return EINA_FALSE;
|
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr->rhs, emask, rhs))
|
2014-08-06 08:30:42 -07:00
|
|
|
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
|
2014-08-07 07:15:07 -07:00
|
|
|
eval_binary(const Eolian_Expression *expr, Eolian_Expression_Mask mask,
|
2014-08-06 08:30:42 -07:00
|
|
|
Eolian_Expression *out)
|
|
|
|
{
|
|
|
|
#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(LDOUBLE, expr, lhs, rhs, ld, 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; \
|
2014-08-07 03:13:40 -07:00
|
|
|
int emask = (mask & EOLIAN_MASK_FLOAT) \
|
|
|
|
? EOLIAN_MASK_NUMBER \
|
|
|
|
: EOLIAN_MASK_INT; \
|
2014-08-06 08:30:42 -07:00
|
|
|
if (!eval_promote_num(expr, &lhs, &rhs, mask, emask)) \
|
|
|
|
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; \
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_promote_num(expr, &lhs, &rhs, mask, EOLIAN_MASK_INT)) \
|
2014-08-06 08:30:42 -07:00
|
|
|
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; \
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_BOOL)) \
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_BOOL, mask); \
|
|
|
|
if (!eval_exp(expr->lhs, allowed, &lhs)) \
|
2014-08-06 08:30:42 -07:00
|
|
|
return EINA_FALSE; \
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr->rhs, allowed, &rhs)) \
|
2014-08-06 08:30:42 -07:00
|
|
|
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, %)
|
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
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)
|
2014-08-06 08:30:42 -07:00
|
|
|
|
2014-08-07 03:13:40 -07:00
|
|
|
CASE_COMP(AND, &&, EOLIAN_MASK_ALL)
|
|
|
|
CASE_COMP(OR, ||, EOLIAN_MASK_ALL)
|
2014-08-06 08:30:42 -07:00
|
|
|
|
|
|
|
CASE_ARITH_INT(BAND, &)
|
|
|
|
CASE_ARITH_INT(BOR , |)
|
|
|
|
CASE_ARITH_INT(BXOR, ^)
|
|
|
|
CASE_ARITH_INT(LSH , <<)
|
|
|
|
CASE_ARITH_INT(RSH , >>)
|
2014-08-07 07:15:07 -07:00
|
|
|
|
|
|
|
default:
|
|
|
|
assert(EINA_FALSE);
|
|
|
|
return EINA_FALSE;
|
2014-08-06 08:30:42 -07:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-08-12 06:25:53 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-08-06 08:30:42 -07:00
|
|
|
static Eina_Bool
|
2014-08-07 07:15:07 -07:00
|
|
|
eval_exp(const Eolian_Expression *expr, Eolian_Expression_Mask mask,
|
|
|
|
Eolian_Expression *out)
|
2014-08-06 08:30:42 -07:00
|
|
|
{
|
|
|
|
switch (expr->type)
|
|
|
|
{
|
|
|
|
case EOLIAN_EXPR_INT:
|
|
|
|
case EOLIAN_EXPR_LONG:
|
|
|
|
case EOLIAN_EXPR_LLONG:
|
|
|
|
{
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_SINT))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_SINT, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
*out = *expr;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
case EOLIAN_EXPR_UINT:
|
|
|
|
case EOLIAN_EXPR_ULONG:
|
|
|
|
case EOLIAN_EXPR_ULLONG:
|
|
|
|
{
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_UINT))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_UINT, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
*out = *expr;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
case EOLIAN_EXPR_FLOAT:
|
|
|
|
case EOLIAN_EXPR_DOUBLE:
|
|
|
|
case EOLIAN_EXPR_LDOUBLE:
|
|
|
|
{
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_FLOAT))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_FLOAT, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
*out = *expr;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
case EOLIAN_EXPR_STRING:
|
|
|
|
{
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_STRING))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_STRING, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
*out = *expr;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
2014-08-07 07:15:07 -07:00
|
|
|
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;
|
|
|
|
}
|
2014-08-06 08:30:42 -07:00
|
|
|
case EOLIAN_EXPR_BOOL:
|
|
|
|
{
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!(mask & EOLIAN_MASK_BOOL))
|
|
|
|
return expr_type_error(expr, EOLIAN_MASK_BOOL, mask);
|
2014-08-06 08:30:42 -07:00
|
|
|
*out = *expr;
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
2014-08-12 06:25:53 -07:00
|
|
|
case EOLIAN_EXPR_NAME:
|
|
|
|
{
|
|
|
|
const Eolian_Variable *var = eolian_variable_constant_get_by_name
|
|
|
|
(expr->value.s);
|
|
|
|
const Eolian_Expression *exp = NULL;
|
|
|
|
|
|
|
|
if (!var)
|
|
|
|
{
|
|
|
|
const Eolian_Type *etp;
|
|
|
|
|
|
|
|
/* 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");
|
|
|
|
|
|
|
|
etp = eolian_type_alias_get_by_name(fulln);
|
|
|
|
while (etp && etp->type == EOLIAN_TYPE_ALIAS)
|
|
|
|
{
|
|
|
|
etp = eolian_type_base_type_get(etp);
|
|
|
|
if (etp->type == EOLIAN_TYPE_ENUM)
|
|
|
|
break;
|
|
|
|
if (etp->type == EOLIAN_TYPE_REGULAR_ENUM)
|
|
|
|
break;
|
|
|
|
if (etp->type != EOLIAN_TYPE_REGULAR)
|
|
|
|
{
|
|
|
|
etp = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
etp = eolian_type_alias_get_by_name(etp->full_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (etp && etp->type == EOLIAN_TYPE_REGULAR_ENUM)
|
|
|
|
etp = eolian_type_enum_get_by_name(etp->full_name);
|
|
|
|
|
|
|
|
if (!etp || etp->type != EOLIAN_TYPE_ENUM)
|
|
|
|
{
|
|
|
|
free(fulln);
|
|
|
|
return expr_error(expr, "undefined variable");
|
|
|
|
}
|
|
|
|
|
|
|
|
exp = eolian_type_enum_field_get(etp, memb);
|
|
|
|
free(fulln);
|
|
|
|
|
|
|
|
if (!exp)
|
|
|
|
return expr_error(expr, "invalid enum field");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
exp = var->value;
|
|
|
|
|
|
|
|
if (!exp)
|
|
|
|
return expr_error(expr, "undefined variable");
|
|
|
|
|
|
|
|
return eval_exp(exp, mask, out);
|
|
|
|
}
|
|
|
|
case EOLIAN_EXPR_ENUM:
|
|
|
|
{
|
|
|
|
const Eolian_Type *etp;
|
|
|
|
const Eolian_Expression *exp;
|
|
|
|
|
|
|
|
char *fulln = NULL, *memb = NULL;
|
|
|
|
if (!split_enum_name(expr->value.s, &fulln, &memb))
|
|
|
|
{
|
|
|
|
return expr_error(expr, "invalid enum");
|
|
|
|
}
|
|
|
|
|
|
|
|
etp = eolian_type_enum_get_by_name(fulln);
|
|
|
|
if (etp && etp->type == EOLIAN_TYPE_REGULAR_ENUM)
|
|
|
|
etp = eolian_type_enum_get_by_name(etp->full_name);
|
|
|
|
|
|
|
|
if (!etp)
|
|
|
|
{
|
|
|
|
free(fulln);
|
|
|
|
return expr_error(expr, "invalid enum");
|
|
|
|
}
|
|
|
|
|
|
|
|
exp = eolian_type_enum_field_get(etp, memb);
|
|
|
|
free(fulln);
|
|
|
|
|
|
|
|
if (!exp)
|
|
|
|
return expr_error(expr, "invalid enum field");
|
|
|
|
|
|
|
|
return eval_exp(exp, mask, out);
|
|
|
|
}
|
2014-08-06 08:30:42 -07:00
|
|
|
case EOLIAN_EXPR_UNARY:
|
|
|
|
return eval_unary(expr, mask, out);
|
|
|
|
case EOLIAN_EXPR_BINARY:
|
|
|
|
return eval_binary(expr, mask, out);
|
|
|
|
default:
|
|
|
|
assert(EINA_FALSE);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
2014-08-07 03:13:40 -07:00
|
|
|
|
2014-08-07 07:15:07 -07:00
|
|
|
Eolian_Expression_Type
|
|
|
|
database_expr_eval(const Eolian_Expression *expr, Eolian_Expression_Mask mask,
|
2014-08-13 02:38:04 -07:00
|
|
|
Eina_Value *outval)
|
2014-08-07 03:13:40 -07:00
|
|
|
{
|
|
|
|
Eolian_Expression out;
|
2014-08-07 07:15:07 -07:00
|
|
|
if (!mask)
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
2014-08-07 03:13:40 -07:00
|
|
|
if (!eval_exp(expr, mask, &out))
|
2014-08-07 07:15:07 -07:00
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
if (!outval)
|
|
|
|
return out.type;
|
2014-08-07 03:13:40 -07:00
|
|
|
switch (out.type)
|
|
|
|
{
|
|
|
|
case EOLIAN_EXPR_INT:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_INT))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.i);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_UINT:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_UINT))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.u);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_LONG:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_LONG))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.l);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_ULONG:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_ULONG))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.ul);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_LLONG:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_INT64))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.ll);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_ULLONG:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_UINT64))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.ull);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_FLOAT:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_FLOAT))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.f);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_DOUBLE:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_DOUBLE))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.d);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_LDOUBLE:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_DOUBLE))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, (double)out.value.ld);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_STRING:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_STRINGSHARE))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, eina_stringshare_ref(out.value.s));
|
2014-08-07 07:15:07 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_CHAR:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_CHAR))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.c);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
|
|
|
case EOLIAN_EXPR_BOOL:
|
2014-08-13 02:38:04 -07:00
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_UCHAR))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, out.value.b);
|
2014-08-07 03:13:40 -07:00
|
|
|
break;
|
2014-08-07 08:43:35 -07:00
|
|
|
case EOLIAN_EXPR_NULL:
|
2014-08-13 03:25:04 -07:00
|
|
|
/* we need to initialize to prevent crashes */
|
|
|
|
if (!eina_value_setup(outval, EINA_VALUE_TYPE_UCHAR))
|
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
eina_value_set(outval, 0);
|
2014-08-07 08:43:35 -07:00
|
|
|
break;
|
2014-08-07 03:13:40 -07:00
|
|
|
default:
|
2014-08-07 07:15:07 -07:00
|
|
|
return EOLIAN_EXPR_UNKNOWN;
|
|
|
|
}
|
|
|
|
return out.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
database_expr_del(expr->lhs);
|
|
|
|
database_expr_del(expr->rhs);
|
|
|
|
}
|
|
|
|
else if (expr->type == EOLIAN_EXPR_UNARY)
|
|
|
|
{
|
|
|
|
database_expr_del(expr->expr);
|
|
|
|
}
|
|
|
|
else if (expr->type == EOLIAN_EXPR_STRING)
|
|
|
|
{
|
|
|
|
eina_stringshare_del(expr->value.s);
|
2014-08-07 03:13:40 -07:00
|
|
|
}
|
2014-08-07 07:15:07 -07:00
|
|
|
free(expr);
|
2014-08-07 03:13:40 -07:00
|
|
|
}
|