Проблема с парсерами Boost Spirit и Lex

Я изо всех сил пытался попытаться (постепенно) изменить пример кода из документации, но с небольшим отличием я не получаю ожидаемого поведения. В частности, оператор «if» терпит неудачу, когда (моя цель состоит в том, что) он должен пройти (было «else», но эта часть синтаксического анализатора была удалена во время отладки). Оператор присваивания работает нормально. У меня также был оператор «пока», который имел ту же проблему, что и оператор «если», поэтому я уверен, что если я смогу получить помощь, чтобы выяснить, почему один не работает, должно быть легко заставить другой работать. Это должно быть немного тонко, потому что это почти дословно то, что есть в одном из примеров.

#include <iostream>
#include <fstream>
#include <string>

#define BOOST_SPIRIT_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_container.hpp>

namespace qi  = boost::spirit::qi;
namespace lex = boost::spirit::lex;

inline std::string read_from_file( const char* infile )
{
    std::ifstream instream( infile );
    if( !instream.is_open() )
    {
        std::cerr << "Could not open file: \"" << infile << "\"" << std::endl;
        exit( -1 );
    }
    instream.unsetf( std::ios::skipws );
    return( std::string(
                std::istreambuf_iterator< char >( instream.rdbuf() ),
                std::istreambuf_iterator< char >()
          ) );
}

template< typename Lexer >
struct LangLexer : lex::lexer< Lexer >
{
    LangLexer()
    {
        identifier = "[a-zA-Z][a-zA-Z0-9_]*";
        number = "[-+]?(\\d*\\.)?\\d+([eE][-+]?\\d+)?";

        if_ = "if";
        else_ = "else";

        this->self = lex::token_def<> ( '(' ) | ')' | '{' | '}' | '=' | ';';
        this->self += identifier | number | if_ | else_;

        this->self( "WS" ) = lex::token_def<>( "[ \\t\\n]+" );

    }

    lex::token_def<> if_, else_;
    lex::token_def< std::string > identifier;
    lex::token_def< double > number;
};

template< typename Iterator, typename Lexer >
struct LangGrammar : qi::grammar< Iterator, qi::in_state_skipper< Lexer > >
{
    template< typename TokenDef >
    LangGrammar( const TokenDef& tok ) : LangGrammar::base_type( program )
    {
        using boost::phoenix::val;
        using boost::phoenix::ref;
        using boost::phoenix::size;

        program = +block;
        block = '{' >> *statement >> '}';
        statement = assignment | if_stmt;
        assignment = ( tok.identifier >> '=' >> expression >> ';' );
        if_stmt = ( tok.if_ >> '(' >> expression >> ')' >> block );
        expression = ( tok.identifier[ qi::_val = qi::_1 ] | tok.number[ qi::_val = qi::_1 ] );

        BOOST_SPIRIT_DEBUG_NODE( program );
        BOOST_SPIRIT_DEBUG_NODE( block );
        BOOST_SPIRIT_DEBUG_NODE( statement );
        BOOST_SPIRIT_DEBUG_NODE( assignment );
        BOOST_SPIRIT_DEBUG_NODE( if_stmt );
        BOOST_SPIRIT_DEBUG_NODE( expression );
    }

    qi::rule< Iterator, qi::in_state_skipper< Lexer > > program, block, statement;
    qi::rule< Iterator, qi::in_state_skipper< Lexer > > assignment, if_stmt;

    typedef boost::variant< double, std::string > expression_type;
    qi::rule< Iterator, expression_type(), qi::in_state_skipper< Lexer > > expression;
};

int main( int argc, char** argv )
{
    typedef std::string::iterator base_iterator_type;
    typedef lex::lexertl::token< base_iterator_type, boost::mpl::vector< double, std::string > > token_type;
    typedef lex::lexertl::lexer< token_type > lexer_type;
    typedef LangLexer< lexer_type > LangLexer;
    typedef LangLexer::iterator_type iterator_type; 
    typedef LangGrammar< iterator_type, LangLexer::lexer_def > LangGrammar;

    LangLexer lexer;
    LangGrammar grammar( lexer );

    std::string str( read_from_file( 1 == argc ? "boostLexTest.dat" : argv[1] ) );

    base_iterator_type strBegin = str.begin();
    iterator_type tokenItor = lexer.begin( strBegin, str.end() );
    iterator_type tokenItorEnd = lexer.end(); 

    std::cout << std::setfill( '*' ) << std::setw(20) << '*' << std::endl <<
        str
        << std::endl << std::setfill( '*' ) << std::setw(20) << '*' << std::endl;

    bool result = qi::phrase_parse( tokenItor, tokenItorEnd, grammar, qi::in_state( "WS" )[ lexer.self ] );

    if( result )
    {
        std::cout << "Parsing successful" << std::endl;
    }
    else
    {
        std::cout << "Parsing error" << std::endl;
    }

    return( 0 );
}

Вот результат выполнения этого (файл, считанный в строку, выгружается первым в main)

********************
{
    a = 5;
    if( a ){ b = 2; }
}


********************
<program>
  <try>{</try>
  <block>
    <try>{</try>
    <statement>
      <try></try>
      <assignment>
        <try></try>
<expression>
  <try></try>
  <success>;</success>
  <attributes>(5)</attributes>
</expression>
        <success></success>
        <attributes>()</attributes>
      </assignment>
      <success></success>
      <attributes>()</attributes>
    </statement>
    <statement>
      <try></try>
      <assignment>
        <try></try>
        <fail/>
      </assignment>
      <if_stmt>
        <try>
    if(</try>
        <fail/>
      </if_stmt>
      <fail/>
    </statement>
    <fail/>
  </block>
  <fail/>
</program>
Parsing error

person bpw1621    schedule 03.05.2010    source источник


Ответы (2)


Проблема заключается в последовательности, в которой вы добавили определения токенов в лексер. Ваш код

this->self += identifier | number | if_ | else_; 

сначала добавляет токен identifier, который также идеально соответствует 'if' (и любому другому ключевому слову). Если вы измените это на

this->self += if_ | else_ | identifier | number; 

все начинает работать как надо.

Это не относится к Spirit.Lex. Любой токенизатор учитывает порядок, в котором определены токены, чтобы определить приоритет сопоставления.

person hkaiser    schedule 04.05.2010
comment
Ваш мужчина Хартмут! Я надеялся, что вы или Джоэл доберетесь до этого. Я не понимал, что порядок, в котором они были добавлены, имел какое-либо отношение к приоритету анализа токена. Кроме того, знаете ли вы о каких-либо новых полностью проработанных примерах, использующих синтаксис версии 2? Большинство примеров, которые я видел в репозитории приложений Spirit, используют старый синтаксис. Кроме того, есть ли прямая ссылка на ваши файлы примеров (например, boost.org/doc/libs/1_42_0/libs/spirit/example/lex/example#.cpp)? - person bpw1621; 04.05.2010

Может быть, нужно изменить пример mini_c, чтобы использовать лексер? Это будет более полный пример того, как использовать два. Большинство образцов Qi (начиная с версии 2.4) вообще не используют лексер.

Хотя это служит иллюстрацией использования Qi, мы, как правило, пытаемся использовать специальный лексер для производственных проектов из-за соображений удобства обслуживания (например, я могу передать загрузку лексера вспомогательному разработчику).

person Uday    schedule 11.12.2010