首页 > 解决方案 > 如何更改此示例中的代码

问题描述

如何更改代码以显示结果?

我无法转换为提升精神 x3

实时代码

#include <string>
#include <vector>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted.hpp>
namespace x3 = boost::spirit::x3;

namespace client {    namespace ast {
    struct op_or {};
    struct op_and {};
    struct op_xor {};
    struct op_not {};
    template <typename tag> struct combination_op;
    template <typename tag>    struct unop;
    typedef std::string var;
    typedef boost::variant<
        var,
        boost::recursive_wrapper<unop<op_not>>,
        boost::recursive_wrapper<combination_op<op_and>>,
        boost::recursive_wrapper<combination_op<op_xor>>,
        boost::recursive_wrapper<combination_op<op_or>>
    >expr;
    template <typename tag> struct combination_op {
        typedef std::vector<expr> operands_t;
        combination_op() = default;
        combination_op(operands_t const& operands) : operands(operands) {}
        operands_t operands;
    };
    template <typename tag> struct unop {
        unop() = default;
        unop(const expr& o) : operand(o) {}
        expr operand;
    };
}}
BOOST_FUSION_ADAPT_STRUCT(client::ast::combination_op<client::ast::op_and>, operands)
BOOST_FUSION_ADAPT_STRUCT(client::ast::combination_op<client::ast::op_xor>, operands)
BOOST_FUSION_ADAPT_STRUCT(client::ast::combination_op<client::ast::op_or>, operands)
BOOST_FUSION_ADAPT_STRUCT(client::ast::unop<client::ast::op_not>, operand)

namespace client {    namespace parser {
    x3::rule<class var,  ast::var> var{ "var" };
    x3::rule<class not,  ast::unop<ast::op_not>> not{ "not" };
    x3::rule<class and,  ast::combination_op<ast::op_and>> and{ "and" };
    x3::rule<class xor,  ast::combination_op<ast::op_xor>> xor{ "xor" };
    x3::rule<class or,   ast::combination_op<ast::op_or >>  or{ "or" };
    x3::rule<class expr, ast::expr> expr { "expr" };

    auto const expr_def = xor | and | or | not | var;
    auto const expr_list = *expr;
    auto const or_def  = x3::no_case["or"]  >> '(' >> expr_list >> ')';
    auto const xor_def = x3::no_case["xor"] >> '(' >> expr_list >> ')';
    auto const and_def = x3::no_case["and"] >> '(' >> expr_list >> ')';
    auto const not_def = x3::no_case["not"] >> expr;
    auto const var_def = x3::lexeme[+x3::alpha];

    BOOST_SPIRIT_DEFINE(var,not,and,xor,or,expr);
}}

namespace client {    namespace ast {
    struct printer :boost::static_visitor<void> {
        printer() {}
        void operator()(const var& v) const{        }
        void operator()(const combination_op<op_and>& b) const { recurse(b); }
        void operator()(const combination_op<op_xor>& b) const { recurse(b); }
        void operator()(const combination_op<op_or>& b) const {  recurse(b); }
        void operator()(const unop<op_not>& u) const { recurse(u.operand); }
        template<typename T>
        void recurse(T const& v) const {
            //boost::apply_visitor(*this, v);
         }
    };
}}

int main() {
    std::string storage = "a or (b and c)";
    client::ast::expr result;
    typedef std::string::const_iterator iterator_t;
    iterator_t iter = storage.begin(), end = storage.end();
    using x3::ascii::space;
    bool ok = phrase_parse(iter, end, client::parser::expr, space, result);
    if (ok && iter == end) {
        boost::apply_visitor(client::ast::printer(), result);
    }
    return 0;
}

标签: c++compiler-errorsboost-spiritboost-spirit-x3

解决方案


void operator()(const combination_op<op_and>& b) const { recurse(b); }
void operator()(const combination_op<op_xor>& b) const { recurse(b); }
void operator()(const combination_op<op_or>& b) const { recurse(b); }
void operator()(const unop<op_not>& u) const { recurse(u.operand); }

What do you suppose these functions do? If you look at the example you linked, you see concrete implementations:

Originally, here:

struct printer : boost::static_visitor<void>
{
    printer(std::ostream& os) : _os(os) {}
    std::ostream& _os;

    //
    void operator()(const var& v) const { _os << v; }

    void operator()(const binop<op_and>& b) const { print(" & ", b.oper1, b.oper2); }
    void operator()(const binop<op_or >& b) const { print(" | ", b.oper1, b.oper2); }
    void operator()(const binop<op_xor>& b) const { print(" ^ ", b.oper1, b.oper2); }

    void print(const std::string& op, const expr& l, const expr& r) const
    {
        _os << "(";
            boost::apply_visitor(*this, l);
            _os << op;
            boost::apply_visitor(*this, r);
        _os << ")";
    }

    void operator()(const unop<op_not>& u) const
    {
        _os << "(";
            _os << "!";
            boost::apply_visitor(*this, u.oper1);
        _os << ")";
    }
};

std::ostream& operator<<(std::ostream& os, const expr& e)
{ boost::apply_visitor(printer(os), e); return os; }

So you'd expect to write something like:

struct printer {
    using result_type = void;
    std::ostream& _os;

    static auto name(op_and /*unused*/) { return "AND"; }
    static auto name(op_not /*unused*/) { return "NOT"; }
    static auto name(op_or /*unused*/) { return "OR"; }
    static auto name(op_xor /*unused*/) { return "XOR"; }

    void operator()(const var& v) const { _os << v; }
    template <typename Op>
    void operator()(const unop<Op>& u) const {
        _os << "(" << name(Op{}) << " ";
        operator()(u.operand);
        _os << ")";
    }
    template <typename Op>
    void operator()(const combination_op<Op>& b) const {
        _os << "(";
        bool first = true;
        for (auto& e : b.operands) {
            if (!std::exchange(first, false)) {
                _os << " " << name(Op{}) << " ";

            }

            operator()(e);
        }
        _os << ")";
    }

    void operator()(expr const& v) const {
        boost::apply_visitor(*this, v);
    }
};

Which prints (Live On Coliru)

"or(a and(b c))" -> (a OR (b AND c))

If you wanted output similar to the thing you parse:

    template <typename Op> void operator()(const unop<Op>& u) const {
        operator()(combination_op<Op>{{u.operand}});
    }
    template <typename Op> void operator()(const combination_op<Op>& b) const {
        _os << name(Op{}) << "(";
        for (auto& e : b.operands) {
            _os << " ";
            operator()(e);
        }
        _os << ")";
    }

Printing (Live On Coliru):

"or(a and(b c))" -> OR( a AND( b c))

As you can see, I that implementation highlights that the "function call" syntax doesn't essentially require difference between unary and binary operators

Full Listing

First variation:

// #define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <string>
#include <iostream>
#include <iomanip>
#include <vector>
namespace x3 = boost::spirit::x3;

namespace client { namespace ast {
    struct op_or {};
    struct op_and {};
    struct op_xor {};
    struct op_not {};
    template <typename tag> struct combination_op;
    template <typename tag> struct unop;
    typedef std::string var;
    typedef boost::variant<var, boost::recursive_wrapper<unop<op_not>>,
                           boost::recursive_wrapper<combination_op<op_and>>,
                           boost::recursive_wrapper<combination_op<op_xor>>,
                           boost::recursive_wrapper<combination_op<op_or>>>
        expr;
    template <typename tag> struct combination_op {
        typedef std::vector<expr> operands_t;
        combination_op() = default;
        combination_op(operands_t const& operands) : operands(operands) {}
        operands_t operands;
    };
    template <typename tag> struct unop {
        unop() = default;
        unop(const expr& o) : operand(o) {}
        expr operand;
    };
} }

BOOST_FUSION_ADAPT_STRUCT(client::ast::combination_op<client::ast::op_and>, operands)
BOOST_FUSION_ADAPT_STRUCT(client::ast::combination_op<client::ast::op_xor>, operands)
BOOST_FUSION_ADAPT_STRUCT(client::ast::combination_op<client::ast::op_or>, operands)
BOOST_FUSION_ADAPT_STRUCT(client::ast::unop<client::ast::op_not>, operand)

namespace client { namespace parser {
    x3::rule<class var, ast::var> var{ "var" };
    x3::rule<class not_, ast::unop<ast::op_not>> not_ { "not" };
    x3::rule<class and_, ast::combination_op<ast::op_and>> and_ { "and" };
    x3::rule<class xor_, ast::combination_op<ast::op_xor>> xor_ { "xor" };
    x3::rule<class or_, ast::combination_op<ast::op_or>> or_ { "or" };
    x3::rule<class expr, ast::expr> expr{ "expr" };

    auto const expr_def = xor_ | and_ | or_ | not_ | var;
    auto const expr_list = *expr;
    auto const or__def = x3::no_case["or"] >> '(' >> expr_list >> ')';
    auto const xor__def = x3::no_case["xor"] >> '(' >> expr_list >> ')';
    auto const and__def = x3::no_case["and"] >> '(' >> expr_list >> ')';
    auto const not__def = x3::no_case["not"] >> expr;
    auto const var_def = x3::lexeme[+x3::alpha];

    BOOST_SPIRIT_DEFINE(var, not_, and_, xor_, or_, expr)
} }

namespace client { namespace ast {
    struct printer {
        using result_type = void;
        std::ostream& _os;

        static auto name(op_and /*unused*/) { return "AND"; }
        static auto name(op_not /*unused*/) { return "NOT"; }
        static auto name(op_or /*unused*/) { return "OR"; }
        static auto name(op_xor /*unused*/) { return "XOR"; }

        void operator()(const var& v) const { _os << v; }
        template <typename Op>
        void operator()(const unop<Op>& u) const {
            _os << "(" << name(Op{}) << " ";
            operator()(u.operand);
            _os << ")";
        }
        template <typename Op>
        void operator()(const combination_op<Op>& b) const {
            _os << "(";
            bool first = true;
            for (auto& e : b.operands) {
                if (!std::exchange(first, false)) {
                    _os << " " << name(Op{}) << " ";

                }

                operator()(e);
            }
            _os << ")";
        }

        void operator()(expr const& v) const {
            boost::apply_visitor(*this, v);
        }
    };
} }

int main() {
    std::string storage = "or(a and(b c))";
    client::ast::expr result;
    typedef std::string::const_iterator iterator_t;
    iterator_t iter = storage.begin(), end = storage.end();
    using x3::ascii::space;
    bool ok = phrase_parse(iter, end, client::parser::expr, space, result);
    if (ok && iter == end) {
        client::ast::printer print{std::cout};
        std::cout << std::quoted(storage) << " -> ";
        print(result);
        std::cout << "\n";
    } else {
        std::cout << "Failed\n";
    }
    if (iter != end) {
        std::cout << "Remaining: " << std::quoted(std::string(iter, end)) << "\n";
    }
}

推荐阅读