/* -*- c -*-
* File: exprtool.c
* Author: Igor Vlasenko <vlasenko@imath.kiev.ua>
* Created: Mon Jul 25 15:29:17 2005
*
* $Id$
*/
/*
#include "exprtool.h"
#include <ctype.h> // for yylex alnum
#include <stdio.h> // for printf
*/
#define EXPR_CHECK_NUMBER(exprobj, ptr) switch (ptr->type) { \
case EXPR_TYPE_INT: case EXPR_TYPE_DBL: break; \
case EXPR_TYPE_UPSTR: case EXPR_TYPE_PSTR: expr_to_num(exprobj, ptr); break; \
default: _tmplpro_expnum_debug(*ptr, "FATAL:internal expr type error. please report\n"); \
ptr->type = EXPR_TYPE_INT; \
}
#define EXPR_CHECK_LOGICAL(exprobj, ptr) switch (ptr->type) { \
case EXPR_TYPE_INT: case EXPR_TYPE_DBL: break; \
case EXPR_TYPE_UPSTR: case EXPR_TYPE_PSTR: expr_to_bool(exprobj, ptr); break; \
default: _tmplpro_expnum_debug(*ptr, "FATAL:internal expr type error. please report\n"); \
ptr->type = EXPR_TYPE_INT; \
}
static
EXPR_char
expr_to_int_or_dbl (struct expr_parser* exprobj, struct exprval* val1, struct exprval* val2) {
EXPR_CHECK_NUMBER(exprobj, val1);
EXPR_CHECK_NUMBER(exprobj, val2);
if ((val1->type == EXPR_TYPE_INT) && (val2->type == EXPR_TYPE_INT))
return EXPR_TYPE_INT;
if ((val1->type == EXPR_TYPE_DBL) && (val2->type == EXPR_TYPE_DBL))
return EXPR_TYPE_DBL;
if (val1->type == EXPR_TYPE_INT) {
val1->type=EXPR_TYPE_DBL;
val1->val.dblval=(double) val1->val.intval;
}
if (val2->type == EXPR_TYPE_INT) {
val1->type=EXPR_TYPE_DBL;
val2->val.dblval=(double) val2->val.intval;
}
return EXPR_TYPE_DBL;
}
static
EXPR_char
expr_to_int_or_dbl_logop (struct expr_parser* exprobj, struct exprval* val1, struct exprval* val2) {
EXPR_CHECK_LOGICAL(exprobj, val1);
EXPR_CHECK_LOGICAL(exprobj, val2);
if (val1->type == EXPR_TYPE_INT && val2->type == EXPR_TYPE_INT)
return EXPR_TYPE_INT;
if (val1->type == EXPR_TYPE_DBL && val2->type == EXPR_TYPE_DBL)
return EXPR_TYPE_DBL;
if (val1->type == EXPR_TYPE_INT) {
val1->type=EXPR_TYPE_DBL;
val1->val.dblval=(double) val1->val.intval;
}
if (val2->type == EXPR_TYPE_INT) {
val1->type=EXPR_TYPE_DBL;
val2->val.dblval=(double) val2->val.intval;
}
return EXPR_TYPE_DBL;
}
static
EXPR_char
expr_to_int_or_dbl_logop1 (struct expr_parser* exprobj, struct exprval* val1) {
EXPR_CHECK_LOGICAL(exprobj, val1);
return val1->type;
}
static
EXPR_char
expr_to_int_or_dbl1 (struct expr_parser* exprobj, struct exprval* val1) {
EXPR_CHECK_NUMBER(exprobj, val1);
return val1->type;
}
static
void
expr_to_dbl1 (struct expr_parser* exprobj, struct exprval* val1) {
EXPR_CHECK_NUMBER(exprobj, val1);
if (val1->type == EXPR_TYPE_INT) {
val1->type=EXPR_TYPE_DBL;
val1->val.dblval=(double) val1->val.intval;
}
}
static
void
expr_to_int1 (struct expr_parser* exprobj, struct exprval* val1) {
EXPR_CHECK_NUMBER(exprobj, val1);
if (val1->type == EXPR_TYPE_DBL) {
val1->type=EXPR_TYPE_INT;
val1->val.intval=(EXPR_int64) val1->val.dblval;
/* _tmplpro_expnum_debug(*val1, "WARN:converting to `int' from `double'"); */
}
}
static
void
expr_to_dbl (struct expr_parser* exprobj, struct exprval* val1, struct exprval* val2) {
expr_to_dbl1(exprobj, val1);
expr_to_dbl1(exprobj, val2);
}
static
void
expr_to_int (struct expr_parser* exprobj, struct exprval* val1, struct exprval* val2) {
expr_to_int1(exprobj, val1);
expr_to_int1(exprobj, val2);
}
#define EXPR_CHECK_STRING(pbuff, ptr) switch (ptr->type) { \
case EXPR_TYPE_PSTR: break; \
case EXPR_TYPE_UPSTR: ptr->val.strval=expr_unescape_pstring_val(pbuff,ptr->val.strval); break; \
case EXPR_TYPE_INT: ptr->val.strval=int_to_pstring(ptr->val.intval,pbuffer_string(pbuff),pbuffer_size(pbuff)); break; \
case EXPR_TYPE_DBL: ptr->val.strval=double_to_pstring(ptr->val.dblval,pbuffer_string(pbuff),pbuffer_size(pbuff)); break; \
default: _tmplpro_expnum_debug(*ptr, "FATAL:internal expr string error. please report\n"); \
} \
ptr->type = EXPR_TYPE_PSTR;
static
void
expr_to_str (struct tmplpro_state* state, struct exprval* val1, struct exprval* val2) {
EXPR_CHECK_STRING(&(state->expr_left_pbuffer), val1);
EXPR_CHECK_STRING(&(state->expr_right_pbuffer), val2);
}
static
void
expr_to_str1 (struct tmplpro_state* state, struct exprval* val1) {
EXPR_CHECK_STRING(&(state->expr_left_pbuffer), val1);
}
static
int
is_float_lex (char c)
{
return (c == '.' || isdigit (c));
}
static
struct exprval
exp_read_number (struct expr_parser* exprobj, const char* *curposptr, const char* endchars) {
char c = **curposptr;
struct exprval retval;
EXPR_int64 iretval=0;
double dretval=0;
EXPR_int64 offset=0;
int sign=1;
retval.type=EXPR_TYPE_INT;
retval.val.intval=0;
if ((*curposptr)<endchars && '-' == c) {
sign=-1;
c = *(++(*curposptr));
}
if (! (c == '.' || isdigit (c))) return retval;
/* double reader
yylval.dblval=atof(fill_symbuf(is_float_lex).begin);
return dblNUM;
*/
while ((*curposptr)<endchars && is_float_lex(c)) {
if (c == '.') {
if (retval.type == EXPR_TYPE_INT) {
retval.type = EXPR_TYPE_DBL;
dretval=iretval;
offset=1;
} else {
/* (*curposptr)--; ??? */
log_expr(exprobj, TMPL_LOG_ERROR, "while reading number: %s\n", "uninspected decimal point");
retval.val.dblval=dretval*sign;
retval.type=EXPR_TYPE_DBL;
return retval;
}
} else {
offset*=10;
if (retval.type == EXPR_TYPE_INT) {
iretval=iretval*10+c-'0';
} else {
dretval=dretval*10+c-'0';
}
}
c = *(++(*curposptr));
}
if (retval.type == EXPR_TYPE_INT) {
retval.val.intval=iretval*sign;
} else {
if (offset) dretval/=offset;
retval.val.dblval=dretval*sign;
}
return retval;
}
static
void
expr_to_num (struct expr_parser* exprobj, struct exprval* val1)
{
const char* curpos=val1->val.strval.begin;
EXPR_char type = val1->type;
if (type == EXPR_TYPE_PSTR || type == EXPR_TYPE_UPSTR) {
if (NULL==curpos) {
val1->type = EXPR_TYPE_INT;
val1->val.intval = 0;
} else {
/* escaped string can't be read properly anyway */
*val1=exp_read_number (exprobj, &curpos, val1->val.strval.endnext);
}
}
}
static
void
expr_to_bool (struct expr_parser* exprobj, struct exprval* val1)
{
if (val1->type == EXPR_TYPE_PSTR || val1->type == EXPR_TYPE_UPSTR) {
const char* begin=val1->val.strval.begin;
const char* end=val1->val.strval.endnext;
const char* curpos=begin;
if (begin==end) {
val1->type = EXPR_TYPE_INT;
val1->val.intval = 0;
} else {
*val1=exp_read_number (exprobj, &curpos, end);
if (val1->type == EXPR_TYPE_INT) {
if (val1->val.intval || (curpos == end)) return;
else val1->val.intval=1; /* strings are true in perl */
}
else
if (val1->type == EXPR_TYPE_DBL) {
if (val1->val.dblval || (curpos == end)) return;
else val1->val.dblval=1.0; /* strings are true in perl */
}
}
}
}
static
void
_tmplpro_expnum_debug (struct exprval val, char* msg)
{
tmpl_log(TMPL_LOG_DEBUG,"--> debug %s:type %c ",msg,val.type);
if (val.type == EXPR_TYPE_INT)
tmpl_log(TMPL_LOG_DEBUG,"ival=%" EXPR_PRId64 "\n",val.val.intval);
else if (val.type == EXPR_TYPE_DBL)
tmpl_log(TMPL_LOG_DEBUG,"dval=%f\n",val.val.dblval);
else if (val.type == EXPR_TYPE_PSTR) {
tmpl_log(TMPL_LOG_DEBUG,"pstr(%c):",(int) val.type);
if (NULL==val.val.strval.begin) tmpl_log(TMPL_LOG_DEBUG,"{begin=NULL}");
if (NULL==val.val.strval.endnext) tmpl_log(TMPL_LOG_DEBUG,"{endnext=NULL}");
tmpl_log(TMPL_LOG_DEBUG,"sval=%.*s\n",(int)(val.val.strval.endnext-val.val.strval.begin),val.val.strval.begin);
} else if (val.type == EXPR_TYPE_NULL) {
tmpl_log(TMPL_LOG_DEBUG,"NULL\n");
if (NULL!=val.val.strval.begin) tmpl_log(TMPL_LOG_DEBUG,"{begin!=NULL}");
if (NULL!=val.val.strval.endnext) tmpl_log(TMPL_LOG_DEBUG,"{endnext!=NULL}");
} else {
tmpl_log(TMPL_LOG_DEBUG,"unknown(%c) as ival=%" EXPR_PRId64 "\n",(int) val.type,val.val.intval);
}
}
/*
* checks if it is stringval and unescape it (copies to the buffer).
* it implies that only one string at a time can use buffer.
*/
static
PSTRING
expr_unescape_pstring_val(pbuffer* pbuff, PSTRING val) {
PSTRING retval;
const char* curpos = val.begin;
const char* endnext= val.endnext;
char* buf=pbuffer_resize(pbuff, endnext-curpos+1);
char* bufpos = buf;
while (curpos < endnext) {
if (*curpos == '\\') {
*bufpos=*(++curpos);
} else {
*bufpos=*curpos;
}
curpos++;
bufpos++;
}
retval.begin = buf;
retval.endnext = bufpos;
return retval;
}