2015-08-26 8 views
1

Я использую boost :: spirit lex и qi для анализа некоторого исходного кода.Почему qi :: skip fail с токенами из lexer?

Я уже пропускаю пробелы со строки ввода, используя лексер. То, что я хотел бы сделать, это переключить пропуски комментариев в зависимости от контекста в синтаксическом анализаторе.

Настоящая базовая демонстрация. Смотрите комментарии в Grammar :: Grammar() для моей проблемы:

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/phoenix.hpp> 

#include <iostream> 

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

typedef lex::lexertl::token<char const*, boost::mpl::vector<std::string>, boost::mpl::false_ > token_type; 
typedef lex::lexertl::actor_lexer<token_type> lexer_type; 

struct TokenId 
{ 
    enum type 
    { 
     INVALID_TOKEN_ID = lex::min_token_id, 
     COMMENT 
    }; 
}; 

struct Lexer : lex::lexer<lexer_type> 
{ 
public: 
    lex::token_def<std::string> comment; 
    lex::token_def<std::string> identifier; 
    lex::token_def<std::string> lineFeed; 
    lex::token_def<std::string> space; 

    Lexer() 
    { 
     comment = "\\/\\*.*?\\*\\/|\\/\\/[^\\r\\n]*"; 
     identifier = "[A-Za-z_][A-Za-z0-9_]*"; 
     space = "[\\x20\\t\\f\\v]+"; 
     lineFeed = "(\\r\\n)|\\r|\\n"; 

     this->self = space[lex::_pass = lex::pass_flags::pass_ignore]; 
     this->self += lineFeed[lex::_pass = lex::pass_flags::pass_ignore]; 
     this->self.add 
     (comment, TokenId::COMMENT) 
     (identifier) 
     (';') 
     ; 
    } 
}; 

typedef Lexer::iterator_type Iterator; 

void traceComment(const std::string& content) 
{ 
    std::cout << " comment: " << content << std::endl; 
} 

class Grammar : public qi::grammar<Iterator> 
{ 
    typedef token_type skipped_t; 

    qi::rule<Iterator, qi::unused_type, qi::unused_type> m_start; 
    qi::rule<Iterator, qi::unused_type, qi::unused_type, skipped_t> m_variable; 
    qi::rule<Iterator, std::string(), qi::unused_type> m_comment; 

public: 
    Lexer lx; 

public: 
    Grammar() : 
     Grammar::base_type(m_start) 
    { 
// This does not work (comments are not skipped in m_variable) 
     m_start = *(
      m_comment[phx::bind(&traceComment, qi::_1)] 
     | qi::skip(qi::token(TokenId::COMMENT))[m_variable] 
     ); 

     m_variable = lx.identifier >> lx.identifier >> ';'; 
     m_comment = qi::token(TokenId::COMMENT); 
/** But this works: 
     m_start = *(
     m_comment[phx::bind(&traceComment, qi::_1)] 
     | m_variable 
     ); 

     m_variable = qi::skip(qi::token(TokenId::COMMENT))[lx.identifier >> lx.identifier >> ';']; 
     m_comment = qi::token(TokenId::COMMENT); 
*/ 
    } 
}; 

void test(const char* code) 
{ 
    std::cout << code << std::endl; 

    Grammar parser; 
    const char* begin = code; 
    const char* end = code + strlen(code); 
    tokenize_and_parse(begin, end, parser.lx, parser); 

    if (begin == end) 
     std::cout << "-- OK --" << std::endl; 
    else 
     std::cout << "-- FAILED --" << std::endl; 
    std::cout << std::endl; 
} 

int main(int argc, char* argv[]) 
{ 
    test("/* kept */ int foo;"); 
    test("int /* ignored */ foo;"); 
    test("int foo /* ignored */;"); 
    test("int foo; // kept"); 
} 

Выход:

/* kept */ int foo; 
    comment: /* kept */ 
-- OK -- 

int /* ignored */ foo; 
-- FAILED -- 

int foo /* ignored */; 
-- FAILED -- 

int foo; // kept 
    comment: // kept 
-- OK -- 

Есть ли какие-либо проблемы с skipped_t?

ответ

2

Поведение, которое вы описываете, - это то, что я ожидаю от своего опыта.

Когда вы пишете

my_rule = qi::skip(ws) [ foo >> lit(',') >> bar >> lit('=') >> baz ]; 

это, по существу такой же, как написание

my_rule = *ws >> foo >> *ws >> lit(',') >> *ws >> bar >> *ws >> lit('=') >> *ws >> baz; 

(при условии, что ws это правило, без атрибута. Если он имеет атрибут в вашей грамматике, этот атрибут игнорируется, как если бы использовался qi::omit.)

Примечательно, что шкипер не распространяется по каналу правила foo , Таким образом, foo, bar и baz все еще могут быть чувствительными к пробелам в приведенном выше. То, что делает директива пропуска, приводит к тому, что грамматика не заботится о ведущих пробелах в этом правиле или пробелы вокруг ',' и '=' в этом правиле.

Больше информации здесь: http://boost-spirit.com/home/2010/02/24/parsing-skippers-and-skipping-parsers/


Edit:

Кроме того, я не думаю, что skipped_t делает то, что вы думаете, что там.

Когда вы используете пользовательский шкипер, проще всего указать фактический экземпляр синтаксического анализатора в качестве анализатора пропусков для этого правила. Когда вы используете тип вместо объекта, например. qi::skip(qi::blank_type), это сокращенное обозначение, где тег-тип qi::blank_type был связан через объявления предшествующего шаблона с типом qi::blank, и qi знает, что когда он видит qi::blank_type в определенных местах, он должен создать экземпляр объекта парсера qi::blank.

Я не вижу никаких доказательств того, что вы на самом деле создали эту технику, вы только что набрали skipped_t до token_type. Что вы должны сделать, если хотите, чтобы это сработало таким образом (если это возможно, я не знаю) читается о точках настройки qi и вместо этого объявляет qi::skipped_t как пустую структуру, которая связана с помощью какой-либо шаблонной плиты котла с правилом m_comment , который, по-видимому, вы действительно хотите пропустить. (Если вы пропустите все маркеры всех типов, то вы не сможете ничего сопоставить, чтобы это не имело смысла, поэтому я не уверен, что вы намеревались сделать с помощью шкипера token_type.)

Мое предположение заключается в том, что когда qi увидел, что typedef token_type в списке параметров, он либо игнорирует его, либо интерпретирует его как часть возвращаемого значения правила или что-то в этом роде, не совсем точно, что он будет делать.

+0

Это звучит странно странно для меня. У меня есть другой код, в котором я использую только qi (no lex), а шкипер распространяется на под-правила. Поэтому из-за qi :: skip (...) [m_variable] правило должно быть объявлено с помощью типа шкипера: skipped_t – ZeeByeZon

+0

Обратите внимание, что на упомянутой странице шкипер прогажирует все правила ... – ZeeByeZon

+0

"обратите внимание, что на странице, которую вы упомянули, шкипер распространяется по всем правилам ..." Нет, это не так. вот пример Хартмута, посмотрите, как он живет на колиру. http://coliru.stacked-crooked.com/a/fd62add9e9ccb930 или вариант с использованием директивы 'skip': http://coliru.stacked-crooked.com/a/5068a53556449c01 –