Attributes from Boost.Spirit grammar: error from std:vector of boost::variant

前端 未结 1 1792
别那么骄傲
别那么骄傲 2020-12-21 04:40

I got a working parser for reading position descriptions for a board game (international draughts, official grammar):

#include 

        
相关标签:
1条回答
  • 2020-12-21 05:10

    I've spent some time trying to understand the grammar.

    I strongly suggest readable identifiers. It's very hard to understand what's going on, while I have the strong impression it's actually a really simple grammar

    I suggest a simplification version shown below.

    • Because your grammar doesn't use recursion there's no real need for the rule and tagged parser types.
    • Also use a namespace for the parser artefacts.
    • Consider encapsulation the use of a skipper instead of letting the caller decide (x3::skip[])
    • Add a few helpers to be able to print the AST for verification:

      template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
          os << "{"; for (auto& el : v) os << el << " "; return os << "}";
      }
      std::ostream& operator<<(std::ostream& os, num_pc const& p)  { if (p.k) os << p.k; return os << p.sq; }
      std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
      std::ostream& operator<<(std::ostream& os, ccn const& o)     { return os << o.c << " " << o.seq;                      } 
      std::ostream& operator<<(std::ostream& os, num_not const& nn) { return os << nn.n; }
      
    • I'd avoid wrapping the other vector unnecessarily too:

      using num_not = std::vector<ccn>;
      
    • Use the modern ADAPT macros (as you're using C++14 by definition):

      BOOST_FUSION_ADAPT_STRUCT(ast::num_pc,  k, sq)
      BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
      BOOST_FUSION_ADAPT_STRUCT(ast::ccn,     c, seq)
      BOOST_FUSION_ADAPT_STRUCT(ast::fen,     c, n)
      
    • -

    Live Demo

    Live On Coliru

    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/include/as_vector.hpp>
    #include <boost/fusion/include/io.hpp>
    #include <boost/optional/optional_io.hpp>
    #include <boost/optional.hpp>
    #include <boost/spirit/home/x3.hpp>
    #include <boost/variant.hpp>
    #include <iostream>
    #include <vector>
    
    namespace ast {
    
        struct num_pc {
            boost::optional<char> k;
            int sq;
        };
    
        struct num_rng {
            num_pc pc;
            int last;
        };
    
        using rng_or_pc = boost::variant<num_rng, num_pc>;
        using num_seq = std::vector<rng_or_pc>;
    
        struct ccn {
            char c;
            boost::optional<num_seq> seq;
        };
    
        using num_not = std::vector<ccn>;
    
        struct fen {
            char c;
            num_not n;
        };
    
        template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
            os << "{"; for (auto& el : v) os << el << " "; return os << "}";
        }
        std::ostream& operator<<(std::ostream& os, num_pc const& p)  { if (p.k) os << p.k; return os << p.sq; }
        std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
        std::ostream& operator<<(std::ostream& os, ccn const& o)     { return os << o.c << " " << o.seq;                      } 
    }
    
    BOOST_FUSION_ADAPT_STRUCT(ast::num_pc,  k, sq)
    BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
    BOOST_FUSION_ADAPT_STRUCT(ast::ccn,     c, seq)
    BOOST_FUSION_ADAPT_STRUCT(ast::fen,     c, n)
    
    namespace FEN {
        namespace x3 = boost::spirit::x3;
    
        namespace grammar
        {
            using namespace x3;
    
            template<typename T>
                auto as = [](auto p) { return rule<struct _, T>{} = as_parser(p); };
    
            uint_type const number {};
            auto const color   = char_("BW");
            auto const num_pc  = as<ast::num_pc>  ( -char_('K') >> number          ); 
            auto const num_rng = as<ast::num_rng> ( num_pc >> '-' >> number        ); 
            auto const num_seq = as<ast::num_seq> ( (num_rng | num_pc) % ','       ); 
            auto const ccn     = as<ast::ccn>     ( ':' >> color >> -num_seq       ); 
            auto const num_not = as<ast::num_not> ( repeat(2)[ccn]                 ); 
            auto const fen     = as<ast::fen>     ( color >> num_not >> -lit('.')  ); 
        }
    
        using grammar::fen;
    }
    
    int main() {
        for (std::string const t : {
            "B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29",
            "B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12",
            "W:B1-20:W31-50",   // initial position
            "W:B:W",            // empty board
            "W:B1:W",           // only black pieces
            "W:B:W50"           // only white pieces
        }) {
            auto b = t.begin(), e = t.end();
            ast::fen data;
            bool ok = phrase_parse(b, e, FEN::fen, FEN::x3::space, data);
    
            std::cout << t << "\n";
            if (ok) {
                std::cout << "Parsed: " << boost::fusion::as_vector(data) << "\n";
            } else {
                std::cout << "Parse failed:\n";
                std::cout << "\t on input: " << t << "\n";
            }
            if (b != e)
                std::cout << "\t Remaining unparsed: '" << std::string(b, e) << '\n';
        }
    }
    

    Prints:

    B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29
    Parsed: (B {W  {18 24 27 28  K10  K15 } B  {12 16 20  K22  K25  K29 } })
    B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12
    Parsed: (B {W  {18 19 21 23 24 26 29 30 31 32 } B  {1 2 3 4 6 7 9 10 11 12 } })
    W:B1-20:W31-50
    Parsed: (W {B  {1-20 } W  {31-50 } })
    W:B:W
    Parsed: (W {B -- W -- })
    W:B1:W
    Parsed: (W {B  {1 } W -- })
    W:B:W50
    Parsed: (W {B -- W  {50 } })
    
    0 讨论(0)
提交回复
热议问题