Почему qi :: skip терпит неудачу с токенами от лексера?

Я использую boost :: spirit lex и qi для разбора исходного кода.

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

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

#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?

1

Решение

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

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

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/


Редактировать:

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

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

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

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

2

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector