分析Boost.Spirit中固定列表中的选择

时间:2018-07-12 23:21:48

标签: c++ boost-spirit

Employee - Parsing into structs示例开始:

quoted_string

假设我想用与存储在给定容器中的任何字符串匹配的规则替换std::array<std::string, 4> match_list = { "string0", "string1", "string2", "string3" };

例如,如果我有一个容器,例如:

{{1}}

并且我希望解析器仅将输入与数组中的值之一进行匹配(容器不必是数组)。

我确定这很简单,但是Spirit帮助页面似乎无法解决这个问题。

2 个答案:

答案 0 :(得分:3)

这很简单:https://www.boost.org/doc/libs/1_67_0/libs/spirit/doc/html/spirit/qi/reference/string/symbols.html

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;

struct employee {
    int id;
    std::string sym;
    std::string name;
    double value;
};

BOOST_FUSION_ADAPT_STRUCT(employee, id, sym, name, value) 

template <typename Iterator, typename Skipper = qi::space_type>
struct employee_parser : qi::grammar<Iterator, employee(), Skipper>
{
    employee_parser() : employee_parser::base_type(start)
    {
        using namespace qi;

        quoted_string = lexeme['"' >> +(char_ - '"') >> '"'];

        symbol_.add
            ("string0")
            ("string1")
            ("string2")
            ("string3")
            ;

        start =
            lit("employee")
            >> '{'
            >>  int_ >> ','
            >>  symbol_ >> ','
            >>  quoted_string >> ','
            >>  double_
            >>  '}'
            ;
    }

    qi::rule<Iterator, std::string(), Skipper> quoted_string;
    qi::rule<Iterator, employee(), Skipper> start;
    qi::symbols<char, std::string> symbol_;
};

int main() {
    std::string const input = "employee { 42, string3, \"more names or stuff\", 6.7 }";

    using It = std::string::const_iterator;
    It f = input.begin(), l = input.end();

    employee_parser<It> p;

    employee e;
    if (phrase_parse(f, l, p, qi::space, e)) {
        using boost::fusion::operator<<;
        std::cout << boost::fusion::tuple_delimiter(';');
        std::cout << "Parsed: " << e << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (f!=l)
        std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}

打印

Parsed: (42;;more names or stuff;6.7)

要实际包含值:

    symbol_.add
        ("string0", "STRING0")
        ("string1", "STRING1")
        ("string2", "STRING2")
        ("string3", "STRING3")
        ;

打印 Live On Coliru

Parsed: (42;STRING3;more names or stuff;6.7)

或者您可以一起使用其他类型:

    symbol_.add
        ("string0", 0)
        ("string1", 1)
        ("string2", 2)
        ("string3", 3)
        ;

使用

    symbol_.add
        ("string0", 0)
        ("string1", 1)
        ("string2", 2)
        ("string3", 3)
        ;

打印 Live On Coliru

Parsed: (42;3;more names or stuff;6.7)

最后,您可以使用raw[]来“转换”输入序列,例如与qi::no_space[]结合使用: Live On Coliru

        >>  raw[no_case[symbol_]] >> ','

打印

Parsed: (42;sTrInG3;more names or stuff;6.7)

答案 1 :(得分:3)

在塞思鼓励我在评论中也张贴我的答案之后,就在这里。正如预期的那样,这与我从传递给语法的std::array动态构建符号的区别非常相似。

#include <iostream>
#include <string>

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

struct employee {
    int age;
    std::string surname;
    std::string forename;
    double salary;
};

BOOST_FUSION_ADAPT_STRUCT(employee, age, surname, forename, salary)

namespace ascii = boost::spirit::ascii;
namespace qi = boost::spirit::qi;

template <typename Iterator, std::size_t N>
struct employee_parser : qi::grammar<Iterator, employee(), ascii::space_type> {
    employee_parser(std::array<std::string, N> const &match_list)
        : employee_parser::base_type(start) {
        using namespace qi;

        for (auto match : match_list) {
            employees.add(match, match);
        }

        quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];

        start %=
            lit("employee")
            >> '{'
            >>  int_ >> ','
            >>  quoted_string >> ','
            >>  employees >> ','
            >>  double_
            >>  '}'
            ;
    }

    qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
    qi::rule<Iterator, employee(), ascii::space_type> start;
    qi::symbols<typename std::iterator_traits<Iterator>::value_type,
                std::string> employees;
};

template <typename Iterator, std::size_t N>
employee parse(Iterator first, Iterator last,
               std::array<std::string, N> const &match_list) {
    employee_parser<Iterator, N> const grammar(match_list);
    employee e;
    bool r = qi::phrase_parse(first, last, grammar, ascii::space, e);

    if (!r || first != last) {
        std::cerr << "Parsing failed at " + std::string(first, last) + "\n";
    }

    return e;
}

int main() {
    employee e;
    std::array<std::string, 4> match_list = {"Homer", "Marge", "Lisa", "Bart"};

    std::string homer = "employee { 38, \"Simpson\", Homer, 3.0 }";
    e = parse(homer.begin(), homer.end(), match_list);
    std::cout << "employee { " << e.age << ", " << e.surname << ", "
              << e.forename << ", " << e.salary << " }\n";

    // Fails parsing because Hans Mole is not in the list
    std::string mole = "employee { 100, \"Mole\", Hans, 0.0 }";
    e = parse(mole.begin(), mole.end(), match_list);
    std::cout << "employee { " << e.age << ", " << e.surname << ", "
              << e.forename << ", " << e.salary << " }\n";
}
$ clang++ -Wall -Wextra -Wpedantic -std=c++11 test.cpp
$ ./a.out 
employee { 38, Simpson, Homer, 3 }
Parsing failed at employee { 100, "Mole", Hans, 0.0 }
employee { 100, Mole, , 0 }

这里也引用荷马的薪水为3.0:https://www.youtube.com/watch?v=HIEWgwRrY9s