Symbolic Expression Evaluation

Suppose you wanted your users to be able to type in equations or expressions, much like Excel. This program provides the capability for parsing and evaluating expressions. As a bonus, it can evaluate the symbolic derivative of an expression as well!

Worked: 1*2 + 3*4 is 14
Worked: 10 * (1+2+3) / $sqrt(4) is 30
Worked: 4 + y is 204    (note: y is your variable)
Worked: 123 - 5 is 118
Worked: "abc" < "abd" is 1
Worked: "ab" + "cdef" + "g" + "h" is "abcdefgh"
Worked: 1 < 2 < 3 <= 4 = 4 < 5 > 4 >= 4 > 3 <> 2 is 1
Worked: $len ("abc") is 3
Worked: $month (\6/5/2000 00:00:00\) is 6
Worked: $minute (\6/5/2000 11:53:44\) is 53
Worked: $delta (revenue, 2) is 80
Worked: (u+1)^(v-1) * (u*$sqrt(u)*dv+v*du) is 40
Failed, as expected:   ( 100  +  5
Failed, as expected:  len('a','b')
Derivative of sin(y) is $cos(y) * d!y!
Derivative of sin(x)*cos(y) is
        $sin(x)*-$sin(y)*d!y! + $cos(y)*$cos(x)*d!x!
Derivative of x^3 is 3 * x^2 * d!x!

No transform errors.
No memory errors or leaks detected (calls=613).

transform.cpp - Main Transformation

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: #include <ctype.h>
   5: 
   6: #include "memory.hpp"
   7: #include "transform.hpp"
   8: 
   9: #define DEFAULT_SIZE 500
  10: 
  11: transform_c::transform_c ()
  12: {
  13:     int i;
  14:     char temp[2];
  15: 
  16:     this->comment_char = ';';
  17:     this->system_variable = '$';
  18:     this->user_variable = '!';
  19:     this->left_paren = '(';
  20:     this->right_paren = ')';
  21:     this->single_quote = '\'';
  22:     this->double_quote = '"';
  23:     this->decimal_point = '.';
  24:     this->expon_letters = "EeDd";
  25:     this->plus_sign = '+';
  26:     this->minus_sign = '-';
  27:     this->underscore = '_';
  28:     this->date_delim = '\\';
  29:     this->comma = ',';
  30:     this->deriv_string = "d";
  31: 
  32:     this->created_uv_symbols = 0;
  33: 
  34:     // Allocate string buffers (they may grow in size)
  35:     for (i=0; i<NUM_VALUE_BUFFERS; i++)
  36:     {
  37:         this->buffer[i] = GetStr (DEFAULT_SIZE+1);
  38:         this->buffer_len[i] = DEFAULT_SIZE;
  39:     }
  40:     this->which_buffer = 0;
  41:     this->expr_buffer = GetStr (DEFAULT_SIZE+1);
  42:     this->expr_buffer_len = DEFAULT_SIZE;
  43: 
  44:     this->prev_string_result = NULL;
  45: 
  46:     this->add_operators ();
  47:     this->add_functions ();
  48:     this->add_derivatives ();
  49: 
  50:     // This is not a real operator. Placeholder for functions
  51:     sprintf (temp, "%c", this->comma);
  52:     this->add_binop (temp, COMMA_PREC, NULL, NULL, NULL, NULL, NULL);
  53: }
  54: 
  55: transform_c::~transform_c ()
  56: {
  57:     int i;
  58: 
  59:     FreeMem (this->prev_string_result);
  60:     FreeMem (this->expr_buffer);
  61: 
  62:     for (i=0; i<NUM_VALUE_BUFFERS; i++)
  63:     {
  64:         FreeMem (this->buffer[i]);
  65:     }
  66: 
  67:     this->remove_operators ();
  68:     this->remove_functions ();
  69:     this->remove_simplifiers ();
  70: }
  71: 
  72: char *transform_c::get_expr (expression_t *expr)
  73: {
  74:     if (!expr) return 0;
  75: 
  76:     this->expression_len = 0;
  77:     strcpy (this->expr_buffer, "");
  78:     if (!this->print_expr_recursive (expr, 0)) return "error";
  79: 
  80:     return this->expr_buffer;
  81: }
  82: 
  83: void transform_c::add_expr_piece (char *piece)
  84: {
  85:     int nc;
  86: 
  87:     nc = strlen (piece);
  88:     if (nc + this->expression_len >= this->expr_buffer_len)
  89:     {
  90:         this->expr_buffer_len = nc + this->expression_len + 100;
  91:         this->expr_buffer = ChangeMem (this->expr_buffer, char,
  92:             this->expr_buffer_len+1);
  93:     }
  94:     strcat (this->expr_buffer, piece);
  95:     this->expression_len += nc;
  96: }
  97: 
  98: void transform_c::add_expr_char (char ch)
  99: {
 100:     char temp[2];
 101: 
 102:     sprintf (temp, "%c", ch);
 103:     this->add_expr_piece (temp);
 104: }
 105: 
 106: int transform_c::print_expr_recursive (expression_t *expr, int depth)
 107: {
 108:     int delta_depth;
 109:     int need_parens;
 110:     int comp;
 111:     int i, nc;
 112: 
 113:     if (!expr) return 0;
 114: 
 115:     switch (expr->expr_type)
 116:     {
 117:         case EXPR_CONST:
 118:             this->add_expr_piece (this->get_value (&(expr->u.value)));
 119:             break;
 120: 
 121:         case EXPR_SYMBOL:
 122:             nc = strlen (expr->u.symbol->name);
 123:             for (i=0; i<nc; i++)
 124:             {
 125:                 if (!isalpha (expr->u.symbol->name[i])) break;
 126:             }
 127:             if (i < nc) this->add_expr_char (this->user_variable);
 128:             this->add_expr_piece (expr->u.symbol->name);
 129:             if (i < nc) this->add_expr_char (this->user_variable);
 130:             break;
 131: 
 132:         case EXPR_DERIV_SYMBOL:
 133:             this->add_expr_piece (this->deriv_string);
 134:             // this->add_expr_char (this->left_paren);
 135:             this->add_expr_char (this->user_variable);
 136:             this->add_expr_piece (expr->u.symbol->name);
 137:             this->add_expr_char (this->user_variable);
 138:             // this->add_expr_char (this->right_paren);
 139:             break;
 140: 
 141:         case EXPR_ZEROP:
 142:             if (isalpha (expr->u.zero_args.zerop->name[0]))
 143:             {
 144:                 this->add_expr_char (this->system_variable);
 145:             }
 146:             this->add_expr_piece (expr->u.zero_args.zerop->name);
 147:             break;
 148: 
 149:         case EXPR_MONOP:
 150:             if (isalpha (expr->u.one_arg.monop->name[0]))
 151:             {
 152:                 need_parens = 1;
 153:             }
 154:             else if (expr->u.one_arg.the_arg->expr_type == EXPR_BINOP)
 155:             {
 156:                 need_parens = 1;
 157:             }
 158:             else need_parens = 0;
 159: 
 160:             if (isalpha (expr->u.one_arg.monop->name[0]))
 161:             {
 162:                 this->add_expr_char (this->system_variable);
 163:             }
 164:             this->add_expr_piece (expr->u.one_arg.monop->name);
 165:             if (need_parens) this->add_expr_char (this->left_paren);;
 166: 
 167:             this->print_expr_recursive (expr->u.one_arg.the_arg, depth);
 168:             if (need_parens) this->add_expr_char (this->right_paren);
 169:             break;
 170: 
 171:         case EXPR_BINOP:
 172:             delta_depth = 1;
 173:             need_parens = 0;
 174:             if (expr->u.two_args.left_arg->expr_type == EXPR_BINOP)
 175:             {
 176:                 comp = expr->u.two_args.left_arg->u.two_args.binop->prec -
 177:                         expr->u.two_args.binop->prec;
 178:                 if (comp < 0) need_parens = 1;
 179:                 if (comp == 0) delta_depth = 0;
 180:             }
 181: 
 182:             if (need_parens) this->add_expr_char (this->left_paren);
 183:             this->print_expr_recursive (expr->u.two_args.left_arg, depth+delta_depth);
 184:             if (need_parens) this->add_expr_char (this->right_paren);
 185:             if (depth == 0) this->add_expr_char (' ');
 186:             if (isalpha (expr->u.two_args.binop->name[0]))
 187:             {
 188:                 this->add_expr_char (this->system_variable);
 189:             }
 190:             this->add_expr_piece (expr->u.two_args.binop->name);
 191:             if (depth == 0) this->add_expr_char (' ');
 192: 
 193:             delta_depth = 1;
 194:             need_parens = 0;
 195:             if (expr->u.two_args.right_arg->expr_type == EXPR_BINOP)
 196:             {
 197:                 comp = expr->u.two_args.right_arg->u.two_args.binop->prec -
 198:                         expr->u.two_args.binop->prec;
 199:                 if (comp < 0) need_parens = 1;
 200:                 if (comp == 0) delta_depth = 0;
 201:             }
 202: 
 203:             if (need_parens)this->add_expr_char (this->left_paren);
 204:             this->print_expr_recursive (expr->u.two_args.right_arg, depth+delta_depth);
 205:             if (need_parens) this->add_expr_char (this->right_paren);
 206:             break;
 207: 
 208:         case EXPR_FUNC:
 209:             this->add_expr_char (this->system_variable);
 210:             this->add_expr_piece (expr->u.many_args.func->name);
 211:             if (depth == 0) this->add_expr_char (' ');
 212:             this->add_expr_char (this->left_paren);
 213:             
 214:             for (i=0; i<expr->u.many_args.num_args; i++)
 215:             {
 216:                 if (i > 0)
 217:                 {
 218:                     this->add_expr_char (this->comma);
 219:                     if (depth == 0) this->add_expr_char (' ');
 220:                 }
 221: 
 222:                 this->print_expr_recursive ((expr->u.many_args.func_args)[i].the_arg,
 223:                     depth+1);
 224:             }
 225:             this->add_expr_char (this->right_paren);
 226:             break;
 227:     }
 228: 
 229:     return 1;
 230: }
 231: 
 232: char *transform_c::get_value (evalue_t *value)
 233: {
 234:     char *pos;
 235:     char *result;
 236:     char *buff;
 237:     int quotes;
 238:     int i, nc, which;
 239: 
 240:     if (!value) return "error";
 241: 
 242:     // Rotate 5 buffers in case somebody is using more than one at a time
 243:     if (++this->which_buffer >= NUM_VALUE_BUFFERS) this->which_buffer = 0;
 244:     which = this->which_buffer;
 245:     buff = this->buffer[which];
 246: 
 247:     switch (value->typ)
 248:     {
 249:         case INT_TYP:
 250:             sprintf (buff, "%d", value->u.i);
 251:             break;
 252: 
 253:         case DOUBLE_TYP:
 254:             sprintf (buff, "%g", value->u.x);
 255:             break;
 256: 
 257:         case DATETIME_TYP:
 258:             pos = this->dt_class.format_dt (value->u.dt, NULL);
 259:             sprintf (buff, "%c%s%c", this->date_delim, pos, this->date_delim);
 260:             break;
 261: 
 262:         case STRING_TYP:
 263:             quotes = 0;
 264:             nc = strlen (value->u.v.str);
 265:             if (strchr (value->u.v.str, '"') != NULL)
 266:             {
 267:                 for (i=0; i<nc; i++)
 268:                 {
 269:                     if (value->u.v.str[i] == '\"') quotes++;
 270:                 }
 271:             }
 272: 
 273:             // Make sure there is enough room
 274:             nc += 2 + 2*quotes;
 275:             if (nc >= this->buffer_len[which])
 276:             {
 277:                 this->buffer_len[which] += nc + 100;
 278:                 this->buffer[which] = ChangeMem (this->buffer[which],
 279:                     char, this->buffer_len[which]);
 280:             }
 281: 
 282:             if (quotes == 0)
 283:             {
 284:                 sprintf (buff, "\"%s\"", value->u.v.str);
 285:             }
 286:             else
 287:             {
 288:                 result = buff;
 289: 
 290:                 *result++ = '\"';
 291:                 pos = value->u.v.str;
 292:                 while (*pos != '\0')
 293:                 {
 294:                     if (*pos == '"') *result++ = '\"';
 295:                     *result++ = *pos++;
 296:                 }
 297:                 *result++ = '\"';
 298:                 *result = '\0';
 299:             }
 300:             break;
 301: 
 302:         default:
 303:             return "error";
 304:     }
 305: 
 306:     return buff;
 307: }
 308: 
 309: void transform_c::free_expr (expression_t *expr)
 310: {
 311:     int i;
 312: 
 313:     if (!expr) return;
 314: 
 315:     switch (expr->expr_type)
 316:     {
 317:         case EXPR_CONST:
 318:             if (expr->u.value.typ == STRING_TYP)
 319:             {
 320:                 FreeMem (expr->u.value.u.v.str);
 321:             }
 322:             break;
 323: 
 324:         case EXPR_MONOP:
 325:             this->free_expr (expr->u.one_arg.the_arg);
 326:             break;
 327: 
 328:         case EXPR_BINOP:
 329:             this->free_expr (expr->u.two_args.left_arg);
 330:             this->free_expr (expr->u.two_args.right_arg);
 331:             break;
 332: 
 333:         case EXPR_FUNC:
 334:             for (i=0; i<expr->u.many_args.num_args; i++)
 335:             {
 336:                 this->free_expr ((expr->u.many_args.func_args)[i].the_arg);
 337:             }
 338:             FreeMem (expr->u.many_args.func_args);
 339:             if (expr->u.many_args.free_state)
 340:             {
 341:                 expr->u.many_args.free_state (expr->u.many_args.state);
 342:             }
 343:             break;
 344: 
 345:         default:
 346:             break;
 347:     }
 348: 
 349:     FreeMem (expr);
 350: }

transform.hpp - header file

   1: #ifndef TRANSFORM_HPP
   2: #define TRANSFORM_HPP
   3: 
   4: #include "export.hpp"
   5: #include "datetime.hpp"
   6: #include "status.hpp"
   7: #include "symbols.hpp"
   8: #include "transform_expression.hpp"
   9: 
  10: #define MAX_OPERATORS 100   // 10 should be plenty
  11: 
  12: #define NUM_VALUE_BUFFERS 5                        // Need at least 2 or 3
  13: 
  14: typedef struct oper_stack_t {
  15:     expression_t *oper;
  16: } oper_stack_t;
  17: 
  18: typedef struct simplifier_t {
  19:     expression_t *pexpr;        // Pattern
  20:     expression_t *rexpr;        // Replacement
  21:     struct simplifier_t *next;
  22: } simplifier_t;
  23: 
  24: class transform_c {
  25:   private:
  26:     char *buffer[NUM_VALUE_BUFFERS];        // For formatting
  27:     int buffer_len[NUM_VALUE_BUFFERS];
  28:     int which_buffer;
  29: 
  30:     char *expr_buffer;
  31:     int expr_buffer_len;
  32:     int expression_len;
  33: 
  34:     zerop_t *first_zerop;
  35:     zerop_t *last_zerop;
  36:     monop_t *first_monop;
  37:     monop_t *last_monop;
  38:     binop_t *first_binop;
  39:     binop_t *last_binop;
  40:     function_t *first_func;
  41:     function_t *last_func;
  42: 
  43:     zerop_t *parse_zerop (char *rec);
  44:     monop_t *parse_monop (char *rec);
  45:     binop_t *parse_binop (char *rec);
  46:     function_t *parse_func (char *rec);
  47: 
  48:     // Used while parsing
  49:     oper_stack_t oper_stack[MAX_OPERATORS+1];
  50:     int oper_stack_size;
  51:     expression_t *top_expression;
  52: 
  53:     char *prev_string_result;
  54: 
  55:     int is_a_number (char *pos, int *floater);
  56: 
  57:     int push_oper (expression_t *oper);
  58:     int pop_opers (oper_prec_e stopper);
  59: 
  60:     int print_expr_recursive (expression_t *expr, int depth);
  61:     void add_expr_piece (char *piece);
  62:     void add_expr_char (char ch);
  63: 
  64:     void create_uv_symbols ();
  65:     int created_uv_symbols;
  66:     symbols_c uv_symbols;
  67: 
  68:     simplifier_t *first_simplifier;
  69:     simplifier_t *last_simplifier;
  70:     int simplify (expression_t *expr, int *more);
  71: 
  72:   public:
  73:     NP_EXPORT transform_c ();
  74:     NP_EXPORT ~transform_c ();
  75: 
  76:     char comment_char;            // ;
  77:     char system_variable;   // $
  78:     char user_variable;            // !
  79:     char left_paren;            // (
  80:     char right_paren;            // )
  81:     char single_quote;            // '
  82:     char double_quote;            // "
  83:     char decimal_point;            // .
  84:     char *expon_letters;    // EeDd
  85:     char plus_sign;            // +
  86:     char minus_sign;            // -
  87:     char underscore;            // _
  88:     char date_delim;            // \ 
  89:     char comma;                    // ,
  90:     char *deriv_string;            // d
  91: 
  92:     void add_operators ();
  93:     void remove_operators ();
  94:     void add_functions ();
  95:     void remove_functions ();
  96:     void add_derivatives ();
  97:     void remove_simplifiers ();
  98: 
  99:     zerop_t *add_zerop (char *oper, int_zerop_fn int_op,
 100:         double_zerop_fn double_op, datetime_zerop_fn datetime_op);
 101:     monop_t *add_monop (char *oper, int_monop_fn int_op,
 102:         double_monop_fn double_op);
 103:     binop_t *add_binop (char *oper, oper_prec_e prec,
 104:         int_binop_fn int_op,
 105:         double_binop_fn double_op, dblint_binop_fn dblint_op,
 106:         strstr_binop_fn strstr_op, strint_binop_fn strint_op);
 107: 
 108:     int add_monop_deriv (char *oper, char *derivative);
 109:     int add_binop_deriv (char *oper, char *derivative);
 110:     int add_simplifier (char *pattern, char *replacement);
 111: 
 112:     function_t *add_func (char *oper, int min_args, int max_args,
 113:         function_fn func_op);
 114:     function_t *add_dt_func (char *oper, int min_args, int max_args,
 115:         dt_function_fn dt_func_op);
 116:     function_t *add_state_func (char *oper, int min_args, int max_args,
 117:         state_function_fn state_func_op);
 118: 
 119:     datetime_c dt_class;
 120: 
 121:     NP_EXPORT expression_t *parse_expr (symbols_c *symbols, char *expr,
 122:         char **emsg, char **errpos);
 123: 
 124:     NP_EXPORT void free_expr (expression_t *expr);
 125: 
 126:     NP_EXPORT int eval_expr (symbols_c *symbols,
 127:         expression_t *expr, evalue_t *value);
 128: 
 129:     NP_EXPORT expression_t *deriv_expr (symbols_c *symbols,
 130:         expression_t *expr);
 131: 
 132:     NP_EXPORT int eval_deriv (symbols_c *symbols, symbol_t *wrt,
 133:         expression_t *expr, evalue_t *value);
 134: 
 135:     NP_EXPORT char *get_expr (expression_t *expr);
 136:     NP_EXPORT char *get_value (evalue_t *value);
 137: };
 138: 
 139: #endif // ! TRANSFORM_HPP
 140: 

transform_expression.hpp - Expressions

   1: #ifndef TRANSFORM_EXPRESSION_HPP
   2: #define TRANSFORM_EXPRESSION_HPP
   3: 
   4: typedef enum {
   5:     EXPR_CONST,                // 55, "abc"
   6:     EXPR_SYMBOL,               // x, y, z
   7:     EXPR_DERIV_SYMBOL,         // dx, dy, dz
   8:     EXPR_ZEROP,                // $pi
   9:     EXPR_MONOP,                // -, sqrt, cos
  10:     EXPR_BINOP,                // + - * /
  11:     EXPR_FUNC                  // $if $delta
  12: } expr_type_e;
  13: 
  14: typedef enum {
  15:     PAREN_PREC,
  16:     COMMA_PREC,
  17:     OR_PREC,
  18:     AND_PREC,
  19:     REL_PREC,
  20:     PLUS_PREC,
  21:     TIMES_PREC,
  22:     POWER_PREC,
  23:     UNARY_PREC
  24: } oper_prec_e;
  25: 
  26: typedef void (*int_zerop_fn) (int *result,
  27:     status_t *status);
  28: typedef void (*double_zerop_fn) (double *result,
  29:     status_t *status);
  30: typedef void (*datetime_zerop_fn) (datetime_c *dt, datetime_t *result,
  31:     status_t *status);
  32: 
  33: typedef void (*int_monop_fn) (int val, int *result,
  34:     status_t *status);
  35: typedef void (*double_monop_fn) (double val, double *result,
  36:     status_t *status);
  37: 
  38: typedef void (*int_binop_fn) (int v1, int v2, int *result,
  39:     status_t *status);
  40: typedef void (*double_binop_fn) (double v1, double v2, double *result,
  41:     status_t *status);
  42: typedef void (*dblint_binop_fn) (double v1, double v2, int *result,
  43:     status_t *status);
  44: typedef void (*strstr_binop_fn) (char *v1, char *v2, char **result,
  45:     status_t *status);
  46: typedef void (*strint_binop_fn) (char *v1, char *v2, int *result,
  47:     status_t *status);
  48: 
  49: typedef struct func_arg_t func_arg_t;        // Forward declarations
  50: typedef struct expression_t expression_t;
  51: 
  52: typedef void (*free_state_fn) (void *ptr);
  53: 
  54: typedef int (*function_fn) (int num_args, func_arg_t *args, evalue_t *result);
  55: typedef int (*dt_function_fn) (datetime_c *dt, int num_args,
  56:     func_arg_t *args, evalue_t *result);
  57: typedef int (*state_function_fn) (free_state_fn *fp, void **state,
  58:     int num_args, func_arg_t *args, evalue_t *result);
  59: 
  60: typedef struct zerop_t {
  61:     char *name;
  62:     int_zerop_fn int_fn;
  63:     double_zerop_fn double_fn;
  64:     datetime_zerop_fn datetime_fn;
  65:     struct zerop_t *next;
  66: } zerop_t;
  67: 
  68: typedef struct monop_t {
  69:     char *name;
  70:     int_monop_fn int_fn;
  71:     double_monop_fn double_fn;
  72:     expression_t *deriv;
  73:     struct monop_t *next;
  74: } monop_t;
  75: 
  76: typedef struct binop_t {
  77:     char *name;
  78:     oper_prec_e prec;
  79:     int_binop_fn int_fn;
  80:     double_binop_fn double_fn;
  81:     dblint_binop_fn dblint_fn;
  82:     strstr_binop_fn strstr_fn;
  83:     strint_binop_fn strint_fn;
  84:     expression_t *deriv;
  85:     struct binop_t *next;
  86: } binop_t;
  87: 
  88: typedef struct function_t {
  89:     char *name;
  90:     int min_args;
  91:     int max_args;
  92:     function_fn func_fn;
  93:     dt_function_fn dt_func_fn;
  94:     state_function_fn state_func_fn;
  95:     struct function_t *next;
  96: } function_t;
  97: 
  98: typedef struct func_arg_t {
  99:     evalue_t the_value;
 100:     expression_t *the_arg;
 101: } func_arg_t;
 102: 
 103: typedef struct expression_t {
 104:     expr_type_e expr_type;
 105: 
 106:     union {
 107:         evalue_t value;                    // EXPR_CONST
 108: 
 109:         symbol_t *symbol;            // EXPR_SYMBOL
 110: 
 111:         struct {                    // EXPR_ZEROP
 112:             zerop_t *zerop;
 113:         } zero_args;
 114: 
 115:         struct {                    // EXPR_MONOP
 116:             monop_t *monop;
 117:             evalue_t the_value;
 118:             expression_t *the_arg;
 119:         } one_arg;
 120: 
 121:         struct {                    // EXPR_BINOP
 122:             binop_t *binop;
 123:             evalue_t left_value;
 124:             evalue_t right_value;
 125:             expression_t *left_arg;
 126:             expression_t *right_arg;
 127:         } two_args;
 128: 
 129:         struct {                    // EXPR_FUNC
 130:             function_t *func;
 131:             func_arg_t *func_args;
 132:             int num_args;
 133:             free_state_fn free_state;
 134:             void *state;    // Usually null
 135:         } many_args;
 136:     } u;
 137: } expression_t;
 138: 
 139: #endif // ! TRANSFORM_EXPRESSION_HPP
 140: 

transform_parse.cpp - Parser

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: #include <ctype.h>
   5: 
   6: #include "memory.hpp"
   7: #include "transform.hpp"
   8: 
   9: #define LONGEST_NUMBER 400        // Probably 25 is very safe
  10: 
  11: ////////////////////////////////////
  12: // Utilities for parser
  13: ////////////////////////////////////
  14: 
  15: int transform_c::push_oper (expression_t *oper)
  16: {
  17:     int sz;
  18: 
  19:     sz = this->oper_stack_size;
  20:     if (sz >= MAX_OPERATORS)
  21:     {
  22:         return 0;
  23:     }
  24: 
  25:     this->oper_stack[sz].oper = oper;
  26:     this->oper_stack_size++;
  27:     return 1;
  28: }
  29: 
  30: int transform_c::pop_opers (oper_prec_e stopper)
  31: {
  32:     int nargs;
  33:     int sz;
  34:     func_arg_t *args;
  35:     expression_t *expr, *tmp;
  36:     int i;
  37:     int check;
  38:     int ret;
  39: 
  40:     sz = this->oper_stack_size;
  41:     ret = 1;
  42:     while (sz > 0)
  43:     {
  44:         sz--;
  45:         if (this->oper_stack[sz].oper == NULL)        // Left parenthesis
  46:         {
  47:             if (PAREN_PREC <= stopper) break;
  48:         }
  49:         else if (this->oper_stack[sz].oper->expr_type == EXPR_BINOP)
  50:         {
  51:             if (this->oper_stack[sz].oper->u.two_args.binop->prec < stopper)
  52:             {
  53:                 break;
  54:             }
  55:             this->oper_stack[sz].oper->u.two_args.right_arg =
  56:                 this->top_expression;
  57:             this->top_expression = this->oper_stack[sz].oper;
  58:         }
  59:         else if (this->oper_stack[sz].oper->expr_type == EXPR_MONOP)
  60:         {
  61:             this->oper_stack[sz].oper->u.one_arg.the_arg =
  62:                 this->top_expression;
  63:             this->top_expression = this->oper_stack[sz].oper;
  64:         }
  65:         else if (this->oper_stack[sz].oper->expr_type == EXPR_FUNC)
  66:         {
  67:             // Count # parameters to the function
  68:             nargs = 1;
  69:             expr = this->top_expression;
  70:             while (expr->expr_type == EXPR_BINOP &&
  71:                 expr->u.two_args.binop->prec == COMMA_PREC)
  72:             {
  73:                 nargs++;
  74:                 expr = expr->u.two_args.left_arg;
  75:             }
  76: 
  77:             args = GetMem (func_arg_t, nargs);
  78: 
  79:             if (nargs > 1)
  80:             {
  81:                 // Pick up first argument
  82:                 args[0].the_arg = expr;
  83: 
  84:                 // Copy args into the array of arguments
  85:                 i = nargs;
  86:                 expr = this->top_expression;
  87:                 while (expr->expr_type == EXPR_BINOP &&
  88:                     expr->u.two_args.binop->prec == COMMA_PREC)
  89:                 {
  90:                     i--;
  91:                     args[i].the_arg = expr->u.two_args.right_arg;
  92: 
  93:                     // Delete the bogus comma operator
  94:                     tmp = expr->u.two_args.left_arg;
  95:                     FreeMem (expr);
  96:                     expr = tmp;
  97:                 }
  98:             }
  99:             else
 100:             {
 101:                 // Pick up only argument, if no commas
 102:                 args[0].the_arg = this->top_expression;
 103:             }
 104: 
 105:             check = this->oper_stack[sz].oper->u.many_args.func->min_args;
 106:             if (check > 0 && nargs < check)
 107:             {
 108:                 ret = 0;
 109:             }
 110:             check = this->oper_stack[sz].oper->u.many_args.func->max_args;
 111:             if (check > 0 && nargs > check)
 112:             {
 113:                 ret = 0;
 114:             }
 115: 
 116:             this->oper_stack[sz].oper->u.many_args.func_args = args;
 117:             this->oper_stack[sz].oper->u.many_args.num_args = nargs;
 118:             this->top_expression = this->oper_stack[sz].oper;
 119: 
 120:             if (ret == 0) break;
 121:         }
 122: 
 123:         this->oper_stack_size = sz;
 124:     }
 125: 
 126:     return ret;
 127: }
 128: 
 129: int transform_c::is_a_number (char *pos, int *floater)
 130: {
 131:     int len = 0;
 132: 
 133:     *floater = 0;
 134:     if (pos[0] == this->plus_sign || pos[0] == this->minus_sign)
 135:     {
 136:         len++;
 137:     }
 138:     if (pos[len] == this->decimal_point)
 139:     {
 140:         *floater = 1;
 141:         len++;
 142:     }
 143:     if (!isdigit (pos[len])) return 0;        // No number there
 144: 
 145:     // len can be 0, 1 or 2 before the while loop
 146:     while (isdigit (pos[len])) len++;
 147: 
 148:     // Check for decimal point
 149:     if (!(*floater) && pos[len] == this->decimal_point)
 150:     {
 151:         *floater = 1;
 152:         len++;
 153:         while (isdigit (pos[len])) len++;   // More digits!
 154:     }
 155: 
 156:     // Check for exponent
 157:     if (pos[len] != '\0' && strchr (this->expon_letters, pos[len]) != NULL)
 158:     {
 159:         *floater = 1;
 160:         len++;
 161:         if (pos[len] == this->plus_sign || pos[len] == this->minus_sign)
 162:         {
 163:             len++;
 164:         }
 165:         if (!isdigit (pos[len])) return 0;  // Screwed up exponent
 166:         while (isdigit (pos[len])) len++;   // More digits!
 167:     }
 168: 
 169:     return len;
 170: }
 171: 
 172: ////////////////////////////////////
 173: // Parse a symbolic expression
 174: ////////////////////////////////////
 175: 
 176: expression_t *transform_c::parse_expr (symbols_c *symbols, char *expr,
 177:     char **emsg, char **errpos)
 178: {
 179:     int expecting_binop;
 180:     char *last_binop_pos;
 181:     expression_t *node;
 182:     symbol_t *symbol;
 183:     char *pos, *pos2, *pos3;
 184:     zerop_t *zerop;
 185:     monop_t *monop;
 186:     binop_t *binop;
 187:     function_t *funcp;
 188:     int nc;
 189:     int floater;
 190:     int cnt;
 191:     char numb[LONGEST_NUMBER+1];
 192:     datetime_t dt;
 193:     double x = 0;
 194:     int i = 0;
 195:     char quotech;
 196:     int quotes;
 197:     char *newstr;
 198: #if 0
 199:     int nargs;
 200:     int check;
 201: #endif
 202: 
 203:     if (!symbols || !expr || !emsg || !errpos) return NULL;
 204: 
 205:     expecting_binop = 0;
 206:     pos = expr;
 207:     last_binop_pos = NULL;
 208:     this->oper_stack_size = 0;
 209:     *emsg = NULL;
 210:     this->top_expression = NULL;
 211: 
 212:     while (*pos != '\0')
 213:     {
 214:         while (isspace (*pos)) pos++;        // Get past spaces, tabs, etc.
 215:         if (*pos == '\0' || *pos == this->comment_char) break;
 216: 
 217:         if (expecting_binop)
 218:         {
 219:             // Maybe it is a right paren
 220:             if (*pos == this->right_paren)
 221:             {
 222:                 // Pop 'em off until find a matching left paren
 223:                 if (!pop_opers (PAREN_PREC))
 224:                 {
 225:                     goto parse_error;
 226:                 }
 227: 
 228:                 // Must be a left paren on top of stack, or else!!
 229:                 if (this->oper_stack_size < 1 ||
 230:                     this->oper_stack[this->oper_stack_size-1].oper != NULL)
 231:                 {
 232:                     *emsg = "Left and right parentheses do not match";
 233:                     goto parse_error;
 234:                 }
 235:                 this->oper_stack_size--;    // Toss it ...
 236: 
 237: #if 0
 238:                 // If top item is a function, check it's #args now
 239:                 if (this->oper_stack[this->oper_stack_size-1].oper->expr_type == EXPR_FUNC)
 240:                 {
 241:                     nargs = this->oper_stack[this->oper_stack_size-1].oper->u.many_args.num_args;
 242:                     check = this->oper_stack[this->oper_stack_size-1].oper->u.many_args.func->min_args;
 243:                     if (check > 0 && nargs < check)
 244:                     {
 245:                         *emsg = "Not enough arguments for function.";
 246:                         goto parse_error;
 247:                     }
 248:                     check = this->oper_stack[this->oper_stack_size-1].oper->u.many_args.func->max_args;
 249:                     if (check > 0 && nargs > check)
 250:                     {
 251:                         *emsg = "Too many arguments for function.";
 252:                         goto parse_error;
 253:                     }
 254:                 }
 255: #endif
 256: 
 257: #if 0
 258:                 // If it is a function, check it's #args now
 259:                 if (this->top_expression->expr_type == EXPR_FUNC)
 260:                 {
 261:                     nargs = this->top_expression->u.many_args.num_args;
 262:                     check = this->top_expression->u.many_args.func->min_args;
 263:                     if (check > 0 && nargs < check)
 264:                     {
 265:                         *emsg = "Not enough arguments for function.";
 266:                         goto parse_error;
 267:                     }
 268:                     check = this->top_expression->u.many_args.func->max_args;
 269:                     if (check > 0 && nargs > check)
 270:                     {
 271:                         *emsg = "Too many arguments for function.";
 272:                         goto parse_error;
 273:                     }
 274:                 }
 275: #endif
 276:                 pos++;
 277:                 continue;
 278:             }
 279: 
 280:             // Might be a leading $ on system variables and functions
 281:             // Beware order of these checks!
 282:             if (*pos == this->system_variable && isalpha(pos[1])) pos++;
 283: 
 284:             // Maybe it is an operator like + or -
 285:             binop = this->parse_binop (pos);
 286:             if (binop != NULL)
 287:             {
 288:                 // Pop 'em off all higher precedence operators
 289:                 if (!this->pop_opers (binop->prec))
 290:                 {
 291:                     goto parse_error;
 292:                 }
 293: 
 294:                 // Add to operator stack, not tree
 295:                 node = GetMem (expression_t, 1);
 296:                 if (node == NULL) goto parse_error;
 297:                 node->expr_type = EXPR_BINOP;
 298:                 node->u.two_args.binop = binop;
 299:                 node->u.two_args.left_arg = this->top_expression;
 300:                 node->u.two_args.right_arg = NULL;        // In case an error occurs in parsing
 301:                 this->top_expression = NULL;
 302: 
 303:                 if (!this->push_oper (node)) goto parse_error;
 304: 
 305:                 last_binop_pos = pos;
 306:                 pos += strlen (binop->name);
 307:                 expecting_binop = 0;
 308:                 continue;
 309:             }
 310: 
 311:             *emsg = "Expected a binary operator or right parenthesis.";
 312:             goto parse_error;
 313:         }
 314: 
 315:         // Might be a variable, function, constant or left paren
 316:         if (*pos == this->left_paren)
 317:         {
 318:             this->push_oper (NULL);
 319:             pos++;
 320:             continue;
 321:         }
 322: 
 323:         // See if it is a numeric constant
 324:         nc = is_a_number (pos, &floater);
 325:         if (nc > 0 && nc < LONGEST_NUMBER)
 326:         {
 327:             strncpy (numb, pos, nc);
 328:             numb[nc] = '\0';
 329: 
 330:             if (floater)    // Must be a double
 331:             {
 332:                 cnt = sscanf (numb, "%lf", &x);
 333:             }
 334:             else            // Must be an int
 335:             {
 336:                 cnt = sscanf (numb, "%d", &i);
 337:             }
 338: 
 339:             if (cnt != 1)
 340:             {
 341:                 *emsg = "Invalid number";
 342:                 goto parse_error;
 343:             }
 344: 
 345:             // Add to tree
 346:             node = GetMem (expression_t, 1);
 347:             if (node == NULL) goto parse_error;
 348:             node->expr_type = EXPR_CONST;
 349:             node->u.value.status = STATUS_OK;
 350:             this->top_expression = node;
 351: 
 352:             if (floater)    // Must be a double
 353:             {
 354:                 node->u.value.typ = DOUBLE_TYP;
 355:                 node->u.value.u.x = x;
 356:             }
 357:             else            // Must be an int
 358:             {
 359:                 node->u.value.typ = INT_TYP;
 360:                 node->u.value.u.i = i;
 361:             }
 362: 
 363:             pos += nc;
 364:             expecting_binop = 1;
 365:             continue;
 366:         }
 367: 
 368:         // See if it is  a string constant
 369:         if (*pos == this->single_quote || *pos == this->double_quote)
 370:         {
 371:             quotech = *pos++;
 372:             quotes = 0;
 373: 
 374:             // Loop in case quotes are doubled: 'Bob''s house'
 375:             pos2 = pos;
 376:             for (;;)
 377:             {
 378:                 pos2 = strchr (pos2, quotech);
 379:                 if (pos2 == NULL)
 380:                 {
 381:                     *emsg = "Missing end quote.";
 382:                     goto parse_error;
 383:                 }
 384:                 if (pos2[1] != quotech) break;
 385:                 quotes++;
 386:                 pos2 += 2;
 387:             }
 388: 
 389:             nc = pos2 - pos - quotes;        // Might be zero
 390:             newstr = GetMem (char, nc+1);
 391: 
 392:             // Copy over the quoted literal, in pieces
 393:             pos3 = pos;
 394:             for (i=0; i<nc; i++)
 395:             {
 396:                 newstr[i] = *pos3;
 397:                 if (*pos3 == quotech) pos3++;
 398:                 pos3++;
 399:             }
 400:             newstr[nc] = '\0';
 401: 
 402:             // Add to tree
 403:             node = GetMem (expression_t, 1);
 404:             if (node == NULL) goto parse_error;
 405:             node->expr_type = EXPR_CONST;
 406:             node->u.value.typ = STRING_TYP;
 407:             node->u.value.u.v.str = newstr;
 408:             node->u.value.u.v.need_to_free = 1;
 409:             node->u.value.status = STATUS_OK;
 410:             this->top_expression = node;
 411: 
 412:             pos = pos2 + 1;
 413:             expecting_binop = 1;
 414:             continue;
 415:         }
 416: 
 417:         // See if it is a date/time constant
 418:         if (*pos == this->date_delim)
 419:         {
 420:             pos++;
 421:             pos2 = strchr (pos, this->date_delim);
 422:             if (pos2 == NULL)
 423:             {
 424:                 *emsg = "Missing end of date/time delimiter";
 425:                 goto parse_error;
 426:             }
 427: 
 428:             nc = pos2 - pos;
 429:             if (nc >= LONGEST_NUMBER) goto parse_error;
 430:             strncpy (numb, pos, nc);
 431:             numb[nc] = '\0';
 432: 
 433:             if (!this->dt_class.parse_dt (numb, &dt, NULL, emsg))
 434:             {
 435:                 goto parse_error;
 436:             }
 437: 
 438:             // Add to tree
 439:             node = GetMem (expression_t, 1);
 440:             if (node == NULL) goto parse_error;
 441:             node->expr_type = EXPR_CONST;
 442:             node->u.value.typ = DATETIME_TYP;
 443:             node->u.value.u.dt = dt;
 444:             node->u.value.status = STATUS_OK;
 445:             this->top_expression = node;
 446: 
 447:             pos = pos2 + 1;
 448:             expecting_binop = 1;
 449:             continue;
 450:         }
 451: 
 452:         // See if it is a user variable
 453:         symbol = NULL;
 454:         if (isalpha (*pos))        // Plain variable, no bangs
 455:         {
 456:             nc = 0;
 457:             while (isalnum(pos[nc]) || pos[nc] == this->underscore) nc++;
 458:             symbol = symbols->match_symbol (pos, nc);
 459:             if (symbol != NULL) pos += nc;
 460:         }
 461:         if (*pos == this->user_variable)
 462:         {
 463:             pos++;  // Get past first bang
 464:             pos2 = strchr (pos, this->user_variable);
 465:             if (pos2 == NULL)
 466:             {
 467:                 *emsg = "Missing end-of-variable indicator.";
 468:                 goto parse_error;
 469:             }
 470:             nc = (int) (pos2 - pos);
 471:             symbol = symbols->match_symbol (pos, nc);
 472:             if (symbol == NULL)
 473:             {
 474:                 *emsg = "Undefined variable.";
 475:                 goto parse_error;
 476:             }
 477:             pos = pos2 + 1;
 478:         }
 479:         if (symbol != NULL)
 480:         {
 481:             // Add to tree
 482:             node = GetMem (expression_t, 1);
 483:             if (node == NULL) goto parse_error;
 484:             node->expr_type = EXPR_SYMBOL;
 485:             node->u.symbol = symbol;
 486:             this->top_expression = node;
 487: 
 488:             expecting_binop = 1;
 489:             continue;
 490:         }
 491: 
 492:         // Might be a leading $ on system variables and functions
 493:         // Beware order of these checks!
 494:         if (*pos == this->system_variable && isalpha(pos[1])) pos++;
 495: 
 496:         // See if it is a unary operator (monop)
 497:         monop = this->parse_monop (pos);
 498:         if (monop != NULL)
 499:         {
 500:             // Add to tree
 501:             node = GetMem (expression_t, 1);
 502:             if (node == NULL) goto parse_error;
 503:             node->expr_type = EXPR_MONOP;
 504:             node->u.one_arg.monop = monop;
 505:             this->top_expression = node;
 506: 
 507:             if (!this->push_oper (node)) goto parse_error;
 508: 
 509:             pos += strlen (monop->name);
 510:             continue;
 511:         }
 512: 
 513:         // See if it is a system variable (zerop)
 514:         zerop = this->parse_zerop (pos);
 515:         if (zerop != NULL)
 516:         {
 517:             // Add to tree
 518:             node = GetMem (expression_t, 1);
 519:             if (node == NULL) goto parse_error;
 520:             node->expr_type = EXPR_ZEROP;
 521:             node->u.zero_args.zerop = zerop;
 522:             this->top_expression = node;
 523: 
 524:             pos += strlen (zerop->name);
 525:             expecting_binop = 1;
 526:             continue;
 527:         }
 528: 
 529:         // See if it is a function
 530:         funcp = this->parse_func (pos);
 531:         if (funcp != NULL)
 532:         {
 533:             // Must have a left paren immediately
 534:             pos += strlen (funcp->name);
 535:             while (isspace (*pos)) pos++;        // Get past spaces, tabs, etc.
 536:             if (*pos != '(')
 537:             {
 538:                 *emsg = "Missing left parenthesis.";
 539:                 goto parse_error;
 540:             }
 541: 
 542:             // Add to tree
 543:             node = GetMem (expression_t, 1);
 544:             if (node == NULL) goto parse_error;
 545:             node->expr_type = EXPR_FUNC;
 546:             node->u.many_args.func = funcp;
 547:             node->u.many_args.num_args = 0;
 548:             node->u.many_args.func_args = NULL;
 549:             node->u.many_args.free_state = NULL;
 550:             node->u.many_args.state = NULL;
 551:             this->top_expression = node;
 552: 
 553:             if (!this->push_oper (node)) goto parse_error;
 554:             continue;
 555:         }
 556: 
 557:         *emsg = "Unknown expression or items out of order.";
 558:         goto parse_error;
 559:     }
 560: 
 561:     // Pop rest of stuff off stack
 562:     this->pop_opers (PAREN_PREC);
 563: 
 564:     if (this->oper_stack_size != 0)
 565:     {
 566:         *emsg = "Incomplete expression.";
 567:         pos = last_binop_pos;
 568:         goto parse_error;
 569:     }
 570: 
 571:     if (!expecting_binop)
 572:     {
 573:         *emsg = "Cannot end an expression with a binary operator.";
 574:         pos = last_binop_pos;
 575:         goto parse_error;
 576:     }
 577: 
 578:     return this->top_expression;
 579: 
 580: parse_error:
 581:     *errpos = pos;
 582:     this->free_expr (this->top_expression);
 583:     return NULL;
 584: }
 585: 
 586: ////////////////////////////////////
 587: // Parse current string to see if it is a monop (or binop)
 588: ////////////////////////////////////
 589: 
 590: static int matches (char *pattern, char *rec)
 591: {
 592:     char ch;
 593:     int nc;
 594:     char *p, *r;
 595: 
 596:     ch = pattern[0];
 597:     if (isalpha (ch))        // Case insensitive compare
 598:     {
 599:         // Make sure first letter matches
 600:         if (toupper(ch) != toupper(rec[0])) return 0;
 601:         p = pattern;
 602:         r = rec;
 603:         for (;;)
 604:         {
 605:             p++;
 606:             r++;
 607:             if (*p == '\0')
 608:             {
 609:                 if (isalnum (*r)) return 0;
 610:                 return 1;        // Matched!
 611:             }
 612:             if (*r == '\0') return 0;        // Too short
 613:             if (toupper(*p) != toupper(*r)) return 0;        // Failed
 614:         }
 615:     }
 616:     else    // Must be 1 or 2 char punctuation
 617:     {
 618:         if (ch != rec[0]) return 0;
 619:         nc = strlen (pattern);
 620:         if (nc == 1) return 1;
 621:         else if (nc == 2)
 622:         {
 623:             if (pattern[1] == rec[1]) return 1;
 624:         }
 625:         else return 0;        // punct length must be 1 or 2
 626:     }
 627: 
 628:     return 0;
 629: }
 630: 
 631: zerop_t *transform_c::parse_zerop (char *rec)
 632: {
 633:     zerop_t *zerop;
 634: 
 635:     if (!rec) return NULL;
 636: 
 637:     for (zerop=this->first_zerop; zerop!=NULL; zerop=zerop->next)
 638:     {
 639:         if (matches (zerop->name, rec)) return zerop;
 640:     }
 641: 
 642:     return NULL;    // Not found
 643: }
 644: 
 645: monop_t *transform_c::parse_monop (char *rec)
 646: {
 647:     monop_t *monop;
 648: 
 649:     if (!rec) return NULL;
 650: 
 651:     for (monop=this->first_monop; monop!=NULL; monop=monop->next)
 652:     {
 653:         if (matches (monop->name, rec)) return monop;
 654:     }
 655: 
 656:     return NULL;    // Not found
 657: }
 658: 
 659: binop_t *transform_c::parse_binop (char *rec)
 660: {
 661:     binop_t *binop;
 662: 
 663:     if (!rec) return NULL;
 664: 
 665:     for (binop=this->first_binop; binop!=NULL; binop=binop->next)
 666:     {
 667:         if (matches (binop->name, rec)) return binop;
 668:     }
 669: 
 670:     return NULL;    // Not found
 671: }
 672: 
 673: function_t *transform_c::parse_func (char *rec)
 674: {
 675:     function_t *func;
 676: 
 677:     if (!rec) return NULL;
 678: 
 679:     for (func=this->first_func; func!=NULL; func=func->next)
 680:     {
 681:         if (matches (func->name, rec)) return func;
 682:     }
 683: 
 684:     return NULL;    // Not found
 685: }
 686: 

transform_eval.cpp - Evaluator

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: 
   4: #include "memory.hpp"
   5: #include "transform.hpp"
   6: 
   7: static void force_double (evalue_t *val)
   8: {
   9:     switch (val->typ)
  10:     {
  11:         case INT_TYP:
  12:             val->typ = DOUBLE_TYP;
  13:             val->u.x = (double) val->u.i;
  14:             break;
  15: 
  16:         case DOUBLE_TYP:
  17:             break;
  18: 
  19:         case DATETIME_TYP:
  20:             val->typ = DOUBLE_TYP;
  21:             val->u.x = val->u.dt;
  22:             break;
  23: 
  24:         default:
  25:             val->status |= STATUS_ERROR;
  26:             break;
  27:     }
  28: }
  29: 
  30: ////////////////////////////////////
  31: // Evaluate operators for a single point
  32: ////////////////////////////////////
  33: 
  34: static int eval_zerop (datetime_c *dt, zerop_t *zerop,
  35:     evalue_t *result)
  36: {
  37:     result->status = STATUS_OK;
  38: 
  39:     if (zerop->int_fn != NULL)
  40:     {
  41:         result->typ = INT_TYP;
  42:         zerop->int_fn (&(result->u.i), &(result->status));
  43:     }
  44:     else if (zerop->double_fn != NULL)
  45:     {
  46:         result->typ = DOUBLE_TYP;
  47:         zerop->double_fn (&(result->u.x), &(result->status));
  48:     }
  49:     else if (zerop->datetime_fn != NULL)
  50:     {
  51:         result->typ = DATETIME_TYP;
  52:         zerop->datetime_fn (dt, &(result->u.dt), &(result->status));
  53:     }
  54:     else return 0;
  55: 
  56:     return 1;
  57: }
  58: 
  59: static int eval_monop (monop_t *monop, evalue_t *val,
  60:     evalue_t *result)
  61: {
  62:     result->status = val->status;
  63: 
  64:     if (val->typ == INT_TYP && monop->int_fn != NULL)
  65:     {
  66:         result->typ = INT_TYP;
  67:         monop->int_fn (val->u.i, &(result->u.i), &(result->status));
  68:     }
  69:     else if (monop->double_fn != NULL)
  70:     {
  71:         if (val->typ != DOUBLE_TYP) force_double (val);
  72:         result->typ = DOUBLE_TYP;
  73:         monop->double_fn (val->u.x, &(result->u.x), &(result->status));
  74:     }
  75:     else return 0;
  76: 
  77:     return 1;
  78: }
  79: 
  80: static int eval_binop (binop_t *binop, evalue_t *v1, evalue_t *v2,
  81:     evalue_t *result)
  82: {
  83:     int any_datetimes;
  84: 
  85:     result->status = STATUS_COMBINE (v1->status, v2->status);
  86: 
  87:     if (v1->typ == INT_TYP && v2->typ == INT_TYP && binop->int_fn != NULL)
  88:     {
  89:         result->typ = INT_TYP;
  90:         binop->int_fn (v1->u.i, v2->u.i, &(result->u.i), &(result->status));
  91:     }
  92:     else if (v1->typ == STRING_TYP && v2->typ == STRING_TYP)
  93:     {
  94:         if (binop->strint_fn != NULL)
  95:         {
  96:             result->typ = INT_TYP;
  97:             binop->strint_fn (v1->u.v.str, v2->u.v.str, &(result->u.i), &(result->status));
  98:         }
  99:         else if (binop->strstr_fn != NULL)
 100:         {
 101:             result->typ = STRING_TYP;
 102:             binop->strstr_fn (v1->u.v.str, v2->u.v.str, &(result->u.v.str), &(result->status));
 103:             result->u.v.need_to_free = 1;
 104:         }
 105:         else return 0;
 106:     }
 107:     else if (binop->double_fn != NULL)
 108:     {
 109:         any_datetimes = (v1->typ == DATETIME_TYP || v2->typ == DATETIME_TYP ? 1 : 0);
 110:         if (v1->typ != DOUBLE_TYP) force_double (v1);
 111:         if (v2->typ != DOUBLE_TYP) force_double (v2);
 112:         result->typ = DOUBLE_TYP;
 113:         binop->double_fn (v1->u.x, v2->u.x, &(result->u.x), &(result->status));
 114: 
 115:         if (any_datetimes)
 116:         {
 117:             result->u.dt = result->u.x;
 118:             result->typ = DATETIME_TYP;
 119:         }
 120:     }
 121:     else if (binop->dblint_fn != NULL)
 122:     {
 123:         if (v1->typ != DOUBLE_TYP) force_double (v1);
 124:         if (v2->typ != DOUBLE_TYP) force_double (v2);
 125:         result->typ = INT_TYP;
 126:         binop->dblint_fn (v1->u.x, v2->u.x, &(result->u.i), &(result->status));
 127:     }
 128:     else return 0;
 129: 
 130:     return 1;
 131: }
 132: 
 133: static int eval_func (expression_t *expr, datetime_c *dt, evalue_t *value)
 134: {
 135:     if (expr->u.many_args.func->func_fn != NULL)
 136:     {
 137:         if (!expr->u.many_args.func->func_fn (expr->u.many_args.num_args,
 138:             expr->u.many_args.func_args, value))
 139:         {
 140:             return 0;
 141:         }
 142:     }
 143:     else if (expr->u.many_args.func->dt_func_fn != NULL)
 144:     {
 145:         if (!expr->u.many_args.func->dt_func_fn (dt,
 146:             expr->u.many_args.num_args, expr->u.many_args.func_args, value))
 147:         {
 148:             return 0;
 149:         }
 150:     }
 151:     else if (expr->u.many_args.func->state_func_fn != NULL)
 152:     {
 153:         if (!expr->u.many_args.func->state_func_fn (&(expr->u.many_args.free_state),
 154:             &(expr->u.many_args.state), expr->u.many_args.num_args,
 155:             expr->u.many_args.func_args, value))
 156:         {
 157:             return 0;
 158:         }
 159:     }
 160:     else return 0;
 161: 
 162:     return 1;
 163: }
 164: 
 165: void maybe_free_temp_str (evalue_t *value)
 166: {
 167:     if (value->typ == STRING_TYP)
 168:     {
 169:         if (value->u.v.need_to_free)
 170:         {
 171:             FreeMem (value->u.v.str);
 172:         }
 173:     }
 174: }
 175: 
 176: static int evaluate (symbols_c *symbols, datetime_c *dt,
 177:     expression_t *expr, evalue_t *value)
 178: {
 179:     evalue_t tempvalue;
 180:     int i;
 181:     int ret = 1;
 182: 
 183:     switch (expr->expr_type)
 184:     {
 185:         case EXPR_CONST:
 186:             value->typ = expr->u.value.typ;
 187:             value->status = expr->u.value.status;
 188:             switch (value->typ)
 189:             {
 190:                 case INT_TYP:
 191:                     value->u.i = expr->u.value.u.i;
 192:                     break;
 193: 
 194:                 case DOUBLE_TYP:
 195:                     value->u.x = expr->u.value.u.x;
 196:                     break;
 197: 
 198:                 case DATETIME_TYP:
 199:                     value->u.dt = expr->u.value.u.dt;
 200:                     break;
 201: 
 202:                 case STRING_TYP:
 203:                     value->u.v.str = expr->u.value.u.v.str;        // SHARES A POINTER, Careful!
 204:                     value->u.v.need_to_free = 0;
 205:                     break;
 206: 
 207:                 default:
 208:                     return 0;
 209:             }
 210:             break;
 211: 
 212:         case EXPR_DERIV_SYMBOL:
 213:             // Should add partials here someday
 214:             value->typ = INT_TYP;
 215:             value->status = STATUS_OK;
 216:             value->u.i = (expr->u.symbol == symbols->deriv_symbol ? 1 : 0);
 217:             return 1;
 218: 
 219:         case EXPR_SYMBOL:
 220:             if (!copy_value (value, &(expr->u.symbol->value)))
 221:             {
 222:                 return 0;
 223:             }
 224:             break;
 225: 
 226:         case EXPR_ZEROP:
 227:             if (!eval_zerop (dt, expr->u.zero_args.zerop, value))
 228:             {
 229:                 return 0;
 230:             }
 231:             break;
 232: 
 233:         case EXPR_MONOP:
 234:             if (!evaluate (symbols, dt, expr->u.one_arg.the_arg,
 235:                 &(expr->u.one_arg.the_value)))
 236:             {
 237:                 return 0;
 238:             }
 239:             if (!eval_monop (expr->u.one_arg.monop,
 240:                 &(expr->u.one_arg.the_value), value))
 241:             {
 242:                 ret = 0;
 243:             }
 244:             maybe_free_temp_str (&(expr->u.one_arg.the_value));
 245:             break;
 246: 
 247:         case EXPR_BINOP:
 248:             if (!evaluate (symbols, dt, expr->u.two_args.left_arg,
 249:                 &(expr->u.two_args.left_value)))
 250:             {
 251:                 return 0;
 252:             }
 253: 
 254:             //
 255:             // If you wanted to short-circuit AND's and OR's
 256:             // This is the place for it. You'd lose the status
 257:             // from the RHS though.
 258:             //
 259: 
 260:             if (!evaluate (symbols, dt, expr->u.two_args.right_arg,
 261:                 &(expr->u.two_args.right_value)))
 262:             {
 263:                 maybe_free_temp_str (&(expr->u.two_args.left_value));
 264:                 return 0;
 265:             }
 266: 
 267:             //
 268:             // Special case of a < b <= c = d
 269:             //
 270:             if (expr->u.two_args.binop->prec == REL_PREC)
 271:             {
 272:                 if (expr->u.two_args.left_arg->expr_type == EXPR_BINOP)
 273:                 {
 274:                     if (expr->u.two_args.left_arg->u.two_args.binop->prec == REL_PREC)
 275:                     {
 276:                         if (!eval_binop (expr->u.two_args.binop,
 277:                             &(expr->u.two_args.left_arg->u.two_args.right_value),
 278:                             &(expr->u.two_args.right_value),
 279:                             &tempvalue))
 280:                         {
 281:                             return 0;
 282:                         }
 283: 
 284:                         // "And" operation implicit
 285:                         value->typ = INT_TYP;
 286:                         value->status = STATUS_COMBINE (expr->u.two_args.left_value.status,
 287:                             expr->u.two_args.right_value.status);
 288:                         value->u.i = ((expr->u.two_args.left_value.u.i != 0) &&
 289:                             (tempvalue.u.i != 0) ? 1 : 0);
 290:                     }
 291:                     break;
 292:                 }
 293:             }
 294: 
 295:             if (!eval_binop (expr->u.two_args.binop,
 296:                 &(expr->u.two_args.left_value),
 297:                 &(expr->u.two_args.right_value),
 298:                 value))
 299:             {
 300:                 ret = 0;
 301:             }
 302: 
 303:             maybe_free_temp_str (&(expr->u.two_args.left_value));
 304:             maybe_free_temp_str (&(expr->u.two_args.right_value));
 305:             break;
 306: 
 307:         case EXPR_FUNC:
 308:             for (i=0; i<expr->u.many_args.num_args; i++)
 309:             {
 310:                 if (!evaluate (symbols, dt,
 311:                     (expr->u.many_args.func_args)[i].the_arg,
 312:                     &(expr->u.many_args.func_args)[i].the_value))
 313:                 {
 314:                     return 0;
 315:                 }
 316:             }
 317: 
 318:             if (!eval_func (expr, dt, value))
 319:             {
 320:                 ret = 0;
 321:             }
 322: 
 323:             for (i=0; i<expr->u.many_args.num_args; i++)
 324:             {
 325:                 maybe_free_temp_str (&(expr->u.many_args.func_args)[i].the_value);
 326:             }
 327:             break;
 328:     }
 329: 
 330:     return ret;
 331: }
 332: 
 333: int transform_c::eval_expr (symbols_c *symbols, expression_t *expr,
 334:     evalue_t *value)
 335: {
 336:     int ret;
 337: 
 338:     if (!expr || !value) return 0;
 339: 
 340:     FreeMem (this->prev_string_result);   // Often null
 341: 
 342:     ret = evaluate (symbols, &(this->dt_class), expr, value);
 343: 
 344:     // If result is a string, remember it's pointer and free it next time
 345:     if (value->typ == STRING_TYP)
 346:     {
 347:         if (value->u.v.need_to_free)
 348:         {
 349:             this->prev_string_result = value->u.v.str;
 350:         }
 351:     }
 352: 
 353:     return ret;
 354: }
 355: 

transform_operators.cpp - Arithmetic Operators

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <errno.h>
   4: #include <string.h>
   5: #include <ctype.h>
   6: 
   7: #ifdef WIN32
   8: #pragma warning(disable:4514)        //unreferenced inline function has been removed
   9: // #pragma warning(default:4514)       // turns it back on
  10: #endif
  11: 
  12: #include <math.h>
  13: 
  14: #include "transform.hpp"
  15: #include "memory.hpp"
  16: 
  17: #ifndef M_PI
  18: #define M_PI 3.14159265358979323846
  19: #endif
  20: 
  21: #ifndef M_E
  22: #define M_E 2.7182818284590452354
  23: #endif
  24: 
  25: #define PI180 (M_PI / 180.0)
  26: 
  27: ////////////////////////////////////
  28: // Start system variables (aka zerops)
  29: ////////////////////////////////////
  30: 
  31: // $blank
  32: static void blank_sysvar (int *result, status_t *status)
  33: {
  34:     *result = 0;
  35:     *status |= STATUS_BLANK;
  36: }
  37: 
  38: // $error
  39: static void error_sysvar (int *result, status_t *status)
  40: {
  41:     *result = 0;
  42:     *status |= STATUS_ERROR;
  43: }
  44: 
  45: // $e
  46: static void e_sysvar (double *result, status_t *)
  47: {
  48:     *result = M_E;
  49: }
  50: 
  51: // $pi
  52: static void pi_sysvar (double *result, status_t *)
  53: {
  54:     *result = M_PI;
  55: }
  56: 
  57: // $now
  58: static void now_sysvar (datetime_c *dt, datetime_t *result, status_t *)
  59: {
  60:     *result = dt->datetime_now ();
  61: }
  62: 
  63: ////////////////////////////////////
  64: // Start unary operators (aka monops)
  65: ////////////////////////////////////
  66: 
  67: // +val (nop)
  68: static void int_pos_op (int val, int *result, status_t *)
  69: {
  70:     *result = val;
  71: }
  72: 
  73: static void double_pos_op (double val, double *result, status_t *)
  74: {
  75:     *result = val;
  76: }
  77: 
  78: // -val
  79: static void int_neg_op (int val, int *result, status_t *)
  80: {
  81:     *result = -val;
  82: }
  83: 
  84: static void double_neg_op (double val, double *result, status_t *)
  85: {
  86:     *result = -val;
  87: }
  88: 
  89: // not val
  90: static void int_not_op (int val, int *result, status_t *)
  91: {
  92:     *result = (val == 0 ? 1 : 0);
  93: }
  94: 
  95: // sqrt(val)
  96: static void double_sqrt_op (double val, double *result, status_t *status)
  97: {
  98:     if (val < 0)
  99:     {
 100:         *result = 0;
 101:         *status |= STATUS_ERROR;
 102:     }
 103:     else
 104:     {
 105:         *result = sqrt (val);
 106:     }
 107: }
 108: 
 109: // sin(val) and sind(val)
 110: static void double_sin_op (double val, double *result, status_t *)
 111: {
 112:     *result = sin (val);
 113: }
 114: 
 115: static void double_sind_op (double val, double *result, status_t *)
 116: {
 117:     *result = sin (val * PI180);
 118: }
 119: 
 120: // cos(val) and cosd(val)
 121: static void double_cos_op (double val, double *result, status_t *)
 122: {
 123:     *result = cos (val);
 124: }
 125: 
 126: static void double_cosd_op (double val, double *result, status_t *)
 127: {
 128:     *result = cos (val * PI180);
 129: }
 130: 
 131: // tan(val) and tand(val)
 132: static void double_tan_op (double val, double *result, status_t *)
 133: {
 134:     *result = tan (val);
 135: }
 136: 
 137: static void double_tand_op (double val, double *result, status_t *)
 138: {
 139:     *result = tan (val * PI180);
 140: }
 141: 
 142: // atan(val) and atand(val)
 143: static void double_atan_op (double val, double *result, status_t *)
 144: {
 145:     *result = atan (val);
 146: }
 147: 
 148: static void double_atand_op (double val, double *result, status_t *)
 149: {
 150:     *result = atan (val) / PI180;
 151: }
 152: 
 153: // ln(val)
 154: static void double_ln_op (double val, double *result, status_t *status)
 155: {
 156:     if (val <= 0)
 157:     {
 158:         *result = 0;
 159:         *status |= STATUS_ERROR;
 160:     }
 161:     else
 162:     {
 163:         errno = 0;
 164:         *result = log (val);
 165:         if (errno != 0)
 166:         {
 167:             *result = 0;
 168:             *status |= STATUS_ERROR;
 169:         }
 170:     }
 171: }
 172: 
 173: // log10(val)
 174: static void double_log10_op (double val, double *result, status_t *status)
 175: {
 176:     if (val <= 0)
 177:     {
 178:         *result = 0;
 179:         *status |= STATUS_ERROR;
 180:     }
 181:     else
 182:     {
 183:         errno = 0;
 184:         *result = log (val) / log(10.0);
 185:         if (errno != 0)
 186:         {
 187:             *result = 0;
 188:             *status |= STATUS_ERROR;
 189:         }
 190:     }
 191: }
 192: 
 193: // exp(val)
 194: static void double_exp_op (double val, double *result, status_t *status)
 195: {
 196:     errno = 0;
 197:     *result = exp (val);
 198:     if (errno != 0)
 199:     {
 200:         *result = 0;
 201:         *status |= STATUS_ERROR;
 202:     }
 203: }
 204: 
 205: ////////////////////////////////////
 206: // Start binary operators (aka binops)
 207: ////////////////////////////////////
 208: 
 209: // v1 + v2
 210: static void int_plus_op (int v1, int v2,
 211:     int *result, status_t *)
 212: {
 213:     *result = v1 + v2;
 214: }
 215: 
 216: static void double_plus_op (double v1, double v2,
 217:     double *result, status_t *)
 218: {
 219:     *result = v1 + v2;
 220: }
 221: 
 222: static void strstr_plus_op (char *v1, char *v2,
 223:     char **result, status_t *)
 224: {
 225:     int nc;
 226: 
 227:     nc = strlen(v1) + strlen(v2);
 228:     *result = GetMem (char, nc+1);
 229:     strcpy (*result, v1);
 230:     strcat (*result, v2);
 231: }
 232: 
 233: // v1 - v2
 234: static void int_minus_op (int v1, int v2,
 235:     int *result, status_t *)
 236: {
 237:     *result = v1 - v2;
 238: }
 239: 
 240: static void double_minus_op (double v1, double v2,
 241:     double *result, status_t *)
 242: {
 243:     *result = v1 - v2;
 244: }
 245: 
 246: // v1 * v2
 247: static void int_times_op (int v1, int v2,
 248:     int *result, status_t *)
 249: {
 250:     *result = v1 * v2;
 251: }
 252: 
 253: static void double_times_op (double v1, double v2,
 254:     double *result, status_t *)
 255: {
 256:     *result = v1 * v2;
 257: }
 258: 
 259: // v1 / v2
 260: static void double_divide_op (double v1, double v2,
 261:     double *result, status_t *status)
 262: {
 263:     if (v2 == 0)
 264:     {
 265:         *result = 0;
 266:         *status |= STATUS_ERROR;
 267:     }
 268:     else
 269:     {
 270:         *result = v1 / v2;
 271:     }
 272: }
 273: 
 274: // v1 mod v2
 275: static void double_mod_op (double v1, double v2,
 276:     double *result, status_t *status)
 277: {
 278:     if (v2 == 0)
 279:     {
 280:         *result = 0;
 281:         *status |= STATUS_ERROR;
 282:     }
 283:     else
 284:     {
 285:         *result = fmod (v1,  v2);
 286:     }
 287: }
 288: 
 289: // v1 ^ v2
 290: static void double_power_op (double v1, double v2,
 291:     double *result, status_t *status)
 292: {
 293:     errno = 0;
 294:     *result = pow (v1, v2);
 295:     if (errno != 0)
 296:     {
 297:         *result = 0;
 298:         *status |= STATUS_ERROR;
 299:     }
 300: }
 301: 
 302: // v1 and v2
 303: static void int_and_op (int v1, int v2,
 304:     int *result, status_t *)
 305: {
 306:     *result = (((v1 != 0) && (v2 != 0)) ? 1 : 0);
 307: }
 308: 
 309: // v1 or v2
 310: static void int_or_op (int v1, int v2,
 311:     int *result, status_t *)
 312: {
 313:     *result = (((v1 != 0) || (v2 != 0)) ? 1 : 0);
 314: }
 315: 
 316: // v1 xor v2
 317: static void int_xor_op (int v1, int v2,
 318:     int *result, status_t *)
 319: {
 320:     *result = (((v1 != 0) ^ (v2 != 0)) ? 1 : 0);
 321: }
 322: 
 323: // v1 <= v2
 324: static void int_le_op (int v1, int v2,
 325:     int *result, status_t *)
 326: {
 327:     *result = ((v1 <= v2) ? 1 : 0);
 328: }
 329: 
 330: static void double_le_op (double v1, double v2,
 331:     int *result, status_t *)
 332: {
 333:     *result = ((v1 <= v2) ? 1 : 0);
 334: }
 335: 
 336: static void strint_le_op (char *v1, char *v2,
 337:     int *result, status_t *)
 338: {
 339:     *result = (strcmp (v1, v2) <= 0 ? 1 : 0);
 340: }
 341: 
 342: // v1 < v2
 343: static void int_lt_op (int v1, int v2,
 344:     int *result, status_t *)
 345: {
 346:     *result = ((v1 < v2) ? 1 : 0);
 347: }
 348: 
 349: static void double_lt_op (double v1, double v2,
 350:     int *result, status_t *)
 351: {
 352:     *result = ((v1 < v2) ? 1 : 0);
 353: }
 354: 
 355: static void strint_lt_op (char *v1, char *v2,
 356:     int *result, status_t *)
 357: {
 358:     *result = (strcmp (v1, v2) < 0 ? 1 : 0);
 359: }
 360: 
 361: // v1 = v2
 362: static void int_eq_op (int v1, int v2,
 363:     int *result, status_t *)
 364: {
 365:     *result = ((v1 == v2) ? 1 : 0);
 366: }
 367: 
 368: static void double_eq_op (double v1, double v2,
 369:     int *result, status_t *)
 370: {
 371:     *result = ((v1 == v2) ? 1 : 0);
 372: }
 373: 
 374: static void strint_eq_op (char *v1, char *v2,
 375:     int *result, status_t *)
 376: {
 377:     *result = (strcmp (v1, v2) == 0 ? 1 : 0);
 378: }
 379: 
 380: // v1 <> v2
 381: static void int_ne_op (int v1, int v2,
 382:     int *result, status_t *)
 383: {
 384:     *result = ((v1 != v2) ? 1 : 0);
 385: }
 386: 
 387: static void double_ne_op (double v1, double v2,
 388:     int *result, status_t *)
 389: {
 390:     *result = ((v1 != v2) ? 1 : 0);
 391: }
 392: 
 393: static void strint_ne_op (char *v1, char *v2,
 394:     int *result, status_t *)
 395: {
 396:     *result = (strcmp (v1, v2) != 0 ? 1 : 0);
 397: }
 398: 
 399: // v1 >= v2
 400: static void int_ge_op (int v1, int v2,
 401:     int *result, status_t *)
 402: {
 403:     *result = ((v1 >= v2) ? 1 : 0);
 404: }
 405: 
 406: static void double_ge_op (double v1, double v2,
 407:     int *result, status_t *)
 408: {
 409:     *result = ((v1 >= v2) ? 1 : 0);
 410: }
 411: 
 412: static void strint_ge_op (char *v1, char *v2,
 413:     int *result, status_t *)
 414: {
 415:     *result = (strcmp (v1, v2) >= 0 ? 1 : 0);
 416: }
 417: 
 418: // v1 > v2
 419: static void int_gt_op (int v1, int v2,
 420:     int *result, status_t *)
 421: {
 422:     *result = ((v1 > v2) ? 1 : 0);
 423: }
 424: 
 425: static void double_gt_op (double v1, double v2,
 426:     int *result, status_t *)
 427: {
 428:     *result = ((v1 > v2) ? 1 : 0);
 429: }
 430: 
 431: static void strint_gt_op (char *v1, char *v2,
 432:     int *result, status_t *)
 433: {
 434:     *result = (strcmp (v1, v2) > 0 ? 1 : 0);
 435: }
 436: 
 437: ////////////////////////////////////
 438: // Add the default monops and binops
 439: ////////////////////////////////////
 440: 
 441: zerop_t *transform_c::add_zerop (char *oper, int_zerop_fn int_op,
 442:         double_zerop_fn double_op, datetime_zerop_fn datetime_op)
 443: {
 444:     zerop_t *zerop;
 445: 
 446:     zerop = GetMem (zerop_t, 1);
 447:     if (!zerop) return NULL;
 448: 
 449:     zerop->name = DupStr (oper);
 450:     zerop->int_fn = int_op;
 451:     zerop->double_fn = double_op;
 452:     zerop->datetime_fn = datetime_op;
 453:     zerop->next = NULL;
 454: 
 455:     if (this->last_zerop == NULL) this->first_zerop = zerop;
 456:     else this->last_zerop->next = zerop;
 457:     this->last_zerop = zerop;
 458: 
 459:     return zerop;
 460: }
 461: 
 462: monop_t *transform_c::add_monop (char *oper, int_monop_fn int_op,
 463:         double_monop_fn double_op)
 464: {
 465:     monop_t *monop;
 466: 
 467:     monop = GetMem (monop_t, 1);
 468:     if (!monop) return NULL;
 469: 
 470:     monop->name = DupStr (oper);
 471:     monop->int_fn = int_op;
 472:     monop->double_fn = double_op;
 473:     monop->deriv = NULL;
 474:     monop->next = NULL;
 475: 
 476:     if (this->last_monop == NULL) this->first_monop = monop;
 477:     else this->last_monop->next = monop;
 478:     this->last_monop = monop;
 479: 
 480:     return monop;
 481: }
 482: 
 483: binop_t *transform_c::add_binop (char *oper, oper_prec_e prec,
 484:         int_binop_fn int_op,
 485:         double_binop_fn double_op, dblint_binop_fn dblint_op,
 486:         strstr_binop_fn strstr_op, strint_binop_fn strint_op)
 487: {
 488:     binop_t *binop;
 489: 
 490:     binop = GetMem (binop_t, 1);
 491:     if (!binop) return NULL;
 492: 
 493:     binop->name = DupStr (oper);
 494:     binop->prec = prec;
 495:     binop->int_fn = int_op;
 496:     binop->double_fn = double_op;
 497:     binop->dblint_fn = dblint_op;
 498:     binop->strstr_fn = strstr_op;
 499:     binop->strint_fn = strint_op;
 500:     binop->deriv = NULL;
 501:     binop->next = NULL;
 502: 
 503:     if (this->last_binop == NULL) this->first_binop = binop;
 504:     else this->last_binop->next = binop;
 505:     this->last_binop = binop;
 506: 
 507:     return binop;
 508: }
 509: 
 510: void transform_c::add_operators ()
 511: {
 512:     this->first_zerop = NULL;
 513:     this->last_zerop = NULL;
 514:     this->first_monop = NULL;
 515:     this->last_monop = NULL;
 516:     this->first_binop = NULL;
 517:     this->last_binop = NULL;
 518: 
 519:     this->add_zerop ("blank", blank_sysvar, NULL, NULL);
 520:     this->add_zerop ("error", error_sysvar, NULL, NULL);
 521:     this->add_zerop ("e", NULL, e_sysvar, NULL);
 522:     this->add_zerop ("pi", NULL, pi_sysvar, NULL);
 523:     this->add_zerop ("now", NULL, NULL, now_sysvar);
 524: 
 525:     this->add_monop ("+", int_pos_op, double_pos_op);
 526:     this->add_monop ("-", int_neg_op, double_neg_op);
 527:     this->add_monop ("not", int_not_op, NULL);
 528:     this->add_monop ("sqrt", NULL, double_sqrt_op);
 529:     this->add_monop ("sin", NULL, double_sin_op);
 530:     this->add_monop ("cos", NULL, double_cos_op);
 531:     this->add_monop ("tan", NULL, double_tan_op);
 532:     this->add_monop ("atan", NULL, double_atan_op);
 533:     this->add_monop ("sind", NULL, double_sind_op);
 534:     this->add_monop ("cosd", NULL, double_cosd_op);
 535:     this->add_monop ("tand", NULL, double_tand_op);
 536:     this->add_monop ("atand", NULL, double_atand_op);
 537:     this->add_monop ("ln", NULL, double_ln_op);
 538:     this->add_monop ("log10", NULL, double_log10_op);
 539:     this->add_monop ("exp", NULL, double_exp_op);
 540: 
 541:     this->add_binop ("+", PLUS_PREC, int_plus_op, double_plus_op, NULL, strstr_plus_op, NULL);
 542:     this->add_binop ("-", PLUS_PREC, int_minus_op, double_minus_op, NULL, NULL, NULL);
 543:     this->add_binop ("*", TIMES_PREC, int_times_op, double_times_op, NULL, NULL, NULL);
 544:     this->add_binop ("/", TIMES_PREC, NULL, double_divide_op, NULL, NULL, NULL);
 545:     this->add_binop ("^", POWER_PREC, NULL, double_power_op, NULL, NULL, NULL);
 546:     this->add_binop ("mod", TIMES_PREC, NULL, double_mod_op, NULL, NULL, NULL);
 547:     this->add_binop ("and", AND_PREC, int_and_op, NULL, NULL, NULL, NULL);
 548:     this->add_binop ("or", OR_PREC, int_or_op, NULL, NULL, NULL, NULL);
 549:     this->add_binop ("xor", OR_PREC, int_xor_op, NULL, NULL, NULL, NULL);
 550:     this->add_binop ("=", REL_PREC, int_eq_op, NULL, double_eq_op, NULL, strint_eq_op);
 551:     this->add_binop ("<>", REL_PREC, int_ne_op, NULL, double_ne_op, NULL, strint_ne_op);
 552:     this->add_binop ("<=", REL_PREC, int_le_op, NULL, double_le_op, NULL, strint_le_op);
 553:     this->add_binop ("<", REL_PREC, int_lt_op, NULL, double_lt_op, NULL, strint_lt_op);
 554:     this->add_binop (">=", REL_PREC, int_ge_op, NULL, double_ge_op, NULL, strint_ge_op);
 555:     this->add_binop (">", REL_PREC, int_gt_op, NULL, double_gt_op, NULL, strint_gt_op);
 556: }
 557: 
 558: void transform_c::remove_operators ()
 559: {
 560:     zerop_t *zerop, *nz=NULL;
 561:     monop_t *monop, *nm=NULL;
 562:     binop_t *binop, *nb=NULL;
 563: 
 564:     for (zerop=this->first_zerop; zerop!=NULL; zerop=nz)
 565:     {
 566:         FreeMem (zerop->name);
 567:         nz = zerop->next;
 568:         FreeMem (zerop);
 569:     }
 570: 
 571:     for (monop=this->first_monop; monop!=NULL; monop=nm)
 572:     {
 573:         FreeMem (monop->name);
 574:         if (monop->deriv) this->free_expr (monop->deriv);
 575:         nm = monop->next;
 576:         FreeMem (monop);
 577:     }
 578: 
 579:     for (binop=this->first_binop; binop!=NULL; binop=nb)
 580:     {
 581:         FreeMem (binop->name);
 582:         if (binop->deriv) this->free_expr (binop->deriv);
 583:         nb = binop->next;
 584:         FreeMem (binop);
 585:     }
 586: }
 587: 

transform_derivative.cpp - Takes Symbolic Derviatives

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: 
   5: #include "memory.hpp"
   6: #include "transform.hpp"
   7: 
   8: static expression_t *copy_expr (expression_t *u, expression_t *du,
   9:     expression_t *v, expression_t *dv, expression_t *expr)
  10: {
  11:     expression_t *result;
  12:     expression_t *which;
  13: 
  14:     if (expr == NULL) return NULL;
  15: 
  16:     result = NULL;
  17:     switch (expr->expr_type)
  18:     {
  19:         case EXPR_CONST:
  20:             result = GetMem (expression_t, 1);
  21:             result->expr_type = EXPR_CONST;
  22:             copy_value (&(result->u.value), &(expr->u.value));
  23:             break;
  24: 
  25:         case EXPR_DERIV_SYMBOL:
  26:             result = GetMem (expression_t, 1);
  27:             result->expr_type = EXPR_DERIV_SYMBOL;
  28:             result->u.symbol = expr->u.symbol;
  29:             break;
  30: 
  31:         case EXPR_SYMBOL:
  32:             if (u == NULL)
  33:             {
  34:                 // If just copying a sub-expression
  35:                 result = GetMem (expression_t, 1);
  36:                 result->expr_type = EXPR_SYMBOL;
  37:                 result->u.symbol = expr->u.symbol;
  38:                 break;
  39:             }
  40: 
  41:             // Must be a deriv expression
  42:             if (strcmp (expr->u.symbol->name, "u") == 0) which = u;
  43:             else if (strcmp (expr->u.symbol->name, "v") == 0) which = v;
  44:             else if (strcmp (expr->u.symbol->name, "du") == 0) which = du;
  45:             else if (strcmp (expr->u.symbol->name, "dv") == 0) which = dv;
  46:             else return NULL;
  47:             result = copy_expr (NULL, NULL, NULL, NULL, which);
  48:             break;
  49: 
  50:         case EXPR_MONOP:
  51:             result = GetMem (expression_t, 1);
  52:             result->expr_type = EXPR_MONOP;
  53:             result->u.one_arg.monop = expr->u.one_arg.monop;
  54:             result->u.one_arg.the_arg = copy_expr (u, du, v, dv,
  55:                 expr->u.one_arg.the_arg);
  56:             break;
  57: 
  58:         case EXPR_BINOP:
  59:             result = GetMem (expression_t, 1);
  60:             result->expr_type = EXPR_BINOP;
  61:             result->u.two_args.binop = expr->u.two_args.binop;
  62:             result->u.two_args.left_arg = copy_expr (u, du, v, dv,
  63:                 expr->u.two_args.left_arg);
  64:             result->u.two_args.right_arg = copy_expr (u, du, v, dv,
  65:                 expr->u.two_args.right_arg);
  66:             break;
  67: 
  68:         default:
  69:             break;
  70:     }
  71:     return result;
  72: }
  73: 
  74: expression_t *transform_c::deriv_expr (symbols_c *symbols,
  75:     expression_t *expr)
  76: {
  77:     expression_t *result;
  78:     expression_t *u, *v;
  79:     expression_t *du, *dv;
  80:     int more;
  81: 
  82:     if (!symbols || !expr) return NULL;
  83: 
  84:     result = NULL;
  85:     switch (expr->expr_type)
  86:     {
  87:         case EXPR_CONST:
  88:         case EXPR_ZEROP:
  89:             result = GetMem (expression_t, 1);
  90:             result->expr_type = EXPR_CONST;
  91:             result->u.value.typ = INT_TYP;
  92:             result->u.value.u.i = 0;
  93:             break;
  94: 
  95:         case EXPR_SYMBOL:
  96:             result = GetMem (expression_t, 1);
  97:             result->expr_type = EXPR_DERIV_SYMBOL;
  98:             result->u.symbol = expr->u.symbol;
  99:             break;
 100: 
 101:         case EXPR_MONOP:
 102:             if (expr->u.one_arg.monop->deriv == NULL)
 103:             {
 104:                 return NULL;        // this monop not differentiable
 105:             }
 106: 
 107:             u = expr->u.one_arg.the_arg;
 108:             du = this->deriv_expr (symbols, u);
 109:             
 110:             result = copy_expr (u, du, NULL, NULL, expr->u.one_arg.monop->deriv);
 111: 
 112:             this->free_expr (du);
 113:             break;
 114: 
 115:         case EXPR_BINOP:
 116:             if (expr->u.two_args.binop->deriv == NULL)
 117:             {
 118:                 return NULL;        // this binop not differentiable
 119:             }
 120: 
 121:             u = expr->u.two_args.left_arg;
 122:             v = expr->u.two_args.right_arg;
 123:             du = this->deriv_expr (symbols, u);
 124:             dv = this->deriv_expr (symbols, v);
 125: 
 126:             result = copy_expr (u, du, v, dv, expr->u.two_args.binop->deriv);
 127: 
 128:             this->free_expr (du);
 129:             this->free_expr (dv);
 130:             break;
 131: 
 132:         case EXPR_FUNC:                    // Can't take derivatives of these
 133:             goto failed;
 134: 
 135:         default :
 136:             goto failed;
 137:     }
 138: 
 139:     // Tree completely built and functional
 140:     // Can I simplify it at all??
 141:     do
 142:     {
 143:         more = 0;
 144:         this->simplify (result, &more);
 145:     } while (more);
 146: 
 147:     return result;
 148: 
 149: failed:
 150:     this->free_expr (result);
 151:     return NULL;
 152: }
 153: 
 154: static int same_sub_tree (expression_t *expr, expression_t *pattern)
 155: {
 156:     double x;
 157: 
 158:     switch (pattern->expr_type)
 159:     {
 160:         case EXPR_CONST :
 161:             // Match type first
 162:             if (pattern->u.value.typ != INT_TYP) return 0;
 163: 
 164:             // Now match value
 165:             if (expr->u.value.typ == INT_TYP) x = (double) expr->u.value.u.i;
 166:             else if (expr->u.value.typ == DOUBLE_TYP) x = expr->u.value.u.x;
 167:             else return 0;
 168:             if ((double) pattern->u.value.u.i != x) return 0;
 169:             return 1;        // Matched!
 170: 
 171:         case EXPR_SYMBOL :
 172:             // "u" matches almost anything
 173:             if (expr->expr_type == EXPR_CONST) return 0;
 174:             return 1;
 175: 
 176:         default :
 177:             return 0;
 178:     }
 179: }
 180: 
 181: static int try_simplification_rule (expression_t *expr,
 182:     expression_t *pattern)
 183: {
 184:     if (pattern->expr_type != EXPR_BINOP) return 0;
 185:     if (pattern->u.two_args.binop != expr->u.two_args.binop) return 0;
 186: 
 187:     // First check left side for a match
 188:     if (!same_sub_tree (expr->u.two_args.left_arg,
 189:         pattern->u.two_args.left_arg))
 190:     {
 191:         return 0;
 192:     }
 193: 
 194:     // Now check right side for a match
 195:     if (!same_sub_tree (expr->u.two_args.right_arg,
 196:         pattern->u.two_args.right_arg))
 197:     {
 198:         return 0;
 199:     }
 200: 
 201:     // TODO: If both sides were "u", make sure same subtree
 202:     if (pattern->u.two_args.left_arg->expr_type == EXPR_SYMBOL &&
 203:         pattern->u.two_args.right_arg->expr_type == EXPR_SYMBOL)
 204:     {
 205:         return 0;   // For now
 206:     }
 207: 
 208:     return 1;
 209: }
 210: 
 211: int transform_c::simplify (expression_t *expr, int *more)
 212: {
 213:     int i;
 214:     evalue_t val;
 215:     simplifier_t *simp;
 216:     expression_t *lhs, *rhs;
 217:     expression_t *temp, *u;
 218: 
 219:     if (!expr) return 0;
 220: 
 221:     switch (expr->expr_type)
 222:     {
 223:         case EXPR_CONST:
 224:         case EXPR_ZEROP:
 225:         case EXPR_DERIV_SYMBOL:
 226:         case EXPR_SYMBOL:
 227:             break;        // Can't do anything here
 228: 
 229:         case EXPR_MONOP:
 230:             // First, see if it is a monop of a constant
 231:             if (expr->u.one_arg.the_arg->expr_type == EXPR_CONST)
 232:             {
 233:                 if (this->eval_expr (NULL, expr, &val))
 234:                 {
 235:                     this->free_expr (expr->u.one_arg.the_arg);
 236:                     expr->expr_type = EXPR_CONST;
 237:                     copy_value (&(expr->u.value), &val);
 238:                     *more = 1;
 239:                     return 1;
 240:                 }
 241:             }
 242: 
 243:             // No monop simplifications yet ...
 244: 
 245:             if (this->simplify (expr->u.one_arg.the_arg, more)) return 1;
 246:             break;
 247: 
 248:         case EXPR_BINOP:
 249:             // First, see if it is a binop of 2 constants
 250:             if (expr->u.two_args.left_arg->expr_type == EXPR_CONST &&
 251:                 expr->u.two_args.right_arg->expr_type == EXPR_CONST)
 252:             {
 253:                 if (this->eval_expr (NULL, expr, &val))
 254:                 {
 255:                     this->free_expr (expr->u.two_args.left_arg);
 256:                     this->free_expr (expr->u.two_args.right_arg);
 257:                     expr->expr_type = EXPR_CONST;
 258:                     copy_value (&(expr->u.value), &val);
 259:                     *more = 1;
 260:                     return 1;
 261:                 }
 262:             }
 263: 
 264:             // See if any rules match for simplification
 265:             for (simp=this->first_simplifier; simp!=NULL; simp=simp->next)
 266:             {
 267:                 if (try_simplification_rule (expr, simp->pexpr))
 268:                 {
 269:                     lhs = expr->u.two_args.left_arg;
 270:                     rhs = expr->u.two_args.right_arg;
 271:                     if (lhs->expr_type == EXPR_CONST) u = rhs;
 272:                     else if (rhs->expr_type == EXPR_CONST) u = lhs;
 273:                     else u = NULL;
 274: 
 275:                     // Replace it!
 276:                     temp = copy_expr (u, NULL, NULL, NULL, simp->rexpr);
 277:                     memcpy ((void *)expr, (void *)temp, sizeof(expression_t));
 278:                     FreeMem (temp);
 279: 
 280:                     // Toss old stuff
 281:                     this->free_expr (lhs);
 282:                     this->free_expr (rhs);
 283:                     *more = 1;
 284:                     return 1;
 285:                 }
 286:             }
 287: 
 288:             if (this->simplify (expr->u.two_args.left_arg, more)) return 1;
 289:             if (this->simplify (expr->u.two_args.right_arg, more)) return 1;
 290:             break;
 291: 
 292:         case EXPR_FUNC:
 293:             for (i=0; i<expr->u.many_args.num_args; i++)
 294:             {
 295:                 if (this->simplify (expr->u.many_args.func_args[i].the_arg,
 296:                     more))
 297:                 {
 298:                     return 1;
 299:                 }
 300:             }
 301:             break;
 302: 
 303:         default :
 304:             break;
 305:     }
 306: 
 307:     return 0;        // Did nothing
 308: }
 309: 
 310: void transform_c::create_uv_symbols ()
 311: {
 312:     if (this->created_uv_symbols) return;
 313: 
 314:     this->uv_symbols.add_symbol ("u");
 315:     this->uv_symbols.add_symbol ("v");
 316:     this->uv_symbols.add_symbol ("du");
 317:     this->uv_symbols.add_symbol ("dv");
 318:     this->created_uv_symbols = 1;
 319: }
 320: 
 321: int transform_c::eval_deriv (symbols_c *symbols, symbol_t *wrt,
 322:     expression_t *expr, evalue_t *value)
 323: {
 324:     int ret;
 325: 
 326:     symbols->deriv_symbol = wrt;
 327:     ret = this->eval_expr (symbols, expr, value);
 328:     symbols->deriv_symbol = NULL;
 329: 
 330:     return ret;
 331: }
 332: 
 333: int transform_c::add_monop_deriv (char *oper, char *derivative)
 334: {
 335:     monop_t *monop;
 336:     char *emsg, *errpos;
 337: 
 338:     if (!oper || !derivative) return 0;
 339:     monop = this->parse_monop (oper);
 340:     if (!monop) return 0;
 341: 
 342:     this->create_uv_symbols ();
 343: 
 344:     monop->deriv = this->parse_expr (&(this->uv_symbols), derivative, &emsg, &errpos);
 345:     if (monop->deriv == NULL)
 346:     {
 347:         fprintf (stderr, "\nError setting up derivative for %s\n", oper);
 348:         fprintf (stderr, "  Unable to parse %s\n", derivative);
 349:         if (emsg) fprintf (stderr, "  %s\n", emsg);
 350:         if (errpos) fprintf (stderr, "  Position: %s\n", errpos);
 351:         return 0;
 352:     }
 353: 
 354:     return 1;
 355: }
 356: 
 357: int transform_c::add_binop_deriv (char *oper, char *derivative)
 358: {
 359:     binop_t *binop;
 360:     char *emsg, *errpos;
 361: 
 362:     if (!oper || !derivative) return 0;
 363:     binop = this->parse_binop (oper);
 364:     if (!binop) return 0;
 365: 
 366:     this->create_uv_symbols ();
 367: 
 368:     binop->deriv = this->parse_expr (&(this->uv_symbols), derivative, &emsg, &errpos);
 369:     if (binop->deriv == NULL)
 370:     {
 371:         fprintf (stderr, "\nError setting up derivative for %s\n", oper);
 372:         fprintf (stderr, "  Unable to parse %s\n", derivative);
 373:         if (emsg) fprintf (stderr, "  %s\n", emsg);
 374:         if (errpos) fprintf (stderr, "  Position: %s\n", errpos);
 375:         return 0;
 376:     }
 377: 
 378:     return 1;
 379: }
 380: 
 381: int transform_c::add_simplifier (char *pattern, char *replacement)
 382: {
 383:     char *emsg, *errpos;
 384:     expression_t *pexpr, *rexpr;
 385:     simplifier_t *simp;
 386: 
 387:     this->create_uv_symbols ();
 388: 
 389:     pexpr = this->parse_expr (&(this->uv_symbols), pattern, &emsg, &errpos);
 390:     if (pexpr == NULL)
 391:     {
 392:         fprintf (stderr, "\nError with simplification term\n");
 393:         fprintf (stderr, "  Unable to parse %s\n", pattern);
 394:         if (emsg) fprintf (stderr, "  %s\n", emsg);
 395:         if (errpos) fprintf (stderr, "  Position: %s\n", errpos);
 396:         return 0;
 397:     }
 398: 
 399:     rexpr = this->parse_expr (&(this->uv_symbols), replacement, &emsg, &errpos);
 400:     if (rexpr == NULL)
 401:     {
 402:         fprintf (stderr, "\nError with simplification term\n");
 403:         fprintf (stderr, "  Unable to parse %s\n", replacement);
 404:         if (emsg) fprintf (stderr, "  %s\n", emsg);
 405:         if (errpos) fprintf (stderr, "  Position: %s\n", errpos);
 406:         this->free_expr (pexpr);
 407:         return 0;
 408:     }
 409: 
 410:     simp = GetMem (simplifier_t, 1);
 411:     simp->pexpr = pexpr;
 412:     simp->rexpr = rexpr;
 413:     simp->next = NULL;
 414:     if (this->last_simplifier == NULL) this->first_simplifier = simp;
 415:     else this->last_simplifier->next = simp;
 416:     this->last_simplifier = simp;
 417: 
 418:     return 1;
 419: }
 420: 
 421: void transform_c::add_derivatives ()
 422: {
 423:     this->add_monop_deriv ("+", "du");
 424:     this->add_monop_deriv ("-", "-du");
 425:     this->add_monop_deriv ("sqrt", "(du/2) / sqrt(u)");
 426:     this->add_monop_deriv ("sin", "cos(u) * du");
 427:     this->add_monop_deriv ("cos", "-sin(u) * du");
 428:     this->add_monop_deriv ("ln", "du / u");
 429:     
 430:     this->add_binop_deriv ("+", "du + dv");
 431:     this->add_binop_deriv ("-", "du - dv");
 432:     this->add_binop_deriv ("*", "u*dv + v*du");
 433:     this->add_binop_deriv ("/", "(du - (u*dv) / v) / v");
 434:     this->add_binop_deriv ("^", "v*u^(v-1)*du + u*ln(u)*u^(v-1)*dv");
 435: 
 436:     this->first_simplifier = NULL;
 437:     this->last_simplifier = NULL;
 438: 
 439:     this->add_simplifier ("u+0", "u");
 440:     this->add_simplifier ("0+u", "u");
 441:     this->add_simplifier ("u+u", "2*u");
 442:     this->add_simplifier ("u-0", "u");
 443:     this->add_simplifier ("0-u", "-u");
 444:     this->add_simplifier ("u-u", "0");
 445:     this->add_simplifier ("u*0", "0");
 446:     this->add_simplifier ("0*u", "0");
 447:     this->add_simplifier ("1*u", "u");
 448:     this->add_simplifier ("u*1", "u");
 449:     this->add_simplifier ("u*u", "u^2");
 450:     this->add_simplifier ("0/u", "0");
 451:     this->add_simplifier ("u/1", "u");
 452:     this->add_simplifier ("u/u", "1");
 453:     this->add_simplifier ("u^1", "u");
 454:     this->add_simplifier ("u^0", "1");
 455:     this->add_simplifier ("1^u", "1");
 456:     this->add_simplifier ("0^u", "0");
 457: }
 458: 
 459: void transform_c::remove_simplifiers ()
 460: {
 461:     simplifier_t *simp, *nxt;
 462: 
 463:     nxt = NULL;
 464:     for (simp=this->first_simplifier; simp!=NULL; simp=nxt)
 465:     {
 466:         nxt = simp->next;
 467:         this->free_expr (simp->pexpr);
 468:         this->free_expr (simp->rexpr);
 469:         FreeMem (simp);
 470:     }
 471: 
 472:     this->first_simplifier = NULL;
 473:     this->last_simplifier = NULL;
 474: }
 475: 

transform_functions.cpp - Arithmetic Functions

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: 
   5: #include "memory.hpp"
   6: #include "datetime.hpp"
   7: #include "transform.hpp"
   8: 
   9: 
  10: ////////////////////////////////////
  11: // Shared routines, for convenience
  12: ////////////////////////////////////
  13: 
  14: static int mondayear (datetime_c *dt, evalue_t *dat, evalue_t *result,
  15:     int *y, int *m, int *d, int *yd, int *wd)
  16: {
  17:     if (dat->typ != DATETIME_TYP) return 0;
  18: 
  19:     result->typ = INT_TYP;
  20:     dt->get_date (dat->u.dt, y, m, d, yd, wd);
  21:     result->status = dat->status;
  22: 
  23:     return 1;
  24: }
  25: 
  26: static int hrminsec (datetime_c *dt, evalue_t *dat, evalue_t *result,
  27:     int *h, int *m, int *s, double *ms)
  28: {
  29:     if (dat->typ != DATETIME_TYP) return 0;
  30: 
  31:     result->typ = INT_TYP;
  32:     dt->get_time (dat->u.dt, h, m, s, ms);
  33:     result->status = dat->status;
  34: 
  35:     return 1;
  36: }
  37: 
  38: static void simple_free_state (void *state)
  39: {
  40:     FreeMem (state);
  41: }
  42: 
  43: ////////////////////////////////////
  44: // Start functions
  45: ////////////////////////////////////
  46: 
  47: // len(str)
  48: int len_fn (int, func_arg_t *args, evalue_t *result)
  49: {
  50:     if (args[0].the_value.typ != STRING_TYP) return 0;
  51: 
  52:     result->typ = INT_TYP;
  53:     result->u.i = strlen (args[0].the_value.u.v.str);
  54:     result->status = args[0].the_value.status;
  55: 
  56:     return 1;
  57: }
  58: 
  59: // if(cond,true,false)
  60: int if_fn (int, func_arg_t *args, evalue_t *result)
  61: {
  62:     int which;
  63: 
  64:     if (args[0].the_value.typ != INT_TYP) return 0;
  65:     which = (args[0].the_value.u.i != 0) ? 1 : 2;
  66: 
  67:     copy_value (result, &(args[which].the_value));
  68:     result->status |= args[0].the_value.status;
  69: 
  70:     return 1;
  71: }
  72: 
  73: // month(date)
  74: int month_fn (datetime_c *dt, int, func_arg_t *args, evalue_t *result)
  75: {
  76:     return mondayear (dt, &(args[0].the_value), result,
  77:         NULL, &(result->u.i), NULL, NULL, NULL);
  78: }
  79: 
  80: // day(date)
  81: int day_fn (datetime_c *dt, int, func_arg_t *args, evalue_t *result)
  82: {
  83:     return mondayear (dt, &(args[0].the_value), result,
  84:         NULL, NULL, &(result->u.i), NULL, NULL);
  85: }
  86: 
  87: // year(date)
  88: int year_fn (datetime_c *dt, int, func_arg_t *args, evalue_t *result)
  89: {
  90:     return mondayear (dt, &(args[0].the_value), result,
  91:         &(result->u.i), NULL, NULL, NULL, NULL);
  92: }
  93: 
  94: // hour(time)
  95: int hour_fn (datetime_c *dt, int, func_arg_t *args, evalue_t *result)
  96: {
  97:     return hrminsec (dt, &(args[0].the_value), result,
  98:         &(result->u.i), NULL, NULL, NULL);
  99: }
 100: 
 101: // minute(time)
 102: int minute_fn (datetime_c *dt, int, func_arg_t *args, evalue_t *result)
 103: {
 104:     return hrminsec (dt, &(args[0].the_value), result,
 105:         NULL, &(result->u.i), NULL, NULL);
 106: }
 107: 
 108: // second(time)
 109: int second_fn (datetime_c *dt, int, func_arg_t *args, evalue_t *result)
 110: {
 111:     return hrminsec (dt, &(args[0].the_value), result,
 112:         NULL, NULL, &(result->u.i), NULL);
 113: }
 114: 
 115: // delta(x [,rows])
 116: int delta_fn (free_state_fn *fp, void **state,
 117:     int nargs, func_arg_t *args, evalue_t *result)
 118: {
 119:     int nrows;
 120:     int i;
 121:     double x, *prev;
 122: 
 123:     if (nargs <= 1) nrows = 2;
 124:     else
 125:     {
 126:         if (args[1].the_value.typ != INT_TYP) return 0;
 127:         nrows = args[1].the_value.u.i;
 128:         if (nrows < 2 || nrows > 100000)
 129:         {
 130:             return 0;
 131:         }
 132:     }
 133: 
 134:     // Type check input variable
 135:     // TODO: allow int, string, datetime, etc.
 136:     if (args[0].the_value.typ != DOUBLE_TYP) return 0;
 137:     x = args[0].the_value.u.x;
 138: 
 139:     // First time here?
 140:     if (*state == NULL)
 141:     {
 142:         prev = GetMem (double, nrows);
 143:         for (i=0; i<nrows; i++) prev[i] = x;
 144: 
 145:         *fp = simple_free_state;
 146:         *state = prev;
 147:     }
 148: 
 149:     prev = (double *) *state;
 150:     result->status = STATUS_OK;
 151:     result->typ = DOUBLE_TYP;
 152:     result->u.x = x - prev[nrows-2];
 153: 
 154:     for (i=nrows-1; i>0; i--)
 155:     {
 156:         prev[i] = prev[i-1];
 157:     }
 158:     prev[0] = x;
 159: 
 160:     return 1;
 161: }
 162: 
 163: ////////////////////////////////////
 164: // Add in functions
 165: ////////////////////////////////////
 166: 
 167: static function_t *add_a_func (function_t **first_func, function_t **last_func,
 168:     char *func_name, int min_args, int max_args, function_fn func_op,
 169:     dt_function_fn dt_func_op, state_function_fn state_func_op)
 170: {
 171:     function_t *funcp;
 172: 
 173:     funcp = GetMem (function_t, 1);
 174:     if (!funcp) return NULL;
 175: 
 176:     funcp->name = DupStr (func_name);
 177:     funcp->min_args = min_args;
 178:     funcp->max_args = max_args;
 179:     funcp->func_fn = func_op;
 180:     funcp->dt_func_fn = dt_func_op;
 181:     funcp->state_func_fn = state_func_op;
 182:     funcp->next = NULL;
 183: 
 184:     if (*last_func == NULL) *first_func = funcp;
 185:     else (*last_func)->next = funcp;
 186:     *last_func = funcp;
 187: 
 188:     return funcp;
 189: }
 190: 
 191: function_t *transform_c::add_func (char *func_name, int min_args, int max_args,
 192:     function_fn func_op)
 193: {
 194:     return add_a_func (&(this->first_func), &(this->last_func),
 195:         func_name, min_args, max_args, func_op, NULL, NULL);
 196: }
 197: 
 198: function_t *transform_c::add_dt_func (char *func_name, int min_args, int max_args,
 199:     dt_function_fn dt_func_op)
 200: {
 201:     return add_a_func (&(this->first_func), &(this->last_func),
 202:         func_name, min_args, max_args, NULL, dt_func_op, NULL);
 203: }
 204: 
 205: function_t *transform_c::add_state_func (char *func_name, int min_args, int max_args,
 206:     state_function_fn state_func_op)
 207: {
 208:     return add_a_func (&(this->first_func), &(this->last_func),
 209:         func_name, min_args, max_args, NULL, NULL, state_func_op);
 210: }
 211: 
 212: void transform_c::add_functions ()
 213: {
 214:     this->first_func = NULL;
 215:     this->last_func = NULL;
 216: 
 217:     this->add_func ("len", 1, 1, len_fn);
 218:     this->add_func ("if", 3, 3, if_fn);
 219: 
 220:     this->add_dt_func ("month", 1, 1, month_fn);
 221:     this->add_dt_func ("day", 1, 1, day_fn);
 222:     this->add_dt_func ("year", 1, 1, year_fn);
 223:     this->add_dt_func ("hour", 1, 1, hour_fn);
 224:     this->add_dt_func ("minute", 1, 1, minute_fn);
 225:     this->add_dt_func ("second", 1, 1, second_fn);
 226: 
 227:     this->add_state_func ("delta", 1, 2, delta_fn);
 228: }
 229: 
 230: void transform_c::remove_functions ()
 231: {
 232:     function_t *funcp, *nf=NULL;
 233: 
 234:     for (funcp=this->first_func; funcp!=NULL; funcp=nf)
 235:     {
 236:         FreeMem (funcp->name);
 237:         nf = funcp->next;
 238:         FreeMem (funcp);
 239:     }
 240: }
 241: 

symbols.cpp - Symbol Table

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: #include <ctype.h>
   5: 
   6: #include "memory.hpp"
   7: #include "symbols.hpp"
   8: 
   9: symbols_c::symbols_c ()
  10: {
  11:     this->first_symbol = NULL;
  12:     this->last_symbol = NULL;
  13:     this->deriv_symbol = NULL;
  14: }
  15: 
  16: symbols_c::~symbols_c ()
  17: {
  18:     symbol_t *symbol, *sp=NULL;
  19: 
  20:     for (symbol=this->first_symbol; symbol!=NULL; symbol=sp)
  21:     {
  22:         FreeMem (symbol->name);
  23:         sp = symbol->next;
  24:         FreeMem (symbol);
  25:     }
  26: }
  27: 
  28: symbol_t *symbols_c::add_symbol (char *name)
  29: {
  30:     symbol_t *symbol;
  31: 
  32:     if (!name) return NULL;
  33: 
  34:     symbol = GetMem (symbol_t, 1);
  35:     if (!symbol) return NULL;
  36: 
  37:     symbol->name = DupStr (name);
  38:     symbol->next = NULL;
  39: 
  40:     if (this->last_symbol == NULL) this->first_symbol = symbol;
  41:     else this->last_symbol->next = symbol;
  42:     this->last_symbol = symbol;
  43: 
  44:     return symbol;
  45: }
  46: 
  47: int copy_value (evalue_t *toval, evalue_t *fromval)
  48: {
  49:     if (!toval || !fromval) return 0;
  50: 
  51:     toval->status = fromval->status;
  52:     toval->typ = fromval->typ;
  53:     switch (fromval->typ)
  54:     {
  55:         case INT_TYP:
  56:             toval->u.i = fromval->u.i;
  57:             break;
  58: 
  59:         case DOUBLE_TYP:
  60:             toval->u.x = fromval->u.x;
  61:             break;
  62: 
  63:         case DATETIME_TYP:
  64:             toval->u.dt = fromval->u.dt;
  65:             break;
  66: 
  67:         default:
  68:             return 0;
  69:     }
  70: 
  71:     return 1;
  72: }
  73: 
  74: symbol_t *symbols_c::match_symbol (char *name)
  75: {
  76:     return this->match_symbol (name, strlen(name));
  77: }
  78: 
  79: symbol_t *symbols_c::match_symbol (char *name, int namelen)
  80: {
  81:     symbol_t *symbol;
  82:     char *sp, *np;
  83:     int nc;
  84: 
  85:     for (symbol=this->first_symbol; symbol!=NULL; symbol=symbol->next)
  86:     {
  87:         sp = symbol->name;
  88:         np = name;
  89:         nc = namelen;
  90:         for (;;)
  91:         {
  92:             if (nc-- == 0)
  93:             {
  94:                 if (*sp == '\0') return symbol;        // Matched!
  95:                 break;
  96:             }
  97:             if (toupper(*sp) != toupper(*np)) break;        // Failed this one
  98:             if (*sp == '\0' || *np == '\0') break;
  99:             sp++;
 100:             np++;
 101:         }
 102:     }
 103: 
 104:     return NULL;
 105: }
 106: 

symbols.hpp - header file

   1: #ifndef SYMBOLS_HPP
   2: #define SYMBOLS_HPP
   3: 
   4: #include "export.hpp"
   5: #include "status.hpp"
   6: #include "datetime.hpp"
   7: 
   8: typedef enum {
   9:     INT_TYP,
  10:     DOUBLE_TYP,
  11:     DATETIME_TYP,
  12:     STRING_TYP
  13: } type_e;
  14: 
  15: typedef struct evalue_t {
  16:     type_e typ;
  17:     union {
  18:         int i;
  19:         double x;
  20:         datetime_t dt;
  21:         struct {
  22:             char *str;
  23:             int need_to_free;
  24:         } v;
  25:     } u;
  26:     status_t status;
  27: } evalue_t;
  28: 
  29: typedef struct symbol_t {
  30:     char *name;
  31:     evalue_t value;
  32:     struct symbol_t *next;
  33: } symbol_t;
  34: 
  35: class symbols_c {
  36:   private:
  37:     symbol_t *first_symbol;
  38:     symbol_t *last_symbol;
  39: 
  40:   public:
  41:     symbol_t *deriv_symbol; // Only for eval_deriv()
  42: 
  43:     NP_EXPORT symbols_c ();
  44:     NP_EXPORT ~symbols_c ();
  45: 
  46:     NP_EXPORT symbol_t *add_symbol (char *name);
  47: 
  48:     NP_EXPORT symbol_t *match_symbol (char *name);
  49:     NP_EXPORT symbol_t *match_symbol (char *name, int nc);
  50: };
  51: 
  52: extern NP_EXPORT int copy_value (evalue_t *toval, evalue_t *fromval);
  53: 
  54: #endif // ! SYMBOLS_HPP
  55: 
  56: 

datetime.cpp - Date / Time

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <time.h>
   4: 
   5: #include "datetime.hpp"
   6: 
   7: #define START_YEAR 1800            // Arbitrary actually
   8: #define WEEK_START 2            // Varies with START_YEAR
   9: 
  10: datetime_t datetime_c::make_date (int y, int m, int d)
  11: {
  12:     // If month is out of bounds, adjust years
  13:     if (m < 1)
  14:     {
  15:         y -= (-m) / this->months_in_year + 1;                        // -25 goes to 3 years
  16:         m = this->months_in_year - (-m) % this->months_in_year;        // -25 goes to month 11
  17:     }
  18:     else if (m > this->months_in_year)
  19:     {
  20:         y += (m-1) / this->months_in_year;        // 75 goes to 6 years
  21:         m = (m-1) % this->months_in_year + 1;        // 75 goes to month 3
  22:     }
  23:     // Now m is guaranteed to be from 1 to 12
  24: 
  25:     // Take care of months and leap years
  26:     d += this->cum_days[m-1];
  27:     if (m > this->leap_month && this->is_leap_year (y)) d++;
  28: 
  29:     // Now take care of the year
  30:     d += (int) (this->average_days_per_year * (y - START_YEAR));
  31: 
  32:     return (double) d;
  33: }
  34: 
  35: datetime_t datetime_c::make_time (int h, int m, int s, double ms)
  36: {
  37:     return (((ms/1000+s)/60.0+m)/60.0 + h) / 24.0;
  38: }
  39: 
  40: void datetime_c::get_date (datetime_t dt, int *y, int *m, int *d, int *yd, int *wd)
  41: {
  42:     int yr;
  43:     int mon;
  44:     int days;
  45: 
  46:     days = (int) dt;            // Toss fractional time
  47: 
  48:     if (wd) *wd = ((days+WEEK_START) % this->days_in_week) + 1;
  49: 
  50:     yr = (int) (days / this->average_days_per_year);
  51:     if (y) *y = START_YEAR + yr;
  52:     days = days - (int) (yr * this->average_days_per_year);
  53: 
  54:     if (yd) *yd = days + 1;        // 1 = first
  55: 
  56:     // Take care of leap year
  57:     if (this->is_leap_year (START_YEAR+yr) && days > this->cum_days[this->leap_month])
  58:     {
  59:         days--;
  60:     }
  61: 
  62:     for (mon=1; mon<this->months_in_year; mon++)
  63:     {
  64:         if (days < this->cum_days[mon]) break;
  65:     }
  66: 
  67:     if (m) *m = mon;
  68:     if (d) *d = days - this->cum_days[mon-1];
  69: }
  70: 
  71: void datetime_c::get_time (datetime_t dt, int *h, int *m, int *s, double *ms)
  72: {
  73:     datetime_t tim;
  74: 
  75:     tim = dt - (long) dt;            // Toss days
  76: 
  77:     tim = 24 * tim;
  78:     if (h) *h = (int) tim;
  79: 
  80:     tim = 60 * (tim - (long) tim);
  81:     if (m) *m = (int) tim;
  82: 
  83:     tim = 60 * (tim - (long) tim);
  84:     if (s) *s = (int) tim;
  85: 
  86:     tim = 1000 * (tim - (long) tim);
  87:     if (ms) *ms = tim;
  88: }
  89: 
  90: datetime_t datetime_c::datetime_now ()
  91: {
  92:     time_t now;
  93:     struct tm *tm;
  94:     datetime_t dat, tim;
  95: 
  96:     now = time(NULL);
  97:     tm = localtime (&now);
  98:     dat = this->make_date (tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
  99:     tim = this->make_time (tm->tm_hour, tm->tm_min, tm->tm_sec, 0);
 100:     return dat + tim;
 101: }
 102: 
 103: datetime_c::datetime_c ()
 104: {
 105:     time_t now;
 106:     struct tm *tm;
 107:     int m, cum;
 108: 
 109:     this->days_in_week = 7;
 110:     this->weekdays[0] = "Sunday";
 111:     this->weekdays[1] = "Monday";
 112:     this->weekdays[2] = "Tuesday";
 113:     this->weekdays[3] = "Wednesday";
 114:     this->weekdays[4] = "Thursday";
 115:     this->weekdays[5] = "Friday";
 116:     this->weekdays[6] = "Saturday";
 117:     this->short_weekdays[0] = "Sun";
 118:     this->short_weekdays[1] = "Mon";
 119:     this->short_weekdays[2] = "Tue";
 120:     this->short_weekdays[3] = "Wed";
 121:     this->short_weekdays[4] = "Thu";
 122:     this->short_weekdays[5] = "Fri";
 123:     this->short_weekdays[6] = "Sat";
 124: 
 125:     this->months_in_year = 12;
 126:     this->monthnames[0] = "January";
 127:     this->monthnames[1] = "February";
 128:     this->monthnames[2] = "March";
 129:     this->monthnames[3] = "April";
 130:     this->monthnames[4] = "May";
 131:     this->monthnames[5] = "June";
 132:     this->monthnames[6] = "July";
 133:     this->monthnames[7] = "August";
 134:     this->monthnames[8] = "September";
 135:     this->monthnames[9] = "October";
 136:     this->monthnames[10] = "November";
 137:     this->monthnames[11] = "December";
 138:     this->short_monthnames[0] = "Jan";
 139:     this->short_monthnames[1] = "Feb";
 140:     this->short_monthnames[2] = "Mar";
 141:     this->short_monthnames[3] = "Apr";
 142:     this->short_monthnames[4] = "May";
 143:     this->short_monthnames[5] = "Jun";
 144:     this->short_monthnames[6] = "Jul";
 145:     this->short_monthnames[7] = "Aug";
 146:     this->short_monthnames[8] = "Sep";
 147:     this->short_monthnames[9] = "Oct";
 148:     this->short_monthnames[10] = "Nov";
 149:     this->short_monthnames[11] = "Dec";
 150: 
 151:     this->days_in_month[0] = 31;        // Jan
 152:     this->days_in_month[1] = 28;        // Feb
 153:     this->days_in_month[2] = 31;        // Mar
 154:     this->days_in_month[3] = 30;        // Apr
 155:     this->days_in_month[4] = 31;        // May
 156:     this->days_in_month[5] = 30;        // Jun
 157:     this->days_in_month[6] = 31;        // Jul
 158:     this->days_in_month[7] = 31;        // Aug
 159:     this->days_in_month[8] = 30;        // Sep
 160:     this->days_in_month[9] = 31;        // Oct
 161:     this->days_in_month[10] = 30;        // Nov
 162:     this->days_in_month[11] = 31;        // Dec
 163: 
 164:     this->which_buffer = 0;
 165: 
 166:     cum = 0;
 167:     for (m=0; m<this->months_in_year; m++)
 168:     {
 169:         this->cum_days[m] = cum;
 170:         cum += this->days_in_month[m];
 171:     }
 172:     this->cum_days[m] = cum;        // 13th month might be useful ... 365 total
 173: 
 174:     this->month_first = 1;        // 0=no for European; 1=yes for American
 175: 
 176:     this->leap_month = 2;        // Feb
 177: 
 178:     this->average_days_per_year = 365.2425;
 179: 
 180:     now = time(NULL);
 181:     tm = localtime (&now);
 182:     this->current_year = tm->tm_year + 1900;
 183: }
 184: 
 185: datetime_c::~datetime_c ()
 186: {
 187: }
 188: 
 189: int datetime_c::is_leap_year (int year)
 190: {
 191:     if ((year % 4) != 0) return 0;            // Years like 1977, 2002 are not
 192:     if ((year % 400) == 0) return 1;            // Years like 1600, 2000 are
 193:     if ((year % 100) == 0) return 0;            // Years like 1800, 1900 are not
 194:     return 1;                                    // Years like 1996, 2004 are
 195: }
 196: 
 197: int datetime_c::parse_dt (char *rec, datetime_t *dt, char *fmt, char **emsg)
 198: {
 199:     int mon, day, yr;
 200:     int hr, min, sec;
 201:     int cnt;
 202: 
 203:     if (fmt != NULL)
 204:     {
 205:         *emsg = "Can't handle formats yet";
 206:         return 0;
 207:     }
 208: 
 209:     cnt = sscanf (rec, "%d/%d/%d %d:%d:%d",
 210:         &mon, &day, &yr, &hr, &min, &sec);
 211:     
 212:     if (cnt < 3)
 213:     {
 214:         *emsg = "parse error";
 215:         return 0;
 216:     }
 217:     *dt = this->make_date (yr, mon, day);
 218:     if (cnt >= 6) *dt += this->make_time (hr, min, sec, 0);
 219:     return 1;
 220: }
 221: 
 222: char *datetime_c::format_dt (datetime_t dt, char *fmt)
 223: {
 224:     int mon, day, yr, wd, yd;
 225:     int hr, min, sec;
 226:     double ms;
 227:     char *buff;
 228: 
 229:     // Can't handle formats yet
 230:     if (fmt != NULL) fmt = NULL;
 231: 
 232:     // Rotate 3 buffers in case somebody is using more than one at a time
 233:     if (++this->which_buffer >= NUM_DATE_BUFFERS) this->which_buffer = 0;
 234:     buff = this->buffer[this->which_buffer];
 235: 
 236:     this->get_date (dt, &yr, &mon, &day, &yd, &wd);
 237:     this->get_time (dt, &hr, &min, &sec, &ms);
 238:     sprintf (buff, "%d/%d/%d %2.2d:%2.2d:%2.2d",
 239:         mon, day, yr, hr, min, sec);
 240:     return buff;
 241: }
 242: 

datetime.hpp - header file

   1: #ifndef DATETIME_HPP
   2: #define DATETIME_HPP
   3: 
   4: #include "export.hpp"
   5: 
   6: #define LONGEST_DATE_STRING 200                   // 30 should be plenty
   7: #define NUM_DATE_BUFFERS 3                        // Need at least 2
   8: 
   9: #define MAXMONTHS 15                          // actual 12
  10: #define MAXWEEKDAYS 10                        // actual 7
  11: 
  12: typedef double datetime_t;
  13: 
  14: class datetime_c {
  15:   private:
  16:     char buffer[NUM_DATE_BUFFERS][LONGEST_DATE_STRING+1];        // For formatting
  17:     int which_buffer;
  18: 
  19:   public:
  20:     datetime_c ();
  21:     ~datetime_c ();
  22: 
  23:     datetime_t datetime_now ();
  24: 
  25:     int days_in_week;
  26:     char *weekdays[MAXWEEKDAYS];
  27:     char *short_weekdays[MAXWEEKDAYS];
  28: 
  29:     int months_in_year;
  30:     char *monthnames[MAXMONTHS];
  31:     char *short_monthnames[MAXMONTHS];
  32: 
  33:     int days_in_month[MAXMONTHS];
  34:     int cum_days[MAXMONTHS+1];
  35:     int leap_month;        // Feb = 2
  36:     int is_leap_year (int year);
  37: 
  38:     int month_first;        // 0=no for European; 1=yes for American
  39: 
  40:     int current_year;        // In case none given on parse, use this year
  41: 
  42:     double average_days_per_year;   // 365.2425
  43: 
  44:     NP_EXPORT datetime_t make_date (int y, int m, int d);
  45:     NP_EXPORT datetime_t make_time (int h, int m, int s, double ms);
  46: 
  47:     NP_EXPORT void get_date (datetime_t dt, int *y, int *m, int *d, int *yd, int *wd);
  48:     NP_EXPORT void get_time (datetime_t dt, int *h, int *m, int *s, double *ms);
  49: 
  50:     NP_EXPORT int parse_dt (char *rec, datetime_t *dt, char *fmt, char **emsg);
  51:     NP_EXPORT char *format_dt (datetime_t dt, char *fmt);
  52: };
  53: 
  54: #endif // ! DATETIME_HPP
  55: 

memory.cpp - Memory Management

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: #include <ctype.h>
   5: 
   6: #include "export.hpp"
   7: #include "memory.hpp"
   8: 
   9: #if MEMCHK
  10: 
  11: #define SENTINEL_TOP 0x0F01010F        // Anything hard to match
  12: #define SENTINEL_BOT 0x08070708
  13: 
  14: #define FILENAME_LEN 4                // in words
  15: #define WORDS_AT_TOP 4
  16: #define HEADER_LEN (FILENAME_LEN + WORDS_AT_TOP)
  17: #define TRAILER_LEN 1
  18: 
  19: // Careful -- these are all hardcoded
  20: // Word 0 = SENTINEL_TOP
  21: // Word 1 = # bytes in block
  22: // Word 2 = source line number of caller
  23: // Word 3 = forward link to next block
  24: // Words 4-6 = filename (truncated to fit, no path)
  25: // Words 7 on = the data
  26: // Word at end = SENTINEL_BOT
  27: //
  28: 
  29: // Add blocks to front -- don't use a queue, a stack is better
  30: static int *first_memory_block = NULL;
  31: 
  32: static int memory_errors = 0;
  33: static int calls_to_getmem = 0;
  34: 
  35: NP_EXPORT void *
  36: safeGetMem (char *file, int line, int sz)
  37: {
  38:     void *p;
  39:     int newsz;
  40:     int *intp;
  41:     char *charp;
  42:     char *pos;
  43: 
  44:     calls_to_getmem++;
  45: 
  46:     // fprintf (stderr, "%s, line %d, getmem size=%d\n", file, line, sz);
  47: 
  48:     newsz = (sz+sizeof(int)-1)/sizeof(int) + HEADER_LEN + TRAILER_LEN;
  49:     intp = (int *) calloc (newsz, sizeof(int));
  50:     charp = (char *) (void *) (intp+WORDS_AT_TOP);
  51: 
  52:     // Fill in header information
  53:     intp[0] = SENTINEL_TOP;
  54:     intp[1] = sz;
  55:     intp[2] = line;
  56: 
  57:     // Add to list of memory blocks
  58:     intp[3] = (int) first_memory_block;
  59:     first_memory_block = intp;
  60: 
  61:     // Strip off directories
  62:     pos = strrchr (file, '/');
  63:     if (pos == NULL) pos = strrchr (file, '\\');
  64:     if (pos == NULL) pos = file-1;
  65:     strncpy (charp, pos+1, FILENAME_LEN*sizeof(int)-1);
  66:     charp[FILENAME_LEN*sizeof(int)-1] = '\0';
  67: 
  68:     // And trailer information
  69:     intp[newsz-1] = SENTINEL_BOT;
  70: 
  71:     // Return pointer to the middle of the block
  72:     p = (void *) (intp+HEADER_LEN);
  73:     return p;
  74: }
  75: 
  76: NP_EXPORT char *
  77: safeDupStr (char *file, int line, char *s)
  78: {
  79:     int nc;
  80:     char *tmp;
  81: 
  82:     if (s == NULL) nc = 0;
  83:     else nc = strlen(s);
  84: 
  85:     tmp = (char *) safeGetMem (file, line, nc+1);
  86:     if (!tmp) return NULL;
  87: 
  88:     if (s == NULL) *tmp = '\0';
  89:     else strcpy (tmp, s);
  90:     return tmp;
  91: }
  92: 
  93: static void memory_fault (char *file, int line, void *ptr,
  94:     int *intp, int shouldhave, char *which)
  95: {
  96:     int i;
  97:     char ch;
  98: 
  99:     memory_errors++;
 100: 
 101:     if (strcmp (which, "badptr") == 0)
 102:     {
 103:         fprintf (stderr, "\nMEMCHK FAULT. Attempt to free non-allocated pointer.");
 104:     }
 105:     else if (strcmp (which, "start") == 0 ||
 106:         strcmp (which, "end") == 0)
 107:     {
 108:         fprintf (stderr, "\nMEMCHK FAULT. Value is 0x%x (%d) \"",
 109:             ((unsigned int *) ptr)[0], ((int *) ptr)[0]);
 110:         for (i=0; i<(int)sizeof(int); i++)
 111:         {
 112:             ch = ((char *) ptr)[i];
 113:             if (!isprint((int)ch) || ch < ' ') ch = '.';
 114:             fprintf (stderr, "%c", ch);
 115:         }
 116:         fprintf (stderr, "\"\n  instead of 0x%x at %s of block.",
 117:             shouldhave, which);
 118:         fprintf (stderr, "\n  Clobbered block size was %d byte%s",
 119:             intp[1], (intp[1] == 1 ? "" : "s"));
 120:         fprintf (stderr, "\n  Allocated in file %s at line %d",
 121:             (char *)(void *)(intp+WORDS_AT_TOP), intp[2]);
 122:     }
 123: 
 124:     fprintf (stderr, "\n  Freed in file %s at line %d\n", file, line);
 125: }
 126: 
 127: NP_EXPORT int
 128: safeFreeMem (char *file, int line, void *ptr)
 129: {
 130:     int *intp, *prevp;
 131:     char *charp;
 132:     int newsz;
 133: 
 134:     if (ptr == NULL) return 1;
 135: 
 136:     // Make sure it is ours!
 137:     prevp = NULL;
 138:     for (intp=first_memory_block; intp!=NULL; intp=(int *)intp[3])
 139:     {
 140:         if (ptr == (void *) (intp+HEADER_LEN)) break;
 141:         prevp = intp;
 142:     }
 143:     if (intp == NULL)        // Yikes, not ours!
 144:     {
 145:         memory_fault (file, line, intp, NULL, NULL, "badptr");
 146:         return 0;
 147:     }
 148: 
 149:     // Remove it from the linked list
 150:     if (prevp == NULL)                // Was first
 151:     {
 152:         first_memory_block = (int *) intp[3];
 153:     }
 154:     else
 155:     {
 156:         prevp[3] = intp[3];
 157:     }
 158: 
 159:     // Check sentinels
 160:     intp = (int *) ptr - HEADER_LEN;
 161:     charp = (char *) (void *) (intp+WORDS_AT_TOP);
 162: 
 163:     if (intp[0] != SENTINEL_TOP)
 164:     {
 165:         memory_fault (file, line, intp, intp, SENTINEL_TOP, "start");
 166:     }
 167: 
 168:     newsz = (intp[1]+sizeof(int)-1)/sizeof(int) + HEADER_LEN + TRAILER_LEN;
 169:     if (intp[newsz-1] != SENTINEL_BOT)
 170:     {
 171:         memory_fault (file, line, intp+newsz-1, intp, SENTINEL_BOT, "end");
 172:     }
 173: 
 174:     // Now, finally, get to free it
 175:     free ((void *) intp);
 176:     return 1;
 177: }
 178: 
 179: NP_EXPORT void *
 180: safeChangeMem (char *file, int line, void *oldp, int sz)
 181: {
 182:     void *newp;
 183:     int *intp;
 184: 
 185:     // Reallocate by getting new block, copying, freeing old
 186:     newp = safeGetMem (file, line, sz);
 187:     if (oldp != NULL)
 188:     {
 189:         intp = (int *) oldp - HEADER_LEN;
 190:         memcpy (newp, oldp, intp[1]);
 191:         safeFreeMem (file, line, oldp);
 192:     }
 193: 
 194:     return newp;
 195: }
 196: 
 197: NP_EXPORT int
 198: ShowMemInUse ()
 199: {
 200:     int *intp, *nextp, *p;
 201:     int minb, maxb, cntb;
 202: 
 203:     if (memory_errors > 0)
 204:     {
 205:         fprintf (stderr, "\n\nMemory errors = %d\n", memory_errors);
 206:     }
 207: 
 208:     if (first_memory_block == NULL)
 209:     {
 210:         if (memory_errors == 0)
 211:         {
 212:             fprintf (stdout, "\nNo memory errors or leaks detected (calls=%d).\n",
 213:                 calls_to_getmem);
 214:             return 1;
 215:         }
 216:         return 0;
 217:     }
 218: 
 219:     fprintf (stderr, "\n\nMemory still in use:\n");
 220:     for (intp=first_memory_block; intp!=NULL; intp=(int *)intp[3])
 221:     {
 222:         // Same as next block?
 223:         nextp = (int *) intp[3];
 224:         if (nextp != NULL && intp[2] == nextp[2] &&
 225:             strcmp ((char *)(intp+WORDS_AT_TOP),
 226:                 (char *)(nextp+WORDS_AT_TOP)) == 0)
 227:         {
 228:             cntb = 1;
 229:             minb = maxb = intp[1];
 230:             p = nextp;
 231:             while (p != NULL && intp[2] == p[2] &&
 232:                 strcmp ((char *)(intp+WORDS_AT_TOP),
 233:                     (char *)(p+WORDS_AT_TOP)) == 0)
 234:             {
 235:                 cntb++;
 236:                 if (p[1] < minb) minb = p[1];
 237:                 if (p[1] > maxb) maxb = p[1];
 238:                 nextp = p;
 239:                 p = (int *) p[3];
 240:             }
 241:             if (minb == maxb)
 242:             {
 243:                 fprintf (stderr, "  Allocated %d blocks of %d "
 244:                     "byte%s in file %s at line %d\n",
 245:                     cntb, minb, (minb==1 ? "" : "s"),
 246:                     (char *)(void *)(intp+WORDS_AT_TOP), intp[2]);
 247:             }
 248:             else
 249:             {
 250:                 fprintf (stderr, "  Allocated %d blocks from %d to %d "
 251:                     "bytes in file %s at line %d\n",
 252:                     cntb, minb, maxb,
 253:                     (char *)(void *)(intp+WORDS_AT_TOP), intp[2]);
 254:             }
 255:             intp = nextp;
 256:         }
 257:         else        // Just one block
 258:         {
 259:             fprintf (stderr, "  Allocated %d byte%s in file %s at line %d\n",
 260:                 intp[1], (intp[1]==1 ? "" : "s"),
 261:                 (char *)(void *)(intp+WORDS_AT_TOP), intp[2]);
 262:         }
 263:     }
 264: 
 265:     return 0;
 266: }
 267: 
 268: #else        // ! MEMCHK
 269: 
 270: NP_EXPORT int ShowMemInUse ()
 271: {
 272:     // Nothing was turned on, just return
 273:     return 1;
 274: }
 275: 
 276: #endif        // ! MEMCHK

memory.hpp - header file

   1: #ifndef MEMORY_HPP
   2: #define MEMORY_HPP
   3: 
   4: #ifndef MEMCHK
   5: #define MEMCHK 0
   6: #endif
   7: 
   8: #include "export.hpp"
   9: 
  10: #if ! MEMCHK
  11: 
  12: #define GetMem(t,n) (t *) (void *) calloc (n, sizeof(t))
  13: #define ChangeMem(p,t,n) (t *) (void *) realloc (p, n)
  14: #define FreeMem(p) (void) ((void) ((p) && (free(p), 1), p = NULL))
  15: #define DupStr(s) strdup(s)
  16: 
  17: #else        /* MEMCHK, Do extensive memory checks */
  18: 
  19: #define GetMem(t,n) (t *) safeGetMem(__FILE__,__LINE__,sizeof(t)*(n))
  20: #define ChangeMem(p,t,n) (t *) safeChangeMem(__FILE__,__LINE__,p,sizeof(t)*(n))
  21: #define FreeMem(p) (void) ((void) ((p) && (safeFreeMem(__FILE__,__LINE__,p)), p = NULL))
  22: #define DupStr(s) safeDupStr(__FILE__,__LINE__,s)
  23: 
  24: extern NP_EXPORT void * safeGetMem (char *file, int line, int sz);
  25: extern NP_EXPORT void * safeChangeMem (char *file, int line,
  26:     void *oldp, int sz);
  27: extern NP_EXPORT int safeFreeMem (char *file, int line, void *ptr);
  28: extern NP_EXPORT char * safeDupStr (char *file, int line, char *s);
  29: 
  30: #endif        /* MEMCHK */
  31: 
  32: extern NP_EXPORT int ShowMemInUse ();
  33: 
  34: #define GetMemBytes(n) (void *) GetMem (char, n)
  35: #define GetStr(nc) GetMem (char, (nc)+1)
  36: 
  37: #endif // ! MEMORY_HPP
  38: 

export.hpp - header file

   1: #ifndef EXPORT_HPP
   2: #define EXPORT_HPP
   3: 
   4: #if defined(WIN32)
   5: 
   6: #define NP_EXPORT __declspec(dllexport)
   7: 
   8: #else        // Unix or whatever
   9: 
  10: #define NP_EXPORT // Nothing
  11: 
  12: #endif
  13: 
  14: #endif // ! EXPORT_HPP
  15: 

status.hpp - header file

   1: #ifndef STATUS_HPP
   2: #define STATUS_HPP
   3: 
   4: typedef unsigned char status_t;
   5: 
   6: #define STATUS_OK 0x040                // 020=Space 040=@
   7: #define STATUS_ERROR_BIT 0x002         // 022="     042=B
   8: #define STATUS_BLANK_BIT 0x008         // 028=(     048=H
   9: 
  10: #define STATUS_ERROR (STATUS_OK | STATUS_ERROR_BIT)
  11: #define STATUS_BLANK (STATUS_OK | STATUS_BLANK_BIT)
  12: 
  13: #define STATUS_ERRORS (STATUS_ERROR_BIT | STATUS_BLANK_BIT)
  14: 
  15: #define STATUS_COMBINE(s,t) (status_t) ((s) | (t))
  16: 
  17: #define IS_STATUS_OK(s) (((int)(s) & STATUS_ERRORS) == 0)
  18: #define IS_STATUS_ERROR(s) (((int)(s) & STATUS_ERROR_BIT) != 0)
  19: #define IS_STATUS_BLANK(s) (((int)(s) & STATUS_BLANK_BIT) != 0)
  20: 
  21: #endif // ! STATUS_HPP
  22: 

test_transform.cpp - Test Transformations

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: 
   5: #include "memory.hpp"
   6: #include "symbols.hpp"
   7: #include "transform.hpp"
   8: 
   9: #define ITERATIONS 5
  10: 
  11: #define NONE -1234  // Means don't check derivative value
  12: 
  13: static int quiet = 0;
  14: 
  15: static int testone (transform_c *trans, symbols_c *symbols,
  16:     char *expression, int should_fail, evalue_t *expected)
  17: {
  18:     expression_t *expr;
  19:     char *emsg, *errpos;
  20:     evalue_t val;
  21:     int ret = 1;
  22:     int failed = 0;
  23:     int j;
  24:     symbol_t *z;
  25: 
  26:     copy_value (&val, NULL);        // Does nothing!
  27:     z = symbols->match_symbol ("revenue");
  28:     if (z == NULL) return 0;
  29:     z->value.u.x = 10;
  30: 
  31:     expr = trans->parse_expr (symbols, expression, &emsg, &errpos);
  32:     if (!expr)
  33:     {
  34:         if (!should_fail)
  35:         {
  36:             fprintf (stderr, "\nError parsing '%s'\n", expression);
  37:             if (emsg) fprintf (stderr, "Error message: %s\n", emsg);
  38:             if (errpos) fprintf (stderr, "Error at %s\n", errpos);
  39:             fprintf (stderr, "\n");
  40:         }
  41:         ret = 0;
  42:     }
  43:     else
  44:     {
  45:         if (!trans->eval_expr (symbols, expr, &val))
  46:         {
  47:             if (!should_fail)
  48:             {
  49:                 fprintf (stderr, "\nError evaluating '%s'\n",
  50:                     trans->get_expr (expr));
  51:             }
  52:             ret = 0;
  53:         }
  54:         else 
  55:         {
  56:             for (j=1; j<ITERATIONS; j++)
  57:             {
  58:                 if (z != NULL) z->value.u.x *= 2;
  59:                 if (!trans->eval_expr (symbols, expr, &val))
  60:                 {
  61:                     break;
  62:                 }
  63:             }
  64:             if (j < ITERATIONS)        // broke
  65:             {
  66:                 if (!should_fail)
  67:                 {
  68:                     fprintf (stderr, "\nError re-evaluating '%s'\n",
  69:                         trans->get_expr (expr));
  70:                 }
  71:                 ret = 0;
  72:             }
  73:             else
  74:             {
  75:                 if (!should_fail)
  76:                 {
  77:                     if (val.typ != expected->typ)
  78:                     {
  79:                         fprintf (stderr, "\nWrong result type\n");
  80:                         fprintf (stderr, "  %s\n", trans->get_expr (expr));
  81:                         ret = 0;
  82:                     }
  83:                     else if (val.status != expected->status)
  84:                     {
  85:                         fprintf (stderr, "\nStatus is %d instead of %d\n",
  86:                             val.status, expected->status);
  87:                         fprintf (stderr, "  %s\n", trans->get_expr (expr));
  88:                         ret = 0;
  89:                     }
  90:                     else
  91:                     {
  92:                         switch (expected->typ)
  93:                         {
  94:                             case INT_TYP :
  95:                                 failed = (val.u.i != expected->u.i);
  96:                                 break;
  97: 
  98:                             case DOUBLE_TYP :
  99:                                 failed = (val.u.x != expected->u.x);
 100:                                 break;
 101: 
 102:                             case DATETIME_TYP :
 103:                                 failed = (val.u.dt != expected->u.dt);
 104:                                 break;
 105: 
 106:                             case STRING_TYP :
 107:                                 failed = (strcmp (val.u.v.str, expected->u.v.str) != 0);
 108:                                 break;
 109: 
 110:                             default :
 111:                                 ret = 0;
 112:                         }
 113:                         if (failed)
 114:                         {
 115:                             fprintf (stderr, "\nIncorrect Result\n");
 116:                             fprintf (stderr, "  %s", trans->get_expr (expr));
 117:                             fprintf (stderr, " is %s instead of %s\n",
 118:                                 trans->get_value (&val),
 119:                                 trans->get_value (expected));
 120:                             ret = 0;
 121:                         }
 122:                         else if (!quiet)   // Worked!
 123:                         {
 124:                             fprintf (stdout, "Worked: %s",
 125:                                 trans->get_expr (expr));
 126:                             fprintf (stdout, " is %s\n",
 127:                                 trans->get_value (expected));
 128:                         }
 129:                     }
 130:                 }
 131:             }
 132:         }
 133:     }
 134: 
 135:     if (should_fail)
 136:     {
 137:         if (ret != 0)
 138:         {
 139:             fprintf (stderr, "\nWas supposed to fail and did not\n");
 140:             fprintf (stderr, "  %s   (%s)", expression, trans->get_expr (expr));
 141:             ret = 0;
 142:         }
 143:         else
 144:         {
 145:             if (!quiet) // Failed, as expected
 146:             {
 147:                 fprintf (stdout, "Failed, as expected:  %s\n", expression);
 148:             }
 149:             ret = 1;
 150:         }
 151:     }
 152: 
 153:     trans->free_expr (expr);
 154:     return ret;
 155: }
 156: 
 157: static int testdouble (transform_c *trans, symbols_c *symbols,
 158:     char *expression, double x)
 159: {
 160:     evalue_t value;
 161: 
 162:     value.typ = DOUBLE_TYP;
 163:     value.u.x = x;
 164:     value.status = STATUS_OK;
 165:     return testone (trans, symbols, expression, 0, &value);
 166: }
 167: 
 168: static int testint (transform_c *trans, symbols_c *symbols,
 169:     char *expression, int i)
 170: {
 171:     evalue_t value;
 172: 
 173:     value.typ = INT_TYP;
 174:     value.u.i = i;
 175:     value.status = STATUS_OK;
 176:     return testone (trans, symbols, expression, 0, &value);
 177: }
 178: 
 179: static int testdatetime (transform_c *trans, symbols_c *symbols,
 180:     char *expression, char *dt)
 181: {
 182:     char *emsg;
 183:     evalue_t value;
 184: 
 185:     value.typ = DATETIME_TYP;
 186:     if (!trans->dt_class.parse_dt (dt, &(value.u.dt), NULL, &emsg))
 187:     {
 188:         fprintf (stderr, "\nError parsing result date/time: %s\n", emsg);
 189:         return 0;
 190:     }
 191:     value.status = STATUS_OK;
 192:     return testone (trans, symbols, expression, 0, &value);
 193: }
 194: 
 195: static int teststr (transform_c *trans, symbols_c *symbols,
 196:     char *expression, char *expected)
 197: {
 198:     evalue_t value;
 199: 
 200:     value.typ = STRING_TYP;
 201:     value.u.v.str = expected;
 202:     value.u.v.need_to_free = 0;
 203:     value.status = STATUS_OK;
 204:     return testone (trans, symbols, expression, 0, &value);
 205: }
 206: 
 207: static int testfail (transform_c *trans, symbols_c *symbols,
 208:     char *expression)
 209: {
 210:     evalue_t value;
 211: 
 212:     value.typ = DOUBLE_TYP;
 213:     value.u.x = 0;
 214:     value.status = STATUS_OK;
 215:     return testone (trans, symbols, expression, 1, &value);
 216: }
 217: 
 218: static int testderiv (transform_c *trans, symbols_c *symbols,
 219:     char *expression, char *expected, symbol_t *wrt, double dv)
 220: {
 221:     expression_t *deriv;
 222:     expression_t *expr;
 223:     char *emsg, *errpos;
 224:     char *deriv_str;
 225:     int ret;
 226:     evalue_t val;
 227: 
 228:     expr = trans->parse_expr (symbols, expression, &emsg, &errpos);
 229:     if (!expr)
 230:     {
 231:         fprintf (stderr, "\nError parsing '%s'\n", expression);
 232:         if (emsg) fprintf (stderr, "Error message: %s\n", emsg);
 233:         if (errpos) fprintf (stderr, "Error at %s\n", errpos);
 234:         fprintf (stderr, "\n");
 235:         return 0;
 236:     }
 237: 
 238:     deriv = trans->deriv_expr (symbols, expr);
 239:     if (deriv == NULL)
 240:     {
 241:         fprintf (stderr, "\nError differentiating '%s'\n",
 242:             trans->get_expr (expr));
 243:         trans->free_expr (expr);
 244:         return 0;
 245:     }
 246: 
 247:     ret = 1;
 248:     deriv_str = trans->get_expr (deriv);
 249:     if (strcmp (expected, deriv_str) != 0)
 250:     {
 251:         fprintf (stderr, "\nWrong derivative, %s instead of %s\n",
 252:             deriv_str, expected);
 253:         ret = 0;
 254:     }
 255:     else
 256:     {
 257:         if (!trans->eval_deriv (symbols, wrt, deriv, &val))
 258:         {
 259:             fprintf (stderr, "\nUnable to evaluate derivative of %s\n",
 260:                 expression);
 261:             ret = 0;
 262:         }
 263:         else
 264:         {
 265:             if (!((val.typ == INT_TYP && dv == (double) val.u.i) ||
 266:                 (val.typ == DOUBLE_TYP && dv == val.u.x)))
 267:             {
 268:                 fprintf (stderr, "\nWrong value for derivative.");
 269:                 ret = 0;
 270:             }
 271:             if (!quiet)
 272:             {
 273:                 fprintf (stdout, "Derivative of %s is %s\n",
 274:                     expression, deriv_str);
 275:             }
 276:         }
 277:     }
 278: 
 279:     trans->free_expr (expr);
 280:     trans->free_expr (deriv);
 281:     return ret;
 282: }
 283: 
 284: static int dotests ()
 285: {
 286:     transform_c trans;
 287:     symbols_c symb;
 288:     symbol_t *x, *y, *z;
 289:     symbol_t *u, *v, *du, *dv;
 290:     int errs;
 291: 
 292:     x = symb.add_symbol ("x");
 293:     x->value.typ = INT_TYP;
 294:     x->value.status = STATUS_OK;
 295:     x->value.u.i = 49;
 296: 
 297:     y = symb.add_symbol ("y");
 298:     y->value.typ = DOUBLE_TYP;
 299:     y->value.status = STATUS_OK;
 300:     y->value.u.x = 200;
 301: 
 302:     z = symb.add_symbol ("revenue");
 303:     z->value.typ = DOUBLE_TYP;
 304:     z->value.status = STATUS_OK;
 305:     // z->value.u.x = 10;
 306: 
 307:     u = symb.add_symbol ("u");
 308:     u->value.typ = INT_TYP;
 309:     u->value.status = STATUS_OK;
 310:     u->value.u.i = 1;
 311: 
 312:     du = symb.add_symbol ("du");
 313:     du->value.typ = INT_TYP;
 314:     du->value.status = STATUS_OK;
 315:     du->value.u.i = 2;
 316: 
 317:     v = symb.add_symbol ("v");
 318:     v->value.typ = INT_TYP;
 319:     v->value.status = STATUS_OK;
 320:     v->value.u.i = 3;
 321: 
 322:     dv = symb.add_symbol ("dv");
 323:     dv->value.typ = INT_TYP;
 324:     dv->value.status = STATUS_OK;
 325:     dv->value.u.i = 4;
 326: 
 327:     errs = 0;
 328: 
 329:     // These cases should succeed
 330:     if (!testint (&trans, &symb, "(1*2) + (3*4)", 14)) errs++;
 331:     if (!testint (&trans, &symb, "(1+2+3) * (4+5+6)", 90)) errs++;
 332:     if (!testdouble (&trans, &symb, " 10 * ( 1 + 2 + 3) / sqrt(4)", 30)) errs++;
 333:     if (!testdouble (&trans, &symb, "cos pi", -1)) errs++;
 334:     if (!testint (&trans, &symb, "1000 + 2*30 + 4*2", 1068)) errs++;
 335:     if (!testint (&trans, &symb, "+ - ++---+ -100", -100)) errs++;
 336:     if (!testdouble (&trans, &symb, "sqrt 100", 10)) errs++;
 337:     if (!testdouble (&trans, &symb, "4+y", 204)) errs++;
 338:     if (!testint (&trans, &symb, "123-5", 118)) errs++;
 339:     if (!testint (&trans, &symb, "1+2+3+4+5", 15)) errs++;
 340:     if (!testdouble (&trans, &symb, "  100  -  50-25.0  ", 25)) errs++;
 341:     if (!testdatetime (&trans, &symb, " \\6/5/2000\\ ", "6/5/2000")) errs++;
 342:     // if (!testdatetime (&trans, &symb, " $now ", trans.dt.format_dt (trans.dt.datetime_now(), NULL))) errs++;
 343:     if (!testint (&trans, &symb, "\\6/5/2000\\ <= \\6/5/2000\\", 1)) errs++;
 344:     if (!testint (&trans, &symb, "\\6/5/2000\\ <= \\6/4/2000\\", 0)) errs++;
 345:     if (!teststr (&trans, &symb, "   'abc''def''''ghi'   ", "abc'def''ghi")) errs++;
 346:     if (!testint (&trans, &symb, "'abc' < 'abd'", 1)) errs++;
 347:     if (!testint (&trans, &symb, "'abc' >= 'abd'", 0)) errs++;
 348:     if (!teststr (&trans, &symb, "'ab'+'cdef'+'g'+\"h\"", "abcdefgh")) errs++;
 349:     if (!testint (&trans, &symb, "1<2<3<=4=4<5>4>=4>3<>2=2", 1)) errs++;
 350:     if (!testint (&trans, &symb, "1<2<3<=4=9<5>4>=4>3<>2=2", 0)) errs++;
 351:     if (!testint (&trans, &symb, "    len    (   \"abc\"   )", 3)) errs++;
 352:     if (!testint (&trans, &symb, "if ( 1 < 2, 8+8, 3+3)", 16)) errs++;
 353:     if (!testint (&trans, &symb, "if ( 1 > 2, 8+8, 3+3)", 6)) errs++;
 354:     if (!testint (&trans, &symb, "if(1,2,3)", 2)) errs++;
 355:     if (!testint (&trans, &symb, "$month(\\6/5/2000\\)", 6)) errs++;
 356:     if (!testint (&trans, &symb, "$minute(\\6/5/2000 11:53:45\\)", 53)) errs++;
 357:     if (!testdouble (&trans, &symb, " delta(revenue,2) ", 80)) errs++;
 358:     if (!testdouble (&trans, &symb, "(u+1)^(v-1) * (u*sqrt(u)*dv + v*du)", 40)) errs++;
 359: 
 360:     // These should fail
 361:     if (!testfail (&trans, &symb, "  100  +  ")) errs++;
 362:     if (!testfail (&trans, &symb, " ( 100  +  5 ")) errs++;
 363:     if (!testfail (&trans, &symb, "  100  +  5 )")) errs++;
 364:     if (!testfail (&trans, &symb, "  * 88  ")) errs++;
 365:     if (!testfail (&trans, &symb, "len 'abc'")) errs++;
 366:     if (!testfail (&trans, &symb, "len('a','b')")) errs++;
 367:     if (!testfail (&trans, &symb, "if(1,2)")) errs++;
 368:     if (!testfail (&trans, &symb, "if(1,2,3,4)")) errs++;
 369: 
 370:     // These derivative cases should succeed
 371:     if (!testderiv (&trans, &symb, "sin(y)",
 372:         "$cos(y) * d!y!", x, 0)) errs++;
 373:     if (!testderiv (&trans, &symb, "x+y",
 374:         "d!x! + d!y!", y, 1)) errs++;
 375:     if (!testderiv (&trans, &symb, "sin(x)*cos(y)",
 376:         "$sin(x)*-$sin(y)*d!y! + $cos(y)*$cos(x)*d!x!", NULL, 0)) errs++;
 377:     if (!testderiv (&trans, &symb, "x^3",
 378:         "3 * x^2 * d!x!", x, 7203)) errs++;
 379: 
 380:     if (errs == 0)
 381:     {
 382:         fprintf (stdout, "\nNo transform errors.\n");
 383:         return 0;
 384:     }
 385:         
 386:     fprintf (stderr, "\n\nTransform Error Count = %d\n", errs);
 387:     return errs;
 388: }
 389: 
 390: int main (int argc, char *argv[])
 391: {
 392:     int errs;
 393: 
 394:     if (argc > 1 && strcmp (argv[1], "-quiet") == 0) quiet = 1;
 395:     
 396:     errs = dotests ();
 397: 
 398:     if (!ShowMemInUse ()) errs++;
 399: 
 400:     if (errs != 0)
 401:     {
 402:         return (EXIT_FAILURE);
 403:     }
 404:     return (EXIT_SUCCESS);
 405: }
 406: 

test_derivative.cpp - Test Derivatives

   1: #include <stdio.h>
   2: #include <stdlib.h>
   3: #include <string.h>
   4: 
   5: #include "memory.hpp"
   6: #include "symbols.hpp"
   7: #include "transform.hpp"
   8: 
   9: static int quiet = 0;
  10: 
  11: #define NONE -1234  // Means don't check derivative value
  12: 
  13: static int testderiv (transform_c *trans, symbols_c *symbols,
  14:     char *expression, char *expected, symbol_t *wrt, double dv)
  15: {
  16:     expression_t *deriv;
  17:     expression_t *expr;
  18:     char *emsg, *errpos;
  19:     char *deriv_str;
  20:     int ret;
  21:     evalue_t val;
  22: 
  23:     expr = trans->parse_expr (symbols, expression, &emsg, &errpos);
  24:     if (!expr)
  25:     {
  26:         fprintf (stderr, "\nError parsing '%s'\n", expression);
  27:         if (emsg) fprintf (stderr, "Error message: %s\n", emsg);
  28:         if (errpos) fprintf (stderr, "Error at %s\n", errpos);
  29:         fprintf (stderr, "\n");
  30:         return 0;
  31:     }
  32: 
  33:     deriv = trans->deriv_expr (symbols, expr);
  34:     if (deriv == NULL)
  35:     {
  36:         fprintf (stderr, "\nError differentiating '%s'\n",
  37:             trans->get_expr (expr));
  38:         trans->free_expr (expr);
  39:         return 0;
  40:     }
  41: 
  42:     ret = 1;
  43:     deriv_str = trans->get_expr (deriv);
  44:     if (strcmp (expected, deriv_str) != 0)
  45:     {
  46:         fprintf (stderr, "\nWrong derivative, %s instead of %s\n",
  47:             deriv_str, expected);
  48:         ret = 0;
  49:     }
  50:     else
  51:     {
  52:         if (!trans->eval_deriv (symbols, wrt, deriv, &val))
  53:         {
  54:             fprintf (stderr, "\nUnable to evaluate derivative of %s\n",
  55:                 expression);
  56:             ret = 0;
  57:         }
  58:         else
  59:         {
  60:             if (!((val.typ == INT_TYP && dv == (double) val.u.i) ||
  61:                 (val.typ == DOUBLE_TYP && dv == val.u.x)))
  62:             {
  63:                 fprintf (stderr, "\nWrong value for derivative.");
  64:                 ret = 0;
  65:             }
  66:             if (!quiet)
  67:             {
  68:                 fprintf (stderr, "\nDerivative of %s is %s",
  69:                     expression, deriv_str);
  70:             }
  71:         }
  72:     }
  73: 
  74:     trans->free_expr (expr);
  75:     trans->free_expr (deriv);
  76:     return ret;
  77: }
  78: 
  79: static int dotests ()
  80: {
  81:     transform_c trans;
  82:     symbols_c symb;
  83:     symbol_t *x, *y;
  84:     int errs;
  85: 
  86:     x = symb.add_symbol ("x");
  87:     x->value.typ = INT_TYP;
  88:     x->value.status = STATUS_OK;
  89:     x->value.u.i = 49;
  90: 
  91:     y = symb.add_symbol ("y");
  92:     y->value.typ = DOUBLE_TYP;
  93:     y->value.status = STATUS_OK;
  94:     y->value.u.x = 200;
  95: 
  96:     errs = 0;
  97: 
  98:     // These cases should succeed
  99:     if (!testderiv (&trans, &symb, "sin(y)",
 100:         "$cos(y) * d!y!", x, 0)) errs++;
 101:     if (!testderiv (&trans, &symb, "x+y",
 102:         "d!x! + d!y!", y, 1)) errs++;
 103:     if (!testderiv (&trans, &symb, "sin(x)*cos(y)",
 104:         "$sin(x)*-$sin(y)*d!y! + $cos(y)*$cos(x)*d!x!", NULL, 0)) errs++;
 105:     if (!testderiv (&trans, &symb, "x^3",
 106:         "3 * x^2 * d!x!", x, 7203)) errs++;
 107: 
 108:     if (errs == 0)
 109:     {
 110:         fprintf (stderr, "\n\nNo derivative errors.\n");
 111:         return 0;
 112:     }
 113:         
 114:     fprintf (stderr, "\n\nDerivative Error Count = %d\n", errs);
 115:     return errs;
 116: }
 117: 
 118: int main (int argc, char *argv[])
 119: {
 120:     int errs;
 121: 
 122:     if (argc > 1 && strcmp (argv[1], "-quiet") == 0) quiet = 1;
 123:     
 124:     errs = dotests ();
 125: 
 126:     if (!ShowMemInUse ()) errs++;
 127: 
 128:     if (errs != 0)
 129:     {
 130:         return (EXIT_FAILURE);
 131:     }
 132:     return (EXIT_SUCCESS);
 133: }
 134: 

tt.out - Sample output

   1: Worked: 1*2 + 3*4 is 14
   2: Worked: (1+2+3) * (4+5+6) is 90
   3: Worked: 10 * (1+2+3) / $sqrt(4) is 30
   4: Worked: $cos($pi) is -1
   5: Worked: 1000 + 2*30 + 4*2 is 1068
   6: Worked: +-++---+-100 is -100
   7: Worked: $sqrt(100) is 10
   8: Worked: 4 + y is 204
   9: Worked: 123 - 5 is 118
  10: Worked: 1 + 2 + 3 + 4 + 5 is 15
  11: Worked: 100 - 50 - 25 is 25
  12: Worked: \6/5/2000 00:00:00\ is \6/5/2000 00:00:00\
  13: Worked: \6/5/2000 00:00:00\ <= \6/5/2000 00:00:00\ is 1
  14: Worked: \6/5/2000 00:00:00\ <= \6/4/2000 00:00:00\ is 0
  15: Worked: "abc'def''ghi" is "abc'def''ghi"
  16: Worked: "abc" < "abd" is 1
  17: Worked: "abc" >= "abd" is 0
  18: Worked: "ab" + "cdef" + "g" + "h" is "abcdefgh"
  19: Worked: 1 < 2 < 3 <= 4 = 4 < 5 > 4 >= 4 > 3 <> 2 = 2 is 1
  20: Worked: 1 < 2 < 3 <= 4 = 9 < 5 > 4 >= 4 > 3 <> 2 = 2 is 0
  21: Worked: $len ("abc") is 3
  22: Worked: $if (1<2, 8+8, 3+3) is 16
  23: Worked: $if (1>2, 8+8, 3+3) is 6
  24: Worked: $if (1, 2, 3) is 2
  25: Worked: $month (\6/5/2000 00:00:00\) is 6
  26: Worked: $minute (\6/5/2000 11:53:44\) is 53
  27: Worked: $delta (revenue, 2) is 80
  28: Worked: (u+1)^(v-1) * (u*$sqrt(u)*dv+v*du) is 40
  29: Failed, as expected:    100  +  
  30: Failed, as expected:   ( 100  +  5 
  31: Failed, as expected:    100  +  5 )
  32: Failed, as expected:    * 88  
  33: Failed, as expected:  len 'abc'
  34: Failed, as expected:  len('a','b')
  35: Failed, as expected:  if(1,2)
  36: Failed, as expected:  if(1,2,3,4)
  37: Derivative of sin(y) is $cos(y) * d!y!
  38: Derivative of x+y is d!x! + d!y!
  39: Derivative of sin(x)*cos(y) is $sin(x)*-$sin(y)*d!y! + $cos(y)*$cos(x)*d!x!
  40: Derivative of x^3 is 3 * x^2 * d!x!
  41: 
  42: No transform errors.
  43: 
  44: No memory errors or leaks detected (calls=613).
  45: 
Email: steve@oharasteve.com