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

时间:2013-03-04 22:50:05

标签: c++ boost boost-spirit

我想使用boost spirit来解析一个可以有多种类型的值;例如类似的东西:

singleValueToBeParsed %= (double_ | int_ | bool_ | genericString);

genericString   %= +(char_("a-zA-Z"));

解析为int或double的情况看起来相当简单:

Parse int or double using boost spirit (longest_d)

..但我不确定如何扩展它以包含其他类型,包括通用字符串和bools ..

有什么想法吗?

谢谢,

本。

编辑:基于答案,我更新了我的语法如下:

genericString   %= +(char_("a-zA-Z"));
intRule         %= int_;

doubleRule      %= (&int_ >> (double_ >> 'f'))
                | (!int_ >> double_ >> -lit('f'));

boolRule        %= bool_;

其中每个qi规则都有一个字符串,int,double或bool迭代器

然后我有一条规则

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

期望采用语法add(5,6.1,result);或添加(a,b,结果);但到目前为止,只有解析前两个参数是整数。

请注意,添加规则指定为:

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

功能指定为:

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提供。欢呼声。

1 个答案:

答案 0 :(得分:4)

这是一项有趣的练习。

当然,一切都取决于您无法指定的输入语法。

但是,为了演示而假设一个文字语法(非常)松散地基于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

注意只有一个测试输入(“无效”)应该失败。其余的应解析为文字,可选择留下未解析的剩余输入。

带有测试的完整演示

#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 << "success\n";
            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 << "------\n\n";
        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
            "    0\n   ",       // whitespace is fine
            "42\n.0",           // but not considered as part of a literal
            })
    {
        testcase(s);
    }
}