130 lines
4.0 KiB
C++
130 lines
4.0 KiB
C++
// Copyright (c) 2014-2016 Dr. Colin Hirsch and Daniel Frey
|
|
// Please see LICENSE for license or visit https://github.com/ColinH/PEGTL/
|
|
|
|
#ifndef TAO_CPP_PEGTL_ANALYSIS_ANALYZE_CYCLES_HH
|
|
#define TAO_CPP_PEGTL_ANALYSIS_ANALYZE_CYCLES_HH
|
|
|
|
#include <cassert>
|
|
|
|
#include <map>
|
|
#include <set>
|
|
|
|
#include <utility>
|
|
#include <iostream>
|
|
|
|
#include "grammar_info.hh"
|
|
#include "insert_guard.hh"
|
|
|
|
namespace tao_json_pegtl
|
|
{
|
|
namespace analysis
|
|
{
|
|
class analyze_cycles_impl
|
|
{
|
|
protected:
|
|
explicit
|
|
analyze_cycles_impl( const bool verbose )
|
|
: m_verbose( verbose ),
|
|
m_problems( 0 )
|
|
{ }
|
|
|
|
const bool m_verbose;
|
|
unsigned m_problems;
|
|
grammar_info m_info;
|
|
std::set< std::string > m_stack;
|
|
std::map< std::string, bool > m_cache;
|
|
std::map< std::string, bool > m_results;
|
|
|
|
const std::map< std::string, rule_info >::const_iterator find( const std::string & name ) const
|
|
{
|
|
const auto iter = m_info.map.find( name );
|
|
assert( iter != m_info.map.end() );
|
|
return iter;
|
|
}
|
|
|
|
bool work( const std::map< std::string, rule_info >::const_iterator & start, const bool accum )
|
|
{
|
|
const auto j = m_cache.find( start->first );
|
|
|
|
if ( j != m_cache.end() ) {
|
|
return j->second;
|
|
}
|
|
if ( const auto g = make_insert_guard( m_stack, start->first ) ) {
|
|
switch ( start->second.type ) {
|
|
case rule_type::ANY: {
|
|
bool a = false;
|
|
for ( const auto & r : start->second.rules ) {
|
|
a = a || work( find( r ), accum || a );
|
|
}
|
|
return m_cache[ start->first ] = true;
|
|
}
|
|
case rule_type::OPT: {
|
|
bool a = false;
|
|
for ( const auto & r : start->second.rules ) {
|
|
a = a || work( find( r ), accum || a );
|
|
}
|
|
return m_cache[ start->first ] = false;
|
|
}
|
|
case rule_type::SEQ: {
|
|
bool a = false;
|
|
for ( const auto & r : start->second.rules ) {
|
|
a = a || work( find( r ), accum || a );
|
|
}
|
|
return m_cache[ start->first ] = a;
|
|
}
|
|
case rule_type::SOR: {
|
|
bool a = true;
|
|
for ( const auto & r : start->second.rules ) {
|
|
a = a && work( find( r ), accum );
|
|
}
|
|
return m_cache[ start->first ] = a;
|
|
}
|
|
}
|
|
throw std::runtime_error( "code should be unreachable" ); // LCOV_EXCL_LINE
|
|
}
|
|
if ( ! accum ) {
|
|
++m_problems;
|
|
if ( m_verbose ) {
|
|
std::cout << "problem: cycle without progress detected at rule class " << start->first << std::endl;
|
|
}
|
|
}
|
|
return m_cache[ start->first ] = accum;
|
|
}
|
|
};
|
|
|
|
template< typename Grammar >
|
|
class analyze_cycles
|
|
: private analyze_cycles_impl
|
|
{
|
|
public:
|
|
explicit
|
|
analyze_cycles( const bool verbose )
|
|
: analyze_cycles_impl( verbose )
|
|
{
|
|
Grammar::analyze_t::template insert< Grammar >( m_info );
|
|
}
|
|
|
|
std::size_t problems()
|
|
{
|
|
for ( auto i = m_info.map.begin(); i != m_info.map.end(); ++i ) {
|
|
m_results[ i->first ] = work( i, false );
|
|
m_cache.clear();
|
|
}
|
|
return m_problems;
|
|
}
|
|
|
|
template< typename Rule >
|
|
bool consumes() const
|
|
{
|
|
const auto i = m_results.find( internal::demangle< Rule >() );
|
|
assert( i != m_results.end() );
|
|
return i->second;
|
|
}
|
|
};
|
|
|
|
} // namespace analysis
|
|
|
|
} // namespace tao_json_pegtl
|
|
|
|
#endif
|