使用boost::spirit解析单个值的多种类型

Using boost::spirit to parse multiple types of single value

本文关键字:种类 单个值 类型 boost spirit 使用      更新时间:2023-10-16

我想使用boost spirit来解析可以有多个类型的单个值;例如:

singleValueToBeParsed %= (double_ | int_ | bool_ | genericString);
genericString   %= +(char_("a-zA-Z"));

解析为整型或双精度类型的情况似乎相当简单:

使用boost spirit (longest_d)解析int或double

. .但我不确定如何扩展这一点,以纳入其他类型,包括通用字符串和bool .

任何想法?

谢谢,

本。编辑:所以根据答案,我更新了我的语法如下:

genericString   %= +(char_("a-zA-Z"));
intRule         %= int_;
doubleRule      %= (&int_ >> (double_ >> 'f'))
                | (!int_ >> double_ >> -lit('f'));
boolRule        %= bool_;

,其中每个qi规则有一个string、int、double或bool迭代器

然后我有一个规则

        add    %= string("add") >> '('
               >> (intRule | doubleRule | genericString) >> ','
               >> (intRule | doubleRule | genericString) >> ','
               >> genericString
               >> ')' >> ';';

期望使用语法add(5, 6.1, result);或者add(a, b, result);但到目前为止,它只解析前两个参数是整数的情况。

注意添加规则指定为:

qi::rule<Iterator, Function(), ascii::space_type> add;

和Function指定为:

typedef boost::any DirectValue;
struct Function
{
    //
    // name of the Function; will always be a string
    //
    std::string name;
    //
    // the function parameters which can be initialized to any type
    //
    DirectValue paramA;
    DirectValue paramB;
    DirectValue paramC;
    DirectValue paramD;
    DirectValue paramE;
};
BOOST_FUSION_ADAPT_STRUCT(
    Function,
    (std::string, name)
    (DirectValue, paramA)
    (DirectValue, paramB)
    (DirectValue, paramC)
    (DirectValue, paramD)
    (DirectValue, paramE)
)
编辑2:

现在解析正确了。参见http://liveworkspace.org/code/3asg0X%247,由llonesmiz提供。欢呼。

这是一个有趣的练习。

当然,一切都取决于输入语法,而您很容易无法指定输入语法。

然而,为了演示,让我们假设字面量语法(非常)松散地基于c++字面量,我们可以提出以下方法来解析十进制(带符号)整数值、浮点值、bool字面量和简单的字符串字面量:

typedef boost::variant<
    double, unsigned int, 
    long, unsigned long, int, 
    bool, std::string> attr_t;
// ...
start = 
    (
        // number formats with mandatory suffixes first
        ulong_rule | uint_rule | long_rule | 
        // then those (optionally) without suffix
        double_rule | int_rule | 
        // and the simple, unambiguous cases
        bool_rule | string_rule
    );
double_rule = 
         (&int_ >> (double_ >> 'f'))     // if it could be an int, the suffix is required
       | (!int_ >> double_ >> -lit('f')) // otherwise, optional
       ;   
int_rule    = int_;
uint_rule   = uint_ >> 'u' ;
long_rule   = long_ >> 'l' ;
ulong_rule  = ulong_ >> "ul" ;
bool_rule   = bool_;
string_rule = '"' >> *~char_('"') >> '"';

查看测试用例输出的实时演示链接:http://liveworkspace.org/code/goPNP

注意只有一个测试输入("invalid")应该失败。其余部分应解析为文本,可选地留下未解析的剩余输入。

带有测试的完整演示

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;
typedef boost::variant<double, unsigned int, long, unsigned long, int, bool, std::string> attr_t;
template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, attr_t(), Skipper>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;
        start = 
            (
                // number formats with mandatory suffixes first
                ulong_rule | uint_rule | long_rule | 
                // then those (optionally) without suffix
                double_rule | int_rule | 
                // and the simple, unambiguous cases
                bool_rule | string_rule
            );
        double_rule = 
                 (&int_ >> (double_ >> 'f'))     // if it could be an int, the suffix is required
               | (!int_ >> double_ >> -lit('f')) // otherwise, optional
               ;   
        int_rule    = int_;
        uint_rule   = uint_ >> 'u' ;
        long_rule   = long_ >> 'l' ;
        ulong_rule  = ulong_ >> "ul" ;
        bool_rule   = bool_;
        string_rule = '"' >> *~char_('"') >> '"';
        BOOST_SPIRIT_DEBUG_NODE(start);
        BOOST_SPIRIT_DEBUG_NODE(double_rule);
        BOOST_SPIRIT_DEBUG_NODE(ulong_rule);
        BOOST_SPIRIT_DEBUG_NODE(long_rule);
        BOOST_SPIRIT_DEBUG_NODE(uint_rule);
        BOOST_SPIRIT_DEBUG_NODE(int_rule);
        BOOST_SPIRIT_DEBUG_NODE(bool_rule);
        BOOST_SPIRIT_DEBUG_NODE(string_rule);
    }
  private:
    qi::rule<It, attr_t(), Skipper> start;
    // no skippers in here (important):
    qi::rule<It, double()>        double_rule;
    qi::rule<It, int()>           int_rule;
    qi::rule<It, unsigned int()>  uint_rule;
    qi::rule<It, long()>          long_rule;
    qi::rule<It, unsigned long()> ulong_rule;
    qi::rule<It, bool()>          bool_rule;
    qi::rule<It, std::string()>   string_rule;
};
struct effective_type : boost::static_visitor<std::string> {
    template <typename T>
        std::string operator()(T const& v) const {
            return typeid(v).name();
        }
};
bool testcase(const std::string& input)
{
    typedef std::string::const_iterator It;
    auto f(begin(input)), l(end(input));
    parser<It, qi::space_type> p;
    attr_t data;
    try
    {
        std::cout << "parsing '" << input << "': ";
        bool ok = qi::phrase_parse(f,l,p,qi::space,data);
        if (ok)   
        {
            std::cout << "successn";
            std::cout << "parsed data: " << karma::format_delimited(karma::auto_, ' ', data) << "n";
            std::cout << "effective typeid: " << boost::apply_visitor(effective_type(), data) << "n";
        }
        else      std::cout << "failed at '" << std::string(f,l) << "'n";
        if (f!=l) std::cout << "trailing unparsed: '" << std::string(f,l) << "'n";
        std::cout << "------nn";
        return ok;
    } catch(const qi::expectation_failure<It>& e)
    {
        std::string frag(e.first, e.last);
        std::cout << e.what() << "'" << frag << "'n";
    }
    return false;
}
int main()
{
    for (auto const& s : std::vector<std::string> {
            "1.3f",
            "0.f",
            "0.",
            "0f",
            "0", // int will be preferred
            "1u",
            "1ul",
            "1l",
            "1",
            "false",
            "true",
            ""hello world"",
            // interesting cases
            "invalid",
            "4.5e+7f",
            "-inf",
            "-nan",
            "42 is the answer", // 'is the answer' is simply left unparsed, it's up to the surrounding grammar/caller
            "    0n   ",       // whitespace is fine
            "42n.0",           // but not considered as part of a literal
            })
    {
        testcase(s);
    }
}