Ambiguous variant and boost spirit x3

前端 未结 1 2065
北海茫月
北海茫月 2021-02-14 16:07

Trying to tweak the boost spirit x3 calc example to parse functions that can take functions as arguments. However it does not compile.

namespace client{ namespa         


        
相关标签:
1条回答
  • 2021-02-14 16:50

    I spotted this old question. X3 has evolved a bit in the mean time and I though I'd answer it now anyways.

    I suspected that the main issue might have been with (missing) (implicit) constructors on the variant members.

    Anyhow, here's a live demo with a more lightweight grammar:

    namespace grammar_def {
        using namespace x3;
    
        rule<class funct, ast::fnc> const funct("function");
    
        auto const ts        = lexeme [ '#' >> uint_ >> '#' ];
        auto const fname     = lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
        auto const expr      = ts|funct;
    
        auto const funct_def = fname >> '(' >> -expr % ',' >> ')';
    
        BOOST_SPIRIT_DEFINE(funct)
    }
    

    I also added some output streaming helpers. Note how I changed the id type to std::string for simplicity (it's hard/impossible to overload operator<< for vector<char> without invading namespace std):

    namespace client { namespace ast {
        static std::ostream& operator<<(std::ostream& os, ts const& v) {
            using namespace boost::fusion;
            return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
        }
    
        static std::ostream& operator<<(std::ostream& os, fnc const& v) {
            using namespace boost::fusion;
            return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
        }
        template<typename T>
        static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
            os << "("; for (auto& el : v) os << (&el==&v[0]?"":", ") << el; return os << ")";
        }
    } }
    

    Demo

    This has more (optional) plumbing to allow for richer debug information:

    Live On Coliru

    //#define BOOST_SPIRIT_X3_DEBUG
    #include <iostream>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/include/as_vector.hpp>
    #include <boost/fusion/include/io.hpp>
    #include <boost/spirit/home/x3.hpp>
    
    namespace client { namespace ast {
        struct ts;
        struct fnc;
    
        //using string = std::vector<char>;
        using string = std::string; // for easier printing/debugging
    
        struct ts {
            unsigned int id;
            ts(unsigned id=0):id(id) {}
        };
    
        typedef boost::variant<ts, boost::recursive_wrapper<fnc> > node;
    
        struct fnc {
            string id;
            std::vector<node> args;
        };
    } }
    
    BOOST_FUSION_ADAPT_STRUCT(client::ast::ts, id)
    BOOST_FUSION_ADAPT_STRUCT(client::ast::fnc, id, args)
    
    //namespace std { static ostream& operator<<(ostream&os, vector<char> const& v) { return os.write(&v[0], v.size()); } }
    
    namespace client { namespace ast {
        static std::ostream& operator<<(std::ostream& os, ts const& v) {
            using namespace boost::fusion;
            return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
        }
    
        static std::ostream& operator<<(std::ostream& os, fnc const& v) {
            using namespace boost::fusion;
            return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
        }
        template<typename T>
        static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
            os << "("; for (auto& el : v) os << (&el==&v[0]?"":", ") << el; return os << ")";
        }
    } }
    
    namespace client {
        namespace x3 = boost::spirit::x3;
        namespace grammar_def {
            using namespace x3;
    
            x3::rule<class funct, ast::fnc> const funct("function");
    
            auto const ts     // = x3::rule<class ts, ast::ts> {"timeseries"}
                                 = lexeme [ '#' >> uint_ >> '#' ];
            auto const fname  // = x3::rule<class fname, ast::string> {"function_name"}
                                 = lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
            auto const expr   // = rule<struct expr_, ast::node > {"expr"}
                                 = ts|funct;
    
            auto const funct_def = fname >> '(' >> -expr % ',' >> ')';
    
            BOOST_SPIRIT_DEFINE(funct)
        }
    
        auto const& grammar = x3::skip(x3::space) [grammar_def::funct];
    }
    
    #include <iostream>
    
    int main() {
        std::string const s {
            "@pow( #1#, \n"
                "     @trunc(\n"
                "           @pi ()\n"
                "   ) )"};
        std::cout << "Parsing '" << s << "'\n";
    
        auto f = s.begin();
        client::ast::fnc parsed;
    
        if (parse(f, s.end(), client::grammar, parsed)) {
            std::cout << "Parse succeeded: " << parsed << "\n";
        } else {
            std::cout << "Parse failed\n";
        }
    
        if (f != s.end())
            std::cout << "Remaining unparsed input: '" << std::string(f, s.end()) << "'\n";
    }
    

    Prints:

    Parsing '@pow( #1#, 
         @trunc(
               @pi ()
       ) )'
    Parse succeeded: pow(1, trunc(pi()))
    
    0 讨论(0)
提交回复
热议问题