/**
 *	\file "lexer/instref-lex.ll"
 *	vi: ft=lex
 *	Will generate .cc (C++) file for the token-scanner.  
 *	$Id: instref-lex.ll,v 1.6 2008/03/21 00:20:19 fang Exp $
 */

/****** DEFINITIONS **********************************************************/

%{
/* scanner-specific header */

#include <iostream>
#include <cstdlib>
#include <iomanip>
#include "util/sstream.h"

#if 0
#ifdef	LIBBOGUS
#define	__LEXYACC_INSTREF__PREFIX_H__
// prevents renaming
#endif
#endif

#include "util/macros.h"
#include "util/using_ostream.h"
#include "parser/instref-prefix.h"
#include "AST/AST.h"		/* everything needed for "y.tab.h" */
using namespace HAC::parser;

// DIRTY MAKE HACK ALERT
#if	defined(LIBBOGUS)
#include "parser/instref-parse.h"		/* symbols generated by yacc */
#else
#include "parser/instref-parse-real.h"	/* symbols generated by yacc */
#endif
// END DIRTY HACK ALERT

#include "lexer/hac_lex.h"
#include "lexer/instref-lex-options.h"
#include "lexer/flex_lexer_state.h"
using flex::lexer_state;

namespace HAC {

namespace lexer {

/**
	Maximum string length.  Can be extended arbitrarily.  
 */
#define	STRING_MAX_LEN		1024

static token_position	CURRENT(1,0,1);

/**
	Thie macro is intended for use with ostream& operator << .
	\param c is a token_position.  
 */
#define	LINE_COL(c)	"on line " << c.line << ":" << c.col

/* debugging switches -- consider making these macro-defined */
static const int token_feedback = 0;

/**
	Debugging tool.  
	This will generate excessive feedback for every detailed
	action of the lexer, even in the middle of tokenizing.  
 */
static inline void
DEFAULT_TOKEN_FEEDBACK(__YYLEX_PARAM_VOID) {
	if (token_feedback) {
		cerr << "token = " << yytext <<
			" " LINE_COL(CURRENT) << endl;
	}
}

/* macros for tracking single line tokens (no new line) */

static inline void
TOKEN_UPDATE(__YYLEX_PARAM_VOID) {
	DEFAULT_TOKEN_FEEDBACK(__YYLEX_ARG_VOID);
	CURRENT.col += yyleng;
}

static inline void
NEWLINE_UPDATE(__YYLEX_PARAM_VOID) {
	CURRENT.line++; CURRENT.col = 1;
#if 0
	cerr << "Line number advanced to " << CURRENT.line << endl;
#endif
}

static inline void
NODE_POSITION_UPDATE(YYSTYPE& lval __YYLEX_PARAM) {
	lval._node_position = new node_position(yytext, CURRENT);
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
}

}	/* end namespace lexer */
}	/* end namespace HAC */

using namespace HAC::lexer;

%}

DIGIT		[0-9]
HEXDIGIT	[0-9A-Fa-f]
IDHEAD		[a-zA-Z_]
IDBODY		[a-zA-Z0-9_]
INT		{DIGIT}+
SIGN_INT	[+-]?{INT}
EXP		[eE]{SIGN_INT}
FRACTIONAL	"."{INT}
FLOAT		({INT}{FRACTIONAL}{EXP}?)|({INT}{FRACTIONAL}?{EXP})
HEX		0x{HEXDIGIT}+

/* note: '-' signed ints are lexed as two tokens and combined in the parser
	as a unary expr, thus, no numerical tokens in the language 
	*start* with a sign.  
	A sign in the exponent of a floating-point number is acceptable.  
*/

ID		{IDHEAD}{IDBODY}*
BADID		({INT}{ID})|({FLOAT}{ID})
BADHEX		{HEX}{ID}
WHITESPACE	[ \t]+
NEWLINE		"\n"
WS		{WHITESPACE}

POSITIONTOKEN	[][.]

SCOPE		"::"
RANGE		".."

/*
	Explicitly stating options to guarantee proper definition of 
	macros in the generated source file, because I've turned on
	-Wundef for all translation units.  
 */
%option never-interactive
%option nomain
%option nostack
/** I wish! **/
/** 	%option reentrant	**/

/****** rules ****************************************************************/
%%

<INITIAL>{

{SCOPE}		{ NODE_POSITION_UPDATE(*yylval __YYLEX_ARG); return SCOPE; }

{POSITIONTOKEN} { NODE_POSITION_UPDATE(*yylval __YYLEX_ARG); return yytext[0]; }

{WHITESPACE}	TOKEN_UPDATE(__YYLEX_ARG_VOID);
{NEWLINE}	NEWLINE_UPDATE(__YYLEX_ARG_VOID);

{INT}	{
	if (token_feedback) {
		cerr << "int = " << yytext << " " << LINE_COL(CURRENT) << endl;
	}
	yylval->_token_int = new token_int(atoi(yytext));
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
	return INT;
}

{HEX}   {
	if (token_feedback) {
		cerr << "int = " << yytext << " " << LINE_COL(CURRENT) << endl;
	}
	/* TODO: error handling of value-ranges */
	/* consider using stream conversions to avoid precision errors */
	/* what if we need atol? */
	HAC::entity::pint_value_type v;
	std::istringstream iss(yytext); /* slower, but safer */
	iss >> std::hex >> v;
	/* could try to use faster, but unsafe istrstream (deprecated) */
	instref_lval->_token_int = new token_int(v);
	/* yylval->_token_int = new token_int(atoi(yytext)); */
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
	return INT;
}

{ID}	{
	if (token_feedback) {
		cerr << "identifier = \"" << yytext << "\" " << 
			LINE_COL(CURRENT) << endl;
	}
	yylval->_token_identifier = new token_identifier(yytext);
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
	return ID;
}

{BADHEX}	{ 
	hackt_parse_file_manager.dump_file_stack(cerr);
	cerr << "bad hexadecimal integer: \"" << yytext << "\" " <<
		LINE_COL(CURRENT) << endl;
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
	yylval->_token_identifier = NULL;
	THROW_EXIT;
}

{BADID}	{
	hackt_parse_file_manager.dump_file_stack(cerr);
	cerr << "bad identifier: \"" << yytext << "\" " <<
		LINE_COL(CURRENT) << endl;
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
	yylval->_token_identifier = NULL;
	THROW_EXIT;
}

.	{
	hackt_parse_file_manager.dump_file_stack(cerr);
	/* for everything else that doesn't match... */
	cerr << "unexpected character: \'" << yytext << "\' " <<
		LINE_COL(CURRENT) << endl;
	TOKEN_UPDATE(__YYLEX_ARG_VOID);
	THROW_EXIT;
}
}

%%
/****** user-code ************************************************************/


