/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/boolexp.c,v 1.8 90/09/16 04:40:58 rearl Exp $ */

/*
 * $Log:	boolexp.c,v $
 * Revision 1.8  90/09/16  04:40:58  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.7  90/09/10  02:19:08  rearl
 * Introduced string compression of properties, for the
 * COMPRESS compiler option.
 * 
 * Revision 1.6  90/08/27  03:19:28  rearl
 * Added match_here to key match routines.
 * 
 * Revision 1.5  90/08/11  03:49:51  rearl
 * *** empty log message ***
 * 
 * Revision 1.4  90/08/05  03:18:56  rearl
 * Redid matching routines.
 * 
 * Revision 1.3  90/08/02  18:49:20  rearl
 * Fixed some calls to logging functions.
 * 
 * Revision 1.2  90/07/29  17:30:37  rearl
 * Added support for ROOM locks to programs.
 * 
 * Revision 1.1  90/07/19  23:01:56  casie
 * Initial revision
 * 
 * 
 */

#include "copyright.h"
#include "config.h"

#include <ctype.h>

#include "strings.h"
#include "db.h"
#include "match.h"
#include "externs.h"
#include "params.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, dbref thing )
{
  int result;
  
  if(b == TRUE_BOOLEXP) {
    return 1;
  } else {
    switch(b->type) {
    case BOOLEXP_AND:
      return (eval_boolexp(player, b->sub1, thing)
	      && eval_boolexp(player, b->sub2, thing));
    case BOOLEXP_OR:
      return (eval_boolexp(player, b->sub1, thing)
	      || eval_boolexp(player, b->sub2, thing));
    case BOOLEXP_NOT:
      return !eval_boolexp(player, b->sub1, thing);
    case BOOLEXP_CONST:
      if (Typeof(b -> thing) == TYPE_PROGRAM)
	{
	  if (DBFETCH(player)->run) return 0;
	  result = interp(player, b -> thing, thing);
	  if (!(DBFETCH(player)->run -> pc))
	    {
	      free((void *) DBFETCH(player)->run);
	      DBFETCH(player)->run = 0;
	    }
	  return result;
	}
      return (b->thing == player
	      || member(b->thing, DBFETCH(player)->contents)
	      || b->thing == DBFETCH(player)->location);
    case BOOLEXP_PROP:
      return( has_property(player, b -> prop_check -> type,
			   b -> prop_check -> class));
    default:
      abort();		/* bad type */
      return 0;
    }
  }
}

struct boolexp *
  copy_bool(struct boolexp *old)
{
  struct boolexp *o = (struct boolexp *)malloc(sizeof(struct boolexp));
  
  if (!old || !o)
    return 0;
  
  o -> type = old -> type;
  
  switch ( old -> type )
    {
    case BOOLEXP_AND:
    case BOOLEXP_OR:
      o -> sub1 = copy_bool(old -> sub1);
      o -> sub2 = copy_bool(old -> sub2);
      break;
    case BOOLEXP_NOT:
      o -> sub1 = copy_bool(old -> sub1);
      break;
    case BOOLEXP_CONST:
      o -> thing = old -> thing;
      break;
    case BOOLEXP_PROP:
      if (!old -> prop_check)
	{
	  free((void *) o);
	  return 0;
	}
      o -> prop_check = new_prop();
      o -> prop_check -> class = alloc_string(old -> prop_check -> class);
      o -> prop_check -> type = alloc_string(old -> prop_check -> type);
      break;
    default:
      log_status("PANIC: copy_boolexp: Error in boolexp!\n");
      abort();
    }
  return o;
}

/* 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;
  struct match_data md;
  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, &md);
    match_neighbor(&md);
    match_possession(&md);
    match_me(&md);
    match_here(&md);
    match_absolute(&md);
    match_player(&md);
    b->thing = match_result(&md);
    
    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 = alloc_string(buf);
  char    *class = (char *)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 --)
    ;
  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++)
    ;
  *temp = '\0';
  p -> type = alloc_string(type);
  p -> class = alloc_string(class);

  free((void *) x);
  return b;
}