Используйте Boost Phoenix в Qi для ссылки на предыдущее совпадение в грамматике

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

Мне бы хотелось, чтобы строка «14,14, тест» была проанализирована, но «14,12, тест» не удалось, так как 14 не равно 12. Я бы хотел, чтобы напечатанный ниже код напечатал:

Good: (14 14 test)
Fail

В настоящее время оба ввода пройдут, поскольку я разрешаю и qi :: int_ анализировать второе значение без каких-либо проверок.

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/spirit/include/qi_matches.hpp>

#include <string>
#include <vector>
#include <algorithm>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;


int main(){


    std::vector<std::string> test_inputs = {"14,14,test","14,12,test"};

    std::for_each(test_inputs.begin(),test_inputs.end(),[](const std::string& input){
        boost::fusion::vector<int,int,std::string> result;
        int i(0);

        auto res = qi::parse(input.begin(),input.end(),
            //works but also parses "14,12,test"
            qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_ >> qi::lit(",") >> +qi::char_,
            //Fails to compile
            //qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_(phx::val(i)) >> qi::lit(",") >> +qi::char_,
            //qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_(phx::ref(i)) >> qi::lit(",") >> +qi::char_,
            result);
        if(res){
            std::cout << "Good: " << result << std::endl;
        }
        else{
            std::cout << "Fail" << std::endl;
        }
    });         
}

Я могу использовать phoenix :: ref для захвата первого значения, но я не могу понять, как проверить мой второй синтаксический анализатор qi :: int_ на значение i. Я пробовал использовать phoenix :: val и phoenix :: ref без компиляции. В идеале я хотел бы захватить значение во втором int и подумал, что парсер int_ возьмет ленивый литерал из Phoenix.

Спасибо за любую помощь в этом вопросе.


person Joel    schedule 05.09.2013    source источник
comment
Альтернатива, в которой дважды используется phx::ref, который вы отметили как не компилируемый, работает, если вы определяете BOOST_SPIRIT_USE_PHOENIX_V3 при использовании g ++ 4.8.1 и boost 1.54. Я бы рекомендовал всегда использовать это определение, оно решает множество проблем.   -  person llonesmiz    schedule 05.09.2013
comment
Я имел в виду определение перед включением заголовков. Вы можете увидеть его здесь. В любом случае я думаю, что должны быть альтернативные решения, поэтому надеюсь, что вы получите информативный ответ.   -  person llonesmiz    schedule 05.09.2013
comment
Определение получает код для компиляции как в gcc 4.7, так и в Clang. Спасибо!   -  person Joel    schedule 05.09.2013


Ответы (1)


Самое простое решение - передать значение i, полученное от первого qi :: int_, ко второму, то есть:

qi::int_[phx::ref(i) = qi::_1] >> qi::lit(",") >> qi::int_(phx::ref(i)) >> qi::lit(",") >> +qi::char_,

qi :: _ int с параметром ожидает предоставленное значение.

Другой альтернативой, которую следует рассмотреть, является использование локальной переменной правила. Вы могли сделать это:

typedef boost::fusion::vector<int,int,std::string> result_t;
result_t result;
qi::rule<std::string::const_iterator, result_t(),
     boost::spirit::qi::locals<int> > r;
r %= qi::int_[qi::_a = qi::_1] >> qi::lit(",") >> qi::int_(qi::_a) >> qi::lit(",") >> +qi::char_;

Затем используйте "r" вместо своего правила. Хотя я полагаю, что если вы просто собираетесь запустить правило напрямую, используя qi :: parse, это не имеет смысла.

person Jeff Trull    schedule 05.09.2013
comment
Я думаю, что ваше первое решение - это именно то, что я прокомментировал. Чтобы это скомпилировать, нужно было определить BOOST_SPIRIT_USE_PHOENIX_V3. Это было предложено @cv_and_he в комментарии. Когда я это делаю, это действительно работает. - person Joel; 05.09.2013
comment
Я также должен упомянуть, что приветствуется фрагмент qi :: rule. Проблема заключается в более крупном проекте, в котором использовался синтаксический анализатор, производный от qi :: grammar, и определенные правила. Я просто пробовал простой пример кода, который показал проблему. Изначально я хотел использовать locals, но у меня не получалось заставить это работать хорошо (потому что я, без сомнения, отказывался от qi :: locals). Спасибо! - person Joel; 05.09.2013
comment
Ах, почему-то мне для этого не понадобился BOOST_SPIRIT_USE_PHOENIX_V3 ... Boost 1.54 с -std = c ++ 0x - person Jeff Trull; 06.09.2013