#ifdef __RCSID__
static char rcsid[] = "$Id: parser.c,v 1.15 1997/11/06 23:42:46 brazil Exp brazil $";
#endif
#include "conf.h"
#include "sysdep.h"
typedef struct
{
int type;
int value;
} TOKEN;
#define TOP 256
#define WTOP 3
#define NON_TERMINAL 0
#define END_OF_INPUT 10
#define MAX_ANSWER 32767
#define SCR_WIDTH 76
#include "structs.h"
#include "utils.h"
#define NO 0
#define YES 1
/* local functions */
void parse_pop( TOKEN *working, TOKEN *stack );
int HasKet( char *s );
char strip_expression( char *word, char *expression_str, char *offset );
char get_numbers( char *expression_str, int *bracket_i, int *word_picked );
int lexical_analyser( char *in_string, struct char_data *ch );
char lookup_value( char *variable, int *value, CHAR_DATA *ch, ROOM_DATA *room, OBJ_DATA *obj );
char calculate_value( char *variable, int *value );
int syntax_analyser( TOKEN *input );
int top_most_terminal( TOKEN *stack );
long parser_evaluate_expression( TOKEN *working );
int power10( int num );
void clean_up( char *in, CHAR_DATA *ch );
void parse_push( TOKEN *input, TOKEN *stack );
/* ------------------------------------------------ */
void parse_push(TOKEN * input, TOKEN * stack)
{
stack[TOP].type++;
stack[stack[TOP].type].type = input->type;
stack[stack[TOP].type].value = input->value;
return;
}
void parse_pop(TOKEN * working, TOKEN * stack)
{
working[WTOP].type++;
working[working[WTOP].type].type = stack[stack[TOP].type].type;
working[working[WTOP].type].value = stack[stack[TOP].type].value;
stack[TOP].type--;
return;
}
int HasKet(char *s)
{
int i;
for (i = 0; s[i] != 0; i++)
if (s[i] == ']')
return (1);
return (0);
}
void master_parser(char *buf, CHAR_DATA *ch, ROOM_DATA *room, OBJ_DATA *obj)
{
char *buf2p, *str[10], *pos[10];
int wordnum = 0;
int i, j, k, l, m;
int bracket_i, word_picked, picked;
char any_true, offset, result;
char buf2[1000];
char tmp[100];
char expression_str[256];
int selection[25];
int all_words, poss_words, p_words, bracket;
bracket = 1;
/* begin parse */
while (HasKet (buf))
{
wordnum = 0;
poss_words = 0;
p_words = 0;
any_true = NO;
for (j = 0; (buf[j] != ']') && (buf[j] != '\0'); j++);
for (i = j; (buf[i] != '[') && (i != 0); i--);
for (k = 0; k < (j - i - 1); k++)
buf2[k] = buf[k + i + 1];
buf2[k] = '\0';
str[wordnum] = buf2;
for (buf2p = buf2; *buf2p; buf2p++)
{
if (*buf2p == '\\')
{
*buf2p = '\0';
wordnum++;
str[wordnum] = buf2p + 1;
}
}
for (all_words = 0; all_words < wordnum + 1; all_words++)
{
if ((result = strip_expression (str[all_words], expression_str, &offset)))
str[all_words] += offset; /* Offset the string past the expression */
switch (result)
{
case 0: /* No expression */
/* Add to group of possibilities */
str[poss_words] = str[all_words];
poss_words++;
any_true = YES;
break;
case 1: /* A '!' */
pos[p_words] = str[all_words]; /* Save to check later */
p_words++;
break;
case 2: /* An expression */
/* If exp is true */
if (lexical_analyser(expression_str, ch))
{
/* Add to group of possibilities */
str[poss_words] = str[all_words];
poss_words++;
any_true = YES;
}
break;
case 3: /* A '#' */
if (get_numbers (expression_str, &bracket_i, &word_picked))
{
/*
* If the word picked for the 'bracket_i'th bracket was
* 'word_picked' then make this a possibility
*/
if (selection[bracket_i] == word_picked)
{
str[poss_words] = str[all_words];
poss_words++;
any_true = YES;
}
}
} /* End Case */
} /* End For */
/*
* No go through all the ! expressions and make them possibilities
* if none of the other expressions were true
*/
if ((!any_true) && (p_words > 0))
{
for (all_words = 0; all_words < p_words; all_words++)
{
str[poss_words] = pos[all_words];
poss_words++;
}
}
if (poss_words == 0)
{
*tmp = 0;
}
else
{
picked = (rand () % poss_words);
selection[bracket] = picked + 1;
bracket++;
strcpy (tmp, str[picked]);
}
/* Now, between put tmp back into buf where it belongs. */
for (l = i; l < (i + (int)strlen(tmp)); l++)
buf[l] = tmp[l - i];
for (m = l; (buf[m] != '\0'); m++)
buf[m] = buf[m + ((j + 1) - l)];
buf[m] = '\0';
/* Now run buf through cleanup to distribute evenly on lines */
}
clean_up(buf, ch);
}
/*
* Strips the expression out of the word passed to it, and returns the
* expression in expression_str, offset is the value that must be added
* to the string passed to strip out the expression. Also returns a
* char
* representing what kind of expression there was:
* 0 - No Expression, 1 - !, 2 - ?, 3 - #
*/
char strip_expression(char *word, char *expression_str, char *offset)
{
char type, spaces;
int i;
char buf0[128];
spaces = 0;
while (*word == ' ') /* Skip spaces */
{
word++;
spaces++;
}
if (*word != '!' && *word != '?' && *word != '#')
return (0); /* No expression */
/* Zip through string until we hit the ':' or end */
for (i = 0; word[i] != ':' && word[i]; i++);
if (word[i] == ':')
word[i] = 0; /* Set end of string */
else /* No ':' found */
{
sprintf(buf0, "SPARSER: %c found without a matching ':'", word[i]);
log (buf0);
return (0);
}
if (*word == '!')
type = 1;
else if (*word == '?')
type = 2;
else
type = 3;
word++; /* Skip over first character */
strcpy (expression_str, word);
*offset = spaces + i + 1;
return (type);
}
char get_numbers(char *expression_str, int *bracket_i, int *word_picked)
{
char number[3], new_str[20], *exp_str;
int i;
char buf0[128];
i = 0;
exp_str = new_str;
while (*expression_str) /* Eat spaces, put in exp_str */
{
if (*expression_str != ' ')
{
new_str[i] = *expression_str;
i++;
}
expression_str++;
}
new_str[i] = 0;
i = 0;
while (*exp_str && isdigit (*exp_str) && i < 3) /* Get first number */
{
number[i] = *exp_str;
i++;
exp_str++;
}
if (*exp_str == 0) /* If we're at end of string already - error */
{
log ("SPARSER: # Statement doesn't have 2 numbers.");
return (0);
}
if (i == 3 && isdigit (*exp_str)) /* If number is bigger than 3 digits - error */
{
log ("SPARSER: First number too large in # statement.");
return (0);
}
if (*exp_str != ',') /* If numbers not separated by comma - error */
{
sprintf(buf0, "SPARSER: Illegal character '%c' in # statement.", *exp_str);
log (buf0);
return (0);
}
if (i == 0)
{
log ("SPARSER: Missing first number in # statement.");
return (0);
}
/* Otherwise, convert to a number */
number[i] = 0;
calculate_value (number, bracket_i);
exp_str++; /* Skip by comma */
i = 0;
while (*exp_str && isdigit (*exp_str) && i < 3) /* Get second number */
{
number[i] = *exp_str;
i++;
exp_str++;
}
if (*exp_str == 0 && i == 0) /* If we're at end of string - error */
{
log ("SPARSER: Second number missing in # statement.");
return (0);
}
if (i == 3 && isdigit (*exp_str)) /* If number is bigger than 3 digits - error */
{
log ("SPARSER: Second number too large in # statement.");
return (0);
}
if (*exp_str != 0) /* Illegal character */
{
sprintf(buf0, "SPARSER: Illegal character '%c' in # statement.", *exp_str);
log (buf0);
return (0);
}
/*Otherwise, convert second number */
number[i] = 0;
calculate_value (number, word_picked);
return (1);
}
/* Lexical Analyser: Takes in an input string, converts to tokens, passes
tokens onto
syntax analyser, then calculates and returns answer */
int lexical_analyser(char *in_string, CHAR_DATA *ch)
{
TOKEN output[257];
ROOM_DATA *room = NULL;
OBJ_DATA *obj = NULL;
char *c, variable[25], token_found, error_found;
int output_i, variable_i, state;
int value, answer, prev_token;
int state_to_token_type[22] =
{ 0, 7, 7, 18, 12, 11, 0, 0, 0, 16, 14,
13, 15, 5, 6, 17, 1, 3, 4, 8, 9, 2 };
state = 0;
variable_i = 0;
output_i = 0;
token_found = NO;
error_found = NO;
prev_token = 0;
c = in_string;
while (*c != 0 && error_found == NO)
{
if (*c != ' ') /* Skip spaces */
{
switch (state)
{
case 0:
if (isalpha (*c))
{
variable[variable_i] = *c;
variable_i++;
variable[variable_i] = 0;
state = 1;
}
else if (isdigit (*c))
{
variable[variable_i] = *c;
variable_i++;
variable[variable_i] = 0;
state = 2;
}
else
{
switch (*c)
{
case '!':
state = 3;
break;
case '<':
state = 4;
break;
case '>':
state = 5;
break;
case '=':
state = 6;
break;
case '&':
state = 7;
break;
case '|':
state = 8;
break;
case '-':
state = 15;
break;
case '+':
state = 16;
break;
case '*':
state = 17;
break;
case '/':
state = 18;
break;
case '(':
state = 19;
break;
case ')':
state = 20;
break;
default:
error_found = YES;
}
}
break; /* end of case 0 */
case 1:
if (isalnum (*c))
{
variable[variable_i] = *c;
variable_i++;
variable[variable_i] = 0;
}
else
{
variable_i = 0;
token_found = YES;
}
break;
case 2:
if (isdigit (*c))
{
variable[variable_i] = *c;
variable_i++;
variable[variable_i] = 0;
}
else
{
variable_i = 0;
token_found = YES;
}
break;
case 3:
case 4:
case 5:
if (*c == '=')
state += 6;
else
token_found = YES;
break;
case 6:
if (*c == '=')
state = 12;
else
error_found = YES;
break;
case 7:
if (*c == '&')
state = 13;
else
error_found = YES;
break;
case 8:
if (*c == '|')
state = 14;
else
error_found = YES;
break;
} /* end of switch (state) */
} /* end of skip if *c == ' ' */
if (state > 8 || token_found || *(c + 1) == 0)
{
value = 0;
/* IF '-' is the token, make it a binary minus if previous token
* was ')' or an id/constant */
if ((state == 15) && ((prev_token == 9) || (prev_token == 7)))
state = 21;
if (state == 1)
error_found = lookup_value(variable, &value, ch, room, obj);
if (state == 2)
error_found = calculate_value (variable, &value);
if (state_to_token_type[state] == 0)
error_found = YES;
else
{
output[output_i].type = state_to_token_type[state];
output[output_i].value = value;
prev_token = output[output_i].type;
output_i++;
state = 0;
}
}
if (!token_found)
c++;
token_found = NO;
} /*end of while */
if (error_found)
{
/* Log error here */
log ("SPARSER: Error found - lexical parse aborted.");
answer = 0;
}
else
{
/* add end of stream token */
output[output_i].type = END_OF_INPUT;
output[TOP].type = 0;
if (output_i == 1) /* If just 1 token */
answer = output[0].value;
else
answer = syntax_analyser (output);
}
return (answer);
}
/*************************************************************************
* *
* LOOKUP_VALUE: Compares the string 'variable' passed to it with all *
* the strings in 'var_list', if it finds a match, it uses a co-respond- *
* ing Macro to change the int '*value' passed to it; otherwise it sets *
* '*value' to zero. Returns 1 if no value set, or 0 if all is ok. *
* *
*************************************************************************/
#define NUMBER_OF_VARS 13
char lookup_value(char *variable, int *value, CHAR_DATA *ch, ROOM_DATA *room, OBJ_DATA *obj)
{
char *var_list[NUMBER_OF_VARS] =
{
"str" , "int" , "dex" , "wis" , /* 0-3 */
"hour" , "cha" , "level" , "align" , /* 4-7 */
"iscleric", "isthief" , "con" , "height", /* 8-11 */
"weight" /* 12 */
};
int i;
char buf0[128];
extern TIME_INFO_DATA time_info;
for (i = 0; i < NUMBER_OF_VARS; i++)
{
/* If match found, break out of the loop */
if (!str_cmp(variable, var_list[i]))
break;
}
/* Place Macros here to calculate value of variable, 0 is the first
* variable, etc. Note, may have to pass ch to this function to find
* variables relating to character - such as str, dex, int, etc. */
switch (i)
{
case 0:
*value = GET_STR (ch);
break;
case 1:
*value = GET_INT (ch);
break;
case 2:
*value = GET_DEX (ch);
break;
case 3:
*value = GET_WIS (ch);
break;
case 4:
*value = time_info.hours;
break;
case 5:
*value = GET_CHA (ch);
break;
case 6:
*value = GET_LEVEL (ch);
break;
case 7:
*value = GET_ALIGNMENT(ch);
break;
case 8:
*value = (GET_CLASS(ch) == CLASS_CLERIC);
break;
case 9:
*value = (GET_CLASS(ch) == CLASS_THIEF);
break;
case 10:
*value = GET_CON(ch);
break;
case 11:
*value = GET_HEIGHT(ch);
break;
case 12:
*value = GET_WEIGHT(ch);
break;
default:
*value = 0;
/* Log error or whatever here */
sprintf(buf0, "SPARSER: Couldn't find variable - '%s'", variable);
log (buf0);
return (YES);
}
return (NO);
}
/*************************************************************************
* *
* CALCULATE_VALUE: Converts the string 'variable' passed to it into a *
* number which it stores in '*variable' - returns YES if the number *
* exceeded 32767, and sets the value to 32767, otherwise, it returns NO *
* *
*************************************************************************/
char calculate_value(char *variable, int *value)
{
char was_error;
int len, i;
long calc_value;
was_error = NO;
len = strlen (variable);
calc_value = 0;
for (i = len - 1; i >= 0; i--)
{
calc_value += ((variable[i] - 48) * power10 (len - i - 1));
if (calc_value > MAX_ANSWER)
{
/* Log error message here */
log ("SPARSER: Varaible overflow - setting to MAX_ANSWER");
calc_value = MAX_ANSWER;
/* was_error = YES ; */
break;
}
}
*value = calc_value;
return (was_error);
}
int syntax_analyser(TOKEN * input)
{
TOKEN stack[257], working[4], answer;
char f[19] = {0, 11, 11, 13, 13, 5, 3, 16, 0, 16, 0, 9, 9, 9, 9, 7, 7, 14, 14};
char g[19] = {0, 10, 10, 12, 12, 4, 2, 17, 17, 0, 0, 8, 8, 8, 8, 6, 6, 15, 15};
stack[TOP].type = 0;
stack[0].type = END_OF_INPUT;
while (input->type != END_OF_INPUT || stack[TOP].type > 1)
{
if (f[top_most_terminal (stack)] <= g[input->type])
{
parse_push (input, stack);
input++;
}
else
{
working[WTOP].type = -1;
parse_pop(working, stack);
while (f[top_most_terminal (stack)] >= g[working[working[WTOP].type].type])
parse_pop(working, stack);
if (stack[stack[TOP].type].type == NON_TERMINAL)
parse_pop(working, stack);
answer.type = NON_TERMINAL;
answer.value = parser_evaluate_expression (working);
parse_push(&answer, stack);
}
}
return (answer.value);
}
int top_most_terminal(TOKEN * stack)
{
int index;
index = stack[TOP].type;
while (stack[index].type == NON_TERMINAL)
index--;
return (stack[index].type);
}
long parser_evaluate_expression(TOKEN * working)
{
long answer = 0;
if (working[WTOP].type == 2) /* 3 operators */
{
if (working[0].type == NON_TERMINAL) /* It's an E op E */
{
switch (working[1].type)
{
case 1:
answer = (long) working[2].value + (long) working[0].value;
if (answer > MAX_ANSWER)
{
log ("SPARSER: Answer would exceed Max, truncating...");
answer = MAX_ANSWER;
}
if (answer < -MAX_ANSWER)
{
log ("SPARSER: Answer would be below -Max, truncating...");
answer = -MAX_ANSWER;
}
break;
case 2:
answer = (long) working[2].value - (long) working[0].value;
if (answer > MAX_ANSWER)
{
log ("SPARSER: Answer would exceed Max, truncating...");
answer = MAX_ANSWER;
}
if (answer < -MAX_ANSWER)
{
log ("SPARSER: Answer would be below -Max, truncating...");
answer = -MAX_ANSWER;
}
break;
case 3:
answer = (long) working[2].value * (long) working[0].value;
if (answer > MAX_ANSWER)
{
log ("SPARSER: Answer would exceed Max, truncating...");
answer = MAX_ANSWER;
}
if (answer < -MAX_ANSWER)
{
log ("SPARSER: Answer would be below -Max, truncating...");
answer = -MAX_ANSWER;
}
break;
case 4:
if (working[0].value == 0)
{
log ("SPARSER: Cannot divide by zero, setting answer to 0...");
answer = 0;
}
else
answer = working[2].value / working[0].value;
break;
case 5:
answer = working[2].value && working[0].value;
break;
case 6:
answer = working[2].value || working[0].value;
break;
case 11:
answer = working[2].value > working[0].value;
break;
case 12:
answer = working[2].value < working[0].value;
break;
case 13:
answer = working[2].value >= working[0].value;
break;
case 14:
answer = working[2].value <= working[0].value;
break;
case 15:
answer = working[2].value == working[0].value;
break;
case 16:
answer = working[2].value != working[0].value;
break;
} /* End switch */
}
else /* It's (E) */
answer = working[1].value;
}
if (working[WTOP].type == 1) /* 2 operators */
if (working[1].type == 17) /* Unary Minus */
answer = -working[0].value;
else /* ! sign */
answer = !working[0].value;
if (working[WTOP].type == 0) /* 1 operator */
answer = working[0].value;
return (answer);
}
int power10(int num)
{
int i;
int value;
value = 1;
for (i = 0; i < num; i++)
value *= 10;
return (value);
}
#define RETURN_CHAR '|'
void clean_up (char *in, CHAR_DATA *ch)
{
char buf[1000];
int notdone, linelen, bufi, ini, wordlen, whitespacelen;
int exit, word, letter, endspacelen;
int width = SCR_WIDTH;
notdone = 1;
linelen = 0;
bufi = 0;
ini = 0;
word = 0;
strcpy (buf, in);
while (notdone)
{
wordlen = 0;
whitespacelen = 0;
endspacelen = 0;
word++;
while (buf[bufi] == ' ') /* snarf initial whitespace */
{
in[ini++] = buf[bufi++];
whitespacelen++;
}
exit = 0;
letter = 0;
while (buf[bufi] != ' ' && !exit)
{
letter++;
switch (buf[bufi])
{
case '\n':
case '\r':
if (letter > 1 && word > 1)
{
in[ini++] = ' ';
whitespacelen++;
}
endspacelen++;
bufi++;
while (buf[bufi] == '\n' || buf[bufi] == '\r')
{
bufi++;
endspacelen++;
}
exit = 1;
break;
case '\0':
notdone = 0;
linelen = 0;
in[ini] = '\0';
exit = 1;
break;
case RETURN_CHAR:
if (letter > 1)
exit = 1;
else
{
in[ini++] = '\n';
in[ini++] = '\r';
bufi++;
word = 0;
linelen = 0;
endspacelen = 0;
exit = 1;
}
break;
default:
in[ini++] = buf[bufi++];
wordlen++;
}
}
linelen += (wordlen + whitespacelen);
if (linelen >= width)
{
bufi -= (wordlen + endspacelen);
ini -= (wordlen + whitespacelen);
in[ini++] = '\n';
in[ini++] = '\r';
linelen = 0;
word = 0;
}
} /* end outer while */
}