/* This file is part of the KDE project
   Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
   Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

%{
#include <field.h>
#include <expression.h>

#include "sqlparser.h"
#include "sqltypes.h"
#include <iostream>
#include <kdebug.h>
#include <tdelocale.h>

#define YY_NO_UNPUT
#define ECOUNT current += yyleng; ctoken = yytext

extern void setError(const TQString& errDesc);
extern void setError(const TQString& errName, const TQString& errDesc);

%}

/* *** Please reflect changes to this file in ../driver_p.cpp *** */

%option case-insensitive
%option noyywrap
%option never-interactive

whitespace	[ \t\n]
digit				[0-9]
/*identifier	[a-zA-Z_][a-zA-Z_0-9]* */
identifier	[a-zA-Z_0-9]+
/* quoted_identifier (\"[a-zA-Z_0-9]+\") */
query_parameter	\[[^\[\]]+\]

integer			{digit}+
decimal			(({digit}*\.{digit}+)|({digit}+\.{digit}*))
real				((({digit}*\.{digit}+)|({digit}+\.{digit}*)|({digit}+))([Ee][-+]?{digit}+))
/* todo: support for real numbers */


%%


"<>" {
	ECOUNT;
	return NOT_EQUAL;
}

"!=" {
	ECOUNT;
	return NOT_EQUAL2;
}

"==" {
	ECOUNT;
	return '=';
}

"<=" {
	ECOUNT;
	return LESS_OR_EQUAL;
}

">=" {
	ECOUNT;
	return GREATER_OR_EQUAL;
}

"IN" {
	ECOUNT;
	return SQL_IN;
}

{integer} {
//TODO: what about hex or octal values?
	//we're using TQString:toLongLong() here because atoll() is not so portable:
	ECOUNT;
	bool ok;
	yylval.integerValue = TQString(yytext).toLongLong( &ok );
	if (!ok) {
		setError(i18n("Invalid integer number"),i18n("This integer number may be too large."));
		return SCAN_ERROR;
	}
//	yylval.integerValue = atol(yytext);
	return INTEGER_CONST;
}

{decimal} {
	char *p = yytext;
	if (yytext[0]=='.') { /* no integer part */
		yylval.realValue.integer = 0;
	}
	else {
		yylval.realValue.integer = atoi(p);
		int i=0;
		while (p && i < yyleng && *p != '.') {
			i++;
			p++;
		}
		if (i==0 || !p || *p!='.') {
			yylval.realValue.fractional = 0;
			return REAL_CONST;
		}
	}
	/* fractional part */
	p++;
	yylval.realValue.fractional = atoi(p);
	return REAL_CONST;
}

("AND"|"&&") {
	ECOUNT;
	return AND;
}

"AS" {
	ECOUNT;
	return AS;
}

"CREATE" {
	ECOUNT;
	return CREATE;
}

"FROM" {
	ECOUNT;
	return FROM;
}

"INTEGER" {
	ECOUNT;
	return SQL_TYPE;
}

"JOIN" {
	ECOUNT;
	return JOIN;
}

"LEFT" {
	ECOUNT;
	return LEFT;
}

"LIKE" {
	ECOUNT;
	return LIKE;
}

"NOT"{whitespace}+"SIMILAR"{whitespace}+"TO" {
	ECOUNT;
	return NOT_SIMILAR_TO;
}

"SIMILAR"{whitespace}+"TO" {
	ECOUNT;
	return SIMILAR_TO;
}

"IS"{whitespace}+"NOT"{whitespace}+"NULL" {
	ECOUNT;
	return SQL_IS_NOT_NULL;
}

"IS"{whitespace}+"NULL" {
	ECOUNT;
	return SQL_IS_NULL;
}

"NOT" {
	ECOUNT;
	return NOT;
}

"IS" {
	ECOUNT;
	return SQL_IS;
}

"NULL" {
	ECOUNT;
	return SQL_NULL;
}

"ON" {
	ECOUNT;
	return SQL_ON;
}

"OR" {
	ECOUNT;
	return OR;
}

"||" { /* also means OR for numbers (mysql) */
	ECOUNT;
	return CONCATENATION;
}

"<<" {
	ECOUNT;
	return BITWISE_SHIFT_LEFT;
}

">>" {
	ECOUNT;
	return BITWISE_SHIFT_RIGHT;
}

"XOR" {
	ECOUNT;
	return XOR;
}

"RIGHT" {
	ECOUNT;
	return RIGHT;
}

"SELECT" {
	ECOUNT;
	return SELECT;
}

"TABLE" {
	ECOUNT;
	return TABLE;
}

"WHERE" {
	ECOUNT;
	return WHERE;
}

"ORDER" {
	ECOUNT;
	return ORDER;
}

"BY" {
	ECOUNT;
	return BY;
}

"ASC" {
	ECOUNT;
	return ASC;
}

"DESC" {
	ECOUNT;
	return DESC;
}

(['][^']*[']|["][^\"]*["]) {
	ECOUNT;
	yylval.stringValue = new TQString(TQString::fromUtf8(yytext+1, yyleng-2));
	return CHARACTER_STRING_LITERAL;

/* "ZZZ" sentinel for script */
}

{identifier} {
	KexiDBDbg << "yytext: '" << yytext << "' (" << yyleng << ")" << endl;
	ECOUNT;
	yylval.stringValue = new TQString(TQString::fromUtf8(yytext, yyleng));
	if (yytext[0]>='0' && yytext[0]<='9') {
		setError(i18n("Invalid identifier"),
		  i18n("Identifiers should start with a letter or '_' character"));
		return SCAN_ERROR;
	}
	return IDENTIFIER;
}

{query_parameter} {
	KexiDBDbg << "yytext: '" << yytext << "' (" << yyleng << ")" << endl;
	ECOUNT;
	yylval.stringValue = new TQString(TQString::fromUtf8(yytext+1, yyleng-2));
	return QUERY_PARAMETER;
}

{whitespace}+ {
	ECOUNT;
}

[\~\!\@\#\^\&\|\`\?,()\[\]\.;\:\+\-\*\/\%\^\<\>\=] {
	KexiDBDbg << "char: '" << yytext[0] << "'" << endl;
	ECOUNT;
	return yytext[0];
}

%%

void tokenize(const char *data)
{
	yy_switch_to_buffer(yy_scan_string(data));
	ctoken = "";
	current = 0;
}

