# include "opihi.h"

void interpolate_slash (char *line);

char *parse (char *line) {

  double fval;
  char *newline, *N, *L, *val, *B, *V, *V0, *V1, *end, *c1, *c2;
  int Nval, Nbytes, status, size;
  FILE *f;
  Vector *vec;

  val = V0 = NULL;

  if (line == (char *) NULL) goto error;

  /* case 1: $var = expression */
  if (line[0] == '$') {  
    /* find the target variable name and the rest of the line */
    interpolate_slash (line);
    V0 = thisvar (line);
    if (V0 == NULL) goto error;
    V1 = aftervar (line);
    if (V1 == NULL) goto error;
 
    /* increment operator */
    if (!strcmp (V1, "++")) {
      val = get_variable (V0);
      if (val == NULL) {
	fval = 1.0;
      } else {
	fval = atof (val) + 1.0;
      }
      set_variable (V0, fval);
      goto escape;
    }

    /* decrement operator */
    if (!strcmp (V1, "--")) {
      val = get_variable (V0);
      if (val == NULL) {
	fval = -1.0;
      } else {
	fval = atof (val) - 1.0;
      }
      set_variable (V0, fval);
      goto escape;
    }

    /* not an assignement, syntax error */
    if (*V1 != '=') goto error;

    /* find first non-WHITESPACE character after = */
    V1 ++;
    while (isspace (*V1)) V1++;
    if (*V1 == 0) goto error;

    /* command replacement.  execute the line, place answer in val. */
    if (*V1 == '`') {

      /* look for end of line, must be ` */
      B = V1 + strlen (V1) - 1;
      if (*B != '`') goto error;

      /* val will hold the result */
      ALLOCATE (val, char, 1024);

      /* B is the command to be executed */
      B = strncreate (V1 + 1, strlen(V1) - 2);
      f = popen (B, "r");
      Nbytes = fread (val, 1, 1023, f);
      val[Nbytes] = 0;
      status = pclose (f);
      free (B);

      if (status) gprint (GP_ERR, "warning: exit status of command %d\n", status);

      /* convert all but last return to ' '.  drop last return */
      for (B = val; *B != 0; B++) {
	if (*B == '\n') {
	  if (B[1]) {
	    *B = ' ';
	  } else {
	    *B = 0;
	  }
	}
      }
    } 

    /* simple variable assignment */
    if (*V1 != '`') {
      /* dvomath returns a new string, or NULL, with the result of the expression */
      val = dvomath (1, &V1, &size, 0);
      if (val == NULL) { 
	while (OHANA_WHITESPACE (*V1)) V1++;
	val = strcreate (V1);
      } 
    }
    /* both dvomath and command replacement create (char *) val */
    set_str_variable (V0, val);
    goto escape;  /* frees temp variables */
  }

  /* case 2: vect[N] = value */
  V0 = thiscomm (line);
  if (strchr(V0, '[') != (char *) NULL) { 
    /* get vector name (left in V0) */
    N = strchr (V0, '[');
    if (N == V0) goto error;

    /* get bracket contents (left in V) */
    L = strchr (V0, ']');
    if (L == (char *) NULL) goto error;
    if (L != V0 + strlen (V0) - 1) goto error;

    *N = 0;
    V = strncreate (N+1, L - N - 1);

    /* expand value in brackets */
    val = dvomath (1, &V, &size, 0);
    if (val == NULL) {
      print_error ();
      goto error;
    }
    Nval = atoi (val);
    free (val); val = NULL;
    free (V);

    /* find vector */
    if ((vec = SelectVector (V0, OLDVECTOR, TRUE)) == NULL) goto error;
    free (V0); V0 = (char *) NULL;
    if (Nval >= vec[0].Nelements) {
      gprint (GP_ERR, "no element %d\n", Nval);
      goto escape;
    }

    /* find value for assignment */
    V1 = nextcomm (line);
    if (V1 == (char *) NULL) goto error;
    if (V1[0] != '=') goto error;
    V1 ++;

    /*** assign vector element to value ***/
    val = dvomath (1, &V1, &size, 0); 
    if (val == (char *) NULL) {
      print_error ();
      goto error;
    }
    vec[0].elements[Nval] = atof (val);
    goto escape;
  }
  free (V0); V0 = (char *) NULL;

  /* case 3: {expression} */
  ALLOCATE (newline, char, 1024);
  for (L = line, N = newline; *L != 0; L++, N++) { 

    /* copy elements from L (line) to N (newline) until we hit a '{' */
    for (; (*L != '{') && (*L != 0); L++, N++) *N = *L;
    if (*L == 0) break;

    if ((L != line) && (*(L-1) == 0x5c)) {
      N--;
      *N = *L;
      continue;
    }

    /* check on the syntax of the line */
    L++;
    c1 = strchr (L, '}');
    if (c1 == NULL) {
      gprint (GP_ERR, "no close brackets!\n");
      goto escape;
    }
    c2 = strchr (L, '{');
    if ((c2 != NULL) && (c2 < c1)) {
      gprint (GP_ERR, "can't nest brackets!\n");
      goto escape;
    }
    end = c1;
    *c1 = 0;

    /* value in brackets must be a math expression of some sort.
       isolated words are not valid here */
    val = dvomath (1, &L, &size, -1);
    if (val == NULL) {
      print_error ();
      goto escape;
    } 
    /* copy val to outline */
    for (V = val; *V != 0; V++,N++) *N = *V;
    L = end;
    N--;
    free (val); val = (char *) NULL;
  }
  
  *N = 0;
  free (line); line = (char *) NULL;

  /* \ protects next character */
  interpolate_slash (newline);
  
  REALLOCATE (newline, char, strlen (newline) + 1);
  return (newline); 

  error:
  gprint (GP_ERR, "syntax error\n");

  escape:
  if (line != (char *) NULL) free (line);
  if (val != (char *) NULL) free (val);
  if (V0 != (char *) NULL) free (V0);
  return (NULL);
}

/* replace all instances of \A with A in line */
void interpolate_slash (char *line) {

  char *in, *out;

  for (in = out = line; *in != 0; in++, out++) {
    if (*in == 0x5c) in++;
    *out = *in;
    if (*in == 0) return;
  }
  *out = *in;
}

  /* this routine looks for math and/or logic expressions that 
     need to be evaluated and passes them on to "parenthesis",
     which evaluates the expression */

  /* There are two situations now which define an expression to 
     be evaluated:
     1) $var = expression   -- everything after the = must be a math expression or a string
     2) {expression}        -- everything within the {} must be a math expression
   */

  /* case 1: $var = expression.  test that
     a) there is a $ as the first character
     b) the variable defined by that $ is not null
     c) there is an = sign following the variable
     d) there is something following the = sign
     */
  
  /* case 2: vect[N] = expression. check syntax:
     a) last char must be ]
     b) word must contain [
     c) between [ & ] must be an integer
     d) vector must exist
     e) there is an = sign following the first word
     f) there is something following the = sign
  */
     

  /* case 3: we hunt for '{', and then the first '}' and pass everything
     inbetween.  no nested {{}}s are allowed! */
  
/* thisword ALLOCATES
   thisvar  ALLOCATES
   thiscomm ALLOCATES
   
   nextword !ALLOCATE
   nextcomm !ALLOCATE
   
   lastword !ALLOCATE
   lastvar !ALLOCATE
   
   aftervar !ALLOCATE
*/   

