Logo Search packages:      
Sourcecode: mathomatic version File versions

parse.h

/*
 * Expression parsing routines for Mathomatic.
 */

/*
 * This is a simple mathematical expression parser.
 *
 * Returns the new string position or NULL on error.
 */
char  *
parse_section(equation, np, cp)
token_type  *equation;  /* where the parsed expression is stored */
int         *np;        /* pointer to parsed expression size */
char        *cp;        /* string to parse */
{
      int         n;
      int         cur_level;
      int         operand;
      char        *startp;
      char        *cp_start;
      char        *cp1;
      double            d;
      int         abs_count;
      int         abs_array[10];

      cp_start = cp;
      n = 0;
      *np = 0;
      cur_level = 1;
      abs_count = 0;
      operand = false;
      for (;; cp++) {
            switch (*cp) {
            case ' ':
            case '\t':
                  continue;
            case '(':
            case '[':
                  cur_level++;
                  if (operand) {
                        goto syntax_error;
                  }
                  continue;
            case ')':
            case ']':
                  cur_level--;
                  if (cur_level <= 0
                      || (abs_count > 0 && cur_level < abs_array[abs_count-1])) {
#if   !SILENT
                        put_up_arrow((int) (cp - cp_start));
                        printf(_("Too many right parentheses.\n"));
#endif
                        return(NULL);
                  }
                  if (!operand) {
                        goto syntax_error;
                  }
                  continue;
            case '=':
            case 0:
            case '\n':
                  goto p_out;
            }
            if (n > (n_tokens - 6)) {
                  error_huge();
            }
            operand = !operand;
            switch (*cp) {
            case '|':
                  if (operand) {
                        if (abs_count >= ARR_CNT(abs_array)) {
#if   !SILENT
                              printf(_("Too many nested absolute values.\n"));
#endif
                              return(NULL);
                        }
                        cur_level += 2;
                        abs_array[abs_count++] = cur_level;
                        operand = false;
                  } else {
                        if (abs_count <= 0
                            || cur_level != abs_array[--abs_count]) {
                              goto syntax_error;
                        }
                        cur_level--;
                        equation[n].level = cur_level;
                        equation[n].kind = OPERATOR;
                        equation[n].token.operatr = POWER;
                        n++;
                        equation[n].level = cur_level;
                        equation[n].kind = CONSTANT;
                        equation[n].token.constant = 2.0;
                        n++;
                        equation[n].level = cur_level;
                        equation[n].kind = OPERATOR;
                        equation[n].token.operatr = POWER;
                        n++;
                        equation[n].level = cur_level;
                        equation[n].kind = CONSTANT;
                        equation[n].token.constant = 0.5;
                        n++;
                        cur_level--;
                        operand = true;
                  }
                  break;
            case '!':
                  if (operand) {
                        goto syntax_error;
                  }
                  equation[n].level = cur_level;
                  equation[n].kind = OPERATOR;
                  equation[n].token.operatr = FACTORIAL;
                  n++;
                  equation[n].level = cur_level;
                  equation[n].kind = CONSTANT;
                  equation[n].token.constant = 0.0;
                  n++;
                  operand = true;
                  break;
            case '^':
                  if (operand) {
                        goto syntax_error;
                  }
                  equation[n].level = cur_level;
                  equation[n].kind = OPERATOR;
                  equation[n].token.operatr = POWER;
                  n++;
                  break;
            case '*':
            case '/':
            case '%':
                  if (operand) {
                        goto syntax_error;
                  }
                  equation[n].level = cur_level;
                  equation[n].kind = OPERATOR;
                  switch (*cp) {
                  case '*':
                        equation[n].token.operatr = TIMES;
                        break;
                  case '/':
                        equation[n].token.operatr = DIVIDE;
                        break;
                  case '%':
                        equation[n].token.operatr = MODULUS;
                        break;
                  }
                  n++;
                  break;
            case '+':
            case '-':
                  if (!operand) {
                        equation[n].level = cur_level;
                        equation[n].kind = OPERATOR;
                        equation[n].token.operatr = ((*cp == '+') ? PLUS : MINUS);
                        n++;
                  }
#if   !GRAPH
                  if (strncasecmp(cp, "+/-", 3) == 0) {
                        equation[n].level = cur_level;
                        equation[n].kind = VARIABLE;
                        next_sign(&equation[n].token.variable);
                        n++;
                        equation[n].level = cur_level;
                        equation[n].kind = OPERATOR;
                        equation[n].token.operatr = TIMES;
                        n++;
                        cp += 2;
                        operand = false;
                        break;
                  }
#endif
                  if (!operand)
                        break;
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '.':
                  if (!operand) {
                        goto syntax_error;
                  }
                  if (*cp == '-' && !((isascii(cp[1]) && isdigit(cp[1]))
                      || cp[1] == '.')) {
                        equation[n].kind = CONSTANT;
                        equation[n].token.constant = -1.0;
                        equation[n].level = cur_level;
                        n++;
                        equation[n].kind = OPERATOR;
                        equation[n].token.operatr = NEGATE;
                        equation[n].level = cur_level;
                        n++;
                        operand = false;
                        continue;
                  }
                  startp = cp;
                  errno = 0;
                  d = strtod(startp, &cp);
                  if (errno) {
#if   !SILENT
                        put_up_arrow((int) (startp - cp_start));
                        printf(_("Constant out of range.\n"));
#endif
                        return(NULL);
                  }
                  if (cp == startp) {
#if   !SILENT
                        put_up_arrow((int) (startp - cp_start));
                        printf(_("Error parsing constant.\n"));
#endif
                        return(NULL);
                  }
                  equation[n].kind = CONSTANT;
                  equation[n].token.constant = d;
                  equation[n].level = cur_level;
                  n++;
                  cp--;
                  break;
            default:
                  if (!(isascii(*cp) && isalpha(*cp))) {
                        goto syntax_error;
                  }
                  if (!operand) {
                        operand = true;
                        equation[n].level = cur_level;
                        equation[n].kind = OPERATOR;
                        equation[n].token.operatr = TIMES;
                        n++;
                  }
                  if (strncasecmp(cp, "inf", 3) == 0) {
                        equation[n].kind = CONSTANT;
                        equation[n].token.constant = HUGE_VAL;
                        if (strncasecmp(cp, "infinity", 8) == 0) {
                              cp += 8;
                        } else {
                              cp += 3;
                        }
                  } else {
                        equation[n].kind = VARIABLE;
                        cp1 = cp;
                        cp = parse_var(&equation[n].token.variable, cp);
                        if (cp == NULL) {
#if   !SILENT
                              put_up_arrow((int) (cp1 - cp_start));
                              printf(_("Invalid variable.\n"));
#endif
                              return(NULL);
                        }
                  }
                  cp--;
                  equation[n].level = cur_level;
                  n++;
                  break;
            }
      }
p_out:
      if (abs_count != 0 || (n && !operand)) {
            goto syntax_error;
      }
      if (cur_level != 1) {
#if   !SILENT
            put_up_arrow((int) (cp - cp_start));
            printf(_("Missing right parenthesis.\n"));
#endif
            return(NULL);
      }
      if (*cp == '=')
            cp++;
      *np = n;
      handle_negate(equation, *np);
#if   !GRAPH
      prior_sub(equation, *np);
#endif
      return cp;

syntax_error:
#if   !SILENT
      put_up_arrow((int) (cp - cp_start));
      printf(_("Syntax error.\n"));
#endif
      return(NULL);
}

/*
 * Parse variable pointed to by "cp".
 * Variable name is converted to Mathomatic format and stored in "*vp".
 * Return new string position, or NULL on failure.
 */
char  *
parse_var(vp, cp)
long  *vp;
char  *cp;
{
      int   i, j;
      long  l;
      char  buf[MAX_VAR_LEN+1];
      char  *cp1;

      if (!(isascii(*cp) && isalpha(*cp))) {    /* variables must start with a letter */
            return(NULL);
      }
      cp1 = cp;
      for (i = 0; *cp1;) {
            if (*cp1 == '_') {
                  if (strncasecmp(cp1, "_percent_change", 15) == 0)
                        break;
            } else if (!(isascii(*cp1) && isalpha(*cp1))) {
                  break;
            }
            if (i >= MAX_VAR_LEN) {
                  return(NULL);
            }
            buf[i++] = *cp1++;
      }
      if (i == 0)
            return(NULL);
      buf[i] = '\0';
      if (strcasecmp(buf, "sign") == 0) {
            l = SIGN;
            cp += 4;
      } else if (strcasecmp(buf, "integer") == 0) {
            l = V_INTEGER;
            cp += 7;
      } else if (strcasecmp(buf, "temp") == 0) {
            l = V_TEMP;
            cp += 4;
      } else {
            if (strncasecmp(cp, "i#", 2) == 0) {
                  *vp = IMAGINARY;
                  return(cp + 2);
            }
            if (strncasecmp(cp, "e#", 2) == 0) {
                  *vp = V_E;
                  return(cp + 2);
            }
            if (strncasecmp(cp, "p#", 2) == 0 || strcasecmp(buf, "pi") == 0) {
                  *vp = V_PI;
                  return(cp + 2);
            }
            for (i = 0; *cp;) {
                  if (*cp == '_') {
                        if (strncasecmp(cp, "_percent_change", 15) == 0)
                              break;
                  } else if (!(isascii(*cp) && (isalpha(*cp) || isdigit(*cp)))) {
                        break;
                  }
                  if (i >= MAX_VAR_LEN) {
                        return(NULL);
                  }
                  buf[i++] = *cp++;
            }
            if (i == 0)
                  return(NULL);
            buf[i] = '\0';
            l = 0;
            for (i = 0; var_names[i]; i++) {
                  if (case_sensitive_flag) {
                        if (strcmp(buf, var_names[i]) == 0) {
                              l = i + VAR_OFFSET;
                              break;
                        }
                  } else {
                        if (strcasecmp(buf, var_names[i]) == 0) {
                              l = i + VAR_OFFSET;
                              break;
                        }
                  }
            }
            if (l == 0) {
                  if (i >= (MAX_VAR_NAMES - 1)) {
#if   !SILENT
                        printf(_("Maximum number of long variable names reached.\n"));
                        printf(_("No new variables may be entered.\n"));
                        printf(_("Please restart or use \"clear all\".\n"));
#endif
                        return(NULL);
                  }
                  var_names[i] = malloc(strlen(buf) + 1);
                  if (var_names[i] == NULL) {
#if   !SILENT
                        printf(_("Out of memory!  (can't malloc()).\n"));
#endif
                        return(NULL);
                  }
                  strcpy(var_names[i], buf);
                  l = i + VAR_OFFSET;
                  var_names[i+1] = NULL;
            }
      }
      if (isascii(*cp) && isdigit(*cp)) {
            j = atoi(cp);
            if (j < 0 || j > MAX_SUBSCRIPT) {
                  return(NULL);
            }
#if   !GRAPH
            if (l == SIGN) {
                  sign_array[j+1] = true;
            }
#endif
            l += ((long) (j + 1)) << VAR_SHIFT;
            while (*cp && isascii(*cp) && isdigit(*cp))
                  cp++;
      }
      while (*cp == '\'') {
            cp++;
            l += PRIME_INCREMENT;
            if (l < 0) {
                  return(NULL);
            }
      }
      if (strncasecmp(cp, "_percent_change", 15) == 0) {
            l |= PERCENT_CHANGE;
            cp += 15;
      }
      *vp = l;
      return cp;
}

/*
 * Return true if passed variable is a constant.
 * Return value of constant in "*dp".
 */
int
var_is_const(v, dp)
long  v;
double      *dp;
{
      switch (v) {
      case V_E:
            *dp = E;
            return true;
      case V_PI:
            *dp = PI;
            return true;
      }
      return false;
}

/*
 * Substitute E and PI variables with their respective constants.
 */
int
subst_constants(equation, np)
token_type  *equation;
int         *np;
{
      int   i;
      int   modified;
      double      d;

      modified = false;
      for (i = 0; i < *np; i += 2) {
            if (equation[i].kind == VARIABLE) {
                  if (var_is_const(equation[i].token.variable, &d)) {
                        equation[i].kind = CONSTANT;
                        equation[i].token.constant = d;
                        modified = true;
                  }
            }
      }
      return modified;
}

binary_parenthesize(equation, n, i)
token_type  *equation;
int         n, i;
{
      register int      j;
      int         level;

      level = equation[i].level++;
      if (equation[i-1].level++ > level) {
            for (j = i - 2; j >= 0; j--) {
                  if (equation[j].level <= level)
                        break;
                  equation[j].level++;
            }
      }
      if (equation[i+1].level++ > level) {
            for (j = i + 2; j < n; j++) {
                  if (equation[j].level <= level)
                        break;
                  equation[j].level++;
            }
      }
}

handle_negate(equation, n)
token_type  *equation;
int         n;
{
      int   i;

      for (i = 1; i < n; i += 2) {
            if (equation[i].token.operatr == NEGATE) {
                  equation[i].token.operatr = TIMES;
                  binary_parenthesize(equation, n, i);
            }
      }
}

str_tolower(cp)
char  *cp;
{
      for (; *cp; cp++) {
            if (isascii(*cp) && isupper(*cp))
                  *cp += 'a' - 'A';
      }
}

Generated by  Doxygen 1.6.0   Back to index