/*
	Copyright (C) 2008-2026 Mark Tyler

	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 3 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 in the file COPYING.
*/

#include "ced_eval.h"



#ifndef	CED_USE_BISON_PARSER



namespace mtCED
{
namespace	// Make this invisible externally
{


class Parser
{
public:
	enum
	{
		// Precedence - higher is done first, from left to right
		GR_PREC_LOWEST		= -999,
		GR_ASSIGN_PREC		= -10,
		GR_CMP_PREC		= -1,
		GR_OP_ADD_PREC		= 1,
		GR_OP_SUB_PREC		= 1,
		GR_OP_MULT_PREC		= 2,
		GR_OP_DIV_PREC		= 2,
		GR_OP_POWER_PREC	= 10
	};


	int evaluate ( char const * const text, CedParser * const cedparser )
	{
		if ( ! text || ! cedparser )
		{
			return 1;
		}

		try
		{
			m_cedparser = cedparser;
			m_lexer.init ( text, cedparser );
			m_nt_num.clear ();

			parse_nt_input ();	// Populates the result

			// No errors/exceptions in the grammar at this point so
			// we have a valid result waiting for us on the stack.

			cedparser->data = m_nt_num.back();

			return 0;
		}
		catch (...)
		{
			cedparser->ced_errno = m_error;
			cedparser->sp = m_lexer.input_pos() + 1;

			return 1;
		}
	}

private:
/*	Recursive descent parser: parse_nt_* throw on error (setting m_error)

	void parse_nt_input ()
	void parse_nt_exp ()
	void parse_nt_exp_line ()
	void parse_nt_op_exp ()
	void parse_nt_num_var_func ()
	void parse_nt_func (
		std::string	const	& name,
		char		const	* var_token_pos
		)
	void parse_nt_argset ( size_t const argtot )
*/

	/// EXPECT: empty, <exp>..., =<exp>...
	void parse_nt_input ()
	{
		int assign_cell = 0;

		switch ( m_lexer.scan_token() )
		{
		case Lexer::TK_CMP_EQ:
			if ( m_cedparser->cell )
			{
				// Only assign if cell exists and '='
				assign_cell = 1;
			}
			break;

		default:
			m_lexer.rewind_token ();
			break;
		}

		while (1)
		{
			switch ( m_lexer.scan_token() )
			{
			case Lexer::TK_END:
				// Always leave something on the stack
				if ( m_nt_num.size() < 1 )
				{
					m_nt_num.emplace_back ( 0 );
				}
				goto finish;
			}

			m_lexer.rewind_token ();

			// Not the end, or a separator
			parse_nt_exp_line ();
		}

finish:
		// Nothing on stack is an unknown/unexpected error
		if ( m_nt_num.size() < 1 )
		{
			throw 123;
		}

		if ( assign_cell )
		{
			double const res = m_nt_num.back();
			if ( isnan(res) )
			{
				m_error = CED_ERROR_NAN;
				throw 123;
			}
			else if ( isinf(res) )
			{
				m_error = CED_ERROR_INFINITY;
				throw 123;
			}

			m_cedparser->cell->value = res;
		}
	}

	/// EXPECT: <exp>... with resulting number added to m_nt_num
	void parse_nt_exp_line ()
	{
		parse_nt_exp ();

		while (1)
		{
			switch ( m_lexer.scan_token() )
			{
			case Lexer::TK_END:
				return;

			case Lexer::TK_FUNC_ARG_SEP:
			case Lexer::TK_PAREN_CLOSE:
				// Caller deals with this
				m_lexer.rewind_token ();
				return;

			default:
				// <op> <exp> is only remaining option
				m_lexer.rewind_token ();
				parse_nt_op_exp ();
				break;
			}
		}
	}

	/// EXPECT: single <exp>, with resulting number added to m_nt_num
	void parse_nt_exp ()
	{
		int token = m_lexer.scan_token ();

		if ( Lexer::TK_PAREN_OPEN == token )
		{
			parse_nt_exp_line ();

			token = m_lexer.scan_token();

			if ( Lexer::TK_PAREN_CLOSE != token )
			{
				m_lexer.rewind_token ();
				m_error = CED_ERROR_MISSING_PAREN_CLOSE;
				throw 123;
			}
		}
		else
		{
			m_lexer.rewind_token ();

			// No parentheses, so just a single item
			parse_nt_num_var_func ();
		}
	}

	/// EXPECT: some operation, then an expression with result added to
	/// m_nt_num.  On entry we must have a number on m_nt_num to use.
	void parse_nt_op_exp ()
	{
		/// Read in op1 (exp1 <op1> exp2 [<op2>])

		int const op1 = m_lexer.scan_token();
		int prec1 = GR_PREC_LOWEST;
		int associativity = 0;

		switch ( op1 )
		{
		case Lexer::TK_OP_ADD:
			prec1 = GR_OP_ADD_PREC;
			break;

		case Lexer::TK_OP_SUB:
			prec1 = GR_OP_SUB_PREC;
			break;

		case Lexer::TK_OP_MULT:
			prec1 = GR_OP_MULT_PREC;
			break;

		case Lexer::TK_OP_DIV:
			prec1 = GR_OP_DIV_PREC;
			break;

		case Lexer::TK_OP_POWER:
			prec1 = GR_OP_POWER_PREC;
			associativity = 1;
			break;

		case Lexer::TK_CMP_LT:
		case Lexer::TK_CMP_LTE:
		case Lexer::TK_CMP_GT:
		case Lexer::TK_CMP_GTE:
		case Lexer::TK_CMP_EQ:
		case Lexer::TK_CMP_NEQ:
		case Lexer::TK_CMP_LEG:
			prec1 = GR_CMP_PREC;
			break;

		case Lexer::TK_BAD_SYMBOL:
			m_lexer.rewind_token ();
			m_error = CED_ERROR_BAD_SYMBOL;
			throw 123;

		default:
			m_lexer.rewind_token ();
			m_error = CED_ERROR_BAD_SYNTAX;
			throw 123;
		}

		char const * const pos_n2 = m_lexer.token_position ();

		/// Read in exp2 (exp1 <op1> exp2 [<op2>])
		parse_nt_exp ();

/*
2 + 2 + xxx	Prec A = Prec B : Rewind, do 2+2, pop, and continue within this function (no nesting)
2 + 2 ^ xxx	Prec A < Prec B : Rewind, nest to new exp
2 ^ 2 + xxx	Prec A > Prec B : Rewind, do 2^2, pop, and continue within this function (no nesting)

^ or = then we nest to the right due to right associativity.  + - * / then we do left to right in sequence if equal (left associativity).
a = b = 3	Prec A = Prec B : Rewind, nest to new exp

-2 ^ (xxx)	Get number, negate it, nest to new exp at (

*/

eval_tk2:
		/// Read in op2 (exp1 <op1> exp2 [<op2>]) to resolve precedence

		int const op2 = m_lexer.scan_token();
		int prec2 = GR_PREC_LOWEST;

		switch ( op2 )
		{
		case Lexer::TK_OP_ADD:
			prec2 = GR_OP_ADD_PREC;
			break;

		case Lexer::TK_OP_SUB:
			prec2 = GR_OP_SUB_PREC;
			break;

		case Lexer::TK_OP_MULT:
			prec2 = GR_OP_MULT_PREC;
			break;

		case Lexer::TK_OP_DIV:
			prec2 = GR_OP_DIV_PREC;
			break;

		case Lexer::TK_OP_POWER:
			prec2 = GR_OP_POWER_PREC;
			break;

		case Lexer::TK_CMP_LT:
		case Lexer::TK_CMP_LTE:
		case Lexer::TK_CMP_GT:
		case Lexer::TK_CMP_GTE:
		case Lexer::TK_CMP_EQ:
		case Lexer::TK_CMP_NEQ:
		case Lexer::TK_CMP_LEG:
			prec2 = GR_CMP_PREC;
			break;

		case Lexer::TK_END:
		case Lexer::TK_FUNC_ARG_SEP:
		case Lexer::TK_PAREN_CLOSE:
			// Caller deals with these so do op1 and exit
			break;

		case Lexer::TK_BAD_SYMBOL:
			m_lexer.rewind_token ();
			m_error = CED_ERROR_BAD_SYMBOL;
			throw 123;

		default:
			// Unexpected symbol, e.g. (
			m_lexer.rewind_token ();
			m_error = CED_ERROR_BAD_SYNTAX;
			throw 123;
		}

		m_lexer.rewind_token();

		if ( prec1 == GR_CMP_PREC && prec2 == GR_CMP_PREC )
		{
			// Disallow ambiguous "1 < 2 < 3", force brackets
			m_error = CED_ERROR_BAD_SYNTAX;
			throw 123;
		}

		// Do RHS operation first if precedence or associativity
		// requires it.
		if (	(prec2 > prec1)
			|| ((prec1 == prec2) && associativity)
			)
		{
			parse_nt_op_exp ();

			// Keep looping until the precedence allows us to
			// continue as normal.
			goto eval_tk2;
		}

		// Do the operation with top 2 numbers on stack
		double & n1 = m_nt_num[ m_nt_num.size() - 2 ];
		double const & n2 = m_nt_num.back();

		switch ( op1 )
		{
		case Lexer::TK_OP_ADD:	n1 += n2;		break;
		case Lexer::TK_OP_SUB:	n1 -= n2;		break;
		case Lexer::TK_OP_MULT:	n1 *= n2;		break;

		case Lexer::TK_OP_DIV:
			if ( 0 == n2 )
			{
				m_error = CED_ERROR_DIVIDE_BY_ZERO;
				m_lexer.set_token_position ( pos_n2 + 1 );
				throw 123;
			}

			n1 /= n2;

			break;

		case Lexer::TK_OP_POWER:
			try
			{
				n1 = ::pow ( n1, n2 );
			}
			catch (...)
			{
				m_error = CED_ERROR_BAD_POWER;
				m_lexer.set_token_position ( pos_n2 + 1 );
				throw;
			}
			break;

		case Lexer::TK_CMP_LT:
			if ( n1 < n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;

		case Lexer::TK_CMP_LTE:
			if ( n1 <= n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;

		case Lexer::TK_CMP_GT:
			if ( n1 > n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;

		case Lexer::TK_CMP_GTE:
			if ( n1 >= n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;

		case Lexer::TK_CMP_EQ:
			if ( n1 == n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;

		case Lexer::TK_CMP_NEQ:
			if ( n1 != n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;

		case Lexer::TK_CMP_LEG:
			if ( n1 < n2 )
			{
				n1 = -1;
			}
			else if ( n1 > n2 )
			{
				n1 = 1;
			}
			else
			{
				n1 = 0;
			}
			break;
		}

		// Remove end item
		m_nt_num.pop_back ();
	}

	/// EXPECT: some num/var/func, with resulting number added to m_nt_num
	void parse_nt_num_var_func ()
	{
		int token = m_lexer.scan_token();

		if ( Lexer::TK_OP_SUB == token )
		{
			// Treat unary minus as: 0 - (whatever follows)
			m_nt_num.emplace_back(0);

			// The subtraction will be handled by parse_nt_op_exp
			// Put the minus back for it to process
			m_lexer.rewind_token();

			// Now parse the "-" as a normal OP
			parse_nt_op_exp();

			return;
		}

		CedSheet * sheet = nullptr;

		switch ( token )
		{
		case Lexer::TK_NUMBER:
			m_nt_num.emplace_back ( m_lexer.number() );
			return;

		case Lexer::TK_SHEETREF:
			sheet = m_lexer.sheetref();
			token = m_lexer.scan_token();
			if ( Lexer::TK_CELLREF != token )
			{
				m_lexer.rewind_token();
				m_error = CED_ERROR_BAD_SYNTAX;
				throw 123;
			}
			// Fall through

		case Lexer::TK_CELLREF:
		{
			double res;
			PCellRef cref = m_lexer.pcellref();
			if ( sheet )
			{
				cref.sheet = sheet;
			}

			if ( cedparser_get_cell_value(m_cedparser, &cref, &res))
			{
				m_error = CED_ERROR_CELLREF;
				throw 123;
			}
			else
			{
				m_cedparser->flag |= CED_PARSER_FLAG_VOLATILE;
			}

			m_nt_num.emplace_back ( res );
			return;
		}

		case Lexer::TK_NAME_STRING:
		{
			char const * const sp = m_lexer.symbol_position () - 1;

			token = m_lexer.scan_token();

			switch ( token )
			{
			case Lexer::TK_PAREN_OPEN:
				// string is a function so read args
				parse_nt_func ( m_lexer.string_lower(),
					m_lexer.token_position () );
				return;
			}

			m_lexer.set_token_position ( sp );
			m_error = CED_ERROR_MISSING_PAREN_OPEN;
			throw 123;
		}

		case Lexer::TK_BAD_SYMBOL:
			m_lexer.rewind_token ();
			m_error = CED_ERROR_BAD_SYMBOL;
			throw 123;

		case Lexer::TK_BAD_NUMBER:
			m_lexer.rewind_token ();
			m_error = CED_ERROR_BAD_NUMBER;
			throw 123;
		}

		// Anything else must be an error
		m_lexer.rewind_token();
		m_error = CED_ERROR_BAD_SYNTAX;
		throw 123;
	}

	/// EXPECT: args separated by "," putting result onto m_nt_num
	void parse_nt_func (
		std::string	const	& name,
		char	const * const	var_token_pos
		)
	{
		CedFunc const * func = ced_func_get ( name.c_str() );

		if ( ! func )
		{
			m_lexer.set_token_position ( var_token_pos - 1 );
			m_error = CED_ERROR_BAD_FUNCTION_NAME;
			throw 123;
		}

		PArgSet		argset = { 0, {} };

		parse_nt_argset ( argset );

		double		func_res = 0.0;
		CedFuncState	funcstate = { func, m_cedparser, &func_res,
					argset.arg };

		if ( ced_func_exe ( &funcstate ) )
		{
			m_lexer.rewind_token();
			m_error = m_cedparser->ced_errno;
			throw 123;
		}

		if ( func->flag & CED_FUNC_FLAG_VOLATILE )
		{
			/* Set the volatile flag for functions like rand ()
			which change upon recalculation */

			m_cedparser->flag |= CED_PARSER_FLAG_VOLATILE;
		}

		m_nt_num.emplace_back ( func_res );
	}

	/// EXPECT: [exp][cellrange]['#' cellref][STRING]	Single arg here
	void parse_nt_arg ( CedFuncArg & arg )
	{
		char const * spos = m_lexer.symbol_position ();
		int token = m_lexer.scan_token();
		CedSheet * sheet = m_cedparser->sheet;

		switch ( token )
		{
		case Lexer::TK_HASH:
			token = m_lexer.scan_token();
			if ( Lexer::TK_SHEETREF == token )
			{
				sheet = m_lexer.sheetref();
				token = m_lexer.scan_token();
			}
			if ( Lexer::TK_CELLREF == token )
			{
				arg.type = CED_FARG_TYPE_CELLREF;
				arg.sheet = sheet;
				arg.u.cellref[0] = m_lexer.pcellref().cellref;
				return;
			}
			m_lexer.rewind_token();
			m_error = CED_ERROR_BAD_SYNTAX;
			throw 123;

		case Lexer::TK_QUOTE_STRING:
			arg.type = CED_FARG_TYPE_STRING;
			arg.u.str = m_lexer.quote_string();
			return;

		case Lexer::TK_SHEETREF:
			sheet = m_lexer.sheetref();
			token = m_lexer.scan_token();
			if ( Lexer::TK_CELLREF != token )
			{
				m_lexer.set_token_position ( spos );
				break;		// Evaluate as exp_line
			}
			// Fall through

		case Lexer::TK_CELLREF:
			arg.u.cellref[0] = m_lexer.pcellref().cellref;

			token = m_lexer.scan_token();
			if ( Lexer::TK_COLON != token )
			{
				m_lexer.set_token_position ( spos );
				break;		// Evaluate as exp_line
			}

			token = m_lexer.scan_token();
			if ( Lexer::TK_CELLREF != token )
			{
				m_lexer.rewind_token();
				m_error = CED_ERROR_BAD_SYNTAX;
				throw 123;
			}

			arg.u.cellref[1] = m_lexer.pcellref().cellref;

			arg.type = CED_FARG_TYPE_CELLRANGE;
			arg.sheet = sheet;
			return;

		default:
			m_lexer.rewind_token();
		}

		// Must be a normal expression line

		parse_nt_exp_line ();

		arg.type = CED_FARG_TYPE_NUM;
		arg.u.val = m_nt_num.back();
		m_nt_num.pop_back();
	}

	/// EXPECT: [arg]["," arg]...")"		0+ args with trailing )
	void parse_nt_argset ( PArgSet & argset )
	{
		int token = m_lexer.scan_token();
		switch ( token )
		{
		case Lexer::TK_PAREN_CLOSE:
			// No arguments in this function
			return;
		}

		m_lexer.rewind_token();		// This must be a func arg

		CedFuncArg arg;

		for (;;)
		{
			char const * sp = m_lexer.symbol_position();

			parse_nt_arg ( arg );

			if ( pargset_arg_add ( &argset, &arg ) )
			{
				m_lexer.set_token_position ( sp );
				m_error = CED_ERROR_BAD_FUNCTION_ARGUMENTS;
				throw 123;
			}

			token = m_lexer.scan_token();

			switch ( token )
			{
			case Lexer::TK_PAREN_CLOSE:
				// No more arguments
				return;

			case Lexer::TK_FUNC_ARG_SEP:
				// Next token must be an argument
				continue;

			default:
				m_lexer.rewind_token();
				m_error = CED_ERROR_BAD_SYNTAX;
				throw 123;
			}
		}
	}

/// ----------------------------------------------------------------------------

	Lexer		m_lexer;
	CedParser	* m_cedparser	= nullptr;

	std::vector< double >	m_nt_num;

	int		m_error		= 0;
};


}		// namespace
}		// namespace mtCED



int mtCED::Lexer::scan_token ()
{
	while ( is_whitespace ( *m_symbol ) )
	{
		m_symbol++;
	}

	// Not whitespace, so this symbol starts a token
	m_token = m_symbol;

	if ( 0 == *m_symbol )
	{
		return TK_END;
	}

	char const * next;
	char const ch = scan_symbol ();

	switch ( ch )
	{
	case '+':	return TK_OP_ADD;
	case '-':	return TK_OP_SUB;
	case '*':	return TK_OP_MULT;
	case '/':	return TK_OP_DIV;
	case '^':	return TK_OP_POWER;
	case '(':	return TK_PAREN_OPEN;
	case ')':	return TK_PAREN_CLOSE;
	case '#':	return TK_HASH;
	case ':':	return TK_COLON;
	case '=':	return TK_CMP_EQ;
	case '<':
		switch ( scan_symbol () )
		{
		case '=':
			switch ( scan_symbol () )
			{
			case '>':
				return TK_CMP_LEG;
			default:
				rewind_symbol ();
				return TK_CMP_LTE;
			}

		default:
			rewind_symbol ();
			return TK_CMP_LT;
		}

	case '>':
		switch ( scan_symbol () )
		{
		case '=':
			return TK_CMP_GTE;
		default:
			rewind_symbol ();
			return TK_CMP_GT;
		}

	case '!':
		switch ( scan_symbol () )
		{
		case '=':
			return TK_CMP_NEQ;
		default:
			rewind_symbol ();
			return TK_BAD_SYMBOL;
		}

	case '.':
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
		return scan_number ();

	case ';':
	case ',':
		return TK_FUNC_ARG_SEP;

	case '"':
		next = ::strchr ( m_symbol, '"' );
		if ( next )
		{
			m_quote_string = m_symbol;
			m_symbol = next + 1;
			return TK_QUOTE_STRING;
		}
		rewind_symbol ();
		break;

	case '\'':
		if ( ced_strtosheetref ( m_symbol-1, &m_pcellref.sheet, &next,
			m_cedparser->sheet->book )
			)
		{
			rewind_symbol ();
			return TK_BAD_SHEETREF;
		}

		m_symbol = next;

		return TK_SHEETREF;

	case 'r':
	case 'R':
		if ( ! ced_strtocellref( m_symbol-1, &m_pcellref.cellref, &next,
			0 ) )
		{
			m_pcellref.sheet = m_cedparser->sheet;
			m_symbol = next;
			return TK_CELLREF;
		}

		// Not a cellref, so maybe a function?
		rewind_symbol ();
		break;
	}

	if ( is_alpha ( ch ) )
	{
		return scan_name_string ();
	}

	// Symbol not recognised
	rewind_symbol ();

	return TK_BAD_SYMBOL;
}

std::string const & mtCED::Lexer::string_lower ()
{
	for ( char & c : m_string )
	{
		c = (char)::tolower( (unsigned char)c );
	}

	return m_string;
}

int mtCED::Lexer::scan_number ()
{
	char * next = nullptr;

	if ( mtkit_strtod ( m_token, &m_number, &next, 0 ) )
	{
		return TK_BAD_NUMBER;
	}

	m_symbol = next;

	return TK_NUMBER;
}

int mtCED::Lexer::scan_name_string ()
{
	while (	is_alpha ( *m_symbol )		||
		is_numeric ( *m_symbol )	||
		*m_symbol == '_'
		)
	{
		m_symbol++;
	}

	assign_string ();

	return TK_NAME_STRING;
}



///	API		--------------------------------------------------------

int ced_parse_text (
	char	const	* const	text,
	CedParser	* const	state
	)
{
	mtCED::Parser	parser;

	if ( parser.evaluate ( text, state ) )
	{
		return 1;
	}

	return 0;
}



#endif	// CED_USE_BISON_PARSER



std::string mtCED::get_function_help ()
{
	auto arg_descriptor = [](std::string &s, CedFuncInfo const * const f)
	{
		s += "(";

		switch ( f->func.type )
		{
		case CED_ARGSET_VARIABLE: s+="..."; break;
		case CED_ARGSET_VOID: break;
		case CED_ARGSET_NUM: s+="NUM"; break;
		case CED_ARGSET_NUM_NUM: s+="NUM, NUM"; break;
		case CED_ARGSET_NUM_NUM_NUM: s+="NUM, NUM, NUM"; break;
		case CED_ARGSET_CELLRANGE: s+="CELLRANGE"; break;
		case CED_ARGSET_CELLRANGE_NUM: s+="CELLRANGE, NUM"; break;
		case CED_ARGSET_NUM_CELLRANGE_NUM: s+="NUM, CELLRANGE, NUM"; break;
		case CED_ARGSET_CELLREF_NUM_NUM: s+="#CELLREF, NUM, NUM"; break;
		case CED_ARGSET_CELLRANGE_STR_NUM: s+="CELLRANGE, STRING, NUM"; break;
		case CED_ARGSET_CELLRANGE_STR_NUM_CELLREF: s+="CELLRANGE, STRING, NUM, #CELLREF"; break;
		case CED_ARGSET_CELLREF_CELLRANGE_NUM: s+="#CELLREF, CELLRANGE, NUM"; break;
		default: s+="??"; break;
		}

		s += ")";
	};

	std::map< std::string, std::string > map;

	for ( int i=0; ; i++ )
	{
		CedFuncInfo const * const fi = ced_func_info ( i );
		if ( ! fi )
		{
			break;
		}

		std::string help;

		arg_descriptor ( help, fi );
		help += " - ";
		help += fi->help;

		map[ fi->name ] = help;
	}

	std::string result;

	for ( auto && it : map )
	{
		result += "\n";
		result += it.first;
		result += " ";
		result += it.second;
		result += "\n";
	}

	return result;
}

