/* $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 <ctype.h>
#include <string.h> /* some of you might have to use strings.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;
}