我如何获取解析的输出并使用它来查找符号

How do I take the output of a parse and use it to look up in a symbols

从代码中可以看出,我获取了一次解析的输出,并在第二次解析中使用它从符号中查找数字。 作为一条规则,我该如何做到这一点?查看文档并进行大量搜索让我相信这可以通过本地变量来完成,但我无法弄清楚如何在该变量上使用我的符号四边形。

int main()
{
  using boost::phoenix::ref;
  using qi::_1;
  using qi::_val;
  using qi::no_case;
  using qi::_a;
  using qi::symbols;
  using qi::char_;
  using qi::omit;

  symbols<char, int> quad;
  quad.add
  ("1", 1)
    ("2", 2)
    ("3", 3)
    ("4", 4)
    ("NE", 1)
    ("SE", 2)
    ("SW", 3)
    ("NW", 4)
    ;

  std::wstring s = L"N44°30'14.950\"W";

  std::wstring out;
  int iQuad;
  qi::parse(s.begin(), s.end(), 
    no_case[char_('N')] >> omit[*(qi::char_ - no_case[char_("NSEW")])] >> no_case[char_('W')],
    out);
  qi::parse(out.begin(), out.end(), quad, iQuad);
  return 0;
}

是的,可以用本地变量来完成。

但是,这会将 symbols 降级为常规地图。那么让我们使用那个¹

1。最简单的事情

首先,我会考虑做最简单的事情:

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

namespace Rules {
    namespace qi = boost::spirit::qi;

    qi::rule<std::wstring::const_iterator, int()> quad = qi::no_case [
        ('N' >> *~qi::char_("EW") >> 'E')[ qi::_val = 1 ] |
        ('S' >> *~qi::char_("EW") >> 'E')[ qi::_val = 2 ] |
        ('S' >> *~qi::char_("EW") >> 'W')[ qi::_val = 3 ] |
        ('N' >> *~qi::char_("EW") >> 'W')[ qi::_val = 4 ] 
    ];
}

int main() {
    for (std::wstring const s : {
            L"NE", L"SE", L"SW", L"NW",
            L"N44°30'14.950\"E", 
            L"N44°30'14.950\"W", 
            L"S44°30'14.950\"W", 
            L"S44°30'14.950\"E", 
            L"1", L"2", L"3", L"4",
        })
    {
        int iQuad;
        auto f = s.begin(), l = s.end();
        bool ok = parse(f, l, Rules::quad, iQuad);

        if (ok)
            std::wcout << L"Parsed: '" << s << L"' -> " << iQuad << L"\n";
        else
            std::wcout << L"Parse failed '" << s << L"'\n";

        if (f!=l)
            std::wcout << L"Remaining unparsed: '" << std::wstring(f,l) << L"'\n";
    }
}

打印

Live On Coliru

Parsed: 'NE' -> 1
Parsed: 'SE' -> 2
Parsed: 'SW' -> 3
Parsed: 'NW' -> 4
Parsed: 'N44?30'14.950"E' -> 1
Parsed: 'N44?30'14.950"W' -> 4
Parsed: 'S44?30'14.950"W' -> 3
Parsed: 'S44?30'14.950"E' -> 2
Parse failed '1'
Remaining unparsed: '1'
Parse failed '2'
Remaining unparsed: '2'
Parse failed '3'
Remaining unparsed: '3'
Parse failed '4'
Remaining unparsed: '4'

如果你想让数字也解析,只需添加

qi::rule<std::wstring::const_iterator, int()> quad = qi::no_case [
    (qi::int_(1) | qi::int_(2) | qi::int_(3) | qi::int_(4)) [ qi::_val = qi::_1 ] |
    ('N' >> *~qi::char_("EW") >> 'E')[ qi::_val = 1 ] |
    ('S' >> *~qi::char_("EW") >> 'E')[ qi::_val = 2 ] |
    ('S' >> *~qi::char_("EW") >> 'W')[ qi::_val = 3 ] |
    ('N' >> *~qi::char_("EW") >> 'W')[ qi::_val = 4 ] 
];

All this can be optimized, but I'll venture the guess that it's more efficient than anything based on symbol and 2-phase parse

2。使用地图查找

只需...使用地图:

template <typename It> struct MapLookup : qi::grammar<It, int()> {
    MapLookup() : MapLookup::base_type(start) {
        namespace px = boost::phoenix;

        start = qi::as_string [
            qi::char_("1234") | 
            qi::char_("nsNS") >> qi::omit[*~qi::char_("weWE")] >> qi::char_("weWE")
        ] [ qi::_val = px::ref(_lookup)[qi::_1] ];
    }
  private:
    struct ci {
        template <typename A, typename B>
        bool operator()(A const& a, B const& b) const { return boost::ilexicographical_compare(a, b); }
    };
    std::map<std::string, int, ci> _lookup = { 
        { "NE", 1 }, { "SE", 2 }, { "SW", 3 }, { "NW", 4 },
        { "1" , 1 }, { "2",  2 }, { "3",  3 }, { "4",  4 } };
    qi::rule<It, int()> start;
};

也看到了Live On Coliru

3。优化它

qi::symbol 使用 Tries。你可能认为那更快。实际上,它的查找速度非常快。但不是在非常小的键组上。在基于节点的容器上。使用动态分配的临时密钥。

换句话说,我们可以做得更好:

template <typename It> struct FastLookup : qi::grammar<It, int()> {
    using key = std::array<char, 2>;

    FastLookup() : FastLookup::base_type(start) {
        namespace px = boost::phoenix;

        start = 
            qi::int_ [ qi::_pass = (qi::_1 > 0 && qi::_1 <= 4), qi::_val = qi::_1 ] |
            qi::raw [
                qi::char_("nsNS") >> qi::omit[*~qi::char_("weWE")] >> qi::char_("weWE")
            ] [ qi::_val = _lookup(qi::_1) ];
    }
  private:
    struct lookup_f {
        template <typename R> int operator()(R const& range) const {
            using key = std::tuple<char, char>;
            static constexpr key index[] = { key {'N','E'}, key {'S','E'}, key {'S','W'}, key {'N','W'}, };

            using namespace std;
            auto a = std::toupper(*range.begin());
            auto b = std::toupper(*(range.end()-1));
            return 1 + (find(begin(index), end(index), key(a, b)) - begin(index));
        }
    };

    boost::phoenix::function<lookup_f> _lookup;
    qi::rule<It, int()> start;
};

看到了Live Again On Coliru


¹ 如果您坚持可以在自己的代码中使用符号