/* $Header: /cvsroot/fbmuck/fbmuck/src/boolexp.c,v 1.10 2003/06/18 07:14:44 revar Exp $ */
#include "copyright.h"
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include "fbstrings.h"
#include "db.h"
#include "props.h"
#include "match.h"
#include "externs.h"
#include "params.h"
#include "tune.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.
* parse_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.
*/
struct boolexp *
alloc_boolnode(void)
{
return ((struct boolexp *) malloc(sizeof(struct boolexp)));
}
void
free_boolnode(struct boolexp *ptr)
{
free(ptr);
}
struct boolexp *
copy_bool(struct boolexp *old)
{
struct boolexp *o;
if (old == TRUE_BOOLEXP)
return TRUE_BOOLEXP;
o = alloc_boolnode();
if (!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_boolnode(o);
return 0;
}
o->prop_check = alloc_propnode(PropName(old->prop_check));
SetPFlagsRaw(o->prop_check, PropFlagsRaw(old->prop_check));
switch (PropType(old->prop_check)) {
case PROP_STRTYP:
SetPDataStr(o->prop_check, alloc_string(PropDataStr(old->prop_check)));
break;
default:
SetPDataVal(o->prop_check, PropDataVal(old->prop_check));
break;
}
break;
default:
log_status("PANIC: copy_boolexp: Error in boolexp!\n");
abort();
}
return o;
}
int
eval_boolexp_rec(int descr, dbref player, struct boolexp *b, dbref thing)
{
if (b == TRUE_BOOLEXP) {
return 1;
} else {
switch (b->type) {
case BOOLEXP_AND:
return (eval_boolexp_rec(descr, player, b->sub1, thing)
&& eval_boolexp_rec(descr, player, b->sub2, thing));
case BOOLEXP_OR:
return (eval_boolexp_rec(descr, player, b->sub1, thing)
|| eval_boolexp_rec(descr, player, b->sub2, thing));
case BOOLEXP_NOT:
return !eval_boolexp_rec(descr, player, b->sub1, thing);
case BOOLEXP_CONST:
if (b->thing == NOTHING)
return 0;
if (Typeof(b->thing) == TYPE_PROGRAM) {
struct inst *rv;
struct frame *tmpfr;
dbref real_player;
if (Typeof(player) == TYPE_PLAYER || Typeof(player) == TYPE_THING)
real_player = player;
else
real_player = OWNER(player);
tmpfr = interp(descr, real_player, DBFETCH(player)->location,
b->thing, thing, PREEMPT, STD_HARDUID, 0);
if (!tmpfr)
return (0);
rv = interp_loop(real_player, b->thing, tmpfr, 0);
return (rv != NULL);
}
return (b->thing == player || b->thing == OWNER(player)
|| member(b->thing, DBFETCH(player)->contents)
|| b->thing == DBFETCH(player)->location);
case BOOLEXP_PROP:
if (PropType(b->prop_check) == PROP_STRTYP) {
if (has_property_strict(descr, player, thing,
PropName(b->prop_check),
PropDataStr(b->prop_check), 0)) return 1;
if (has_property(descr, player, player,
PropName(b->prop_check), PropDataStr(b->prop_check), 0))
return 1;
}
return 0;
default:
abort(); /* bad type */
}
}
return 0;
}
int
eval_boolexp(int descr, dbref player, struct boolexp *b, dbref thing)
{
int result;
b = copy_bool(b);
result = eval_boolexp_rec(descr, player, b, thing);
free_boolexp(b);
return (result);
}
/* If the parser returns TRUE_BOOLEXP, you lose */
/* TRUE_BOOLEXP cannot be typed in by the user; use @unlock instead */
static void
skip_whitespace(const char **parsebuf)
{
while (**parsebuf && isspace(**parsebuf))
(*parsebuf)++;
}
static struct boolexp *parse_boolexp_E(int descr, const char **parsebuf, dbref player, int dbloadp); /* defined below */
static struct boolexp *parse_boolprop(char *buf); /* defined below */
/* F -> (E); F -> !F; F -> object identifier */
static struct boolexp *
parse_boolexp_F(int descr, const char **parsebuf, dbref player, int dbloadp)
{
struct boolexp *b;
char *p;
struct match_data md;
char buf[BUFFER_LEN];
char msg[BUFFER_LEN];
skip_whitespace(parsebuf);
switch (**parsebuf) {
case '(':
(*parsebuf)++;
b = parse_boolexp_E(descr, parsebuf, player, dbloadp);
skip_whitespace(parsebuf);
if (b == TRUE_BOOLEXP || *(*parsebuf)++ != ')') {
free_boolexp(b);
return TRUE_BOOLEXP;
} else {
return b;
}
/* break; */
case NOT_TOKEN:
(*parsebuf)++;
b = alloc_boolnode();
b->type = BOOLEXP_NOT;
b->sub1 = parse_boolexp_F(descr, parsebuf, player, dbloadp);
if (b->sub1 == TRUE_BOOLEXP) {
free_boolnode(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 = alloc_boolnode();
b->type = BOOLEXP_CONST;
/* do the match */
if (!dbloadp) {
init_match(descr, player, buf, TYPE_THING, &md);
match_neighbor(&md);
match_possession(&md);
match_me(&md);
match_here(&md);
match_absolute(&md);
match_registered(&md);
match_player(&md);
b->thing = match_result(&md);
if (b->thing == NOTHING) {
snprintf(msg, sizeof(msg), "I don't see %s here.", buf);
notify(player, msg);
free_boolnode(b);
return TRUE_BOOLEXP;
} else if (b->thing == AMBIGUOUS) {
snprintf(msg, sizeof(msg), "I don't know which %s you mean!", buf);
notify(player, msg);
free_boolnode(b);
return TRUE_BOOLEXP;
} else {
return b;
}
} else {
if (*buf != NUMBER_TOKEN || !number(buf + 1)) {
free_boolnode(b);
return TRUE_BOOLEXP;
}
b->thing = (dbref) atoi(buf + 1);
if (b->thing < 0 || b->thing >= db_top || Typeof(b->thing) == TYPE_GARBAGE) {
free_boolnode(b);
return TRUE_BOOLEXP;
} else {
return b;
}
}
/* break */
}
}
/* T -> F; T -> F & T */
static struct boolexp *
parse_boolexp_T(int descr, const char **parsebuf, dbref player, int dbloadp)
{
struct boolexp *b;
struct boolexp *b2;
if ((b = parse_boolexp_F(descr, parsebuf, player, dbloadp)) == TRUE_BOOLEXP) {
return b;
} else {
skip_whitespace(parsebuf);
if (**parsebuf == AND_TOKEN) {
(*parsebuf)++;
b2 = alloc_boolnode();
b2->type = BOOLEXP_AND;
b2->sub1 = b;
if ((b2->sub2 = parse_boolexp_T(descr, parsebuf, player, dbloadp)) == 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(int descr, const char **parsebuf, dbref player, int dbloadp)
{
struct boolexp *b;
struct boolexp *b2;
if ((b = parse_boolexp_T(descr, parsebuf, player, dbloadp)) == TRUE_BOOLEXP) {
return b;
} else {
skip_whitespace(parsebuf);
if (**parsebuf == OR_TOKEN) {
(*parsebuf)++;
b2 = alloc_boolnode();
b2->type = BOOLEXP_OR;
b2->sub1 = b;
if ((b2->sub2 = parse_boolexp_E(descr, parsebuf, player, dbloadp)) == TRUE_BOOLEXP) {
free_boolexp(b2);
return TRUE_BOOLEXP;
} else {
return b2;
}
} else {
return b;
}
}
}
struct boolexp *
parse_boolexp(int descr, dbref player, const char *buf, int dbloadp)
{
return parse_boolexp_E(descr, &buf, player, dbloadp);
}
/* 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 *strval = (char *) index(type, PROP_DELIMITER);
char *x;
struct boolexp *b;
PropPtr p;
char *temp;
x = type;
b = alloc_boolnode();
b->type = BOOLEXP_PROP;
b->sub1 = b->sub2 = 0;
b->thing = NOTHING;
while (isspace(*type))
type++;
if (*type == PROP_DELIMITER) {
/* Oops! Clean up and return a TRUE */
free((void *) x);
free_boolnode(b);
return TRUE_BOOLEXP;
}
/* get rid of trailing spaces */
for (temp = strval - 1; isspace(*temp); temp--) ;
temp++;
*temp = '\0';
strval++;
while (isspace(*strval) && *strval)
strval++;
if (!*strval) {
/* Oops! CLEAN UP AND RETURN A TRUE */
free((void *) x);
free_boolnode(b);
return TRUE_BOOLEXP;
}
/* get rid of trailing spaces */
for (temp = strval; !isspace(*temp) && *temp; temp++) ;
*temp = '\0';
b->prop_check = p = alloc_propnode(type);
SetPDataStr(p, alloc_string(strval));
SetPType(p, PROP_STRTYP);
free((void *) x);
return b;
}
long
size_boolexp(struct boolexp *b)
{
long result = 0L;
if (b == TRUE_BOOLEXP) {
return 0L;
} else {
result = sizeof(*b);
switch (b->type) {
case BOOLEXP_AND:
case BOOLEXP_OR:
result += size_boolexp(b->sub2);
case BOOLEXP_NOT:
result += size_boolexp(b->sub1);
case BOOLEXP_CONST:
break;
case BOOLEXP_PROP:
result += sizeof(*b->prop_check);
result += strlen(PropName(b->prop_check)) + 1;
if (PropDataStr(b->prop_check))
result += strlen(PropDataStr(b->prop_check)) + 1;
break;
default:
abort(); /* bad type */
}
return (result);
}
}
struct boolexp *
negate_boolexp(struct boolexp *b)
{
struct boolexp *n;
/* Obscure fact: !NOTHING == NOTHING in old-format databases! */
if (b == TRUE_BOOLEXP)
return TRUE_BOOLEXP;
n = alloc_boolnode();
n->type = BOOLEXP_NOT;
n->sub1 = b;
return n;
}
static struct boolexp *
getboolexp1(FILE * f)
{
struct boolexp *b;
PropPtr p;
char buf[BUFFER_LEN]; /* holds string for reading in property */
int c;
int i; /* index into buf */
c = getc(f);
switch (c) {
case '\n':
ungetc(c, f);
return TRUE_BOOLEXP;
/* break; */
case EOF:
abort(); /* unexpected EOF in boolexp */
break;
case '(':
b = alloc_boolnode();
if ((c = getc(f)) == '!') {
b->type = BOOLEXP_NOT;
b->sub1 = getboolexp1(f);
if (getc(f) != ')')
goto error;
return b;
} else {
ungetc(c, f);
b->sub1 = getboolexp1(f);
switch (c = getc(f)) {
case AND_TOKEN:
b->type = BOOLEXP_AND;
break;
case OR_TOKEN:
b->type = BOOLEXP_OR;
break;
default:
goto error;
/* break */
}
b->sub2 = getboolexp1(f);
if (getc(f) != ')')
goto error;
return b;
}
/* break; */
case '-':
/* obsolete NOTHING key */
/* eat it */
while ((c = getc(f)) != '\n')
if (c == EOF)
abort(); /* unexp EOF */
ungetc(c, f);
return TRUE_BOOLEXP;
/* break */
case '[':
/* property type */
b = alloc_boolnode();
b->type = BOOLEXP_PROP;
b->sub1 = b->sub2 = 0;
i = 0;
while ((c = getc(f)) != PROP_DELIMITER && i < BUFFER_LEN) {
buf[i] = c;
i++;
}
if (i >= BUFFER_LEN && c != PROP_DELIMITER)
goto error;
buf[i] = '\0';
p = b->prop_check = alloc_propnode(buf);
i = 0;
while ((c = getc(f)) != ']') {
if (c == '\\')
c = getc(f);
buf[i] = c;
i++;
}
buf[i] = '\0';
if (i >= BUFFER_LEN && c != ']')
goto error;
if (!number(buf)) {
SetPDataStr(p, alloc_string(buf));
SetPType(p, PROP_STRTYP);
} else {
SetPDataVal(p, atol(buf));
SetPType(p, PROP_INTTYP);
}
return b;
default:
/* better be a dbref */
ungetc(c, f);
b = alloc_boolnode();
b->type = BOOLEXP_CONST;
b->thing = 0;
/* NOTE possibly non-portable code */
/* Will need to be changed if putref/getref change */
while (isdigit(c = getc(f))) {
b->thing = b->thing * 10 + c - '0';
}
ungetc(c, f);
return b;
}
error:
abort(); /* bomb out */
return NULL;
}
struct boolexp *
getboolexp(FILE * f)
{
struct boolexp *b;
b = getboolexp1(f);
if (getc(f) != '\n')
abort(); /* parse error, we lose */
return b;
}
void
free_boolexp(struct boolexp *b)
{
if (b != TRUE_BOOLEXP) {
switch (b->type) {
case BOOLEXP_AND:
case BOOLEXP_OR:
free_boolexp(b->sub1);
free_boolexp(b->sub2);
free_boolnode(b);
break;
case BOOLEXP_NOT:
free_boolexp(b->sub1);
free_boolnode(b);
break;
case BOOLEXP_CONST:
free_boolnode(b);
break;
case BOOLEXP_PROP:
free_propnode(b->prop_check);
free_boolnode(b);
break;
}
}
}