#include "config.h"
#include	"recipe.h"
#include	"lexer.h"
#include	"token.h"
#include	"funcs.h"


int Recipe::ParseRecipe(Lexer &l)
{
	lex= &l;
	lex->token(cur_tok);
	try
	{
		topNode=ParseStatementList();
	}
	catch (const char *p)
	{
		l.errmsg(p);
		return (-1);
	}
#if NEED_NONCONST_EXCEPTIONS
	catch (char *p)
	{
		l.errmsg(p);
		return (-1);
	}
#endif
	if (cur_tok.Type() != cur_tok.eof)
	{
		l.errmsg("Syntax error.");
		return (-1);
	}
	return (0);
}

RecipeNode *Recipe::ParseStatementList()
{
RecipeNode *n=alloc(RecipeNode::statementlist);
Token::tokentype type;

	while ( (type=cur_tok.Type()) != cur_tok.rbrace && type != cur_tok.eof)
	{
		if (type == cur_tok.semicolon)
		{
			lex->token(cur_tok);
			continue;
		}

	RecipeNode *o=ParseStatement();

		n->AppendSibling(o);
	}
	return (n);
}

RecipeNode *Recipe::ParseStatement()
{
RecipeNode *n, *o;

	switch (cur_tok.Type())	{
	case Token::tokenif:
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Syntax error after if";

		lex->token(cur_tok);

		n=alloc(RecipeNode::ifelse);
		o=ParseExpr();

		n->AppendSibling(o);

		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";

		lex->token(cur_tok);
		if (cur_tok.Type() != Token::semicolon)	// Newline/semicolon
			throw "Syntax error after )";

		lex->token(cur_tok);
		n->AppendSibling(ParseSubStatement());
		if (cur_tok.Type() == Token::tokenelse)
		{
			lex->token(cur_tok);
			if (cur_tok.Type() != Token::semicolon)	// Newline/semicolon
				throw "Syntax error after else";

			lex->token(cur_tok);
			n->AppendSibling(ParseSubStatement());
		}
		else if (cur_tok.Type() == Token::tokenelsif)
		{
			cur_tok.Type(Token::tokenif);
			n->AppendSibling(ParseStatement());
		}
		return (n);
	case Token::tokenwhile:
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Syntax error after while";

		lex->token(cur_tok);

		n=alloc(RecipeNode::whileloop);
		o=ParseExpr();
		n->AppendSibling(o);

		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";

		lex->token(cur_tok);
		if (cur_tok.Type() != Token::semicolon)	// Newline/semicolon
			throw "Syntax error after else";

		lex->token(cur_tok);
		n->AppendSibling(ParseSubStatement());
		return (n);
	case Token::lbrace:
		lex->token(cur_tok);
		n=ParseStatementList();
		if (cur_tok.Type() != Token::rbrace)
			throw "Missing }";

		lex->token(cur_tok);
		if (cur_tok.Type() != Token::semicolon)
		{
			throw "Syntax error after }";
		}
		lex->token(cur_tok);
		return (n);
	case Token::tokento:
		lex->token(cur_tok);
		n=alloc(RecipeNode::deliver);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";

		lex->token(cur_tok);
		return (n);
	case Token::tokencc:
		lex->token(cur_tok);
		n=alloc(RecipeNode::delivercc);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";

		lex->token(cur_tok);
		return (n);
	case Token::exception:
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lbrace)
			throw "Syntax error.";

		n=alloc(RecipeNode::exception);
		o=ParseStatement();
		n->AppendSibling(o);
		return (n);
	case Token::echo:
		lex->token(cur_tok);
		n=alloc(RecipeNode::echo);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::tokenxfilter:
		lex->token(cur_tok);
		n=alloc(RecipeNode::xfilter);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::dotlock:
		lex->token(cur_tok);
		n=alloc(RecipeNode::dotlock);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::lbrace)
			throw "Syntax error.";
		n->AppendSibling( ParseStatement() );
		return (n);
	case Token::flock:
		lex->token(cur_tok);
		n=alloc(RecipeNode::flock);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::lbrace)
			throw "Syntax error.";
		n->AppendSibling( ParseStatement() );
		return (n);
	case Token::logfile:
		lex->token(cur_tok);
		n=alloc(RecipeNode::logfile);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::log:
		lex->token(cur_tok);
		n=alloc(RecipeNode::log);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::include:
		lex->token(cur_tok);
		n=alloc(RecipeNode::include);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::exit:
		lex->token(cur_tok);
		n=alloc(RecipeNode::exit);
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::foreach:
		lex->token(cur_tok);
		n=alloc(RecipeNode::foreach);
		n->AppendSibling( ParseExpr());
		if (n->firstChild->nodeType != RecipeNode::strregexp &&
			n->firstChild->nodeType != RecipeNode::regexpr)
			throw "Syntax error.";
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		n->AppendSibling( ParseSubStatement() );
		return (n);
	case Token::importtoken:
		lex->token(cur_tok);
		n=alloc(RecipeNode::importtoken);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	case Token::unset:
		lex->token(cur_tok);
		n=alloc(RecipeNode::unset);
		n->AppendSibling( ParseExpr());
		if (cur_tok.Type() != Token::semicolon)
			throw "Syntax error.";
		lex->token(cur_tok);
		return (n);
	default:
		break;
	}
	n=ParseExpr();
	if (cur_tok.Type() != Token::semicolon)
		throw "Syntax error.";
	lex->token(cur_tok);
	return (n);
}

RecipeNode *Recipe::ParseSubStatement()
{
	if (cur_tok.Type() == Token::semicolon)
	{
		lex->token(cur_tok);
		return alloc(RecipeNode::statementlist);
	}
	return ParseStatement();
}

RecipeNode *Recipe::ParseAssign()
{
RecipeNode *n=ParseLogicalOr();

	while (cur_tok.Type() == Token::equals)
	{
		lex->token(cur_tok);

		if (n->nodeType != RecipeNode::qstring)
			throw "Syntax error before =";

	RecipeNode *o=alloc(RecipeNode::assignment);
		o->AppendSibling(n);

		n=ParseLogicalOr();
		o->AppendSibling(n);
		n=o;
	}
	return (n);
}

RecipeNode *Recipe::ParseLogicalOr()
{
RecipeNode *n=ParseLogicalAnd();

	while (cur_tok.Type() == Token::lor)
	{
		lex->token(cur_tok);

	RecipeNode *o=alloc(RecipeNode::logicalor);

		o->AppendSibling(n);

		n=ParseLogicalAnd();
		o->AppendSibling(n);
		n=o;
	}
	return (n);
}

RecipeNode *Recipe::ParseLogicalAnd()
{
RecipeNode *n=ParseComparison();

	while (cur_tok.Type() == Token::land)
	{
		lex->token(cur_tok);

	RecipeNode *o=alloc(RecipeNode::logicaland);

		o->AppendSibling(n);

		n=ParseComparison();
		o->AppendSibling(n);
		n=o;
	}
	return (n);
}

RecipeNode *Recipe::ParseComparison()
{
RecipeNode *n=ParseBitwiseOr();
RecipeNode *o;

	switch (cur_tok.Type())	{
	case Token::lt:
		o=alloc(RecipeNode::lessthan);
		break;
	case Token::le:
		o=alloc(RecipeNode::lessthanoreq);
		break;
	case Token::gt:
		o=alloc(RecipeNode::greaterthan);
		break;
	case Token::ge:
		o=alloc(RecipeNode::greaterthanoreq);
		break;
	case Token::eq:
		o=alloc(RecipeNode::equal);
		break;
	case Token::ne:
		o=alloc(RecipeNode::notequal);
		break;
	case Token::slt:
		o=alloc(RecipeNode::strlessthan);
		break;
	case Token::sle:
		o=alloc(RecipeNode::strlessthanoreq);
		break;
	case Token::sgt:
		o=alloc(RecipeNode::strgreaterthan);
		break;
	case Token::sge:
		o=alloc(RecipeNode::strgreaterthanoreq);
		break;
	case Token::seq:
		o=alloc(RecipeNode::strequal);
		break;
	case Token::sne:
		o=alloc(RecipeNode::strnotequal);
		break;
	default:
		return (n);
	}

	lex->token(cur_tok);
	o->AppendSibling(n);
	n=ParseBitwiseOr();
	o->AppendSibling(n);
	return (o);
}

RecipeNode *Recipe::ParseBitwiseOr()
{
RecipeNode *n=ParseBitwiseAnd();

	while (cur_tok.Type() == Token::bor)
	{
		lex->token(cur_tok);

	RecipeNode *o=alloc(RecipeNode::bitwiseor);

		o->AppendSibling(n);

		n=ParseBitwiseAnd();
		o->AppendSibling(n);
		n=o;
	}
	return (n);
}

RecipeNode *Recipe::ParseBitwiseAnd()
{
RecipeNode *n=ParseAddSub();

	while (cur_tok.Type() == Token::band)
	{
		lex->token(cur_tok);

	RecipeNode *o=alloc(RecipeNode::bitwiseand);

		o->AppendSibling(n);

		n=ParseAddSub();
		o->AppendSibling(n);
		n=o;
	}
	return (n);
}

RecipeNode *Recipe::ParseAddSub()
{
RecipeNode *n=ParseMultDiv();
RecipeNode *o;

	for (;;)	{
		switch (cur_tok.Type())	{
		case Token::plus:
			o=alloc(RecipeNode::add);
			break;
		case Token::minus:
			o=alloc(RecipeNode::subtract);
			break;
		default:
			return (n);
		}

		lex->token(cur_tok);
		o->AppendSibling(n);
		n=ParseMultDiv();
		o->AppendSibling(n);
		n=o;
	}
}

RecipeNode *Recipe::ParseMultDiv()
{
RecipeNode *n=ParseStrRegExp();
RecipeNode *o;

	for (;;)	{
		switch (cur_tok.Type())	{
		case Token::mult:
			o=alloc(RecipeNode::multiply);
			break;
		case Token::divi:
			o=alloc(RecipeNode::divide);
			break;
		default:
			return (n);
		}

		lex->token(cur_tok);
		o->AppendSibling(n);
		n=ParseElement();
		o->AppendSibling(n);
		n=o;
	}
}

RecipeNode *Recipe::ParseStrRegExp()
{
RecipeNode	*n=ParseElement();

	while ( cur_tok.Type() == Token::strregexp)
	{
	RecipeNode	*o;

		o=alloc(RecipeNode::strregexp);
		lex->token(cur_tok);
		o->AppendSibling(n);
		if (cur_tok.Type() != Token::regexpr)
			throw "Syntax error after =~";

		n=ParseElement();
		o->AppendSibling(n);
		n=o;
	}
	return (n);
}

RecipeNode *Recipe::ParseElement()
{
RecipeNode	*n, *o;

	switch (cur_tok.Type())	{
	case Token::length:
		n=alloc(RecipeNode::strlength);
		lex->token(cur_tok);
		o=ParseElement();
		n->AppendSibling(o);
		return (n);
	case Token::substr:
		n=alloc(RecipeNode::strsubstr);
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Missing (";

		lex->token(cur_tok);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() != Token::comma)
			throw "Missing ,";

		lex->token(cur_tok);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() == Token::comma)
		{
			lex->token(cur_tok);
			o=ParseExpr();
			n->AppendSibling(o);
		}
		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";

		lex->token(cur_tok);
		return (n);
	case Token::getaddr:
		lex->token(cur_tok);
		n=alloc(RecipeNode::getaddr);
		n->AppendSibling(ParseElement());
		return (n);
	case Token::escape:
		lex->token(cur_tok);
		n=alloc(RecipeNode::escape);
		n->AppendSibling(ParseElement());
		return (n);
	case Token::regexpr:
		n=alloc(RecipeNode::regexpr);
		n->str=cur_tok.String();
		lex->token(cur_tok);
		return (n);
	case Token::lparen:
		lex->token(cur_tok);
		n=ParseExpr();
		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";

		lex->token(cur_tok);
		return (n);
	case Token::logicalnot:
		lex->token(cur_tok);
		n=alloc(RecipeNode::logicalnot);
		o=ParseElement();
		n->AppendSibling(o);
		return (n);
	case Token::bitwisenot:
		lex->token(cur_tok);
		n=alloc(RecipeNode::bitwisenot);
		o=ParseElement();
		n->AppendSibling(o);
		return (n);
	case Token::lookup:
		n=alloc(RecipeNode::lookup);
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Missing (";

		lex->token(cur_tok);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() != Token::comma)
			throw "Missing ,";

		lex->token(cur_tok);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() == Token::comma)
		{
			lex->token(cur_tok);
			o=ParseExpr();
			n->AppendSibling(o);
		}
		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";
		lex->token(cur_tok);
		return (n);
	case Token::to_lower:
		lex->token(cur_tok);
		n=alloc(RecipeNode::to_lower);
		n->AppendSibling(ParseElement());
		return (n);
	case Token::to_upper:
		lex->token(cur_tok);
		n=alloc(RecipeNode::to_upper);
		n->AppendSibling(ParseElement());
		return (n);
	case Token::hasaddr:
		lex->token(cur_tok);
		n=alloc(RecipeNode::hasaddr);
		n->AppendSibling(ParseElement());
		return (n);
#ifdef	DbObj
	case Token::gdbmopen:
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Missing (.";

		lex->token(cur_tok);
		n=alloc(RecipeNode::gdbmopen);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() == Token::comma)
		{
			lex->token(cur_tok);
			o=ParseExpr();
			n->AppendSibling(o);
		}
		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";
		lex->token(cur_tok);
		return (n);
	case Token::gdbmclose:
		lex->token(cur_tok);
		return (alloc(RecipeNode::gdbmclose));
	case Token::gdbmfetch:
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Missing (.";

		lex->token(cur_tok);
		n=alloc(RecipeNode::gdbmfetch);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() == Token::comma)
		{
			lex->token(cur_tok);
			o=ParseExpr();
			n->AppendSibling(o);
		}
		if (cur_tok.Type() == Token::comma)
		{
			lex->token(cur_tok);
			o=ParseExpr();
			n->AppendSibling(o);
		}
		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";
		lex->token(cur_tok);
		return (n);
	case Token::gdbmstore:
		lex->token(cur_tok);
		if (cur_tok.Type() != Token::lparen)
			throw "Missing (.";

		lex->token(cur_tok);
		n=alloc(RecipeNode::gdbmstore);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() != Token::comma)
			throw "Missing ,.";

		lex->token(cur_tok);
		o=ParseExpr();
		n->AppendSibling(o);
		if (cur_tok.Type() != Token::rparen)
			throw "Missing )";
		lex->token(cur_tok);
		return (n);
#else
	case Token::gdbmopen:
	case Token::gdbmclose:
	case Token::gdbmfetch:
	case Token::gdbmstore:
		throw "GDBM/DB support is not available.";
#endif
	case Token::timetoken:
		lex->token(cur_tok);
		return (alloc(RecipeNode::timetoken));
	default:
		break;
	}
	return (ParseString());
}

////////////////////////////////////////////////////////////////////////////
//
//  Parse a string.  Consecutive strings are automatically concatenated.
//
////////////////////////////////////////////////////////////////////////////

RecipeNode *Recipe::ParseString()
{
RecipeNode	*n=NULL;
RecipeNode	*s=NULL;

	for (;;)
	{
		if (s && s->nodeType != RecipeNode::concat)
		{
			n=alloc(RecipeNode::concat);
			n->AppendSibling(s);
			s=n;
		}

		switch (cur_tok.Type())	{
		case Token::qstring:
			n=alloc(RecipeNode::qstring);
			break;
		case Token::sqstring:
			n=alloc(RecipeNode::sqstring);
			break;
		case Token::btstring:
			n=alloc(RecipeNode::btstring);
			break;
		default:
			throw "Syntax error.";
		}

		n->str=cur_tok.String();
		lex->token(cur_tok);
		if (s)	s->AppendSibling(n);
		else	s=n;

		switch (cur_tok.Type())	{
		case Token::qstring:
		case Token::sqstring:
		case Token::btstring:
			continue;
		default:
			break;
		}
		break;
	}
	return (s);
}
