Я использую 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?
Это звучит странно странно для меня. У меня есть другой код, в котором я использую только qi (no lex), а шкипер распространяется на под-правила. Поэтому из-за qi :: skip (...) [m_variable] правило должно быть объявлено с помощью типа шкипера: skipped_t – ZeeByeZon
Обратите внимание, что на упомянутой странице шкипер прогажирует все правила ... – ZeeByeZon
"обратите внимание, что на странице, которую вы упомянули, шкипер распространяется по всем правилам ..." Нет, это не так. вот пример Хартмута, посмотрите, как он живет на колиру. http://coliru.stacked-crooked.com/a/fd62add9e9ccb930 или вариант с использованием директивы 'skip': http://coliru.stacked-crooked.com/a/5068a53556449c01 –