PEGTL icon indicating copy to clipboard operation
PEGTL copied to clipboard

Error propagation

Open mgood7123 opened this issue 2 years ago • 2 comments

would it be possible to modify parse_error to support error probogation?

i have a basic version working

parse_error.hpp

namespace TAO_PEGTL_NAMESPACE
{
   namespace internal
   {
      class parse_error
      {
      private:
         std::string m_src;

// ...

         explicit parse_error( const char* where, const char* msg )
            : m_src(where), m_msg( msg )
         {}

         explicit parse_error( const std::string& where, const char* msg )
            : m_src(where), m_msg( msg )
         {}

         [[nodiscard]] const char* where() const noexcept
         {
            return m_src.c_str();
         }

// ...

      };

   }  // namespace internal

   class parse_error
      : public std::runtime_error
   {
// ...

      parse_error( const char* where, const char* msg, position p )
         : std::runtime_error( msg ),
           m_impl( std::make_shared< internal::parse_error >(where, msg ) )
      {
         m_impl->add_position( std::move( p ) );
      }

      parse_error( const std::string& where, const char* msg, position p )
         : std::runtime_error( msg ),
           m_impl( std::make_shared< internal::parse_error >(where.c_str(), msg ) )
      {
         m_impl->add_position( std::move( p ) );
      }

      parse_error( const std::string_view& where, const char* msg, position p )
         : std::runtime_error( msg ),
           m_impl( std::make_shared< internal::parse_error >(std::string(where.data(), where.size()), msg ) )
      {
         m_impl->add_position( std::move( p ) );
      }

      parse_error( const char* where, const std::string& msg, position p )
         : parse_error( where, msg.c_str(), std::move( p ) )
      {}

      parse_error( const std::string& where, const std::string& msg, position p )
         : parse_error( where.c_str(), msg.c_str(), std::move( p ) )
      {}

      parse_error( const std::string_view& where, const std::string& msg, position p )
         : parse_error( std::string(where.data(), where.size()), msg.c_str(), std::move( p ) )
      {}

      template< typename ParseInput >
      parse_error( const char* msg, const ParseInput& in )
         : parse_error( in.line_at(in.position()), msg, in.position() )
      {}

      template< typename ParseInput >
      parse_error( const std::string& msg, const ParseInput& in )
         : parse_error( in.line_at(in.position()), msg, in.position() )
      {}

// ...

      [[nodiscard]] const char* where() const noexcept
      {
         return m_impl->where();
      }

// ...

   };

}  // namespace TAO_PEGTL_NAMESPACE

action_input.hpp

// ...
      [[nodiscard]] std::string_view line_at( const TAO_PEGTL_NAMESPACE::position& p ) const noexcept
      {
         return input().line_at(p);
      }
// ...

the source line is obtainable via e.where() and should replace in.line_at(p) or in.line_at(e.positions().front())

the source line is automatically set when given a ParseInput and can be set manually via where prefix as where, msg, pos instead of msg, pos

such enables correct source line probogation when sub-parsing inside of actions

#61                           GLSL_PREPROCESSOR::spaces
                              success
                              position GLSL preprocessor macro expansion:1:10
                              source bar A (4 + (2 * x))()
                                              ^
// ...
#97                         tao::pegtl::raise_message<'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ', 'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', ' ', 'f', 'o', 'u', 'n', 'd'>
                              raise tao::pegtl::raise_message<'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ', 'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', ' ', 'f', 'o', 'u', 'n', 'd'>
                            unwind
                          unwind #83 GLSL_PREPROCESSOR::function_call_arg
                        unwind #82 GLSL_PREPROCESSOR::function_call_arg_and_optional_comma
                      unwind #78 tao::pegtl::sor<tao::pegtl::seq<tao::pegtl::at<tao::pegtl::eof>, tao::pegtl::raise_message<'U', 'n', 't', 'e', 'r', 'm', 'i', 'n', 'a', 't', 'e', 'd', ' ', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', ' ', 'p', 'a', 'r', 'e', 'n', 't', 'h', 'e', 's', 'i', 's', ',', ' ', 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd', ' ', '\'', ')', '\'', ' ', 't', 'o', ' ', 'm', 'a', 't', 'c', 'h', ' ', '\'', '(', '\''> >, GLSL_PREPROCESSOR::function_call_arg_and_optional_comma>
                    unwind #75 tao::pegtl::until<tao::pegtl::at<GLSL_PREPROCESSOR::function_end>, tao::pegtl::sor<tao::pegtl::seq<tao::pegtl::at<tao::pegtl::eof>, tao::pegtl::raise_message<'U', 'n', 't', 'e', 'r', 'm', 'i', 'n', 'a', 't', 'e', 'd', ' ', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', ' ', 'p', 'a', 'r', 'e', 'n', 't', 'h', 'e', 's', 'i', 's', ',', ' ', 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd', ' ', '\'', ')', '\'', ' ', 't', 'o', ' ', 'm', 'a', 't', 'c', 'h', ' ', '\'', '(', '\''> >, GLSL_PREPROCESSOR::function_call_arg_and_optional_comma> >
                  unwind #46 GLSL_PREPROCESSOR::function_call_parens
                unwind #35 GLSL_PREPROCESSOR::function_call_actual
              unwind #34 GLSL_PREPROCESSOR::function_call
            unwind #30 tao::pegtl::sor<GLSL_PREPROCESSOR::whitespaces, GLSL_PREPROCESSOR::function_call, GLSL_PREPROCESSOR::identifier, tao::pegtl::ascii::any>
            position GLSL preprocessor macro expansion:1:5
            source bar A (4 + (2 * x))()
                       ^
          unwind #2 tao::pegtl::plus<tao::pegtl::sor<GLSL_PREPROCESSOR::whitespaces, GLSL_PREPROCESSOR::function_call, GLSL_PREPROCESSOR::identifier, tao::pegtl::ascii::any>>
        unwind #1 GLSL_PREPROCESSOR::grammar___
        position GLSL preprocessor macro expansion:1:1
        source bar A (4 + (2 * x))()
               ^
            unwind
          unwind #11 GLSL_PREPROCESSOR::function_call_actual
          position GLSL preprocessor function expansion:1:1
          source foo(bar)
                 ^
GLSL preprocessor macro expansion:1:10: Invalid character found
bar A (4 + (2 * x))()
         ^
    } catch (const pegtl::parse_error &e) {
      const auto p = e.positions().front();
      std:cerr << e.what() << '\n'
           << e.where() << '\n'
           << std::setw(p.column) << '^' << std::endl;
    }

mgood7123 avatar Dec 22 '21 09:12 mgood7123

This violates the zero-overhead-principle, as everybody would pay the price for this whether they need to information or not.

d-frey avatar Dec 22 '21 17:12 d-frey

hmm what can be another way to achieve this without violating the zero overhead?

mgood7123 avatar Dec 23 '21 03:12 mgood7123

Does the new approach with nested exceptions help (currently on the "work" branch, will be soon merged into "main")? If you look at the anatomy of parse_error you'll see that we are preparing for different position types which gives much greater flexibility regarding what information is put into a parse error. Otherwise you can of course use your own parse_error in your projects with a custom control class whose raise() throws your exceptions rather than the "standard" PEGTL ones.

ColinH avatar Sep 22 '23 18:09 ColinH