То, что вы, вероятно, не поняли, является то, что синтаксический анализатор выражений прекратят автоматическое распространение атрибута в присутствии семантических действий*.
* Документация Backgound: How Do Rules Propagate Their Attributes?
Вы используете семантическое действие, чтобы 'вручную' распространять атрибут uint_
парсер:
[ref(num) = _1] // this is a Semantic Action
Так самый простой способ исправить это, было бы также автоматически распространять num
(способ, которым были привязаны API-интерфейсы qi::parse
и qi::phrase_parse
d):
bool ok = qi::phrase_parse(first, last, // input iterators
uint_ >> lit('X') >> lexeme[+(char_ - ' ')], // parser expr
space, // skipper
num, parsed_str); // output attributes
Или, обращаясь несколько не по теме очки, даже уборщик:
bool ok = qi::phrase_parse(first, last,
uint_ >> 'X' >> lexeme[+graph],
blank,
num, parsed_str);
Как вы можете видеть, вы можете передать несколько lvalues в качестве атрибутов вывода получателей. 1, 2
Смотреть это живой demo on Coliru (link)
Там целая много магии происходит, что на практике приводит к моему эмпирическому правилу:
Избегайте использования семантических действий в Дух Ци выражения, если вы абсолютно не должны
У меня об этом раньше, в ответ конкретно об этом: Boost Spirit: "Semantic actions are evil"?
По моему опыту, это почти всегда чище использовать атрибут Customization очки, чтобы настроить автоматическое распространения, чем отказаться от правил авто и прибегать к ручному атрибуту обработки.
Что технически происходит распространять эти атрибуты, что num
и parsed_str
будет «привязан» ко всему выражению синтаксического анализа в виде последовательности Fusion:
fusion::vector2<unsigned&, std::string&>
и открытой атрибут правила:
fusion::vector2<unsigned, std::vector<char> >
будет «tr в соответствии с этим во время присвоения. Правила совместимости атрибутов позволяют это преобразование и многие другие.
В качестве альтернативы, используйте семантические действия для обоих:
bool ok = qi::phrase_parse(first, last,
(uint_ >> 'X' >> as_string [ lexeme[+graph] ])
[ phx::ref(num) = _1, phx::ref(parsed_str) = _2 ],
blank);
Там в несколько тонкостей здесь:
нам нужно as_string
здесь, чтобы выставить атрибут, как std::string
вместо std::vector<char>
(см. Выше)
мы должны квалифицировать phx::ref(parsed_str)
, поскольку даже using boost::phoenix::ref
не будет достаточно, чтобы неоднозначность std::ref
и phx::ref
: ADL будет тянуться в std::ref
, так как он из того же пространства имен, как тип parsed_str
.
группировать семантическое действие для предотвращения частично назначенных результатов, например. следующий будет перезаписывать num
даже если X
может отсутствовать на входе:
bool ok = qi::phrase_parse(first, last,
uint_ [ phx::ref(num) = _1 ]
>> 'X'
>> as_string [ lexeme[+graph] ] [ phx::ref(parsed_str) = _1 ],
blank);
Все эти сложности могут быть скрыты от вашего зрения, если вы избежать распространения вручную атрибутов!
Есть ли чистое решение для принудительного использования правила «разделение одним или несколькими пробелами»? У меня это до сих пор: 'uint_ >> + no_skip [''] >> 'X' >> + no_skip [''] >> lexeme [+ graph]'. Что может быть лучше? – Arlen
+1 для святой-основательности-батмана – Borgleader
@Arlen Mmm: в репозитории Spirit имеется ['отчетный'] (http://www.boost.org/doc/libs/1_54_0/libs/spirit/repository/doc/ HTML/spirit_repository/qi_components/директивы/distinct.html). На днях кто-то попросил меня исправить его грамматику, и я предложил [быстрый и грязный oneliner, который может дать вам идею тоже] (https://github.com/sehe/streampunk/commit/4c68c37de2a64d4836f931fb779a4f90298cb606). Надеюсь, это поможет! – sehe