TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "externs.h"
#include "softcode.h"

int is_sign(char ch)
{
  if(ch != '+' && ch != '-' && ch != '^' && ch != '*' && ch != '/' &&
    ch != '%')
  {
    return(0);
  }

  return(1);
}

char *is_math(char *str)
{
  char *p;
  int num_parentheses = 0;
  int floating = 0;

  if(!*str)
    return(NULL);

  for(p = str;*p;p++)
  {
    if(!isdigit(*p) && *p != '(' && *p != ')' && !is_sign(*p) &&
      *p != '.')
    {
      break;
    }

    if(is_sign(*p))     /* Reset floating for next number */
      floating = 0;

/* Check for multiple '.' thingies */
    if(*p == '.')
    {
      if(floating)
        return(NULL);
      floating = 1;
    }

    if(*p == '(')
      num_parentheses++;
    else if(*p == ')')
      num_parentheses--;

    if(num_parentheses < 0)
      return(NULL);
  }

  if(!*p || (p != str && *p == '"'))
    return((num_parentheses)?NULL:p);

  return(NULL);
}

static char *find_innermost_paren(char *str)
{
  char *p, *q;

  for(p = str, q = p;*p;p++)
  {
    if(*p == '(')
      q = p;

    else if(*p == ')')
      break;
  }

  return(q);
}

static float do_power(int num, int exp)
{
  int rv = 1;

  while(exp-- > 0)
    rv *= num;

  return(rv);
}

static int parse_one(char *str, char *retbuf)
{
  char *p, *b;
  float op1 = 0, op2 = 0;
  char buf[4096];
  char sign = 0;
  float answer = 0;

  for(p = str;*p;p++)
  {
    b = buf;

    if(*p == '-')
      *b++ = *p++;

    while(isdigit(*p) || *p == '.')
      *b++ = *p++;
    *b = '\0';
    op1 = atof(buf);
    sign = *p++;
    b = buf;

    if(*p == '-')
      *b++ = *p++;

    while(isdigit(*p) || *p == '.')
      *b++ = *p++;
    *b = '\0';
    op2 = atof(buf);
    break;
  }

  switch(sign)
  {
    case '+':
      answer = op1+op2;
      break;
    case '-':
      answer = op1-op2;
      break;
    case '^':
      answer = do_power((int)op1, (int)op2);
      break;
    case '*':
      answer = op1*op2;
      break;
    case '/':
      if(op2 == 0)
      {
        if(code_ptr)
        {
          notify(code_ptr->executor, "ERROR - Division by zero.");
          notify(code_ptr->executor, tprintf("%d %s",
            code_ptr->pline->line, code_ptr->pline->code));
          notify(code_ptr->executor, str);
          end_program(code_ptr->program, 0);
        }
        return(1);
      }
      answer = op1/op2;
      break;
    case '%':
      if((int)op2 == 0)
      {
        if(code_ptr)
        {
          notify(code_ptr->executor, "ERROR - Division by zero.");
          notify(code_ptr->executor, tprintf("%d %s",
            code_ptr->pline->line, code_ptr->pline->code));
          notify(code_ptr->executor, str);
          end_program(code_ptr->program, 0);
        }
        return(1);
      }
      answer = (int)op1%(int)op2;
  }

  sprintf(retbuf, "%f", answer);

  return(0);
}

static char *find_highest_order(char *str)
{
  char *p, *q;
  int exp = 0;

  if(strchr(str, '^'))
    exp = 1;

  for(p = str, q = str;*p;p++)
  {
    if(*p == '-')
      q = p;
    else if(isdigit(*p) || *p == '.')
      q = p;
    while(isdigit(*p) || *p == '.')
      p++;
    if(!is_sign(*p))
      return(str);
    if(*p == '^')
      return(q);
    if(*p == '*' || *p == '/' || *p == '%')
      if(!exp)
        return(q);
  }

  return(str);
}

static int parse_no_paren(char *str, char *retbuf)
{
  char *p, *q;
  char buf[4096], buf2[4096], buf3[4096], buf4[4096];
  char newstr[4096];
  int rv = 0;

  strcpy(newstr, str);

  q = p = find_highest_order(newstr);

  while(*p)
  {
    if(!isdigit(*p) && *p != '.' && *p != '-')
    {
      syntax_error();
      return(1);
    }

    if(*p == '-')
      p++;

    while(isdigit(*p) || *p == '.')
      p++;
    if(!*p)
      break;

    if(!is_sign(*p))
    {
      syntax_error();
      return(1);
    }

    p++;

    if(!isdigit(*p) && *p != '-' && *p != '.')
    {
      syntax_error();
      return(1);
    }

    if(*p == '-')
      p++;

    while(isdigit(*p) || *p == '.')
      p++;

    strncpy(buf, q, p-q);
    *(buf+(p-q)) = '\0';

    if(parse_one(buf, buf2))
      return(1);

    strncpy(buf3, newstr, q-newstr);
    *(buf3+(q-newstr)) = '\0';
    strcpy(buf4, p);
    sprintf(newstr, "%s%s%s", buf3, buf2, buf4);
    q = p = find_highest_order(newstr);
  }

  strcpy(retbuf, newstr);

  return(rv);
}

static int has_sign(char *str)
{
  char *p;

  for(p = str;*p;p++)
  {
    if((is_sign(*p) && *p != '-') || *p == '(' || *p == ')')
      return(1);

/* Check for negativity */
    if(*p == '-' && p != str && !is_sign(*(p-1)))
      return(1);
  }

  return(0);
}

static void strip_float(char *str, char *retbuf)
{
  char *p;

  if(!(p = strchr(str, '.')))
  {
    strcpy(retbuf, str);
    return;
  }

  p++;
  while(*p && *p != '0')
    p++;

  if((!*p || *p == '0') && *(p-1) == '.')
    p--;

  strncpy(retbuf, str, p-str);
  *(retbuf+(p-str)) = '\0';
}

int parse_statement(char *statement, char *final)
{
  char buf[4096], buf2[4096];
  char buf3[4096];
  char *p, *q;

  strcpy(buf, statement);

  while(*(p = find_innermost_paren(buf)))
  {
    if(*p == '(')
      extract_char(p);
    q = p;

    while(*q && *q != ')')
      q++;

    if(!*q && q == p)
    {
      syntax_error();
      return(1);
    }

    if(*q == ')')
      extract_char(q);

    strncpy(buf2, p, q-p);
    *(buf2+(q-p)) = '\0';

    if(parse_no_paren(buf2, buf3))
      return(1);

    if(!has_sign(buf))
      break;

    strncpy(buf2, buf, p-buf);
    *(buf2+(p-buf)) = '\0';
    sprintf(buf2+strlen(buf2), "%s%s", buf3, q);
    strcpy(buf, buf2);
  }

  strcpy(final, buf);
  strip_float(final, buf);
  strcpy(final, buf);

  return(0);
}