簡體   English   中英

如何用 boost 精神構建默認值的語法?

[英]How to build grammar of default value with boost spirit?

我正在分析一些格式類似於的科學文本

Keyword
{ 1.0  22.2  59.6 'cm' 'yes' }

我是spirit新手,研究文檔后,我可以用spirit解決固定格式的關鍵字。

但是對於以下格式,我不知道如何構建語法。 我的問題是:在我遇到的科學關鍵字中,某些數據項可以默認為內置默認值。 關鍵字描述指示何時可以應用默認值。 有兩種方法可以將數量設置為其默認值。 首先,通過用斜杠“}”提前結束數據記錄,將剩余未指定的數量設置為其默認值。 其次,可以通過輸入 n* 來默認位於“}”之前的選定數量,其中 n 是要默認的連續數量的數量。 例如,3* 導致關鍵字數據中接下來的三個數量被賦予它們的默認值。

例如,

Person
{ 'Tom' 188 80 'male' 32 }

說 'male' 和 '32' 是默認值,它的等價物可以是:

Person
{ 'Tom' 188 88 2* }

或者

Person
{ 'Tom' 188 88 'male' 1* }

或者

Person
{ 'Tom' 188 88 }

我搜索了過去的帖子, 給了我一些想法,但是我怎樣才能寫出 n* 的規則呢?

您要求的解析器非常復雜,因為它必須解決幾個任務:

  • 最后處理缺失的元素
  • 處理“2*”語法作為最后缺失元素的替代品
  • 正確地不僅解析所有有效輸入,而且用匹配的值填充給定的數據結構

這里的訣竅是以不同的方式利用qi::attr

  • 為缺失元素提供默認值:

     qi::int_ | qi::attr(180)

    即匹配 integer 或使用默認值180

  • 為“2*”語法提供所有剩余值(如@vines 建議的那樣):

     "2*" >> qi::attr(attr2)

    即如果2*匹配使用默認值 attr2 (這是一個fusion::vector )。

總的來說,我想出了這個解決方案,它似乎可以很好地解析並返回默認值(即使它看起來很復雜):

#include <string>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/vector.hpp>

int main()
{
    namespace qi = boost::spirit::qi;
    namespace fusion = boost::fusion;

    // the attribute passed to the parser has to match (in structure) the 
    // parser, requiring to create nested fusion::vector's
    typedef fusion::vector<std::string, int>              attribute1_type;
    typedef fusion::vector<int, attribute1_type>          attribute2_type;
    typedef fusion::vector<int, attribute2_type>          attribute3_type;

    // overall attribute type
    typedef fusion::vector<std::string, attribute3_type>  attribute_type;

    // initialize attributes with default values
    attribute1_type attr1("male", 32);
    attribute2_type attr2(80, attr1);
    attribute3_type attr3(180, attr2);

    qi::rule<std::string::iterator, std::string()> quoted_string =
        "'" >> *~qi::char_("'") >> "'";

    qi::rule<std::string::iterator, attribute_type(), qi::space_type> data =
        qi::lit("Person") >> "{" 
            >>  quoted_string 
            >> -(   ("4*" >> qi::attr(attr3))
                |   (qi::int_ | qi::attr(180))
                    >> -(   ("3*" >> qi::attr(attr2))
                        |   (qi::int_ | qi::attr(80))
                            >> -(   ("2*" >> qi::attr(attr1))
                                |   (quoted_string | qi::attr("male"))
                                    >> -(   "1*"  
                                        |   qi::int_ 
                                        |   qi::attr(32)
                                        )
                                )
                        )
                )
        >> "}";

    std::string in1 = "Person\n{ 'Tom' 188 80 'male' 32 }";
    attribute_type fullattr1;
    if (qi::phrase_parse(in1.begin(), in1.end(), data, qi::space, fullattr1))
        std::cout << fullattr1 << std::endl;

    std::string in2 = "Person\n{ 'Tom' 188 80 'male' }";
    attribute_type fullattr2;
    if (qi::phrase_parse(in2.begin(), in2.end(), data, qi::space, fullattr2))
        std::cout << fullattr2 << std::endl;

    std::string in3 = "Person\n{ 'Tom' 188 3* }";
    attribute_type fullattr3;
    if (qi::phrase_parse(in3.begin(), in3.end(), data, qi::space, fullattr3))
        std::cout << fullattr3 << std::endl;

    return 0;
}

將規則拆分為單獨的規則(如@vines 建議的那樣)將需要多次解析輸入,這就是我使用這種序列和替代方案的嵌套結構的原因。

我剛剛提出了通用解決方案,雖然它有點復雜=)
它同時處理“過早大括號”和多個任意跳過說明符。 這是它:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <iostream>
#include <string>


namespace qi = boost::spirit::qi;
namespace ph = boost::phoenix;

struct numbers { int i1, i2, i3, i4; };

BOOST_FUSION_ADAPT_STRUCT
(numbers,
    (int, i1)
    (int, i2)
    (int, i3)
    (int, i4)
)

template <typename Iterator, typename Skipper>
struct Grammar : public qi::grammar <Iterator, numbers(), Skipper>
{
    Grammar() : Grammar::base_type(start, "numbers")
    {
    using qi::int_;

    // This rule resets the skip counter:
    init_skip = qi::eps[ph::ref(skp) = 0];

    // This rule parses the skip directive ("n*") and sets the skip counter:
    skip_spec = qi::omit[ (qi::lexeme[ int_ >> "*" ])[ph::ref(skp) = qi::_1] ];

    // This rule checks if we should skip the field, and if so, decrements
    // the skip counter and returns the value given to it (the default one).
    // If not, it tries to parse the int.
    // If int fails to parse, the rule resorts the default value again,
    // thus handling the "premature brace" case.
    int_dflt %= qi::eps(ph::ref(skp) > 0)[--ph::ref(skp)] >> qi::attr(qi::_r1) | int_ | qi::attr(qi::_r1);

    // And this is the grammar:
    start %= init_skip >>
             "{" >> -skip_spec >> int_dflt(-1)
                 >> -skip_spec >> int_dflt(-1)
                 >> -skip_spec >> int_dflt(-1)
                 >> -skip_spec >> int_dflt(-1)
                 >> "}";
    }

    // the skip counter itself:
    int skp;

    qi::rule <Iterator, numbers(), Skipper> start;
    qi::rule <Iterator, Skipper> skip_spec, init_skip;
    qi::rule <Iterator, int(int), Skipper> int_dflt;
};




int main (int argc, char **argv)
{
    using std::cout;
    using std::endl;

    std::string s = argv[1];

    numbers result;

    std::string::iterator ib = s.begin();
    std::string::iterator ie = s.end();
    bool r = qi::phrase_parse(ib, ie, Grammar<std::string::iterator, qi::space_type>(), qi::space, result );

    if (r && ib == ie)
    {
        cout << boost::fusion::tuple_open('[');
        cout << boost::fusion::tuple_close(']');
        cout << boost::fusion::tuple_delimiter(", ");

        cout << "Parsing succeeded\n";
        cout << "got: " << boost::fusion::as_vector(result) << endl;
    }
    else
    {
        cout << "Parsing failed\n";
        cout << "err: " << std::string(ib, ie) << endl;
    }

    return 0;
}

PS:請注意,Skipper 模板參數與字段跳過無關 - 它只是語法使用的空格跳過解析器的類型。

首先我能想到:

如果您的結構沒有太多成員,您可以將 *n 描述為某種語法,例如:

struct_full = "{" >> a >> b >> c >> "}";
struct_reduced_1 = "{" >> a >> b >> "1*" >> attr(c_default) >> "}"
struct_reduced_2 = "{" >> a >> "2*" >> attr(b_default) >> attr(c_default) >> "}";
struct_reduced_3 = "{" >> "3*" >> attr(a_default) >> attr(b_default) >> attr(c_default) >> "}";

當然,這不是最美麗的方式..

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM