package Iota;

import java.io.InputStream;

%%

%init{
    line_separator = System.getProperty ("line.separator", "\n");
%init}

%{

private String line_separator;
private boolean errors;
private String filename;

public Lexer (InputStream in, String filename) {
    this (in);
    this.filename = filename;
    errors=false;
}

public boolean hasErrors() {
	return errors;
}

private void errorMsg (int lineno, int charno, String token,
		       String text) {
	errors=true;
    System.err.println (filename + ":" + lineno +
			": lexical error at character " + charno + 
			", \"" + token + "\": " + (text == null ? "" : text));
}

private void errorMsg (int lineno, int charno, String token) {
    errorMsg (lineno, charno, token, null);
}

private Token t (int id, Object value) {
    return new Token (id, filename, yyline + 1, yychar, yychar + yylength (),
		      value);
}

private Token t (int id) {
    return t (id, yytext ());
}

private String interp_string (String s) {
    String token = "";
    int index = 0;
    int code, i;
    while (index < s.length ()) {
		char c = s.charAt (index++);
		switch (c) {
			case '\\':
			c = s.charAt (index++);
			switch (c) {
				case 'n':
					token += '\n';
					break;
				case 'r':
					token += '\r';
					break;
				case 'N': 
					token += line_separator;
					break;
				case '^':		// "Ctrl-" codes
					c = s.charAt (index++);
					token += (char) ((int) c - 'A' + 1);
					break;
				case ' ' :
				case '\b':
				case '\n':     //Eat formatting characters
				case '\t':
				case '\r':
					while (c!='\\') {
						c=s.charAt(index++);
					}
					break;
				case '0':		//ASCII codes
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					if (index+2>s.length()) {
						System.err.println("Illegal ASCII code at line " +(yyline+1)+", ignoring");
						index+=2;
						break;
					}
					String asciiCode=c+s.substring(index,index+2);
					try {
						code=Integer.parseInt(asciiCode);
					}
					catch (NumberFormatException e) {
						System.err.println("Illegal ASCII code at line " +(yyline+1)+", ignoring");
						index+=2;
						break;
					}
					if (code>127) {
						System.err.println("Illegal ASCII code at line " +(yyline+1)+", ignoring");
						index+=2;
						break;
					}
					token += (char) code;
					index+=2;
					break;
			}
			break;
		default:
			token += c;
			break;
		}
    }
    return token;
}

%}

%eofval{
    return t (Constant.EOF, null);
%eofval}

%class Lexer
%type Token
%function getToken
%public
%char
%line
%state COMMENT
%state STRINGS

digits = 0|[1-9][0-9]*
letter = [A-Za-z]
identifier = {letter}({letter}|[0-9_])*
whitespace = [\ \t\n\r]+
slashcomment = //.*
semielse = ;[\ \t\n\r]*else
blockcomment=[^"*"]*"*"("*"|[^/"*"][^"*"]*"*")*/

PC=[\040-\041\043-\133\135-\176]
ES=\\(N|n|t|"^"[a-zA-Z_@"[""]"\\"^"]|[0-9][0-9][0-9]|\"|\\|{WHITE_SPACE_CHAR}+\\)
STRINGLIT=({PC}|{ES})*
STRINGLITPLUS={STRINGLIT}\\
WHITE_SPACE_CHAR=[\n\ \t\b(\015\012)]

%%

<YYINITIAL> "uses"	{ return t (Constant.USES); }
<YYINITIAL> "int"	{ return t (Constant.INT_TYPE); }
<YYINITIAL> "string"	{ return t (Constant.STRING_TYPE); }
<YYINITIAL> "bool"	{ return t (Constant.BOOLEAN_TYPE); }
<YYINITIAL> "array"	{ return t (Constant.ARRAY); }
<YYINITIAL> "object"	{ return t (Constant.OBJECT_TYPE); /* Iota+ */ }
<YYINITIAL> "interface"	{ return t (Constant.INTERFACE); /* Iota+ */ }
<YYINITIAL> "implements" {return t (Constant.IMPLEMENTS); /* Iota+ */}
<YYINITIAL> "this"	{ return t (Constant.THIS); /* Iota+ */ }
<YYINITIAL> "null"	{ return t (Constant.NULL_CONST); /* Iota+ */ }
<YYINITIAL> "cast"	{ return t (Constant.CAST); /* Iota+ */ }
<YYINITIAL> "class"	{ return t (Constant.CLASS); /* Iota+ */ }
<YYINITIAL> "<="	{ return t (Constant.LEQ); /* Iota+ */ }
<YYINITIAL> ">="	{ return t (Constant.GEQ); /* Iota+ */ }
<YYINITIAL> "!="	{ return t (Constant.NEQ); /* Iota+ */ }
<YYINITIAL> "++"	{ return t (Constant.INCREMENT); /* Iota+ */ }
<YYINITIAL> "--"	{ return t (Constant.DECREMENT); /* Iota+ */ }
<YYINITIAL> "break"	{ return t (Constant.BREAK); /* Iota+ */ }
<YYINITIAL> "true"	{ return t (Constant.TRUE_CONST, Boolean.TRUE); }
<YYINITIAL> "false"	{ return t (Constant.FALSE_CONST, Boolean.FALSE); }
<YYINITIAL> "length"	{ return t (Constant.LENGTH); }
<YYINITIAL> "if"	{ return t (Constant.IF); }
<YYINITIAL> "while"	{ return t (Constant.WHILE); }
<YYINITIAL> "else"	{ return t (Constant.ELSE); }
<YYINITIAL> {semielse} {return t(Constant.SEMIELSE);}
<YYINITIAL> "return"	{ return t (Constant.RETURN); }
<YYINITIAL> "new"	{ return t (Constant.NEW); }
<YYINITIAL> "=="	{ return t (Constant.EQ); }
<YYINITIAL> "&"		{ return t (Constant.AND); }
<YYINITIAL> "|"		{ return t (Constant.OR); }
<YYINITIAL> "<"		{ return t (Constant.LT); }
<YYINITIAL> ">"		{ return t (Constant.GT); }
<YYINITIAL> "+"		{ return t (Constant.PLUS); }
<YYINITIAL> "-"		{ return t (Constant.MINUS); }
<YYINITIAL> "*"		{ return t (Constant.TIMES); }
<YYINITIAL> "/"		{ return t (Constant.DIVIDE); }
<YYINITIAL> "%"		{ return t (Constant.MOD); }
<YYINITIAL> "!"		{ return t (Constant.NOT); }
<YYINITIAL> ","		{ return t (Constant.COMMA); }
<YYINITIAL> "("		{ return t (Constant.LPAREN); }
<YYINITIAL> ")"		{ return t (Constant.RPAREN); }
<YYINITIAL> "="		{ return t (Constant.ASSIGN); }
<YYINITIAL> ";"		{ return t (Constant.SEMI); }
<YYINITIAL> "."		{ return t (Constant.DOT); }
<YYINITIAL> "{"		{ return t (Constant.LCURLY); }
<YYINITIAL> "}"		{ return t (Constant.RCURLY); }
<YYINITIAL> "["		{ return t (Constant.LSQUARE); }
<YYINITIAL> "]"		{ return t (Constant.RSQUARE); }
<YYINITIAL> ":"		{ return t (Constant.COLON); }
<YYINITIAL> {identifier}	{ return t (Constant.IDENTIFIER, yytext ().intern ()); }
					    
<YYINITIAL> {digits}	{ 
	Integer newInt=null;
	try {
		newInt=new Integer(yytext());
	}
	catch (NumberFormatException e) {
		System.err.println("Invalid integer "+yytext()+" at line "+(yyline+1)+", attemping to continue");
	}	
	return t(Constant.INT_CONST, newInt); 
}

<YYINITIAL> \"		{yybegin(STRINGS);}
<STRINGS> {STRINGLIT}\" { 
	yybegin(YYINITIAL);
	
	return t(Constant.STRING_CONST,interp_string(yytext().substring(0,yytext().length()-1)));
}

<STRINGS> {STRINGLIT} {
	System.err.println("Unclosed string literal at line " +(yyline+1)+", attemping to continue");	
	yybegin(YYINITIAL);
}

<STRINGS> {STRINGLITPLUS} {
	System.err.println("Illegal escape character at line " +(yyline+1)+", attemping to continue");
	yybegin(YYINITIAL);
}

<STRINGS> . {
	System.err.println("Incorrect string literal at line " +(yyline+1)+", attemping to continue");
	yybegin(YYINITIAL);
}

<YYINITIAL> {slashcomment}	{}
<YYINITIAL> {whitespace}	{}
<YYINITIAL> "/*"	{ yybegin (COMMENT); }

<COMMENT> {blockcomment} {yybegin (YYINITIAL);}
<COMMENT> . {
	System.err.println("Illegal comment on line "+(yyline+1)+", attemping to continue");
	yybegin(YYINITIAL);
}

<YYINITIAL> .		{ 
	System.err.println("Invalid character "+yytext()+" at line "+(yyline+1)+", attemping to continue");
	yybegin(YYINITIAL);
}
