D                       [0-9]
L                       [a-zA-Z_]
E                       [Ee][+-]?{D}+

%{

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "luban_grammar.tab.hpp"  /* this is yacc generated header */

extern int my_flexyyinput(char* buf, int num);
#undef YY_INPUT
#define YY_INPUT(b,r,ms)  ( r = my_flexyyinput(b,ms) )


void countinputs();
void cstyle_comments();
char yytochar(char *txt, int leng);
char* yytocstr(const char *txt);
void yyerror(char*);


static int lineno = 0;
static int columnno = 0;
static int totalchar = 0;

static char linebuffer[1024];
static char* const bufferend = linebuffer + 1023;

static int errorHappened = 0;
static int maxNumErrors = 16;

static const char *currentfile = 0;


%}

%%

"/*"                    { countinputs(); cstyle_comments(); }
\/\/.*\n		{ countinputs(); }  /* ignore C++ style coments */

"abstract"		{ countinputs();  return ABSTRACT; }
"as"			{ countinputs();  return AS; }
"asynch"		{ countinputs();  return ASYNCH; }
"break"                 { countinputs();  return BREAK; }
"bool"                  { countinputs();  return BOOL; }
"cancel"                { countinputs();  return CANCEL; }
"char"                  { countinputs();  return CHAR; }
"closure"               { countinputs();  return CLOSURE; }
"composition"           { countinputs();  return COMPOSITION; }
"continue"              { countinputs();  return CONTINUE; }
"double"                { countinputs();  return DOUBLE; }
"else"                  { countinputs();  return ELSE; }
"enum"                  { countinputs();  return ENUM; }
"error"                 { countinputs();  return LBERROR; }
"finish"                { countinputs();  return FINISH; }
"false"                	{ countinputs();  yylval.boolit = 0; return BOOLIT; }
"for"                   { countinputs();  return FOR; }
"foreach"               { countinputs();  return FOREACH; }
"hidden"                { countinputs();  return HIDDEN; }
"if"                    { countinputs();  return IF; }
"implements"            { countinputs();  return IMPLEMENTS; }
"in"       	        { countinputs();  return IN; }
"input"       	        { countinputs();  return INPUT; }
"int"                   { countinputs();  return INT; }
"isa"           	{ countinputs();  return ISA; }
"map"       	        { countinputs();  return MAP; }
"namespace"		{ countinputs();  return NAMESPACE; }
"null"			{ countinputs();  return LBNULL; }
"output"		{ countinputs();  return OUT; }
"process"		{ countinputs();  return PROCESS; }
"range"                 { countinputs();  return RANGE; }
"readonly"              { countinputs();  return READONLY; }
"readwrite"             { countinputs();  return READWRITE; }
"set"                	{ countinputs();  return SET; }
"static"                { countinputs();  return STATIC; }
"stationary"            { countinputs();  return STATIONARY; }
"store"                	{ countinputs();  return STORE; }
"string"                { countinputs();  return STRING; }
"struct"                { countinputs();  return STRUCT; }
"synch"                	{ countinputs();  return SYNCH; }
"true"               	{ countinputs(); yylval.boolit = 1; return BOOLIT; }
"typedef"               { countinputs();  return TYPEDEF; }
"typeof"		{ countinputs();  return TYPEOF; }
"typeinfo"		{ countinputs();  return TYPEINFO; }
"vector"       	        { countinputs();  return VECTOR; }
"wait"			{ countinputs();  return WAIT; }
"waitfor"		{ countinputs();  return WAITFOR; }
"while"                 { countinputs();  return WHILE; }


"+="                    { countinputs();  return ADD_ASN; }
"-="                    { countinputs();  return SUB_ASN; }
"*="                    { countinputs();  return MUL_ASN; }
"/="                    { countinputs();  return DIV_ASN; }
"%="                    { countinputs();  return MOD_ASN; }
"++"                    { countinputs();  return INC_OP; }
"--"                    { countinputs();  return DEC_OP; }
"&&"                    { countinputs();  return AND_OP; }
"and"			{ countinputs();  return AND_OP; }
"||"                    { countinputs();  return OR_OP; }
"or"                    { countinputs();  return OR_OP; }
"<="                    { countinputs();  return LE_OP; }
">="                    { countinputs();  return GE_OP; }
"=="                    { countinputs();  return EQ_OP; }
"!="                    { countinputs();  return NE_OP; }
"::"			{  countinputs();  return CLNCLN; }
"[]"			{  countinputs();  return VECIT; }
"[&]"			{  countinputs();  return VECITPAR; }
";"                     { countinputs();  return ';'; }
"{"		        { countinputs();  return '{'; }
"}"                     { countinputs();  return '}'; }
","                     { countinputs();  return ','; }
":"                     { countinputs();  return ':'; }
"="                     { countinputs();  return '='; }
"("                     { countinputs();  return '('; }
")"                     { countinputs();  return ')'; }
"["                     { countinputs();  return '['; }
"]"                     { countinputs();  return ']'; }
"."                     { countinputs();  return '.'; }
"&"                     { countinputs();  return '&'; }
"-"                     { countinputs();  return '-'; }
"+"                     { countinputs();  return '+'; }
"*"                     { countinputs();  return '*'; }
"/"                     { countinputs();  return '/'; }
"%"                     { countinputs();  return '%'; }
"!"			{ countinputs();  return NOT_OP; }
"not"			{ countinputs();  return NOT_OP; }
"<"                     { countinputs();  return '<'; }
">"                     { countinputs();  return '>'; }
"?"                     { countinputs();  return '?'; }
"##"                    { countinputs();  return PNDPND; }

{D}+			{ countinputs(); yylval.integer = atoi(yytext); return INTEGER; }
{D}*\.{D}+{E}?		{ countinputs(); yylval.floater = atof(yytext); return FLOATER; }
{D}+\.{D}*{E}?		{ countinputs(); yylval.floater = atof(yytext); return FLOATER; }

(_|{L})+(_|{L}|[0-9])*	{ countinputs(); yylval.symbol = strdup(yytext); return NAME; }

\"(\\.|[^\\"])*\"     { countinputs();yylval.strlit = yytocstr(&yytext[1]); return(STRLITERAL); }
'(\\.|[^\\'])'       { countinputs(); yylval.charlit = yytochar(yytext, yyleng);  return(CHARLITERAL); }

[ \t\n\r]		{ countinputs(); } /* ignore white spaces and change of line */

.		{ 
                 countinputs();
                 if ( ! isspace( yytext[0] ) )
                  {
                    fprintf(stderr, "%d ", (int)yytext[0]); 
                    yyerror("Unrecognized character");
                  }
                 }

%%

void cstyle_comments()
{
	char c, cnext;	
	int startline = lineno;
	while ( 1 )
	{
		while ( ( c = input() ) != '*' && c != EOF )
		{
			totalchar++;
			if ( c == '\n') 
			{
				lineno++;
				columnno = 0;
			}
			else
				columnno++;
		}
		if ( c == EOF )
		{
			yyerror("Unterminated comments");
			return;
		}
		cnext = input();
		if ( cnext == '/' )
		{
			totalchar++;
			columnno++;
			return;
		}
		else
			unput(cnext);
	}
	
	return;
}


void countinputs()
{
	int i;

	static char *cursor = linebuffer;

	for (i = 0; yytext[i] != '\0'; i++)
	{
		if (yytext[i] == '\n')
		{
			columnno = 0;
			lineno++;
			cursor = linebuffer;
		}
		else
		{
			if ( cursor < bufferend )
			{
				*cursor = yytext[i];
				cursor++;
				if (yytext[i] == '\t')
					columnno += 8 - columnno % 8;
				else
					columnno++;
			}
		}
		*cursor = '\0';
	}
	totalchar += i-1;
}


char yytochar(char *txt, int leng)
{
	if ( leng == 3 )
		return txt[1];
	
	if ( txt[1] == '\\' )
	{
		switch ( txt[2] )
		{
			case 'n': return '\n';
			case 't': return '\t';
			case 'r': return '\r';
			case 'b': return '\b';
			case 'a': return '\a';
			case 'v': return '\v';
			case 'f': return '\f';
			case '\\': return '\\';
			case '\'': return '\'';
			case '0': return '\0';
		}
	}

	yyerror("Invalid char literal");

	return '\0';

}

char* yytocstr(const char *txt)
{
        int i=0, j=0;
        char *res = (char*) malloc( yyleng-1 );
	
        for( i=0,j=0; i<yyleng-2; i++, j++)
        {
            res[j]=txt[i];
  	    if ( txt[i] == '\\' )
	    {
		switch ( txt[i+1] )
		{
			case 'n': res[j]= '\n'; i++; break;
			case 't': res[j]= '\t'; i++; break;
			case 'r': res[j]= '\r'; i++; break;
			case 'b': res[j]= '\b'; i++; break;
			case 'a': res[j]= '\a'; i++; break;
			case 'v': res[j]= '\v'; i++; break;
			case 'f': res[j]= '\f'; i++; break;
			case '\\': res[j]= '\\'; i++; break;
			case '"': res[j]= '"'; i++; break;
                        default:;
		}
            }
	}

	res[j] = '\0';
        return res;
}

int lexer_getLineNo()
{
   return lineno+1;
}

int lexer_getColumnNo()
{
   return columnno;
}

char* lexer_getCurrentLine()
{
   return linebuffer;
}

void lexer_setErrorLimit(int e)
{
     maxNumErrors = e;
}

void lexer_clearError()
{
     errorHappened = 0;
}

void lexer_clearLineAndColumnNo()
{
     columnno = 0;
     lineno = 0;
}

int lexer_getNumErrors()
{
     return errorHappened;
}

void yyerror(char *s)
{
	int i =0;
	errorHappened++;
	fprintf( stderr, "%s for %s at line %d\n", s, currentfile, lexer_getLineNo());
	fprintf(stderr, "%s\n", lexer_getCurrentLine());
	for(; i < lexer_getColumnNo(); i++)
		fputc(' ', stderr);
	fprintf(stderr, "^\n");

	if ( errorHappened > maxNumErrors )
	{
		fprintf(stderr, "Too many erros, abort\n");
		exit(1);
	}
}

void lexer_setCurrentFileName( const char* sceuname )
{
	currentfile = sceuname;
}
