Pertanyaan Bagaimana cara membangun tata bahasa nilai default dengan semangat meningkatkan?


Saya menganalisis beberapa teks ilmiah yang formatnya mirip

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

Saya baru mengenal spirit, dan setelah mempelajari dokumen, saya dapat menggunakan semangat untuk menyelesaikan kata kunci berformat tetap.

Tetapi untuk format berikut, saya tidak tahu bagaimana cara membangun tata bahasa. Pertanyaanku adalah: Dalam kata kunci ilmiah yang saya temui, beberapa data tertentu dapat di-default ke nilai default bawaan. Deskripsi kata kunci menunjukkan kapan default dapat diterapkan. Ada dua cara untuk menetapkan jumlah ke nilai defaultnya. Pertama, dengan mengakhiri catatan data sebelum waktunya dengan garis miring '}', jumlah yang tersisa tidak ditentukan ditetapkan ke nilai defaultnya. Kedua, jumlah yang dipilih yang diposisikan sebelum '}' dapat gagal dengan memasukkan n * di mana n adalah jumlah kuantitas berturut-turut yang akan gagal. Misalnya, 3 * menyebabkan tiga kuantitas berikutnya dalam data kata kunci diberikan nilai defaultnya.

Sebagai contoh,

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

katakan 'laki-laki' dan '32' adalah nilai default, dan padanannya dapat berupa:

Person
{ 'Tom' 188 88 2* }

atau

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

atau

Person
{ 'Tom' 188 88 }

Saya telah mencari posting sebelumnya, dan ini memberi saya beberapa ide, tapi bagaimana saya bisa menulis aturan n *?


5
2018-04-29 20:19


asal


Jawaban:


Parser yang Anda minta sangat rumit karena harus menyelesaikan beberapa tugas:

  • menangani elemen yang hilang pada akhirnya
  • menangani sintaks "2 *" sebagai pengganti elemen yang hilang di bagian akhir
  • benar tidak hanya mem-parse semua input yang valid tetapi juga mengisi struktur data yang diberikan dengan nilai yang cocok

Triknya di sini adalah untuk memanfaatkan qi::attr dalam berbagai cara:

  • untuk memberikan nilai default untuk elemen yang hilang:

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

    yaitu mencocokkan bilangan bulat atau menggunakan nilai default 180

  • untuk menyediakan semua nilai yang tersisa untuk sintaks "2 *" (seperti yang disarankan @vines):

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

    yaitu jika 2* dicocokkan menggunakan nilai default attr2 (yang merupakan a fusion::vector).

Secara keseluruhan, saya menemukan solusi ini, yang tampaknya mengurai dan mengembalikan nilai default dengan baik (meskipun terlihat sangat rumit):

#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;
}

Memisahkan aturan menjadi aturan terpisah (seperti @vines menunjukkan) akan membutuhkan input yang akan diuraikan lebih dari satu kali, itulah mengapa saya menggunakan struktur urutan dan alternatif bersarang ini.


4
2018-04-30 00:36



Saya baru saja menemukan solusi umum, meskipun sedikit lebih rumit =)
Ini menangani baik "penjepit prematur" dan beberapa penentu lompatan acak. Ini dia:

#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: Perhatikan bahwa argumen template Skipper tidak ada hubungannya dengan skipping field - ini hanyalah jenis parser whitespace-skipping yang digunakan oleh grammar.


4
2018-04-30 02:55



Pertama saya bisa memikirkan:

Jika struct Anda belum terlalu banyak anggota, Anda bisa mendeskripsikan * n sebagai semacam sintaks, yaitu sesuatu seperti:

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) >> "}";

Tentu saja, ini bukan cara yang paling indah ..


1
2018-04-29 23:13