Boost Spirit, получите итератор внутри действия semanti c - PullRequest
1 голос
/ 24 февраля 2020

внутри действия semanti c Я хочу получить итератор, предпочтительно весь диапазон итераторов от первого до последнего проанализированного символа. При использовании директивы raw я мог просто получить ее с помощью _attr(context). Я догадался, что _where(context) делает это, но возвращает только пустой диапазон, начальный итератор которого указывает на символ после проанализированной подстроки.

Пример кода:

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <utility>

namespace x3 = boost::spirit::x3;

int main()
{
    const auto action = [](auto &ctx)
    {
        auto range = x3::_where(ctx);
        std::cout << range.size() << '\n';
        std::cout << "range start: " << static_cast<const void*>(&*range.begin()) << '\n';
    };

    const auto rule = x3::int_[action];

    const std::string input = "432";
    std::cout << "string start: " << static_cast<const void*>(input.data()) << '\n';

    int output;
    x3::phrase_parse(input.begin(), input.end(), rule, x3::space, output);
    std::cout << output << '\n';
}

Выход

string start: 0x7ffd65f337c0
0
range start: 0x7ffd65f337c3
432

Длина диапазона равна 0, и начало () его указывает на конец строки. Когда я раскрываю входную строку, диапазон покрывает оставшуюся неразобранную подстроку.

Как получить диапазон итератора, который содержит проанализированную подстроку?

1 Ответ

2 голосов
/ 25 февраля 2020

Ах, увидев твой код, я вспомнил, что делал в прошлом.

В принципе, вы можете

  1. использовать on_error обработку на x3::rule<> и это даст вам соответствующий диапазон итераторов. См. Пример:

    Live On Coliru

    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    #include <utility>
    #include <iomanip>
    
    namespace x3 = boost::spirit::x3;
    
    namespace {
        struct ehbase {
            template <typename It, typename Attr, typename Ctx>
                void on_success(It& f, It const& l, Attr const& attr, Ctx const& /*ctx*/) const {
                    std::cout << "on_succes: " << std::quoted(std::string(f, l)) << " -> " << attr << "\n";
                }
        };
    
        struct rule_type : ehbase {};
    }
    
    int main() {
        const auto rule = x3::rule<rule_type, int>{"rule"} = x3::int_;
    
        for (std::string const input : { "q", "432", " 646 q" }) {
            std::cout << "== " << std::quoted(input) << " ==\n";
            auto f = begin(input), l = end(input);
            int output;
            if (x3::phrase_parse(f, l, rule, x3::space, output))
                std::cout << "Parsed " << output << "\n";
            else
                std::cout << "Parse failed\n";
    
            if (f!=l)
                std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
        }
    }
    

    Отпечатки

    == "q" ==
    Parse failed
    Remaining: "q"
    == "432" ==
    on_succes: "432" -> 432
    Parsed 432
    == " 646 q" ==
    on_succes: "646" -> 646
    Parsed 646
    Remaining: "q"
    

    По небольшому касанию вы можете добавить обработку ошибок в том же духе:

    template <typename It, typename Ctx>
    x3::error_handler_result on_error(It f, It l, x3::expectation_failure<It> const& e, Ctx const& /*ctx*/) const {
        std::cout << std::string(f,l) << "\n"
                  << std::setw(1+std::distance(f, e.where())) << "^"
                  << "-- expected: " << e.which() << "\n";
        return x3::error_handler_result::fail;
    }
    

    Если у вас есть точка ожидания в парсере:

    const auto rule = x3::rule<rule_type, int>{"rule"} = x3::int_ > x3::eoi;
    

    Это сейчас отпечатки: Live On Coliru

    == " 646 q" ==
     646 q
         ^-- expected: eoi
    Parse failed
    Remaining: "646 q"
    
  2. Вы можете использовать директиву x3::raw[] для предоставить диапазон итераторов в качестве атрибута:

    Live On Coliru

    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    #include <utility>
    #include <iomanip>
    
    namespace x3 = boost::spirit::x3;
    
    int main() {
        for (std::string const input : { "q", "432", " 646 q" }) {
            std::cout << "== " << std::quoted(input) << " ==\n";
    
            auto action = [&input](auto& ctx) {
                auto iters = x3::_attr(ctx);
                std::cout
                    << input << "\n"
                    << std::setw(std::distance(input.begin(), iters.begin())) << ""
                    << "^ matched: " <<  std::quoted(std::string(iters.begin(), iters.end())) << "\n";
            };
    
            const auto rule = x3::raw[x3::int_] [action];
    
            auto f = begin(input), l = end(input);
            if (x3::phrase_parse(f, l, rule, x3::space))
                std::cout << "Parse succeeded\n";
            else
                std::cout << "Parse failed\n";
    
            if (f!=l)
                std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
        }
    }
    

    Отпечатки:

    == "q" ==
    Parse failed
    Remaining: "q"
    == "432" ==
    432
    ^ matched: "432"
    Parse succeeded
    == " 646 q" ==
     646 q
     ^ matched: "646"
    Parse succeeded
    Remaining: "q"
    

    Опять же, немного связанный, может стать немного более громоздким, чтобы иметь дело с распространением атрибутов в этом подходе:

    const auto rule
        = x3::rule<struct _rule, int, true> {"rule"}
        = &x3::raw[x3::int_] [action] >> x3::int_;;
    
    auto f = begin(input), l = end(input);
    int output;
    if (x3::phrase_parse(f, l, rule, x3::space, output))
    
  3. Чтобы облегчить Неуклюжее распространение атрибута, вы можете написать собственный компонент синтаксического анализатора, который просто обернет другой и добавит необходимые логики c:

    template <typename SubjectParser>
    struct verbose : x3::parser<verbose<SubjectParser> > {
        explicit verbose(SubjectParser p, std::string name) : _subject(std::move(p)), _name(std::move(name)) {}
    
        SubjectParser _subject;
        std::string _name;
    
        template <typename It, typename Ctx, typename... Other>
        bool parse(It& f, It l, Ctx& ctx, Other&&... args) const {
            auto saved = f;
            auto ok = x3::as_parser(_subject).parse(f, l, ctx, std::forward<Other>(args)...);
    
            if (ok) {
                //optionally adjust for skipper
                x3::skip_over(saved, l, ctx);
                std::cout << "Debug: " << _name << " matched " << std::quoted(std::string(saved, f)) << "\n";
            }
            return ok;
        }
    };
    

    Теперь оберните выражение синтаксического анализатора следующим образом:

    const auto rule = verbose {x3::int_, "YUMMY"};
    

    Результаты в следующем выходные данные: Live On Coliru

    == "q" ==
    Parse failed
    Remaining: "q"
    == "432" ==
    Debug: YUMMY matched "432"
    Parsed 432
    == " 646 q" ==
    Debug: YUMMY matched "646"
    Parsed 646
    Remaining: "q"
    
  4. Перечень этого заставил меня понять, что отладка правил мог быть / все, что вы искали /. В этом случае, просто используя BOOST_SPIRIT_X3_DEBUG, вы должны знать:

    [ Live On Coliru

    #define BOOST_SPIRIT_X3_DEBUG
    #include <boost/spirit/home/x3.hpp>
    #include <iomanip>
    
    namespace x3 = boost::spirit::x3;
    
    int main() {
        const auto rule 
            = x3::rule<struct _rule, int> {"rule"}
            = x3::int_;
    
        for (std::string const input : { "q", "432", " 646 q" }) {
            std::cout << "== " << std::quoted(input) << " ==\n";
    
            auto f = begin(input), l = end(input);
            int output;
            if (x3::phrase_parse(f, l, rule, x3::space, output))
                std::cout << "Parsed " << output << "\n";
            else
                std::cout << "Parse failed\n";
    
            if (f!=l)
                std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
        }
    }
    

    Какие отпечатки:

    == "q" ==
    <rule>
      <try>q</try>
      <fail/>
    </rule>
    Parse failed
    Remaining: "q"
    == "432" ==
    <rule>
      <try>432</try>
      <success></success>
      <attributes>432</attributes>
    </rule>
    Parsed 432
    == " 646 q" ==
    <rule>
      <try> 646 q</try>
      <success> q</success>
      <attributes>646</attributes>
    </rule>
    Parsed 646
    Remaining: "q"
    
...