Рассмотрим следующий код:Пробелы шкипер при использовании Boost.Spirit Ци и Lex
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;
template<typename Lexer>
class expression_lexer
: public lex::lexer<Lexer>
{
public:
typedef lex::token_def<> operator_token_type;
typedef lex::token_def<> value_token_type;
typedef lex::token_def<> variable_token_type;
typedef lex::token_def<lex::omit> parenthesis_token_type;
typedef std::pair<parenthesis_token_type, parenthesis_token_type> parenthesis_token_pair_type;
typedef lex::token_def<lex::omit> whitespace_token_type;
expression_lexer()
: operator_add('+'),
operator_sub('-'),
operator_mul("[x*]"),
operator_div("[:/]"),
value("\\d+(\\.\\d+)?"),
variable("%(\\w+)"),
parenthesis({
std::make_pair(parenthesis_token_type('('), parenthesis_token_type(')')),
std::make_pair(parenthesis_token_type('['), parenthesis_token_type(']'))
}),
whitespace("[ \\t]+")
{
this->self
= operator_add
| operator_sub
| operator_mul
| operator_div
| value
| variable
;
std::for_each(parenthesis.cbegin(), parenthesis.cend(),
[&](parenthesis_token_pair_type const& token_pair)
{
this->self += token_pair.first | token_pair.second;
}
);
this->self("WS") = whitespace;
}
operator_token_type operator_add;
operator_token_type operator_sub;
operator_token_type operator_mul;
operator_token_type operator_div;
value_token_type value;
variable_token_type variable;
std::vector<parenthesis_token_pair_type> parenthesis;
whitespace_token_type whitespace;
};
template<typename Iterator, typename Skipper>
class expression_grammar
: public qi::grammar<Iterator, Skipper>
{
public:
template<typename Tokens>
explicit expression_grammar(Tokens const& tokens)
: expression_grammar::base_type(start)
{
start %= expression >> qi::eoi;
expression %= sum_operand >> -(sum_operator >> expression);
sum_operator %= tokens.operator_add | tokens.operator_sub;
sum_operand %= fac_operand >> -(fac_operator >> sum_operand);
fac_operator %= tokens.operator_mul | tokens.operator_div;
if(!tokens.parenthesis.empty())
fac_operand %= parenthesised | terminal;
else
fac_operand %= terminal;
terminal %= tokens.value | tokens.variable;
if(!tokens.parenthesis.empty())
{
parenthesised %= tokens.parenthesis.front().first >> expression >> tokens.parenthesis.front().second;
std::for_each(tokens.parenthesis.cbegin() + 1, tokens.parenthesis.cend(),
[&](typename Tokens::parenthesis_token_pair_type const& token_pair)
{
parenthesised %= parenthesised.copy() | (token_pair.first >> expression >> token_pair.second);
}
);
}
}
private:
qi::rule<Iterator, Skipper> start;
qi::rule<Iterator, Skipper> expression;
qi::rule<Iterator, Skipper> sum_operand;
qi::rule<Iterator, Skipper> sum_operator;
qi::rule<Iterator, Skipper> fac_operand;
qi::rule<Iterator, Skipper> fac_operator;
qi::rule<Iterator, Skipper> terminal;
qi::rule<Iterator, Skipper> parenthesised;
};
int main()
{
typedef lex::lexertl::token<std::string::const_iterator> token_type;
typedef expression_lexer<lex::lexertl::lexer<token_type>> expression_lexer_type;
typedef expression_lexer_type::iterator_type expression_lexer_iterator_type;
typedef qi::in_state_skipper<expression_lexer_type::lexer_def> skipper_type;
typedef expression_grammar<expression_lexer_iterator_type, skipper_type> expression_grammar_type;
expression_lexer_type lexer;
expression_grammar_type grammar(lexer);
while(std::cin)
{
std::string line;
std::getline(std::cin, line);
std::string::const_iterator first = line.begin();
std::string::const_iterator const last = line.end();
bool const result = lex::tokenize_and_phrase_parse(first, last, lexer, grammar, qi::in_state("WS")[lexer.self]);
if(!result)
std::cout << "Parsing failed! Reminder: >" << std::string(first, last) << "<" << std::endl;
else
{
if(first != last)
std::cout << "Parsing succeeded! Reminder: >" << std::string(first, last) << "<" << std::endl;
else
std::cout << "Parsing succeeded!" << std::endl;
}
}
}
Это простой синтаксический анализатор для арифметических выражений со значениями и переменными. Он создается с использованием expression_lexer
для извлечения токенов, а затем с expression_grammar
для анализа токенов.
Использование lexer для такого маленького корпуса может показаться излишним и, вероятно, одним. Но это стоимость упрощенного примера. Также обратите внимание, что использование lexer позволяет легко определять маркеры с регулярным выражением, в то время как это позволяет легко определить их внешним кодом (и, в частности, предоставленной пользователем конфигурацией). В приведенном примере не было бы вообще никаких проблем читать определение токенов из внешнего файла конфигурации и, например, разрешать пользователю изменять переменные от %name
до $name
.
Код, кажется, работает нормально (проверено на Visual Studio 2013 с Boost 1.61). За исключением того, что я заметил, что если я предоставляю строку вроде 5++5
, она должным образом терпит неудачу, но сообщает в качестве напоминания только 5
, а не +5
, что означает, что оскорбительное +
было «невосстановимо» потреблено. По-видимому, токен, который был выпущен, но не соответствует грамматике, никоим образом не возвращается к исходному входу. Но это не то, о чем я прошу. Только боковое примечание, которое я понял при проверке кода.
Теперь проблема заключается в пропуске пробелов. Мне очень не нравится, как это делается. Хотя я сделал это так, как кажется, это тот, который представлен многими примерами, включая ответы на вопросы здесь, в StackOverflow.
Хуже всего то, что (нигде не зарегистрировано?) qi::in_state_skipper
. Также кажется, что я должен добавить маркер whitespace
как это (с именем), а не как все остальные, так как использование lexer.whitespace
вместо "WS"
не работает.
И, наконец, необходимость «загромождать» грамматику аргументом Skipper
не кажется приятным. Разве я не должен быть свободен от этого? В конце концов, я хочу сделать грамматику на основе токенов вместо прямого ввода, и я хочу, чтобы пробелы были исключены из потока токенов - там больше не нужно!
Какие еще варианты нужно пропустить пробелы? Каковы преимущества этого, как сейчас?
Тот же ответ приведен в http://stackoverflow.com/questions/13361519/troubles-with-boostspiritlex-whitespace в разделе «Упрощение и прибыль». Как я пропустил эти ?! –
Спасибо, что я сам отвечаю на кредит. У меня не было времени (и, честно говоря, я не очень люблю Лекса ...) – sehe
@sehe Если только Qi поддерживает соответствие с регулярным выражением ... Или это так? –