指示Qi变换属性失败的适当方法是什么?

时间:2018-06-18 15:48:30

标签: c++ boost-spirit boost-spirit-qi

boost::spirit::traits::transform_attribute中指示解析失败的正确方法是什么?我可以抛弃任何旧的异常,还是有特殊的事情要它让我做?

namespace boost
{
    namespace spirit
    {
        namespace traits
        {
            template <>
            struct transform_attribute<TwoNums, std::vector<char>, qi::domain>
            {
                typedef std::vector<char> type;

                static type pre(TwoWords&) { return{}; }

                static void post(TwoWords& val, type const& attr) {
                    std::string stringed(attr.begin(), attr.end());
                    //https://stackoverflow.com/questions/236129/the-most-elegant-way-to-iterate-the-words-of-a-string
                    std::vector<std::string> strs;
                    boost::split(strs, stringed, ",");
                    if(strs.size()!=2) 
                    { 
                        //What do I do here?
                    }
                    val = TwoWords(strs[0],strs[1]);
                }
                static void fail(FDate&) { }
            };
        }
    }
}

1 个答案:

答案 0 :(得分:1)

  1. 是的,引发异常似乎是唯一的带外方法。

  2. 您可以使用qi::on_error来捕获并响应它。

  3. 但是,尚不清楚您需要什么。在解析器中使用split似乎有点颠倒。拆分基本上是糟糕的解析版本。

    为什么没有规则进行子解析?

1。简单投掷...

Live On Coliru

#include <boost/algorithm/string.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

struct Invalid {};

struct TwoWords {
    std::string one, two;
};

namespace boost { namespace spirit { namespace traits {

    template <> struct transform_attribute<TwoWords, std::vector<char>, qi::domain> {
        typedef std::vector<char> type;

        static type pre(TwoWords &) { return {}; }

        static void post(TwoWords &val, type const &attr) {
            std::string stringed(attr.begin(), attr.end());

            std::vector<std::string> strs;
            boost::split(strs, stringed, boost::is_any_of(","));
            if (strs.size() != 2) {
                throw Invalid{};
            }
            val = TwoWords{ strs.at(0), strs.at(1) };
        }

        static void fail(TwoWords &) {}
    };

} } }

template <typename It>
struct Demo1 : qi::grammar<It, TwoWords()> {
    Demo1() : Demo1::base_type(start) {
        start = qi::attr_cast<TwoWords>(+qi::char_);
    }
  private:
    qi::rule<It, TwoWords()> start;
};

int main() {
    Demo1<std::string::const_iterator> parser;

    for (std::string const input : { ",", "a,b", "a,b,c" }) {
        std::cout << "Parsing " << std::quoted(input) << " -> ";

        TwoWords tw;
        try {
            if (parse(input.begin(), input.end(), parser, tw)) {
                std::cout << std::quoted(tw.one) << ", " << std::quoted(tw.two) << "\n";
            } else {
                std::cout << "Failed\n";
            }
        } catch(Invalid) {
            std::cout << "Input invalid\n";
        }
    }
}

打印

Parsing "," -> "", ""
Parsing "a,b" -> "a", "b"
Parsing "a,b,c" -> Input invalid

2。处理解析器内部的错误

这有点,因为这将要求您抛出expectation_failure

  

这不是最佳选择,因为它假设您知道解析器将被实例化的迭代器。

     

on_error设计用于expectation points

* Live On Coliru

#include <boost/algorithm/string.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

struct Invalid {};

struct TwoWords {
    std::string one, two;
};

namespace boost { namespace spirit { namespace traits {

    template <> struct transform_attribute<TwoWords, std::vector<char>, qi::domain> {
        typedef std::vector<char> type;

        static type pre(TwoWords &) { return {}; }

        static void post(TwoWords &val, type const &attr) {
            std::string stringed(attr.begin(), attr.end());

            std::vector<std::string> strs;
            boost::split(strs, stringed, boost::is_any_of(","));
            if (strs.size() != 2) {
                throw qi::expectation_failure<std::string::const_iterator>({}, {}, info("test"));
            }
            val = TwoWords{ strs.at(0), strs.at(1) };
        }

        static void fail(TwoWords &) {}
    };

} } }

template <typename It>
struct Demo2 : qi::grammar<It, TwoWords()> {
    Demo2() : Demo2::base_type(start) {
        start = qi::attr_cast<TwoWords>(+qi::char_);

        qi::on_error(start, [](auto&&...){});
        // more verbose spelling:
        // qi::on_error<qi::error_handler_result::fail> (start, [](auto&&...){[>no-op<]});
    }
  private:
    qi::rule<It, TwoWords()> start;
};

int main() {
    Demo2<std::string::const_iterator> parser;

    for (std::string const input : { ",", "a,b", "a,b,c" }) {
        std::cout << "Parsing " << std::quoted(input) << " -> ";

        TwoWords tw;
        try {
            if (parse(input.begin(), input.end(), parser, tw)) {
                std::cout << std::quoted(tw.one) << ", " << std::quoted(tw.two) << "\n";
            } else {
                std::cout << "Failed\n";
            }
        } catch(Invalid) {
            std::cout << "Input invalid\n";
        }
    }
}

打印

Parsing "," -> "", ""
Parsing "a,b" -> "a", "b"
Parsing "a,b,c" -> Failed

3。最后:子规则规则!

让我们假设一个更有趣的语法,其中您有;的{​​{1}}分隔列表:

TwoWords

我们解析为"foo,bar;a,b" 的向量:

TwoWords

我们没有使用特征来“强制”属性,而是改编了结构并依靠自动属性传播:

using Word = std::string;
struct TwoWords { std::string one, two; };
using TwoWordses = std::vector<TwoWords>;

解析器模拟数据类型:

BOOST_FUSION_ADAPT_STRUCT(TwoWords, one, two)

完整测试为 Live On Coliru

template <typename It>
struct Demo3 : qi::grammar<It, TwoWordses()> {
    Demo3() : Demo3::base_type(start) {
        using namespace qi;
        word     = *(graph - ',' - ';');
        twowords = word >> ',' >> word;
        start    = twowords % ';';
    }
  private:
    qi::rule<It, Word()>       word;
    qi::rule<It, TwoWords()>   twowords;
    qi::rule<It, TwoWordses()> start;
};

打印

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

namespace qi = boost::spirit::qi;

using Word = std::string;
struct TwoWords { std::string one, two; };
using TwoWordses = std::vector<TwoWords>;

BOOST_FUSION_ADAPT_STRUCT(TwoWords, one, two);

template <typename It>
struct Demo3 : qi::grammar<It, TwoWordses()> {
    Demo3() : Demo3::base_type(start) {
        using namespace qi;
        word     = *(graph - ',' - ';');
        twowords = word >> ',' >> word;
        start    = twowords % ';';
    }
  private:
    qi::rule<It, Word()>       word;
    qi::rule<It, TwoWords()>   twowords;
    qi::rule<It, TwoWordses()> start;
};

int main() {
    using It = std::string::const_iterator;
    Demo3<It> parser;

    for (std::string const input : {
            ",", 
            "foo,bar",
            "foo,bar;qux,bax",
            "foo,bar;qux,bax;err,;,ful",

            // failing cases or cases with trailing input:
            "",
            "foo,bar;",
            "foo,bar,qux",
            })
    {
        std::cout << "Parsing " << std::quoted(input) << " ->\n";

        TwoWordses tws;
        It f = input.begin(), l = input.end();
        if (parse(f, l, parser, tws)) {
            for(auto& tw : tws) {
                std::cout << " - " << std::quoted(tw.one) << ", " << std::quoted(tw.two) << "\n";
            }
        } else {
            std::cout << "Failed\n";
        }

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

    }
}