/*
0       10        20        30        40        50        60        70        80
12345678901234567890123456789012345678901234567890123456789012345678901234567890
*/
/*----------------------------------------------------------------------------*/
/* Logic evaluator                                                            */
/* LOG32LLC Assignment 2                                                      */
/* Sten M. Andersen                                                           */
/*----------------------------------------------------------------------------*/
#include <stdio.h>
#include <setjmp.h>
#include <ctype.h>		// isprint()

#define MAX_TOKEN_STRING_LENGTH     80
#define MAX_BUFF                    80

typedef enum 
{
	false, 
	true
} BOOL;

/*----------------------------------------------------------------------------*/
/* Error codes                                                                */
/*----------------------------------------------------------------------------*/

typedef enum {
	NO_ERROR,
	EXPECTED_OPER,
	EXPECTED_LEFT_PARENT,
	EXPECTED_RIGHT_PARENT,
	EXPECTED_UNARY,
	UNEXPECTED_ERROR,
	TOO_LONG_INPUT,
} ERROR_CODE;

/*----------------------------------------------------------------------------*/
/* Error messages                                                             */
/*----------------------------------------------------------------------------*/

char *error_messages[] = {
	"No error",
	"Seen %c when & v > = expected",
	"Seen %c when ( expected",				// Not used here
	"Seen %c when ) expected",
	"Seen %c when 0 1 - ( expected",
	"Unexpected error",
	"Input string too long",
};

/*----------------------------------------------------------------------------*/
/* Character codes                                                            */
/*----------------------------------------------------------------------------*/
// uc = upper case, lc = lower case

typedef enum {
	UC_LETTER, LC_LETTER, DIGIT, SPECIAL,
} CHAR_CODE;

/*----------------------------------------------------------------------------*/
/* Token codes                                                                */
/*----------------------------------------------------------------------------*/
// Words are: AND OR NOT IMP IFF
// Symbols are a, b, c, ... ,z.
// number is a digit, sice only 0 and 1 are legal

typedef enum {
	NO_TOKEN, WORD, SYMBOL, NUMBER,
} TOKEN_CODE;

/*----------------------------------------------------------------------------*/
/* Globals                                                                    */
/*----------------------------------------------------------------------------*/

char buff[ MAX_BUFF ];   // Input buffer storage
int opvalues[ MAX_BUFF ];// Truth value of each opeator in formula

char ch;			     // The current char to be processed
BOOL cont = true;        // User tells us when to end
int bufferpointer;       // Keep a count, so we can point to error
jmp_buf start;	     	 // On error: empty stack and go to start 

// This idea for how to extract tokens is from 
// Mak, R. (1991). Writing compilers and interpreters. An applied approach.
// Brisbane: John Wiley & Son, Inc
TOKEN_CODE token;
char token_string[MAX_TOKEN_STRING_LENGTH]; // Token string
char *tokenp = token_string;				// Token string pointer

CHAR_CODE char_table[256];

/*----------------------------------------------------------------------------*/
/* char_code  Return the character code of ch                                 */
/*----------------------------------------------------------------------------*/

#define char_code( ch )	char_table[ch]

/*----------------------------------------------------------------------------*/
/* Function declarations                                                      */
/*----------------------------------------------------------------------------*/

void init();
void formula();          // The work horse
void get_char();	     // Puts the next char in global ch
void get_token();
void get_word();
void get_symbol();
void get_number();
void get_special();
void error(int e);       // Handles errors
void print_value( int res );
void print_blanks( int n );
void print_help();

/*----------------------------------------------------------------------------*/
/* Initialise                                                                 */
/*----------------------------------------------------------------------------*/

void init() {
	bufferpointer = 0;	  
	int i;
	for ( i = 0; i < MAX_BUFF; i++ ) {
		opvalues[ i ] = -1;
		buff[ i ] = NULL;
	}
	
	// Init character table
	for ( i = 0;    i < 256;   ++i  ) char_table[ i  ] = SPECIAL;
	for ( ch = '0'; ch <= '1'; ++ch ) char_table[ ch ] = DIGIT;
	for ( ch = 'A'; ch <= 'Z'; ++ch ) char_table[ ch ] = UC_LETTER;
	for ( ch = 'a'; ch <= 'z'; ++ch ) char_table[ ch ] = LC_LETTER;
}

/*----------------------------------------------------------------------------*/
/* Character routines                                                         */
/*----------------------------------------------------------------------------*/

void get_char() {  // Gets char into ch and skips blanks
	ch = getchar();
	if ( !isprint( ch ) ) ch = ' '; 
	if ( bufferpointer < MAX_BUFF )
		buff[ bufferpointer++ ] = ch;
	else 
		error( TOO_LONG_INPUT );
}

void skip_blanks() {
	do {
		get_char();
	} while ( ch == ' ' );
}

void get_token() {
	skip_blanks();
	tokenp = token_string;
	
	switch ( char_code( ch ) ) {
		case UC_LETTER:	get_word();		break;
		case LC_LETTER: get_symbol();	break;
		case DIGIT:		get_number();   break;
		default:		get_special();  break;
	}
}

void get_word() {
	while ( char_code( ch ) == UC_LETTER ) {
		*tokenp++ = ch;
		get_char();
	}
	
	*tokenp = '\0';
	token = WORD;
	
	if ( strcmp( token_string, "AND") == 0 ) ch = '&';
	if ( strcmp( token_string, "OR" ) == 0 ) ch = 'v';
	if ( strcmp( token_string, "NOT") == 0 ) ch = '-';
	if ( strcmp( token_string, "IMP") == 0 ) ch = '>';
	if ( strcmp( token_string, "IFF") == 0 ) ch = '=';
}

void get_symbol() {
	token = SYMBOL;
}

void get_number() {
	token = NUMBER;
}

void get_special() {
	token = SPECIAL;
}

/*----------------------------------------------------------------------------*/
/* Formula (the work horse)                                                   */
/*----------------------------------------------------------------------------*/

void formula(int* result) {
	char oper;
	int left, right, buffpnt;
	
	get_token();
	
/*	  switch( token )
	{
		case WORD:
			if ( strcmp( token_string, "AND") == 0 ) ch = '&';
			if ( strcmp( token_string, "OR" ) == 0 ) ch = 'v';
			if ( strcmp( token_string, "NOT") == 0 ) ch = '-';
			if ( strcmp( token_string, "IMP") == 0 ) ch = '>';
			if ( strcmp( token_string, "IFF") == 0 ) ch = '=';
			break;
		case NUMBER:
			break;
		case SYMBOL:
			break;
//	  	  default: 
	} 
*/ 
		
	switch ( ch )
	{
		case 'q':				// User wants to quit
			cont = false;
			return;	   
		case 'h':				// User needs help
			print_help();
			return;
		case '0':
			*result = false; 
			break;
		case '1':
			*result = true; 
			break;	   	      	   	   
		case '-':
			formula( &right ); 
			*result = opvalues[ bufferpointer-1 ] = !right; ;
			break;
		case '(':
			formula( &left );
			get_token();
			if ( ch != '&' && ch != 'v' && ch != '>' && ch != '=' ) 
				error( EXPECTED_OPER );
			oper = ch;
			buffpnt = bufferpointer-1;
			formula( &right );
			get_token();
			if ( ch != ')' )
				error( EXPECTED_RIGHT_PARENT );
			
			switch( oper ) {
				case '&': 
					*result = opvalues[ buffpnt ] = left && right;
					break;
				case 'v': 
					*result = opvalues[ buffpnt ] = left || right;
					break;
				case '>': 
					*result = opvalues[ buffpnt ] = !left || right;
					break;
				case '=': 
					*result = opvalues[ buffpnt] = (left == right);
					break;
				default: // Should never reach this stage
					error( UNEXPECTED_ERROR );
			}
			break;
		default:	// error msg
			error( EXPECTED_UNARY );
	}	 	 
}

/*----------------------------------------------------------------------------*/
/* Printing functions                                                         */
/*----------------------------------------------------------------------------*/

void print_help() {
	printf("\nLogic evaluator Ver 0.8 by Sten M. Andersen\n");
	printf("-------------------------------------------\n");
	printf("h    this help menu\n");
	printf("q    quit\n\n");
	printf("formula ::=  '0' | '1'\n");
	printf("            | '-' formula\n");
	printf("            | '(' formula ('&' | 'v' | '>' | '=') formula ')'\n\n");
}

// Prints out error #e, the offending formula, and points to error
void error(int e) {
	printf( "\n" ); printf( error_messages[ e ], ch );
	//	  printf( error_messages[ e ] );
 	printf( "\n%s\n", buff);
	print_blanks( bufferpointer-1 ); // Better way?
	printf( "^\n" );
	fflush( stdin );;
	longjmp( start, 0 );
}

void print_value( int res ) {
	printf( "\n%s\n", buff );          // Print formated formula from buffer
	int i;
	for ( i = 0; i < MAX_BUFF; i++ ) { // Print each operator's value
		if ( opvalues[ i ] != -1 )
			printf( "%i", opvalues[ i ] );
		else 
			printf(" ");               // If not an operator, print a blank
	}
	printf( "Value: %i\n", res );	   // Print out formula's final value
}

void print_blanks( int n ) {
	int i;			// There must be a better way, .e.g., a format-thingy
	for ( i = 0; i < n; i++ )
		printf(" ");
}

/*----------------------------------------------------------------------------*/
/* Main                                                                       */
/*----------------------------------------------------------------------------*/

int main(int argc, char **argv)
{
	int res;	
	while ( cont ) {
		setjmp( start );
		init();
		printf("Formula (h=help): ");
		formula( &res );
		if ( ch != 'q' && ch != 'h' )
			print_value( res );
	}
	return (0);
}


