/* 
   Copyright (c) 2001 Malte Starostik <malte@kde.org>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
 
   This program 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
    General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

/* $Id: lexer.l,v 1.8 2003/02/12 00:07:00 staikos Exp $ */

%option yylineno

%{
#include <kdebug.h>
#include <klocale.h>

#include "configparser.h"
// grammar.cpp defines / declares the stuff in grammar.h,
// which causes trouble with --enable-final, fortunately grammar.cpp
// #define's YYBISON...
#ifndef YYBISON
#include "grammar.h"
#endif

#define YY_NO_UNPUT
#define YY_DECL int linuzlex(YYSTYPE *lvalp)
#define YY_USER_ACTION Parser::self()->currentFile()->addPos(yyleng);
// Prevent compiler warnings
#define YY_ALWAYS_INTERACTIVE 0
#define YY_NEVER_INTERACTIVE 1
#define YY_STACK_USED 0
#define YY_MAIN 0

using namespace Config;

%}

%x include

%%

"=" { return EQ; }
"!=" { return NEQ; }
"-a" { return AND; }
"-o" { return OR; }
"&&" { return AND; }
"||" { return OR; }
"!" { return NOT; }

"mainmenu_name" { return MAINMENU_NAME; }
"comment" { return COMMENT; }
"text" { return TEXT; }

"bool" { return BOOL; }
"hex" { return HEX; }
"int" { return INT; }
"string" { return STRING; }
"tristate" { return TRISTATE; }

"define_bool" { return DEFINE_BOOL; }
"define_hex" { return DEFINE_HEX; }
"define_int" { return DEFINE_INT; }
"define_string" { return DEFINE_STRING; }
"define_tristate" { return DEFINE_TRISTATE; }

"dep_bool" { return DEP_BOOL; }
"dep_mbool" { return DEP_MBOOL; }
"dep_hex" { return DEP_HEX; }
"dep_int" { return DEP_INT; }
"dep_string" { return DEP_STRING; }
"dep_tristate" { return DEP_TRISTATE; }

"unset" { return UNSET; }

"choice" { return CHOICE; }

"if" { return IF; }
"then" { return THEN; }
"else" { return ELSE; }
"fi" { return FI; }

"mainmenu_option" { return MAINMENU_OPTION; }
"next_comment" { return NEXT_COMMENT; }
"endmenu" { return ENDMENU; }

"source" BEGIN(include);

[A-Za-z0-9_$./-]+ {
	lvalp->string = new QString(QString::fromLatin1(yytext, yyleng));
	return UNQUOTED;
}
"'"[^']*"'" {
	lvalp->string = new QString(QString::fromLatin1(yytext + 1, yyleng - 2));
	for (int pos = lvalp->string->find("\\\n");
		pos != -1;
		pos = lvalp->string->find("\\\n", pos))
	{
		Parser::self()->currentFile()->nextLine();
		lvalp->string->replace(pos, 2, "");
	}
	if (lvalp->string->find("\n") != -1)
	{
		*(lvalp->string) = i18n("syntax error");
		return ERROR;
	}
	return SINGLE_QUOTED;
}
"\""[^"]*"\"" {
	lvalp->string = new QString(QString::fromLatin1(yytext + 1, yyleng - 2));
	for (int pos = lvalp->string->find("\\\n");
		pos != -1;
		pos = lvalp->string->find("\\\n", pos))
	{
		Parser::self()->currentFile()->nextLine();
		lvalp->string->replace(pos, 2, "");
	}
	if (lvalp->string->find("\n") != -1)
	{
		*(lvalp->string) = i18n("syntax error");
		return ERROR;
	}
	return DOUBLE_QUOTED;
}

"\\\n" { Parser::self()->currentFile()->nextLine(); } // continuation line
"["|"]"|";" { return (int)(*yytext); }
"\n" { Parser::self()->currentFile()->nextLine(); return (int)'\n'; }
"#".* // ignore comments
[ \t]+ // eat up whitespace
. {
	Parser::self()->addError(i18n("unrecognized character"));
	yyterminate();
	// Never reached, but shuts up g++
	REJECT; yy_flex_realloc(0, 0);
}

<include>[ \t]* // eat whitespace
<include>[^ \t\n]+ {
	if (!Parser::self()->pushInclude(QString::fromLatin1(yytext, yyleng)))
		yyterminate();
	BEGIN(INITIAL);
}

<<EOF>> {
	if (!Parser::self()->popInclude())
		yyterminate();
}

%%

bool Parser::pushInclude(const QString &file)
{
	RuleFile *rule = new RuleFile(file);
	if (rule->data().length())
	{
		if (m_ruleStack.isEmpty())
		{
			YY_NEW_FILE;
			BEGIN(INITIAL);
		}
		m_ruleStack.push(rule);
		rule->setBuffer(yy_scan_string(rule->data().latin1()));
		yy_switch_to_buffer(reinterpret_cast<YY_BUFFER_STATE>(rule->buffer()));
		return true;
	}
	delete rule;
	return false;
}

bool Parser::popInclude()
{
	m_ruleStack.remove();
	if (m_ruleStack.isEmpty())
		return false;
	yy_switch_to_buffer(reinterpret_cast<YY_BUFFER_STATE>(m_ruleStack.top()->buffer()));
	return true;
}

RuleFile::~RuleFile()
{
	yy_delete_buffer(reinterpret_cast<YY_BUFFER_STATE>(m_buffer));
}

int linuzwrap()
{
	return 1;
}

// vim: ts=4 sw=4 noet
