/* Copyright (c) 1997-2006
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   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, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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.
*/

#ifndef _POLYMAKE_PLAIN_PARSER_H
#define _POLYMAKE_PLAIN_PARSER_H "$Project: polymake $$Id: PlainParser.h 7315 2006-04-02 21:37:53Z gawrilow $"

#include <iostream>
#include <string>
#include <stdexcept>

#ifndef _POLYMAKE_GENERIC_IO_H
#  include <GenericIO.h>
#endif

class Rational;

namespace pm {

template <typename Value, typename Options> class PlainListCursor;
template <typename Options> class PlainCompositeCursor;

class PlainParserCommon {
protected:
   mutable std::istream *is;
   char *saved_egptr;

   PlainParserCommon(const PlainParserCommon& other)
      : is(other.is), saved_egptr(other.saved_egptr)
   {
      other.is=0;
   }

   explicit PlainParserCommon(std::istream& is_arg)
      : is(&is_arg), saved_egptr(0) { }
public:
   ~PlainParserCommon()
   {
      if (is && saved_egptr) restore_input_range(saved_egptr);
   }

   template <typename Data>
   void fallback(Data& x) { *is >> x; }

   void finish() const { }
   bool at_end();

   void get_scalar(double&);
   void get_scalar(Rational&);
   void get_string(std::string&, char delim);

   int count_lines();
   int count_all_lines();
protected:
   char* set_temp_range(char opening, char closing);
   void set_range(char opening, char closing)
   {
      saved_egptr=set_temp_range(opening,closing);
   }

   void discard_range(char closing);
   void discard_temp_range(char closing, char *egptr)
   {
      discard_range(closing);
      restore_input_range(egptr);
   }

   int count_words();
   int count_braced(char opening, char closing);
   int count_leading(char);

   char* set_input_range(int offset);
   void restore_input_range(char *egptr);
   void skip_temp_range(char *egptr);

   char* save_read_pos();
   void restore_read_pos(char *pos);
public:
   void skip_item();
   void skip_rest();
};

template <typename Object, typename Model=typename object_traits<Object>::model>
struct composite_depth {
   static const int value=0;
};

template <typename Object>
struct composite_depth<Object, is_composite> {
   static const int value= composite_depth<typename n_th<typename object_traits<Object>::elements, 0>::type>::value + 1;
};

template <typename Options=void>
class PlainParser
   : public PlainParserCommon,
     public GenericInputImpl< PlainParser<Options> >,
     public GenericIOoptions< PlainParser<Options>, Options > {
public:
   PlainParser(std::istream& is_arg) : PlainParserCommon(is_arg) { }

   typedef ostream_wrapper<Options> wrapper;

   template <typename ObjectRef>
   struct list_cursor {
      typedef PlainListCursor<typename deref<ObjectRef>::type::value_type,
			      typename wrapper::template list_cursor<ObjectRef>::cursor_options>
         type;
   };

   template <typename ObjectRef>
   struct composite_cursor {
      typedef PlainCompositeCursor<typename wrapper::template composite_cursor<ObjectRef>::cursor_options>
         type;
   };

   template <typename Object>
   typename list_cursor<Object>::type begin_list(Object*) const
   {
      return typename list_cursor<Object>::type(*this->is);
   }

   template <typename Object>
   typename composite_cursor<Object>::type begin_composite(Object*) const
   {
      return typename composite_cursor<Object>::type(*this->is);
   }

   operator void* () const { return this->is->good() ? (void*)this : 0; }
   bool operator! () const { return !this->is->good(); }
};

template <typename Options> inline
PlainParser<Options>&
operator>> (GenericInput< PlainParser<Options> >& is, double& x)
{
   is.top().get_scalar(x);
   return is.top();
}

template <typename Options> inline
PlainParser<Options>&
operator>> (GenericInput< PlainParser<Options> >& is, Rational& x)
{
   is.top().get_scalar(x);
   return is.top();
}

template <typename Options> inline
PlainParser<Options>&
getline (PlainParser<Options>& is, std::string& s, char delim='\n')
{
   is.get_string(s,delim);
   return is;
}

template <typename Options> inline
PlainParser<Options>&
operator>> (GenericInput< PlainParser<Options> >& is, std::string& s)
{
   is.top().get_string(s,0);
   return is.top();
}

template <typename Options>
class PlainCursor : public PlainParserCommon {
protected:
   char *start_pos;

   static const char
      opening=extract_int_param<Options, OpeningBracket>::value,
      closing=extract_int_param<Options, ClosingBracket>::value,
      separator=extract_int_param<Options, SeparatorChar>::value;
   static const bool
      is_temp=extract_bool_param<Options, LookForward>::value;
public:
   PlainCursor(std::istream& is_arg)
      : PlainParserCommon(is_arg), start_pos(0)
   {
      if (is_temp)
	 start_pos=save_read_pos();
      if (opening) set_range(opening, closing);
   }

   ~PlainCursor()
   {
      if (is_temp)
	 restore_read_pos(start_pos);
   }

   void finish()
   {
      if (closing) discard_range(closing);
   }

   bool at_end()
   {
      return PlainParserCommon::at_end() && (finish(), true);
   }

   PlainParser<Options>* operator-> ()
   {
      return static_cast<PlainParser<Options>*>(static_cast<PlainParserCommon*>(this));
   }
};

template <typename Options>
class PlainCompositeCursor
   : public PlainCursor<Options>,
     public GenericInputImpl< PlainCompositeCursor<Options> >,
     public GenericIOoptions< PlainCompositeCursor<Options>, Options > {
   typedef PlainCursor<Options> super;
public:
   PlainCompositeCursor(std::istream& is_arg)
      : super(is_arg) { }

private:
   template <typename Anything>
   void cleanup(type2type<Anything>) { }

   template <typename Anything, typename Expected>
   void cleanup(type2type< ignore<Anything, Expected> >)
   {
      if (!identical<Expected,nothing>::value) super::skip_item();
   }
public:
   template <typename Data>
   void operator>> (Data& data)
   {
      *this->operator->() >> data;
      cleanup(type2type<Data>());
   }
};

template <typename Value, typename Options>
class PlainListCursor
   : public PlainCursor<Options>,
     public GenericInputImpl< PlainListCursor<Value,Options> >,
     public GenericIOoptions< PlainListCursor<Value,Options>, Options, 1 > {
   typedef PlainCursor<Options> super;
   typedef ostream_wrapper<Options> wrapper;

   static const bool has_sparse_representation=extract_bool_param<Options, SparseRepresentation, false>::value;
protected:
   int _size;
   char *pair_egptr;

   int size(is_scalar)
   {
      return this->count_words();
   }

   int size(nothing)	// e.g. graph incidence line
   {
      return this->count_words();
   }

   int size(is_container)
   {
      typedef typename wrapper::template list_cursor<Value> defs;
      return defs::opening
	     ? this->count_braced(defs::opening, defs::closing) :
	     super::opening
	     ? this->count_lines()
	     : this->count_all_lines();
   }

   int size(is_composite)
   {
      typedef typename wrapper::template composite_cursor<Value> defs;
      return defs::opening
	     ? this->count_braced(defs::opening, defs::closing) :
	     super::separator=='\n'
	     ? super::opening
	       ? this->count_lines()
	       : this->count_all_lines()
	     : this->count_words();
   }

public:
   typedef Value value_type;

   PlainListCursor(std::istream& is_arg)
      : super(is_arg), _size(-1), pair_egptr(0)
   {
      if (!super::opening && object_traits<Value>::total_dimension==0)
	 this->set_range(0, '\n');
   }

   int size()
   {
      if (_size<0)
	 _size=size(typename object_traits<Value>::model());
      return _size;
   }

protected:
   template <typename Model>
   static int missing_parens(Model) { return 0; }

   static int missing_parens(is_composite)
   {
      typedef typename wrapper::template composite_cursor<Value> defs;
      return defs::opening == 0;
   }

   bool _sparse_representation()
   {
      const int own_missing=missing_parens(typename object_traits<Value>::model());
      return this->count_leading('(') == composite_depth<Value>::value - own_missing + 1;
   }
public:
   int index()
   {
      if (!ignore_in_composite<Value>::value) {
	 if (has_sparse_representation)
	    pair_egptr=this->set_temp_range('(', ')');
	 else
	    this->is->setstate(std::ios::failbit);
      }
      int i;
      *this->is >> i;
      return i;
   }

   int get_dim()
   {
      int d=index();
      this->discard_temp_range(')', pair_egptr);
      pair_egptr=0;
      return d;
   }

   bool sparse_representation()
   {
      return extract_type_param<Options,SparseRepresentation>::specified
	     ? has_sparse_representation
	     : _sparse_representation();
   }

   int dim()
   {
      return !ignore_in_composite<Value>::value && sparse_representation()
	     ? this->set_option(SparseRepresentation<True>()).get_dim()
	     : size();
   }

private:
   template <typename Anything>
   void cleanup(type2type<Anything>)
   {
      if (has_sparse_representation) {
	 this->discard_temp_range(')', pair_egptr);
	 pair_egptr=0;
      }
   }

   void cleanup(type2type<nothing>) { }

   template <typename Anything, typename Expected>
   void cleanup(type2type< ignore<Anything, Expected> >)
   {
      if (!identical<Expected,nothing>::value) skip_item();
   }

public:
   template <typename Data>
   void operator>> (Data& x)
   {
      *this->operator->() >> x;
      cleanup(type2type<Value>());
   }
      
   void skip_item()
   {
      if (has_sparse_representation && pair_egptr) {
	 this->skip_temp_range(pair_egptr);
	 pair_egptr=0;
      } else {
	 super::skip_item();
      }
   }
};

} // end namespace pm


#endif // _POLYMAKE_PLAIN_PARSER_H

// Local Variables:
// mode:C++
// End:


syntax highlighted by Code2HTML, v. 0.9.1