У меня есть этот очень простой парсер, использующий Boost :: Spirit:
rule<std::string::iterator, std::string()> zeroTo255 = (string("25") >> char_('0', '5'))
| (char_('2') >> char_('0', '4') >> digit)
| (char_('1') >> repeat[2](digit))
| (char_('1', '9') >> digit) | digit;
Когда я пытаюсь разобрать
std::string o{"1"};
std::string s;
parse(o.begin(), o.end(), zeroTo255, s);
std::cout << o << ": " << s << std::endl;
У меня как выходной
1: 111
Я явно что-то делаю не так, но что?
qi::hold
это один из способов, как правильно сказал @Andrzej
Я думаю, что у меня есть несколько замечаний, которые могут помочь, а также лучшее решение.
Дело в том, что Spirit не будет требовать временного хранения атрибутов по дизайну. На самом деле, он не может предположить, что атрибут в первую очередь копируемый. Это причина здесь (представьте, что все разбирается в одном std :: vector<> а копирование для каждого шага парсера?).
На более существенном уровне мне кажется, что здесь не обратная обработка атрибутов, а само выражение синтаксического анализатора: Это не в состоянии заявить намерение, и несет в себе все виды сложности, связанные с представлениями чисел когда … на самом деле это не должно
Мой взгляд на это будет
rule<std::string::iterator, std::string()> zeroTo255, alternatively;
alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];
Вы видите: вы позволяете Духу анализировать число, и на самом деле просто проверяете диапазон, что вы и хотели сделать в первую очередь.
Второе, что мне кажется типичным, это то, что правило выставляет std::string
атрибут, а не unsigned char
например Это почему?
Предполагая, что это было сознательное дизайнерское решение, вы можете сделать это по-своему, разумно используя
!parser
) — который не влияет на атрибуты&parser
) — который не влияет на атрибутыqi::as_string
, qi::raw
, qi::lexeme
а также qi::no_skip
Вот что минимальное изменение в вашем первоначальном правиле будет иметь работал:
zeroTo255 = raw [
("25" >> char_("0-5"))
| ('2' >> char_("0-4") >> digit)
| ('1' >> digit >> digit)
| (char_("1-9") >> digit)
| digit
];
Это имеет примерно тот же эффект, что и код, использующий qi::hold
но не недостаток производительности значений атрибута _hold_ing.
Надеюсь это поможет.
Полный образец: Live on http://liveworkspace.org/code/4v4CQW$0:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
int main()
{
using namespace qi;
rule<std::string::iterator, std::string()> zeroTo255, alternatively;
zeroTo255 = raw [
("25" >> char_("0-5"))
| ('2' >> char_("0-4") >> digit)
| ('1' >> digit >> digit)
| (char_("1-9") >> digit)
| digit
];
alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];
for (auto& input : std::vector<std::string> { "255", "249", "178", "30", "4" })
{
std::string output;
std::cout << "zeroTo255:\t" << std::boolalpha
<< parse(std::begin(input), std::end(input), zeroTo255, output)
<< ": " << output << std::endl;
output.clear();
std::cout << "alternatively:\t" << std::boolalpha
<< parse(std::begin(input), std::end(input), alternatively, output)
<< ": " << output << std::endl;
}
}
Выход
zeroTo255: true: 255
alternatively: true: 255
zeroTo255: true: 249
alternatively: true: 249
zeroTo255: true: 178
alternatively: true: 178
zeroTo255: true: 30
alternatively: true: 30
zeroTo255: true: 4
alternatively: true: 4
Однажды я столкнулся с подобной проблемой. Это особый способ работы альтернативного оператора в Spirit. Ваш пример должен работать, если вы используете дополнительную директиву «hold».
rule<std::string::iterator, std::string()> zeroTo255
= hold[string("25") >> char_('0', '5')]
| hold[char_('2') >> char_('0', '4') >> digit]
| hold[char_('1') >> repeat[2](digit)]
| hold[char_('1', '9') >> digit] | digit;
Подробнее об этом поведении смотрите эта тема.