// Copyright (c) 2014-2018 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#include <iostream>

#include <tao/pegtl.hpp>
#include <tao/pegtl/analyze.hpp>

using namespace tao::TAO_PEGTL_NAMESPACE;  // NOLINT

namespace sexpr
{
   struct hash_comment
      : until< eolf >
   {
   };

   struct list;

   struct list_comment
      : if_must< at< one< '(' > >, disable< list > >
   {
   };

   struct read_include
      : seq< one< ' ' >, one< '"' >, plus< not_one< '"' > >, one< '"' > >
   {
   };

   struct hash_include
      : if_must< string< 'i', 'n', 'c', 'l', 'u', 'd', 'e' >, read_include >
   {
   };

   struct hashed
      : if_must< one< '#' >, sor< hash_include, list_comment, hash_comment > >
   {
   };

   struct number
      : plus< digit >
   {
   };

   struct symbol
      : identifier
   {
   };

   struct atom
      : sor< number, symbol >
   {
   };

   struct anything;

   struct list
      : if_must< one< '(' >, until< one< ')' >, anything > >
   {
   };

   struct normal
      : sor< atom, list >
   {
   };

   struct anything
      : sor< space, hashed, normal >
   {
   };

   struct main
      : until< eof, must< anything > >
   {
   };

   template< typename Rule >
   struct action
      : nothing< Rule >
   {
   };

   template<>
   struct action< plus< not_one< '"' > > >
   {
      template< typename Input >
      static void apply( const Input& in, std::string& fn )
      {
         fn = in.string();
      }
   };

   template<>
   struct action< hash_include >
   {
      template< typename Input >
      static void apply( const Input& in, std::string& fn )
      {
         std::string f2;
         // Here f2 is the state argument for the nested parsing
         // run (to store the value of the string literal like in
         // the upper-level parsing run), fn is the value of the
         // last string literal that we use as filename here, and
         // the input is passed on for chained error messages (as
         // in "error in line x file foo included from file bar...)
         file_input<> i2( fn );
         parse_nested< main, sexpr::action >( in, i2, f2 );
      }
   };

}  // namespace sexpr

int main( int argc, char** argv )
{
   analyze< sexpr::main >();

   for( int i = 1; i < argc; ++i ) {
      std::string fn;
      argv_input<> in( argv, i );
      parse< sexpr::main, sexpr::action >( in, fn );
   }
   return 0;
}
