/* 
   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: grammar.y,v 1.14 2003/04/06 21:44:19 staikos Exp $

%pure_parser

%{
	// Compiler, shutup
	#define YYDEBUG 0
	#define YYMAXDEPTH 0

	#include <stdlib.h>

	#include <kdebug.h>
	#include <klocale.h>
	#include <kmessagebox.h>

	#include "configparser.h"

	extern "C" { void linuzerror(const char *); }

	using namespace Config;

%}

%union
{
	QString *string;
	QStringList *strings;
	Config::Node *node;
	Config::NodeList *list;
	Config::VariableList *deps;
	Config::ExpressionNode *expr;
}

%{
	extern int linuzlex(YYSTYPE *);
%}

/* Expressions */
%token NOT
%token EQ
%token NEQ
%token OR
%token AND

/* Text statements */
%token MAINMENU_NAME
%token COMMENT
%token TEXT

/* Ask statements */
%token BOOL
%token HEX
%token INT
%token STRING
%token TRISTATE

/* Define statements */
%token DEFINE_BOOL
%token DEFINE_HEX
%token DEFINE_INT
%token DEFINE_STRING
%token DEFINE_TRISTATE

/* Dependent statement */
%token DEP_BOOL
%token DEP_MBOOL
%token DEP_HEX
%token DEP_INT
%token DEP_STRING
%token DEP_TRISTATE

/* Unset statement */
%token UNSET

/* Choice statement */
%token CHOICE

/* If statements */
%token IF
%token THEN
%token ELSE
%token FI

/* Menu block */
%token MAINMENU_OPTION
%token NEXT_COMMENT
%token ENDMENU

%token <string> UNQUOTED
%token <string> SINGLE_QUOTED
%token <string> DOUBLE_QUOTED
%token <string> ERROR

/* internal typed tokens */
%type <string> prompt, word

%type <deps> deps

%type <node> statement,
	text_statement,
	ask_statement,
	define_statement,
	dependent_statement,
	unset_statement,
	choice_statement,
	if_statement,
	expression_list,
	menu_block

%type <list> statements

%type <expr> expression

%type <strings> symbols

%expect 6
/* Grammar follows */

%%

// Rules marked INVALID are invalid according to the config syntax, but
// used in some kernels

config: /* empty */ { Parser::self()->setRoot(0); }
	| statements {
		MainMenuNameNode *menu = 0;
		for ($<list>1->first(); $<list>1->current(); $<list>1->next())
			if ($<list>1->current()->type() == Node::MainMenuName)
			{
				menu = static_cast<MainMenuNameNode *>($<list>1->take());
				break;
			}
		Parser::self()->setRoot(new RootNode(menu, $<list>1));
	}
;

statements: statement {
		$$ = new NodeList;
		$$->setAutoDelete(true);
		if ($<node>1)
			$$->append($<node>1);
	}
	| statements statement {
		if ($<node>2)
			$<list>1->append($<node>2);
	}
;

statement: '\n' { $$ = 0; }
	| text_statement { }
	| ask_statement { }
	| define_statement { }
	| dependent_statement { }
	| unset_statement { }
	| choice_statement { }
	| if_statement { }
	| menu_block { }
	| ERROR {
		Parser::self()->addError(*$<string>1);
		delete $<string>1;
	}
;

text_statement: MAINMENU_NAME prompt '\n' {
		$$ = new MainMenuNameNode($<string>2);
	}
	| COMMENT prompt '\n' {
		$$ = new CommentNode($<string>2);
	}
	| TEXT prompt '\n' {
		$$ = new TextNode($<string>2);
	}
;

ask_statement: BOOL prompt UNQUOTED '\n' {
		$$ = new BoolInputNode($<string>2, $<string>3);
	}
	| BOOL prompt UNQUOTED word '\n' {
		// INVALID (2.4.x)
		$$ = new BoolInputNode($<string>2, $<string>3);
		delete $<string>4;
	}
	| HEX prompt UNQUOTED word '\n' {
		$$ = new HexInputNode($<string>2, $<string>3,
			new VariableNode($<string>4));
	}
	| HEX prompt UNQUOTED word word '\n' {
		// INVALID (2.2.18 at least)
		$$ = new HexInputNode($<string>2, $<string>3,
			new VariableNode($<string>5));
		delete $<string>4;
	}
	| INT prompt UNQUOTED word '\n' {
		$$ = new IntInputNode($<string>2, $<string>3,
			new VariableNode($<string>4));
	}
	| INT prompt UNQUOTED word word '\n' {
		// INVALID (2.2.18 at least)
		$$ = new IntInputNode($<string>2, $<string>3,
			new VariableNode($<string>5));
		delete $<string>4;
	}
	| STRING prompt UNQUOTED word '\n' {
		$$ = new StringInputNode($<string>2, $<string>3,
			new VariableNode($<string>4));
	}
	| TRISTATE prompt UNQUOTED '\n' {
		$$ = new TristateInputNode($<string>2, $<string>3);
	}
	| TRISTATE prompt UNQUOTED word '\n' {
		// INVALID (2.4.x)
		$$ = new TristateInputNode($<string>2, $<string>3);
		delete $<string>4;
	}
;

define_statement: DEFINE_BOOL UNQUOTED word '\n' {
		$$ = new DefineNode(DefineNode::Bool, $<string>2, new VariableNode($<string>3));
	}
	| DEFINE_HEX UNQUOTED word '\n' {
		$$ = new DefineNode(DefineNode::Hex, $<string>2, new VariableNode($<string>3));
	}
	| DEFINE_INT UNQUOTED word '\n' {
		$$ = new DefineNode(DefineNode::Int, $<string>2, new VariableNode($<string>3));
	}
	| DEFINE_STRING UNQUOTED word '\n' {
		$$ = new DefineNode(DefineNode::String, $<string>2, new VariableNode($<string>3));
	}
	| DEFINE_TRISTATE UNQUOTED word '\n' {
		$$ = new DefineNode(DefineNode::Tristate, $<string>2, new VariableNode($<string>3));
	}
;

dependent_statement: DEP_BOOL prompt UNQUOTED deps '\n' {
		$$ = new RestricedBoolInputNode($<string>2, $<string>3,
			new DependencyListNode($<deps>4));
	}
	| DEP_MBOOL prompt UNQUOTED deps '\n' {
		$$ = new BoolInputNode($<string>2, $<string>3,
			new DependencyListNode($<deps>4));
	}
	| DEP_HEX prompt UNQUOTED word deps '\n' {
		$$ = new HexInputNode($<string>2, $<string>3,
			new VariableNode($<string>4),
			new DependencyListNode($<deps>5));
	}
	| DEP_INT prompt UNQUOTED word deps '\n' {
		$$ = new IntInputNode($<string>2, $<string>3,
			new VariableNode($<string>4),
			new DependencyListNode($<deps>5));
	}
	| DEP_STRING prompt UNQUOTED word deps '\n' {
		$$ = new StringInputNode($<string>2, $<string>3,
			new VariableNode($<string>4),
			new DependencyListNode($<deps>5));
	}
	| DEP_TRISTATE prompt UNQUOTED deps '\n' {
		$$ = new TristateInputNode($<string>2, $<string>3,
			new DependencyListNode($<deps>4));
	}
	| DEP_TRISTATE prompt UNQUOTED '\n' {
		// INVALID: SuSE kernels
		$$ = new TristateInputNode($<string>2, $<string>3, 0);
	}
;

unset_statement: UNSET symbols '\n' {
		$$ = new UnsetNode($<strings>2);
	}

choice_statement: CHOICE prompt word word '\n' {
		QStringList choices = QStringList::split(QRegExp("[ \t]+"), *$<string>3);
		delete $<string>3;
		QStringList labels;
		QStringList symbols;
		int defIndex = 0, i = 0;
		for (QStringList::ConstIterator it = choices.begin();
			it != choices.end();
			++it, ++i)
		{
			labels.append(*it);
			if ((*it).startsWith(*$<string>4))
				defIndex = i;
			if (++it != choices.end())
				symbols.append(*it);
			else
				labels.remove(labels.fromLast());
		}
		delete $<string>4;
		$$ = new ChoiceNode($<string>2, labels, symbols, defIndex);
	}

if_statement: IF expression_list ';' THEN '\n' statements FI '\n' {
		$$ = new IfNode($<expr>2, $<list>6);
	}
	| IF expression_list '\n' THEN '\n' statements FI '\n' {
		$$ = new IfNode($<expr>2, $<list>6);
	}
	| IF expression_list ';' THEN '\n' statements ELSE '\n' statements FI '\n' {
		$$ = new IfNode($<expr>2, $<list>6, $<list>9);
	}
	| IF expression_list '\n' THEN '\n' statements ELSE '\n' statements FI '\n' {
		$$ = new IfNode($<expr>2, $<list>6, $<list>9);
	}
;

expression_list: expression_list AND '[' expression ']' {
		$$ = new AndExpressionNode($<expr>1, $<expr>4);
	}
	| expression_list OR '[' expression ']' {
		$$ = new OrExpressionNode($<expr>1, $<expr>4);
	}
	| '[' expression ']' {
		$$ = $<expr>2;
	}
;

menu_block: MAINMENU_OPTION NEXT_COMMENT '\n' statements ENDMENU '\n' {
		$$ = new MenuNode(static_cast<CommentNode *>($<list>4->take(0)),
			$<list>4);
	}
;

expression: DOUBLE_QUOTED EQ DOUBLE_QUOTED {
		$$ = new EqualityExpressionNode(new VariableNode($<string>1),
			new VariableNode($<string>3));
	}
	| DOUBLE_QUOTED NEQ DOUBLE_QUOTED {
		$$ = new UnequalityExpressionNode(new VariableNode($<string>1),
			new VariableNode($<string>3));
	}
	| DOUBLE_QUOTED {
		$$ = new ImplicitExpressionNode(new VariableNode($<string>1));
	}
	| NOT expression {
		$$ = new NotExpressionNode($<expr>2);
	}
	| expression AND expression {
		$$ = new AndExpressionNode($<expr>1, $<expr>3);
	}
	| expression OR expression {
		$$ = new OrExpressionNode($<expr>1, $<expr>3);
	}

deps: word {
		$$ = new VariableList;
		$$->setAutoDelete(true);
		$$->append(new VariableNode($<string>1));
	}
	| deps word {
		$<deps>1->append(new VariableNode($<string>2));
	}
;

symbols: UNQUOTED {
		$$ = new QStringList;
		$$->append(*$<string>1);
		delete $<string>1;
	}
	| symbols UNQUOTED {
		$<strings>1->append(*$<string>2);
		delete $<string>2;
	}
;

prompt: SINGLE_QUOTED
	| DOUBLE_QUOTED
;

word: prompt
	| UNQUOTED
;

%%

void linuzerror(const char *s)
{
	Parser::self()->addError(i18n(s));
}

// vim: ts=4 sw=4 noet
