3

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

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

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

解析为 int 或 double 的情况似乎相当简单:

使用 boost 精神 (longest_d) 解析 int 或 double

..但我不确定如何扩展它以合并其他类型,包括通用字符串和布尔值..

有任何想法吗?

谢谢,

本。

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

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

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

boolRule        %= bool_;

其中每个 qi 规则都有一个字符串、整数、双精度或布尔迭代器

然后我有一个规则

        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 提供。干杯。

4

1 回答 1

4

这是一个有趣的练习。

当然,一切都取决于输入语法,您很方便地没有指定。

但是,为了演示,我们假设一个文字语法(非常)松散地基于 C++ 文字,我们可以提出以下内容来解析十进制(有符号)整数值、浮点值、布尔文字和简单的字符串文字:

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);
    }
}
于 2013-03-05T00:00:56.830 回答