/
2.0.4beta/doc/
2.0.4beta/gnu/
2.0.4beta/sha/
/* expr.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *	    Copyright(C) 1995 by Jason Downs.  All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#include <ctype.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */

#include "conf.h"
#include "teeny.h"
#include "expr.h"
#include "externs.h"

/*
 * The TeenyMUD II math expression evaluator, version 2.
 */

static int isfloat _ANSI_ARGS_((char *));
static struct expr_val expr_sub _ANSI_ARGS_((char *, char **));

static int isfloat(str)
    register char *str;
{
  while(*str && isspace(*str))
    str++;
  if((*str == '.') && isdigit(str[1]))
    return(1);

  while(*str && isdigit(*str))
    str++;
  if((*str == '.') && isdigit(str[1]))
    return(1);
  return(0);
}

/* expression evaluator */
static struct expr_val expr_sub(str, nptr)
    register char *str;
    char **nptr;
{
  struct expr_val val;
  int error;

  switch(str[0]) {
  case '(':
    str++;
    if((*nptr = parse_to(str, '(', ')'))) {
      val = expr_parse(str, &error);
      if(error)
	*nptr = (char *)NULL;
    }
    break;
  case '-':
    str++;
    if(!*str)
      *nptr = (char *)NULL;
    else {
      if(*str == '(') {		/* awe, fuck */
	str++;
	if((*nptr = parse_to(str, '(', ')'))) {
	  if((val = expr_parse(str, &error))) {
	    switch(val.type) {
	    case EXPRVAL_INT:
	      (val.val).ival = -(val.val).ival;
	      break;
	    case EXPRVAL_FLOAT:
	      (val.val).fval = -(val.val).fval;
	      break;
	    }
	    if(error)
	      *nptr = (char *)NULL;
	  }
	}
      } else {
	if(isdouble(str)) {
	  (val.val).fval = strtod(str, nptr);
	  if(*nptr == str)
	    *nptr = (char *)NULL;
	  else {
	    val.type = EXPRVAL_FLOAT;
	    (val.val).fval = -(val.val).fval;
	  }
	} else {
	  (val.val).ival = (int)strtol(str, nptr, 10);
	  if(*nptr == str)
	    *nptr = (char *)NULL;
	  else {
	    val.type = EXPRVAL_INT;
	    (val.val).ival = -(val.val).ival;
	  }
	}
      }
    }
    break;
  case '~':
    str++;
    if(!*str)
      *nptr = (char *)NULL;
    else {
      if(*str == '(') {		/* argh */
	str++;
	if((*nptr = parse_to(str, '(', ')'))) {
	  if((val = expr_parse(str, &error))) {
	    switch(val.type) {
	    case EXPRVAL_INT:
	      (val.val).ival = ~(val.val).ival;
	      break;
	    case EXPRVAL_FLOAT:
	      error++;
	      break;
	    }
	    if(error)
	      *nptr = (char *)NULL;
	  }
        }
      } else {
	if(isdouble(str))
	  *nptr = (char *)NULL;
	else {
	  (val.val).ival = (int)strtol(str, nptr, 10);
	  if(*nptr == str)
	    *nptr = (char *)NULL;
	  else {
	    val.type = EXPRVAL_INT;
	    (val.val).ival = ~(val.val).ival;
	  }
	}
      }
    }
    break;
  case '!':
    str++;
    if(!*str)
      *nptr = (char *)NULL;
    else {
      if(*str == '(') {		/* die, scum */
	str++;
	if((*nptr = parse_to(str, '(', ')'))) {
	  if((val = expr_parse(str, &error))) {
	    switch(val.type) {
	    case EXPRVAL_INT:
	      (val.val).ival = !(val.val).ival;
	      break;
	    case EXPRVAL_FLOAT:
	      error++;
	      break;
	    }
	    if(error)
	      *nptr = (char *)NULL;
	  }
        }
      } else {
	if(isdouble(str))
	  *nptr = (char *)NULL;
	else {
	  (val.val).ival = (int)strtol(str, nptr, 10);
	  if(*nptr == str)
	    *nptr = (char *)NULL;
	  else {
	    val.type = EXPRVAL_INT;
	    (val.val).ival = !(val.val).ival;
	  }
	}
      }
    }
    break;
  default:
    if(isdouble(str)) {
      (val.val).fval = strtod(str, nptr);
      if(*nptr == str)
	*nptr = (char *)NULL;
      else
	val.type = EXPRVAL_FLOAT;
    } else if(isdigit(str[0])) {
      (val.val).ival = strtol(str, nptr, 10);
      if(*nptr == str)
	*nptr = (char *)NULL;
      else
	val.type = EXPRVAL_INT;
    } else {
      *nptr = (char *)NULL;
    }
    break;
  }

  return(val);
}

long expr_parse(str, error)
    char *str;
    int *error;
{
  register long left = 0, right = 0;
  int first = 1;
  char *expr, *nptr;

  if (!str) {
    *error = 1;
    return(0);
  } else
    *error = 0;

  while(*str && !*error) {
    while(*str && isspace(*str))
      str++;
    if(!*str)
      break;
    
    if(first) {
      left = expr_sub(str, &nptr);
      if(!nptr) {
	*error = 1;
	break;
      }
      for(str = nptr; *str && isspace(*str); str++);
      if(!*str)
	break;
      first = 0;
    }
    expr = str;
    while(*str && strchr("*/%+-<>=!&^|", *str))
      str++;
    while(*str && isspace(*str))
      str++;
    if(!*str || (!isdigit(*str) && !strchr("(-~!", *str))) {
      *error = 1;
      break;
    }

    right = expr_sub(str, &nptr);
    if(!nptr) {
      *error = 1;
      break;
    }
    str = nptr;

    /* evaluate */
    switch(expr[0]) {
    case '*':	/* multiply */
      left *= right;
      break;
    case '/':	/* divide */
      left /= right;
      break;
    case '%':	/* modulo */
      left %= right;
      break;
    case '+':	/* addition */
      left += right;
      break;
    case '-':	/* substraction */
      left -= right;
      break;
    case '<':
      switch(expr[1]) {
      case '<':		/* shift */
	left = left << right;
	break;
      case '=':		/* lesser than or equal */
	left = (left <= right);
	break;
      default:		/* lesser than */
	left = (left < right);
        break;
      }
      break;
    case '>':
      switch(expr[1]) {
      case '>':		/* shift */
	left = left >> right;
	break;
      case '=':		/* greater than or equal */
	left = (left >= right);
	break;
      default:		/* greater than */
	left = (left > right);
	break;
      }
      break;
    case '=':
      if(expr[1] != '=') {
	*error = 1;
	break;
      }
      left = (left == right);
      break;
    case '!':
      if(expr[1] != '=') {
	*error = 1;
	break;
      }
      left = (left != right);
      break;
    case '^':
      left ^= right;
      break;
    case '&':
      switch(expr[1]) {
      case '&':
	left = (left && right);
	break;
      default:
	left &= right;
	break;
      }
      break;
    case '|':
      switch(expr[1]) {
      case '|':
	left = (left || right);
	break;
      default:
	left |= right;
	break;
      }
      break;
    default:
      *error = 1;
    }
  }

  return(left);
}