/*******************************************************************************
 * Copyright (c) 2015-2016 ONERA.
 * Ce logiciel est la propriété de l’ONERA (the French Aerospace Lab).
 * Tous droits réservés.
 *
 * Ce programme et les éléments qui l'accompagnent sont mis à disposition
 * aux conditions définies par le contrat de licence logiciel CeCILL-C soumise
 * au droit français et respectant les principes de diffusion des logiciels libres.
 * Vous pouvez utiliser, modifier et/ou redistribuer ce programme
 * sous les conditions de la licence CeCILL-C (http://www.cecill.info).

 * This software is property of ONERA (the French Aerospace Lab).
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under
 * the terms of the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.
 * You can  use, modify and/ or redistribute the software under the terms of
 * the CeCILL-C license (http://www.cecill.info).
 *
 * Contributeurs/contributors:
 *      Florent Teichteil-Königsbuch (ONERA - Centre de Toulouse) - initial API and implementation
 *
 *******************************************************************************/

#ifndef PARSING_DRIVER_BASE_HH
#define PARSING_DRIVER_BASE_HH

#include <boost/any.hpp>
#include <string>
#include <stack>

namespace epoch {
	/**
	 * Parsing errors' reporter
	 * Actual reporters must derive from this abstract class
	 */
	class ParsingDriverReporter {
	public :
		/**
		 * Constructor
		 */
		ParsingDriverReporter() {}
		
		/**
		 * Destructor
		 */
		virtual ~ParsingDriverReporter() {}
		
		/**
		 * Append a report
		 * @param msg Message to append
		 */
		virtual void Append(const std::string& msg) =0;
	}; // class ParsingDriveReporter
	
	/**
	 * Base abstract class of all EPOCH's parsing drivers
	 */
	class ParsingDriverBase {
	public :
		/**
		 * Constructor
		 * @param reporter Parsing driver reporter
		 * @param parsedRessource Ressource to parse (filename or string)
		 * @param traceScanning Boolean indicating if the scanner should report debug information on the standard output
		 * @param traceParsing Boolean indicating if the parser should report debug information on the standard output
		 */
		inline ParsingDriverBase(ParsingDriverReporter& reporter, const std::string& parsedRessource,
									bool traceScanning = false, bool traceParsing = false)
										: m_rReporter(reporter), m_ParsedRessource(parsedRessource),
											m_TraceScanning(traceScanning), m_TraceParsing(traceParsing) {}
		
		/**
		 * Destructor
		 */
		inline virtual ~ParsingDriverBase() {}
		
		/**
		 * Get the parsing driver reporter
		 * @return Parsing driver reporter
		 */
		inline ParsingDriverReporter& getParsingDriverReporter() {return m_rReporter;}
		
		/**
		 * Get the parsed ressource
		 * @return Parsed ressource
		 */
		inline const std::string& getParsedRessource() const {return m_ParsedRessource;}
		
		/**
		 * Indicate whehter the scanner is reporting debug information on the standard output
		 * @return TRUE if the scanner is reporting debug information on the standard output
		 */
		inline bool IsTraceScanning() const {return m_TraceScanning;}
		
		/**
		 * Indicate whehter the parser is reporting debug information on the standard output
		 * @return TRUE if the parser is reporting debug information on the standard output
		 */
		inline bool IsTraceParsing() const {return m_TraceParsing;}
		
		/**
		 * Run the parser
		 * @return 0 on success
		 */
		virtual int Parse() =0;
		
	protected :
		/**
		 * Parsing driver reporter
		 */
		ParsingDriverReporter& m_rReporter;
		
		/**
		 * Ressource to parse (filename or string)
		 */
		std::string m_ParsedRessource;
		
		/**
		 * Boolean indicating if the scanner should report debug information on the standard output
		 */
		bool m_TraceScanning;
		
		/**
		 * Boolean indicating if the parser should report debug information on the standard output
		 */
		bool m_TraceParsing;
		
		/**
		 * Stack of already parsed rules that must deleted in case of parsing errors
		 */
		std::stack<boost::any> m_RuleStack;
		
		/**
		 * Run a Bison-based parser on a filename as m_ParsedRessource
		 * @tparam Tdriver Bison parser's driver's type
		 * @param driver Bison parser's driver
		 * @return 0 on success
		 * @see #m_ParsedRessource
		 */
		template <class Tdriver>
		int ParseFile(Tdriver& driver);
		
		/**
		 * Run a Bison-based parser on a string as m_ParsedRessource
		 * @tparam Tdriver Bison parser's driver's type
		 * @param driver Bison parser's driver
		 * @return 0 on success
		 * @see #m_ParsedRessource
		 */
		template <class Tdriver>
		int ParseString(Tdriver& driver);
		
		/**
		 * Error handling for Bison-based parsers
		 * @tparam TParser Bison parser
		 * @param loc error location in the file
		 * @param msg error message
		 */
		template <class TParser>
		void error(const typename TParser::location_type& loc, const std::string& msg);
		
		/**
		 * Error handling
		 * @param msg error message
		 */
		inline virtual void error(const std::string& msg) {m_rReporter.Append(msg);}
		
		/**
		 * Stack of already parsed rules that must deleted in case of parsing errors
		 * @see #DeleteRule
		 */
		void ClearRuleStack() {
			while (!m_RuleStack.empty()) {
				DeleteRule(m_RuleStack.top());
				m_RuleStack.pop();
			}
		}
		
		/**
		 * Deletes a stacked rule (useful for Bison-based parsers)
		 * The derived parsing driver must cast the given rule to any possible rule in the parser
		 * Typically, all %type declared rules must be deleted in case of matching cast
		 * Use the DeleteRuleHelper function for automatically casting and deleting rules in your derived parsing driver
		 * @param rule Rule to delete
		 * @see #ClearRuleStack
		 * @see #DeleteRuleHelper
		 */
		virtual void DeleteRule(boost::any& rule) =0;
		
		/**
		 * Deletes a rule if its type is the same as the given template type
		 * @tparam Trule rule's type to check
		 * @param rule rule to possibly delete
		 * @return TRUE if the rule has been deleted (template type and rule's actual type match)
		 */
		template <class Trule>
		static bool DeleteRuleHelper(boost::any& rule);
	}; // class ParsingDriverBase
} // namespace epoch


#endif // PARSING_DRIVER_BASE_HH
