/**
	\file "parser/yyerror.cc"
	This file is to be *included*, not compiled directly.  
	$Id: yyerror.cc,v 1.1 2008/12/18 00:25:57 fang Exp $
	Before including this file:
		YYBISON (preprocessor) should be defined.
		YYSTYPE (union type) should be defined.  
		struct flex::lexer_state& should be defined.  
	TODO: factor out the yy_union functions in a template fashion, 
		or use parser_traits.
 */

/*---------------------------------------------------------------------------*/
/**
	The goal is to keep the grammar in this "art.yy" clean, and not
	litter the various productions with error handling cases.  
	When the parser fails to match any productions, we want it
	to report what went wrong, not just give the useless "syntax error"
	message.  Fortunately, all the information about the state of the 
	parser is available, if we know where to look.  
	This function attempts to unwind the parser's value and state stacks
	and report precisely where the error occured -- WITHOUT having
	to write productions that contain the special error token.  
 */
/*	as a reminder, these are the variables in the parser
	hint: look at how they are used in the various yydebug blocks.  
	COMPATIBILITY ISSUE: the code generated by GNU bison differs
	somewhat, which causes the following code to break.  
	TO DO: resolve this, perhaps by using flags

	for yacc:
	short* yyss;		// state stack base
	short* yyssp;		// state stack pointer
	YYSTYPE* yyvs;		// value stack base
	YYSTYPE* yyvsp;		// value stack pointer
	YYSTYPE yylval;		// the last token received

	for bison:
	union yyalloc { short yyss; YYSTYPE yyvs; };
	short* yyss;		// is LOCAL to yyparse! (inaccecssible)
	YYSTYPE* yyvs;		// is LOCAL to yyparse! (inaccecssible)
	// all useful variables localized...

	// HACK TIME: hack the prototype for yyerror, 
	// and pass relevant pointers as arguments.  
*/

#if YYBISON
// shit... bison-isms: names were changed to protect the innocent
// yacc symbol for maximum token number, renamed in bison
#define	YYMAXTOKEN	YYMAXUTOK
#define	__symbol_names	yytname
#define	__shift_table	yypact
#define	__reduce_table	yydefact
#define	__YYTABLESIZE	YYLAST

#else	// !YYBISON
#define	__symbol_names	yyname
#define	__shift_table	yysindex
#define	__reduce_table	yyrindex
#define	__YYTABLESIZE	YYTABLESIZE
#endif

static
void __yyerror(const char* msg
#if	!defined(LIBBOGUS)
		, const short* yyss, 
		const short* yyssp, 
		const YYSTYPE* yyvs, 
		const YYSTYPE* yyvsp, 
		const YYSTYPE yylval, 
		const int yychar, 
		const flex::lexer_state& _lexer_state
#endif
		) {
	const short* s;
	const YYSTYPE* v;
	cerr << "parse error: " << msg << endl;

/*	Define the following (-D) to disable sophisticated error reporting, 
 *	useful for bogus compilations.
 */
#if	!defined(LIBBOGUS)
	// we've kept track of the position of every token
	cerr << "parser stacks:" << endl << "state\tvalue" << endl;

	/* bug fix: bad memory address with first *(s-1) if v is not
	 * guaranteed to be NULL, which it isn't!
	 * Thus we start with yyss+1, yyvs+1 for first valid stack entry.
	 */
	assert(!*yyss);		/* should be zero */
	cerr << *yyss;
	s=yyss+1;
	v=yyvs+1;
	for ( ; s <= yyssp && v <= yyvsp; s++, v++) {
		// how do we know which union member?
		// need to look at the state stack, and the transition
		// from the previous state
		/* assert(v); can have NULL on stack? yes. */
		if (v) {
			yy_union_resolve_dump(*v, *(s-1), *s, cerr << '\t');
		} else {
			cerr << "\t(null) ";
		}
		cerr << endl << *s;
	}
	// sanity check
	assert(s > yyssp && v > yyvsp);
//	NULL check not necessarily valid if last token normally returned NULL
	if (_lexer_state.at_eof() || !yychar) {
		cerr << "\t" << __symbol_names[yychar];	// "end-of-file"
	} else {
//		The last token from the lexer, yychar, tells us the last
//		token type returned.  
//		can't use: yy_union_resolve_dump(yylval, *(s-1), *s, ...);
		yy_union_lookup_dump(yylval, yychar, cerr << '\t');
	}
	cerr << endl;

	// take current state off of top of stack and 
	// print out possible points in productions
	cerr << "in state " << *yyssp << ", possible rules are:" << endl;
	{	int i;
		assert(*yyssp < yynss);
		for (i=0; i < yysss[*yyssp].n; i++) {
			cerr << yysss[*yyssp].rule[i] << endl;
		}
	}

	// list possible expected tokens based on state table
	// code ripped off from YYDEBUG parts of y.tab.c
	cerr << "acceptable tokens are: " << endl;
	int accept_count = 0;
	{
		int _yychar_ = 0;
		int yyn;
		for ( ; _yychar_ <= YYMAXTOKEN; _yychar_++) {
			// try all terminal tokens
			if ((yyn = __shift_table[*yyssp]) && 
					(yyn += _yychar_) >= 0 && 
					yyn <= __YYTABLESIZE && 
					yycheck[yyn] == _yychar_) {
				cerr << '\t' << __symbol_names[_yychar_]
					<< " (shift)" << endl;
				accept_count++;
			} else if ((yyn = __reduce_table[*yyssp]) && 
					(yyn += _yychar_) >= 0 && 
					yyn <= __YYTABLESIZE && 
					yycheck[yyn] == _yychar_) {
				cerr << '\t' << __symbol_names[_yychar_]
					<< " (reduce)" << endl;
				accept_count++;
			}
		}
	}

	yyfreestacks(yyss, yyssp, yyvs, yyvsp, yylval, yychar, _lexer_state);

#endif	// NO_FAKE_PREFIX

/***
	Dr. Fancy-Pants says:
	If there can be only one possible token, such as a terminating
	semicolon, then automatically fill it in or push NULL onto the 
	symbol stack, and proceed as if nothing happened.  
	Just remember that there was an error in the first place.  

	if (accept_count == 1) {
		...
	}
	
	The Oracle says:
	Multiple possible tokens? examine context and read the 
	programmer's mind to guess what was intended.  Duh!

	else if (accept_count < too_many) {
		...
	}
***/
}	// end __yyerror(...)

