/* $Id: scan.l,v 1.50 2009/01/27 15:42:08 potyra Exp $ 
 *
 * Copyright (C) 2007-2009 FAUcc Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

D			[0-9]
L			[a-zA-Z_]
H			[a-fA-F0-9]
E			[Ee][+-]?{D}+
FS			(f|F|l|L)
IS			(u|U|l|L)*

CHAR_BACKSLASH		\\\\
CHAR_BELL		\\a
CHAR_BACKSPACE		\\b
CHAR_ESCAPE		\\e
CHAR_FORMFEED		\\f
CHAR_NEWLINE		\\n
CHAR_RETURN		\\r
CHAR_TAB		\\t
CHAR_VTAB		\\v
CHAR_TICK		\\'
CHAR_DTICK		\\\"
CHAR_OCTAL		\\[0-7]{1,3}
CHAR_HEX		\\x[0-9a-fA-F]{2}
CHAR_COMMON		({CHAR_BACKSLASH}|{CHAR_BELL}|{CHAR_BACKSPACE}|{CHAR_ESCAPE}|{CHAR_FORMFEED}|{CHAR_NEWLINE}|{CHAR_RETURN}|{CHAR_TAB}|{CHAR_VTAB}|{CHAR_TICK}|{CHAR_DTICK}|{CHAR_OCTAL}|{CHAR_HEX})
CHAR_STRING		([^\\"]|{CHAR_COMMON})
CHAR_CHAR		([^\\']|{CHAR_COMMON})

%{
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "scope.h"
#include "expr.h"
#include "parse.h"
#include "cc1.h"
extern void __attribute__((noreturn))
yyerror(const char *s);

int column = 0;
int line = 1;

/*forward*/ static void
comment(void);
/*forward*/ static void
count(void);

static unsigned char
ascii_to_byte(const char *str, const char **endp)
{
	unsigned char c;
	unsigned int i;

	if (*str == '\\') {
		str++;
		switch (*str) {
		case 'a': c = '\a'; str++; break;
		case 'b': c = '\b'; str++; break;
		case 'e': c = '\033'; str++; break; /* Non-ANSI -- FIXME */
		case 'f': c = '\f'; str++; break;
		case 'n': c = '\n'; str++; break;
		case 'r': c = '\r'; str++; break;
		case 't': c = '\t'; str++; break;
		case 'v': c = '\v'; str++; break;
		case 'x': case 'X':
			str++;
			c = 0;
			for (i = 0; i < 2; i++) {
				c *= 16;
				if ('0' <= *str && *str <= '9') {
					c += *str - '0';
				} else if ('A' <= *str && *str <= 'F') {
					c += *str - 'A' + 10;
				} else if ('a' <= *str && *str <= 'f') {
					c += *str - 'a' + 10;
				} else {
					assert(0);
				}
				str++;
			}
			break;
		case '\'': c = '\''; str++; break;
		case '"': c = '"'; str++; break;
		case '\\': c = '\\'; str++; break;
		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
			c = *str++ - '0';
			while ('0' <= *str && *str <= '7') {
				c *= 8;
				c += *str++ - '0';
			}
			break;
		default:
			assert(0);
		}
	} else {
		c = *str++;
	}

	*endp = str;
	return c;
}

static int
integer(char *str)
{
	unsigned long long val;
	int u;
	int l;

	u = 0;
	l = 0;

	val = strtoull(str, &str, 0);
	while (*str) {
		switch (*str) {
		case 'L':
		case 'l':
			l++;
			break;
		case 'U':
		case 'u':
			u++;
			break;
		default:
			assert(0);
			break;
		}
		str++;
	}
	assert(l < 3);
	assert(u < 2);

	yylval.integer = val;
	if (u == 0 && l < 1 && val < (1ULL << (opt_f_sizeof_int * 8 - 1))) {
		return INTEGER_LITERAL;
	} else if (l < 1 && val < (1ULL << (opt_f_sizeof_int * 8))) {
		return INTEGER_LITERAL_U;
	} else if (u == 0 && l < 2 && val < (1ULL << (opt_f_sizeof_long_int * 8 - 1))) {
		return INTEGER_LITERAL_L;
	} else if (l < 2 && val < (1ULL << (opt_f_sizeof_long_int * 8))) {
		return INTEGER_LITERAL_UL;
	} else if (u == 0 && l < 3 && val < (1ULL << (opt_f_sizeof_long_long_int * 8 - 1))) {
		return INTEGER_LITERAL_LL;
	} else {
		return INTEGER_LITERAL_ULL;
	}
}
%}

%%
"/*"			{ comment(); }
"#".*			{ count(); }

"__extension__"		{ count(); }
"__restrict"		{ count(); }

"__builtin_constant_p"	{ count(); return BUILTIN_CONSTANT_P; }
"__builtin_offsetof"	{ count(); return BUILTIN_OFFSETOF; }
"__builtin_va_arg"	{ count(); return BUILTIN_VA_ARG; }
"__builtin_va_start"	{ count(); return BUILTIN_VA_START; }
"__builtin_va_end"	{ count(); return BUILTIN_VA_END; }
"__builtin_va_list"	{ count(); return BUILTIN_VA_LIST; }

"attribute"		{ count(); return ATTRIBUTE; }
"__attribute"		{ count(); return ATTRIBUTE; }
"__attribute__"		{ count(); return ATTRIBUTE; }
"auto"			{ count(); return AUTO; }
"asm"			{ count(); return ASM; }
"__asm__"		{ count(); return ASM; }
"break"			{ count(); return BREAK; }
"case"			{ count(); return CASE; }
"char"			{ count(); return CHAR; }
"const"			{ count(); return CONST; }
"__const"		{ count(); return CONST; }
"continue"		{ count(); return CONTINUE; }
"default"		{ count(); return DEFAULT; }
"do"			{ count(); return DO; }
"double"		{ count(); return DOUBLE; }
"else"			{ count(); return ELSE; }
"enum"			{ count(); return ENUM; }
"extern"		{ count(); return EXTERN; }
"float"			{ count(); return FLOAT; }
"for"			{ count(); return FOR; }
"goto"			{ count(); return GOTO; }
"if"			{ count(); return IF; }
"inline"		{ count(); return INLINE; }
"__inline"		{ count(); return INLINE; }
"int"			{ count(); return INT; }
"long"			{ count(); return LONG; }
"register"		{ count(); return REGISTER; }
"return"		{ count(); return RETURN; }
"short"			{ count(); return SHORT; }
"signed"		{ count(); return SIGNED; }
"sizeof"		{ count(); return SIZEOF; }
"static"		{ count(); return STATIC; }
"struct"		{ count(); return STRUCT; }
"switch"		{ count(); return SWITCH; }
"typedef"		{ count(); return TYPEDEF; }
"union"			{ count(); return UNION; }
"unsigned"		{ count(); return UNSIGNED; }
"void"			{ count(); return VOID; }
"volatile"		{ count(); return VOLATILE; }
"__volatile__"		{ count(); return VOLATILE; }
"while"			{ count(); return WHILE; }

"__PRETTY_FUNCTION__"	{
	struct scope *s;

	count();

	for (s = scope_current; s->type != SCOPE_FUNCTION; s = s->parent) {
	}
	yylval.string.len = strlen(s->function->identifier);
	yylval.string.string = strdup(s->function->identifier);
	assert(yylval.string.string);

	return STRING_LITERAL;
}

{L}({L}|{D})*		{
	struct declaration *dion;
	int ret;

	count();
	yylval.identifier = strdup(yytext);
	ret = scope_lookup_current(yytext, &dion);
	if (ret == STORAGE_TYPEDEF) {
		return TYPE_NAME;
	} else if (ret == TYPE_ENUM) {
		struct expr *initializer;

		initializer = declaration_initializer_get(dion);
		assert(initializer);
		assert(initializer->type == EXPR_INTEGER);
		yylval.integer = initializer->integer;
		return INTEGER_LITERAL;
	} else {
		return IDENTIFIER;
	}
}

0[xX]{H}+{IS}?		{ count(); return integer(yytext); }
0{D}+{IS}?		{ count(); return integer(yytext); }
{D}+{IS}?		{ count(); return integer(yytext); }

{D}+{E}{FS}?		{ count(); yylval.real = strtold(yytext, NULL); return REAL_LITERAL; }
{D}*"."{D}+({E})?{FS}?	{ count(); yylval.real = strtold(yytext, NULL); return REAL_LITERAL; }
{D}+"."{D}*({E})?{FS}?	{ count(); yylval.real = strtold(yytext, NULL); return REAL_LITERAL; }
\"{CHAR_STRING}*\"	{
	unsigned int len;
	char *str;
	const char *p;
	unsigned int i;

	count();

	/* Count characters. */
	len = 0;
	for (p = &yytext[1]; p < &yytext[strlen(yytext) - 1]; ) {
		ascii_to_byte(p, &p);
		len++;
	}

	/* Store characters. */
	str = malloc(len + 1);
	assert(str);
	i = 0;
	for (p = &yytext[1]; p < &yytext[strlen(yytext) - 1]; ) {
		str[i] = ascii_to_byte(p, &p);
		i++;
	}
	str[i] = '\0';

	yylval.string.len = len;
	yylval.string.string = str;
	return STRING_LITERAL;
}

'{CHAR_CHAR}'		{
	const char *p;

	yylval.integer = ascii_to_byte(&yytext[1], &p);
	return INTEGER_LITERAL;
}

"\.\.\."		{ count(); return ELIPSIS; }

">>="			{ count(); return RIGHT_ASSIGN; }
"<<="			{ count(); return LEFT_ASSIGN; }
"+="			{ count(); return ADD_ASSIGN; }
"-="			{ count(); return SUB_ASSIGN; }
"*="			{ count(); return MUL_ASSIGN; }
"/="			{ count(); return DIV_ASSIGN; }
"%="			{ count(); return MOD_ASSIGN; }
"&="			{ count(); return AND_ASSIGN; }
"^="			{ count(); return XOR_ASSIGN; }
"|="			{ count(); return OR_ASSIGN; }
">>"			{ count(); return RIGHT_OP; }
"<<"			{ count(); return LEFT_OP; }
"++"			{ count(); return INC_OP; }
"--"			{ count(); return DEC_OP; }
"->"			{ count(); return PTR_OP; }
"&&"			{ count(); return AND_OP; }
"||"			{ count(); return OR_OP; }
"<="			{ count(); return LE_OP; }
">="			{ count(); return GE_OP; }
"=="			{ count(); return EQ_OP; }
"!="			{ count(); return NE_OP; }
";"			{ count(); return ';'; }
"{"			{ count(); return '{'; }
"}"			{ count(); return '}'; }
","			{ count(); return ','; }
":"			{ count(); return ':'; }
"="			{ count(); return '='; }
"("			{ count(); return '('; }
")"			{ count(); return ')'; }
"["			{ count(); return '['; }
"]"			{ count(); return ']'; }
"."			{ count(); return '.'; }
"&"			{ count(); return '&'; }
"!"			{ count(); return '!'; }
"~"			{ count(); return '~'; }
"-"			{ count(); return '-'; }
"+"			{ count(); return '+'; }
"*"			{ count(); return '*'; }
"/"			{ count(); return '/'; }
"%"			{ count(); return '%'; }
"<"			{ count(); return '<'; }
">"			{ count(); return '>'; }
"^"			{ count(); return '^'; }
"|"			{ count(); return '|'; }
"?"			{ count(); return '?'; }

[ \t\v\n\f]		{ count(); }
.			{ yyerror("syntax error"); }

%%

int
yywrap(void)
{
	return 1;
}

static void
comment(void)
{
	char c, c1;

loop:
	while ((c = input()) != '*' && c != 0)
		;

	if ((c1 = input()) != '/' && c != 0)
	{
		unput(c1);
		goto loop;
	}
}

static void
count(void)
{
	int i;

#if 0
	fprintf(stderr, "-- %s --\n", yytext);
#endif

	for (i = 0; yytext[i] != '\0'; i++) {
		if (yytext[i] == '\n') {
			column = 0;
			line++;
		} else if (yytext[i] == '\t') {
			column += 8 - (column % 8);
		} else {
			column++;
		}
	}
}
