/* FILE: parser.c
 * PURPOSE:
 *    Functions that implement a Predictive Parser with
 *    Panic-Mode error recovery.
 *    The grammar implemented is Program Grammar #2.
 *    (CST 8152 - 98W - Assignment 6)
 *    Grammar productions are shown in the ALGORITHM for each function.
 *    Students should fill in a high-level description of each algorithm.
 * HISTORY:
 *    Ian D. Allen   idallen@freenet.carleton.ca
 */

//FIXME-- add system includes here
//FIXME-- add your local includes here

/* Static storage local to this parser:
 * - the look ahead token returned by the scanner
 * - a counter of statements parsed by the parser
 * - the name of the input stream (for error messages)
 * - the file descriptor of the output stream
 * - the name of the output stream
 */
static Token token;
static long stmtcounter = 0;
static char *pinfname = "UNKNOWN-IN";
static FILE *poutfd = NULL;
static char *poutfname = "UNKNOWN-OUT";

/* Here are some macros that make the parser code nicer to read.
 * We cast the line number into a long int so that it has a known
 * width when passed to varargs functions (such as printf) on
 * different architectures.
 */
#define LOOKAHEAD	token.type
#define LEXEME   	token.lexeme
#define LINENO   	(long int)token.lineno
#define GET_LOOKAHEAD	{token = scanner();}

//FIXME-- Your prototypes for your static functions go here.



/* FUNCTION: item_value
 * PURPOSE:
 *    Return a printable version of whatever is in an item.
 *    Must be able to return at least two different static strings,
 *    so it can be safely used twice in an argument list.
 * ALGORITHM:
 *    If item is an integer,
 *         switch static buffers
 *         safely print it into static buffer
 *         return a pointer to the static buffer.
 *    Otherwise just return the lexeme string.
 *
 * NOTE
 *    This re-uses the same two static buffers to hold numbers, so 
 *    be careful not to call it in a situation where you need three.
 */
#define NUMBUFS    2    /* number of rotating static buffers */
#define NUMBUFMAX 80    /* larger than largest formatted number */
	static char *
item_value(
	Item *itemp	/* IN: pointer to item */
){
	static char numbuffer[NUMBUFS][NUMBUFMAX];
	static bufcount;
	char *ret = "--OOPS--";

	switch(itemp->type){
	case TT_UINT:
		/* keep rotating among static buffers */
		bufcount = (bufcount+1) % NUMBUFS;
		sprintf(ret = numbuffer[bufcount], "%ld", itemp->u.uint);
		break;
	default:
		ret = (itemp->lexeme) ? itemp->lexeme : "[NULL POINTER]";
	}
	return ret;
}


/* FUNCTION: copy_push_item
 * PURPOSE:
 *    Evaluate a token by copying a Token structure to an Item and strdup()
 *    any string(s), then push the resulting Item on the stack.
 * ALGORITHM:
 *    Copy the Token structure to an Item structure.
 *    Use mystrdup() to get private copies of any dynamic memory.
 *    (Parser must use item_free() to free the memory later.)
 *    Push the Item on the value stack.
 */
	static void
copy_push_item(
	Token *tokenp	/* IN: pointer to token to copy */
){
	Item item = *tokenp;	/* bulk copy the token to an item */

	if( item.lexeme != NULL )
		item.lexeme = mystrdup(item.lexeme);
	push(&item);
}

/* FUNCTION: item_free
 * PURPOSE:
 *    Free any dynamic memory in use by this Item.
 *    (Frees the memory obtained by copy_push_item().)
 * ALGORITHM:
 *    Free any dynamic memory or strings used by this Item.
 *    Set those freed pointers to NULL.
 */
	static void
item_free(
	Item *ip	/* IN: pointer to Item whose memory needs freeing */
){
	if( ip->lexeme != NULL )
		mem_free(ip->lexeme);
	ip->lexeme = NULL;	/* ensure string is not re-used */
}

/* FUNCTION: execute
 * PURPOSE:
 *    Execute a semantic value stack operation.
 * ALGORITHM:
 *    Select each case based on the operation being performed.
 *    The number of operands popped off the stack depends on the operator.
 *    Most cases can apply the second operand to the first and
 *    store the result back in the first operand.
 *
 *    The operands popped from the stack must either be freed or
 *    pushed back so something else can free them later.
 *    Anything not pushed back on the stack is freed.
 *
 *    We usually pass the line number of the second operand back as the
 *    line number of the result of the overall operation.
 */
	static Boolean
execute(
	TokenType optype,	/* IN: the type of the token (operator) */
	long int oplineno	/* IN: the line number of the token */
){
	Item op1;	/* first operand */
	Item op2;	/* second operand */

/* CHECK2: A macro to pop two operands and make sure they are integers.
 * The line number is the average of the line number of the two operands.
 */
#define CHECK2(a,b) {\
	b = pop();\
	a = pop();\
	if( a.type != TT_UINT || b.type != TT_UINT  ) {\
		eprintf("File %s Line %ld: Non-numeric operand(s): "\
			"%s '%s' and/or %s '%s'",\
			pinfname,\
			(long)(a.lineno+b.lineno)/2,\
			token_name(a.type),\
			item_value(&a),\
			token_name(b.type),\
			item_value(&b));\
		item_free(&a);\
		item_free(&b);\
		return FALSE;\
	}\
	}

/* PUSHFREE: A macro to push one operand and free the other.
 * The line number is the line number of the second operand.
 */
#define PUSHFREE(a,b) {\
		a.lineno = b.lineno;\
		push(&a);\
		item_free(&b);\
	}


	switch( optype ){
	case ???:		/* '*' -- multiply */
		CHECK2(op1,op2);
//FIXME-- insert the operand math here
		PUSHFREE(op1,op2);
		break;
	case ???:		/* '/' -- divide */
		CHECK2(op1,op2);
//FIXME-- insert the operand math here
		PUSHFREE(op1,op2);
		break;
	case ???:		/* '+' -- add */
		CHECK2(op1,op2);
//FIXME-- insert the operand math here
		PUSHFREE(op1,op2);
		break;
	case ???:		/* '-' -- subtract */
		CHECK2(op1,op2);
//FIXME-- insert the operand math here
		PUSHFREE(op1,op2);
		break;
	case TT_UNARY:		/* '-' -- unary minus (not a real token) */
//FIXME-- insert code for the whole unary minus {pop,negate,push} here
		break;
	case TT_EQUAL:		/* '=' -- assignment */
		/* No symbol table!
		 * Assume we have a pushed identifier and pushed expression.
		 */
		op2 = pop();
		op1 = pop();
		eprintf("File %s Line %ld: Would put %s '%s' into %s '%s'\n",
			pinfname,
			oplineno,
			token_name(op2.type),
			item_value(&op2),
			token_name(op1.type),
			item_value(&op1));
		item_free(&op1);
		item_free(&op2);
		break;
	default:
		/* detect programming errors in parser */
		eprintf("File %s Line %ld: Unknown operator %d %s",
			pinfname,
			(long)oplineno,
			optype,
			token_name(optype));
		abort();
		/*NOTREACHED*/
	}
	return TRUE;
}

/* FUNCTION:
 * PURPOSE:
 *    A matching function (similar to match() in text figure 2.17).
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *    Check that the lookahead token type matches the TokenType argument.
 *    If it doesn't, print an error message that includes the file name,
 *    the line number of the token, the type of token that was expected,
 *    the type of token that was found, and the lexeme that was found;
 *    then return FALSE, indicating failure.  
 *    Load the next lookahead token and return TRUE if it matched.
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize a FACTOR.
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *
 *    factor := TT_STR | TT_ID | TT_UINT | '(' expr ')' | '-' factor
 *
 *    Since we don't have a symbol table, push zero when we find
 *    an identifier.
 *
 *    Error messages contain the file name, the line number of the token, 
 *    the type(s) of token(s) that was/were expected, and the
 *    type of token that was found; then, return FALSE, indicating failure.  
 *    Load the next lookahead token any time it is matched.
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize a TERM.
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *
 *    term := factor ( ('*'|'/') factor ) *
 *
 *    No errors can be detected here.
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize an EXPR.
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *
 *    expr := term ( ('+'|'-') term ) *
 *
 *    No errors can be detected here.
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize an ASST.
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *
 *    asst := TT_ID '=' expr
 *
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize a PRINT command.
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *
 *    print := TT_PRINT expr ( ',' expr ) *
 *
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize a STMT.
 *    Returns TRUE on success; FALSE on error.
 * ALGORITHM:
 *
 *    stmt :=  print ';' | asst ';' | ';'
 *
 *    This function will need to check the FIRST sets of its
 *    productions, to know which production to expand.
 *
 *    Additional actions to perform:
 *       Save the line number and lexeme that starts the statement.
 *       When the end of the STMT is found, print a message to the
 *       parser output stream indicating the line number of the token
 *       starting the statement, the number of the statement, and
 *       the lexeme that started the statement.
 *
 *       Write the code so that even if a parsing error occurs, the memory
 *       used to save the lexeme starting the statement is still freed
 *       before this function returns.  Do this without duplicating code.
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    A panic-mode function that skips to after the next semicolon.
 *    It also frees anything left on the semantic value stack.
 * ALGORITHM:
 *    Keep skipping and printing input tokens until we hit EOF
 *    or a semicolon.  Print the file name, line number, type, and lexeme
 *    of the token to which we skipped.
 *    Free anything left on the semantic value stack, printing any
 *    items freed.
 *    Advance to the token after the semicolon.
 */
//FIXME-- your code goes here

/* FUNCTION:
 * PURPOSE:
 *    Recognize a PROGRAM.
 *    Returns the number of errors in the program.
 * ALGORITHM:
 *
 *    program := stmt * TT_EOF
 *
 *    This is the top-level, root parsing function.
 *    Panic-mode error recovery is done here when an error is detected.
 *    Error handling:
 *       Every time a panic-mode error recovery is done, count it.
 *       Return the count of errors when this function returns.
 */
//FIXME-- your code goes here

/* FUNCTION: parser()
 * PURPOSE:
 *    Parse an open input stream.
 * ALGORITHM:
 *    Initialize the scanner.
 *    Set the statement counter to zero.
 *    Pre-load the first look-ahead token.
 *    Call the root parsing function and receive the count of errors.
 *    Terminate the scanner.
 *    Print some statistics and return.
 */
	void
parser(
	FILE *infd,       /* IN: open input file descriptor to read */
	char *infname,    /* IN: name of open input stream */
	FILE *outfd,      /* IN: open output file descriptor to write */
	char *outfname    /* IN: name of open output stream */
){
	int errcounter;	  /* receive count of errors in program */

	/* Save static copies of initialization data for use by Parser.
	 * We don't have to save a copy of the infd since only the
	 * single scanner_init() call in this function uses it.
	 */
	poutfd = outfd;
	pinfname = infname;
	poutfname = outfname;

	fprintf(poutfd, "PARSER: Now parsing '%s' to '%s'\n",
		pinfname, poutfname);

//FIXME-- your code goes here

	fprintf(poutfd, "PARSER: Parsed %ld statements from '%s' to '%s'.\n",
		stmtcounter, pinfname, poutfname);
	if( errcounter > 0 )
		fprintf(poutfd, "PARSER: File %s had %d errors.\n",
			pinfname, errcounter);

	if( ! empty() )
		fprintf(poutfd, "STACK: Stack not empty.\n");

	/* Undo what was done during the Parser initialization.
	 */
	poutfd = NULL;
	pinfname = NULL;
	poutfname = NULL;
}
