/* $Header: boolexp.c,v 1.2 90/04/21 17:20:32 lachesis Exp $
 * $Log:    boolexp.c,v $
 * Revision 1.2  90/04/21  17:20:32  lachesis
 * Added property lists.
 *
 * Revision 1.1  90/04/14  14:56:38  lachesis
 * Initial revision
 *
 */
#include "copyright.h"

#include "os.h"

#include "db.h"
#include "match.h"
#include "externs.h"
#include "config.h"
#include "interface.h"

/* Lachesis note on the routines in this package:
   eval_booexp does just evaluation.

   parse_boolexp makes potentially recursive calls to several different
   subroutines ---
          parse_boolexp_F
          This routine does the leaf level parsing and the NOT.
      parse_boolexp_E
          This routine does the ORs.
      pasre_boolexp_T
          This routine does the ANDs.

   Because property expressions are leaf level expressions, I have only
   touched eval_boolexp_F, asking it to call my additional parse_boolprop()
   routine.                                                              */

int eval_boolexp (dbref player, struct boolexp *b)
{
  if (b == TRUE_BOOLEXP) {
    return 1;
  } else {
    switch (b->type) {
    case BOOLEXP_AND:
      return (eval_boolexp (player, b->sub1)
        && eval_boolexp (player, b->sub2));
    case BOOLEXP_OR:
      return (eval_boolexp (player, b->sub1)
        || eval_boolexp (player, b->sub2));
    case BOOLEXP_NOT:
      return !eval_boolexp (player, b->sub1);
    case BOOLEXP_CONST:
      return (b->thing == player || member (b->thing, db[player].contents)
        || b->thing == db[player].location);
    case BOOLEXP_PROP:
      return (has_property (player, b->prop_check->type, b->prop_check->class,
#ifdef NUMBER_PROPS
          b->prop_check->value
#else
          0
#endif
        ));
    default:
      abort ();                 /* bad type */
      return 0;
    }
  }
}

/* If the parser returns TRUE_BOOLEXP, you lose */
/* TRUE_BOOLEXP cannot be typed in by the user; use @unlock instead */
static const char *parsebuf;
static dbref parse_player;

static void skip_whitespace (void)
{
  while (*parsebuf && isspace (*parsebuf))
    parsebuf++;
}

static struct boolexp *parse_boolexp_E (void);  /* defined below */
static struct boolexp *parse_boolprop (char *buf);      /* defined below */

/* F -> (E); F -> !F; F -> object identifier */
static struct boolexp *parse_boolexp_F (void)
{
  struct boolexp *b;
  char *p;
  char buf[BUFFER_LEN];
  char msg[BUFFER_LEN];

  skip_whitespace ();
  switch (*parsebuf) {
  case '(':
    parsebuf++;
    b = parse_boolexp_E ();
    skip_whitespace ();
    if (b == TRUE_BOOLEXP || *parsebuf++ != ')') {
      free_boolexp (b);
      return TRUE_BOOLEXP;
    } else {
      return b;
    }
    /* break; */
  case NOT_TOKEN:
    parsebuf++;
    b = (struct boolexp *) malloc (sizeof (struct boolexp));
    b->type = BOOLEXP_NOT;
    b->sub1 = parse_boolexp_F ();
    if (b->sub1 == TRUE_BOOLEXP) {
      free ((void *) b);
      return TRUE_BOOLEXP;
    } else {
      return b;
    }
    /* break */
  default:
    /* must have hit an object ref */
    /* load the name into our buffer */
    p = buf;
    while (*parsebuf
      && *parsebuf != AND_TOKEN
      && *parsebuf != OR_TOKEN && *parsebuf != ')') {
      *p++ = *parsebuf++;
    }
    /* strip trailing whitespace */
    *p-- = '\0';
    while (isspace (*p))
      *p-- = '\0';

    /* check to see if this is a property expression */
    if (index (buf, PROP_DELIMITER)) {
      return parse_boolprop (buf);
    }

    b = (struct boolexp *) malloc (sizeof (struct boolexp));
    b->type = BOOLEXP_CONST;

    /* do the match */
    init_match (parse_player, buf, TYPE_THING);
    match_neighbor ();
    match_possession ();
    match_me ();
    match_absolute ();
    match_player ();
    b->thing = match_result ();

    if (b->thing == NOTHING) {
      sprintf (msg, "I don't see %s here.", buf);
      notify (parse_player, msg);
      free ((void *) b);
      return TRUE_BOOLEXP;
    } else if (b->thing == AMBIGUOUS) {
      sprintf (msg, "I don't know which %s you mean!", buf);
      notify (parse_player, msg);
      free ((void *) b);
      return TRUE_BOOLEXP;
    } else {
      return b;
    }
    /* break */
  }
}

/* T -> F; T -> F & T */
static struct boolexp *parse_boolexp_T (void)
{
  struct boolexp *b;
  struct boolexp *b2;

  if ((b = parse_boolexp_F ()) == TRUE_BOOLEXP) {
    return b;
  } else {
    skip_whitespace ();
    if (*parsebuf == AND_TOKEN) {
      parsebuf++;

      b2 = (struct boolexp *) malloc (sizeof (struct boolexp));
      b2->type = BOOLEXP_AND;
      b2->sub1 = b;
      if ((b2->sub2 = parse_boolexp_T ()) == TRUE_BOOLEXP) {
        free_boolexp (b2);
        return TRUE_BOOLEXP;
      } else {
        return b2;
      }
    } else {
      return b;
    }
  }
}

/* E -> T; E -> T | E */
static struct boolexp *parse_boolexp_E (void)
{
  struct boolexp *b;
  struct boolexp *b2;

  if ((b = parse_boolexp_T ()) == TRUE_BOOLEXP) {
    return b;
  } else {
    skip_whitespace ();
    if (*parsebuf == OR_TOKEN) {
      parsebuf++;

      b2 = (struct boolexp *) malloc (sizeof (struct boolexp));
      b2->type = BOOLEXP_OR;
      b2->sub1 = b;
      if ((b2->sub2 = parse_boolexp_E ()) == TRUE_BOOLEXP) {
        free_boolexp (b2);
        return TRUE_BOOLEXP;
      } else {
        return b2;
      }
    } else {
      return b;
    }
  }
}

struct boolexp *parse_boolexp (dbref player, const char *buf)
{
  parsebuf = buf;
  parse_player = player;
  return parse_boolexp_E ();
}

/* parse a property expression
   If this gets changed, please also remember to modify set.c       */
static struct boolexp *parse_boolprop (char *buf)
{
  char *type = get_string (buf);
  char *class = index (type, PROP_DELIMITER);
  char *x;
  struct boolexp *b;
  struct plist *p;
  char *temp;

  x = type;
  b = (struct boolexp *) malloc (sizeof (struct boolexp));
  p = new_prop ();
  b->type = BOOLEXP_PROP;
  b->sub1 = b->sub2 = 0;
  b->thing = NOTHING;
  b->prop_check = p;
  while (isspace (*type) && (*type != PROP_DELIMITER))
    type++;
  if (*type == PROP_DELIMITER) {
    /* Oops!  Clean up and return a TRUE */
    free ((void *) x);
    free ((void *) b);
    free ((void *) p);
    return TRUE_BOOLEXP;
  }
  /* get rid of trailing spaces */
  for (temp = class - 1; isspace (*temp); temp--)
    /*EMPTY*/;
  temp++;
  *temp = '\0';
  class++;
  while (isspace (*class) && *class)
    class++;
  if (!*class) {
    /* Oops!  CLEAN UP AND RETURN A TRUE */
    free ((void *) x);
    free ((void *) b);
    free ((void *) p);
    return TRUE_BOOLEXP;
  }
  /* get rid of trailing spaces */
  for (temp = class; !isspace (*temp) && *temp; temp++)
    /*EMPTY*/;
  *temp = '\0';
  p->type = alloc_string (type);
  p->class = alloc_string (class);
  free ((void *) x);
  return b;
}