Lyonesse/bin/
Lyonesse/doc/eng/
Lyonesse/doc/ita/
Lyonesse/lib/
Lyonesse/lib/buildings/
Lyonesse/lib/clans/
Lyonesse/lib/data/
Lyonesse/lib/etc/
Lyonesse/lib/house/
Lyonesse/lib/misc/
Lyonesse/lib/plralias/A-E/
Lyonesse/lib/plralias/F-J/
Lyonesse/lib/plralias/K-O/
Lyonesse/lib/plralias/P-T/
Lyonesse/lib/plralias/U-Z/
Lyonesse/lib/plralias/ZZZ/
Lyonesse/lib/plrobjs/A-E/
Lyonesse/lib/plrobjs/F-J/
Lyonesse/lib/plrobjs/K-O/
Lyonesse/lib/plrobjs/P-T/
Lyonesse/lib/plrobjs/U-Z/
Lyonesse/lib/plrobjs/ZZZ/
Lyonesse/lib/plrsave/A-E/
Lyonesse/lib/plrsave/F-J/
Lyonesse/lib/plrsave/K-O/
Lyonesse/lib/plrsave/P-T/
Lyonesse/lib/plrsave/U-Z/
Lyonesse/lib/plrsave/ZZZ/
Lyonesse/lib/ships/
Lyonesse/lib/stables/
Lyonesse/lib/text/help/
Lyonesse/lib/world/
Lyonesse/lib/world/bld/
Lyonesse/lib/world/ship/
Lyonesse/lib/world/shp/
Lyonesse/lib/world/wls/
Lyonesse/lib/world/wls/Life/
Lyonesse/lib/world/wls/Map/
Lyonesse/log/
#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 */
}