//@copyright_begin
// ================================================================
// Copyright Notice
// Copyright (C) 1998-2004 by Joe Linoff
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 
// Comments and suggestions are always welcome.
// Please report bugs to http://ccdoc.sourceforge.net/ccdoc
// ================================================================
//@copyright_end

// MULTIPLE INCLUSION GUARD
#ifndef ccdoc_phase1_parser_h
#define ccdoc_phase1_parser_h

/**
 * This variable allows the header version
 * to be queried at runtime.
 */
namespace {
   char ccdoc_phase1_parser_h_rcsid[] = "$Id: phase1_parser.h,v 1.6 2004/09/30 04:16:07 jlinoff Exp $";
}

#include "switches.h"
#include "database.h"
#include "statement.h"
#include "phase1_scanner.h"

namespace ccdoc {
  namespace phase1 {
    // ================================================================
    //@{
    // Parser object.
    // @author Joe Linoff
    // @version $Id: phase1_parser.h,v 1.6 2004/09/30 04:16:07 jlinoff Exp $
    //@}
    // ================================================================
    class parser {
    public:
      // ================================================================
      //@{
      // Expression tree for #if and #elif expression processing.
      //@}
      // ================================================================
      class cpp_expr {
      public:
	// ================================================================
	//@{
	// Expression tree node for #if and #elif expression processing.
	// @author Joe Linoff
	// @version $Id: phase1_parser.h,v 1.6 2004/09/30 04:16:07 jlinoff Exp $
	//@}
	// ================================================================
	class node {
	public:
	  //@{
	  // The node types. Not all types are covered, only those
	  // that are most likely to simplify the implementation.
	  //@}
	  enum nd_type {
	    nd_type_id,      // MACRO id
	    nd_type_num,     // number
	    nd_type_def,     // defined
	    nd_type_lp,      // (
	    nd_type_rp,      // )
	    nd_type_not,     // !
	    nd_type_and,     // &&
	    nd_type_or,      // ||
	    nd_type_eq,      // ==
	    nd_type_ne,      // !=
	    nd_type_lt,      // <
	    nd_type_le,      // <=
	    nd_type_gt,      // >
	    nd_type_ge,      // >=
	    nd_type_root,
	    nd_type_unknown
	  };
	public:
	  //@{
	  // Get the node type.
	  // @param id The id.
	  // @returns The node type.
	  //@}
	  static nd_type get_type(const string& id);
	public:
	  //@{
	  // Constructor.
	  // @param x The expression.
	  // @param t The type.
	  // @param name The node name. The type is figured out from the name.
	  //@}
	  node(cpp_expr& x,nd_type t,const string& name);
	  //@{
	  // Destructor.
	  //@}
	  ~node();
	public:
	  //@{
	  // Insert the node into the tree near the proposed
	  // parent.
	  // @param parent The proposed parent.
	  // @returns The node that s/b used as the parent for
	  // the next operation.
	  //@}
	  node* insert( node* parent );
	public:
	  //@{ @returns The parent. //@}
	  node* get_parent() const {return m_parent;}
	  //@{ @returns The left child. //@}
	  node* get_left() const {return m_left;}
	  //@{ @returns The right child. //@}
	  node* get_right() const {return m_right;}
	  //@{ @returns The node name. //@}
	  const string& get_name() const {return m_name;}
	  //@{ @returns The node type. //@}
	  nd_type get_type() const {return m_type;}
	  //@{
	  // Get the node type name.
	  // @param t The node type.
	  // @returns The node type name.
	  //@}
	  static const char* get_node_type_name(nd_type t);
	public:
	  //@{
	  // Get the hierarchical name.
	  // @returns The hierarchical name.
	  //@}
	  string get_hier_name() const;
	public:
	  //@{
	  // Get the hierarchical id path.
	  // @returns The hierarchical id path.
	  //@}
	  string get_hier_id_path() const;
	public:
	  //@{
	  // Get the root node.
	  // @returns The root node.
	  //@}
	  node* get_root() const;
	public:
	  //@{
	  // Get the node id.
	  // @returns The node id.
	  //@}
	  int get_node_id() const {return m_id;}
	public:
	  //@{
	  // Evaluate from the current node downwards.
	  //@}
	  int eval();
	public:
	  //@{
	  //@returns The hierarchical depth.
	  //@}
	  unsigned depth() const;
	public:
	  //@{
	  // Debug dump.
	  // @param prefix The debug prefix.
	  //@}
	  void debug_dump(const char* prefix) const;
	private:
	  void error(const char* x,
		     const char* file,
		     int line);
	private:
	  node* insert_and(node* parent);
	  node* insert_cmp(node* parent);
	  node* insert_lor(node* parent);
	  node* insert_or(node* parent);
	  node* insert_rp(node* parent);
	private:
	  int eval_and();
	  int eval_def();
	  int eval_eq();
	  int eval_id();
	  int eval_ge();
	  int eval_gt();
	  int eval_le();
	  int eval_lp();
	  int eval_lt();
	  int eval_ne();
	  int eval_not();
	  int eval_num();
	  int eval_or();
	  int eval_root();
	  int eval_rp();
	private:
	  node* m_parent;
	  node* m_left;
	  node* m_right;
	  nd_type m_type;
	  const string& m_name;
	  cpp_expr& m_expr;
	  int m_id;
	  };
	// ================================================================
	//
	// End of cpp_expr::node.
	//
	// ================================================================
      public:
	//@{
	// Tree constructor.
	// @param phase1 The phase1 object with the defines map.
	//@}
	cpp_expr(parser& in_parser);
	//@{
	// Destructor.
	//@}
	~cpp_expr();
      public:
	//@{
	// Evaluate a statement.
	// @param stmt The statement.
	// @param start The starting token index.
	// @returns True if the statement evaluated to true or
	// false otherwise.
	//@}
	bool eval( vector<string>& stmt,
		   int start);
      public:
	//@{
	// Is this expression ok?
	// @returns True if the expression is ok or false otherwise.
	//@}
	bool ok() const {return m_errors ? false : true; }
      public:
	//@{
	// Debug dump.
	// @param prefix The debug prefix.
	//@}
	void debug_dump(const char* prefix) const;
      public:
	//@{
	// Increment the error count.
	//@}
	void inc_errors() {m_errors++;}
      public:
	//@{
	// Get the next id.
	// @returns The next node id.
	//@}
	int get_next_node_id() {return m_node_id++;}
      public:
	//@{
	// Get the parser object.
	// @returns The parser object.
	//@}
	parser& get_parser() {return m_parser;}
      private:
	parser& m_parser;
	unsigned m_errors;
	node* m_root;
	string m_root_name;
	int m_node_id;
	};
      // ================================================================
      //
      // End of expr.
      //
      // ================================================================
    private:
      //@{
      // Manage the action taken for each statement.
      //<pre>
      //   ACCEPT      Accept this statement (store it in the db).
      //   REJECT      Reject this statement.
      //   REJECT_ALL  Reject this statement. It is set after
      //               a valid accept.
      //</pre>
      // Here is how it works.
      //<pre>
      //   #if exp1         | exp1 == false
      //                    | REJECT
      //   #elif exp2       | exp2 == true
      //                    | ACCEPT
      //   #elif exp3       | exp3 == true
      //                    | REJECT_ALL
      //   #elif exp4       | exp4 == false
      //                    | REJECT_ALL
      //   #else            |
      //                    | REJECT_ALL
      //   #endif
      //</pre>
      enum ACTION { ACCEPT, REJECT, REJECT_ALL };
    public:
      parser(switches& sw,database& db,const string& name);
      ~parser();
      bool parse();
      statement::base::stmts_t& get_stmts() {return m_statements;}
    private:
      void add_statement(statement::base*);
      bool get_next_statement();
      bool get_next_token(string& token);
      statement::base* make_statement( const string& id,
				       statement::base::TYPE );
      statement::base* make_statement( const string& id,
				       statement::base::TYPE,
				       vector<string>& tokens );
    public:
      switches& get_sw() const {return m_sw;}
    public:
      bool get_debug() const {return m_debug;}
      void set_debug(bool f) {m_debug=f;m_scanner.set_debug(f);}
    public:
      //@{
      // Is this macro defined?
      // @param name The macro name.
      // @returns True if it is defined or false otherwise.
      //@}
      bool defined( const string& name ) const;
    public:
      //@{
      // Get the macro value.
      //@param key The macro name.
      //@param value The macro value.
      //@}
      void get_macro_value(const string& key,string& value);
    public:
      //@{
      // Get the macro int value.
      //@param key The macro name.
      //@returns The int value or zero if it was not an int value.
      //@}
      int get_macro_value(const string& key);
    public:
      //@{
      // Get the int value.
      // @param num The number.
      // @returns The numeric value.
      //@}
      int get_int_value( const string& name );
    private:
      bool parse_scoping_stmt_beg(vector<string>& tokens,
				  statement::base::TYPE t);
      bool parse_scoping_stmt_end();
      bool parse_enum(vector<string>& tokens);
      bool parse_fct_or_var(vector<string>&);
      bool parse_operator(vector<string>&);
      bool parse_var_or_fct(vector<string>&);
      bool parse_typedef(vector<string>&);
      bool parse_friend(vector<string>&);
      bool parse_extern(vector<string>&);
      void parse_access_specifier(vector<string>&,
				  string&,
				  statement::base::ACCESS);
    private:
      void parse_special_members(statement::base*);
      void parse_special_members(statement::base* stmt,
				 const char* arg_id,
				 bool& default_constructor,
				 bool& copy_constructor);
    private:
      void get_fct_id(string& id,statement::base* stmt) const;
      bool is_id(const string&) const;
    private:
      void parse_cpp();
      void parse_cpp_define(vector<string>&);
      void parse_cpp_elif(vector<string>&);
      void parse_cpp_else(vector<string>&);
      void parse_cpp_endif(vector<string>&);
      void parse_cpp_if(vector<string>&);
      void parse_cpp_ifdef(vector<string>&);
      void parse_cpp_ifndef(vector<string>&);
      void parse_cpp_undef(vector<string>&);
      void parse_cpp_warn(vector<string>&,const char* msg);
      bool parse_cpp_if_expr(vector<string>&);
    private:
      void parse_comment();
      statement::base* parse_comment_pkg_info(vector<string>&,string&);
    private:
      switches& m_sw;
      database& m_db;
      scanner m_scanner;
      bool m_debug;
      bool m_parsed;
      statement::base::stmts_t m_parents;
      statement::base::stmts_t m_statements;
      // Issue 0136
      //   Create a stack for managing special members
      //   so that nested structures are handled correctly.
      vector<statement::base::stmts_t> m_special_members;
      switches::defines_type m_defines;
      vector<statement::base::ACCESS> m_access_specifier;
      vector<ACTION> m_action;
    };
  }
}

#endif
