简体   繁体   English

在Boost Spirit布尔表达式解析器中支持隐式AND操作

[英]Support implied AND operation in boost spirit boolean expression parser

I have a parser, which parses boolean expressions. 我有一个解析器,用于解析布尔表达式。 How I should modify it to support "implied-And" eg "A1 A2" which must be parsed as A1 and A2? 我应该如何修改它以支持“隐式和”,例如必须解析为A1和A2的“ A1 A2”? I've tried to change the "and_" rule to support it, but it started to treat "xor" as a variable even if it was used as an operation. 我试图更改“ and_”规则以支持它,但是即使将“ xor”用作变量,它也开始将其视为变量。 Excluding of "xor" from the rule of var with (!(" xor ") >>) still fails for simple expressions. 对于简单表达式,用(!(“ xor”)>>)从var规则中排除“ xor”仍然失败。

The source code : 源代码

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/variant/recursive_wrapper.hpp>

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

struct op_or  {};
struct op_and {};
struct op_xor {};
struct op_not {};

typedef std::string var;
template <typename tag> struct binop;
template <typename tag> struct unop;

typedef boost::variant<var,
    boost::recursive_wrapper<unop <op_not> >,
    boost::recursive_wrapper<binop<op_and> >,
    boost::recursive_wrapper<binop<op_xor> >,
    boost::recursive_wrapper<binop<op_or> >
    > expr;

template <typename tag> struct binop
{
    explicit binop(const expr& l, const expr& r) : oper1(l), oper2(r) { }
    expr oper1, oper2;
};

template <typename tag> struct unop
{
    explicit unop(const expr& o) : oper1(o) { }
    expr oper1;
};

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

template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, expr(), Skipper>
{
parser() : parser::base_type(expr_)
{
    using namespace qi;

    expr_  = or_.alias();

    or_  = xor_ [_val = _1] >> *(("or"  >> xor_ ) [ _val = phx::construct<binop<op_or >>(_val, _1) ]) | xor_ [ _val = _1 ];
    xor_ = and_ [_val = _1] >> *(("xor" >> and_) [ _val = phx::construct<binop<op_xor>>(_val, _1) ]) | and_   [ _val = _1 ];
    and_ = not_ [_val = _1] >> *(("and" >> not_) [ _val = phx::construct<binop<op_and>>(_val, _1) ]) | not_ [ _val = _1 ];
    not_ = ("not" > simple       ) [ _val = phx::construct<unop <op_not>>(_1)     ] | simple [ _val = _1 ];

    simple = (('(' > expr_ > ')') | var_);
    var_ = qi::lexeme[ +alpha ];

    BOOST_SPIRIT_DEBUG_NODE(expr_);
    BOOST_SPIRIT_DEBUG_NODE(or_);
    BOOST_SPIRIT_DEBUG_NODE(xor_);
    BOOST_SPIRIT_DEBUG_NODE(and_);
    BOOST_SPIRIT_DEBUG_NODE(not_);
    BOOST_SPIRIT_DEBUG_NODE(simple);
    BOOST_SPIRIT_DEBUG_NODE(var_);
}

private:
qi::rule<It, var() , Skipper> var_;
qi::rule<It, expr(), Skipper> not_, and_, xor_, or_, simple, expr_;
};

int main()
{
for (auto& input : std::list<std::string> {
        "(a and b) xor ((c and d) or (a and b));",
        "a and b xor c and d or a and b;",

        /// Simpler tests:
        "a and b;",
        "a or b;",
        "xor_in1 xor xor_in2;",
        "xorin1 xor xorin2;",
        "and1 xor xor;",
        "not a;",
        "not a and b;",
        "not (a and b);",
        "a or b or c;",
        })
{
    auto f(std::begin(input)), l(std::end(input));
    parser<decltype(f)> p;

    try
    {
        expr result;
        bool ok = qi::phrase_parse(f,l,p > ';',qi::space,result);

        if (!ok)
            std::cerr << "invalid input\n";
        else
            std::cout << "result: " << result << "\n";

    } catch (const qi::expectation_failure<decltype(f)>& e)
    {
        std::cerr << "expectation_failure at '" << std::string(e.first, e.last) << "'\n";
    }

    if (f!=l) std::cerr << "unparsed: '" << std::string(f,l) << "'\n";
    }
    return 0;
}

First of I restored the parser rules with the original ones from Boolean expression (grammar) parser in c++ that you obviously started with: 首先,我从C ++中的布尔表达式(语法)解析器中恢复了原始的解析器规则:

or_  = (xor_ >> "or"  >> or_ ) [ _val = phx::construct<binop<op_or >>(_1, _2) ] | xor_   [ _val = _1 ];
xor_ = (and_ >> "xor" >> xor_) [ _val = phx::construct<binop<op_xor>>(_1, _2) ] | and_   [ _val = _1 ];
and_ = (not_ >> "and" >> and_) [ _val = phx::construct<binop<op_and>>(_1, _2) ] | not_   [ _val = _1 ];
not_ = ("not" > simple       ) [ _val = phx::construct<unop <op_not>>(_1)     ] | simple [ _val = _1 ];

Now the failing cases like "xorin1 xor xorin2;" 现在失败的案例是"xorin1 xor xorin2;" don't have anything todo with the things you ask about (implicit and ). 与您询问的内容(隐式 )没有任何关系。 In fact you just found out that you need to do something to correctly parse keywords distinctly: 实际上,您刚刚发现需要做一些事情来正确地正确解析关键字:

Here's the fix using the distinct directive from the Spirit Repository : 这是使用Spirit存储库中distinct指令的修复程序:

or_  = (xor_ >> qr::distinct(alnum|'_')[ "or" ]  >> or_ ) [ _val = phx::construct<binop<op_or >>(_1, _2) ] | xor_   [ _val = _1 ];
xor_ = (and_ >> qr::distinct(alnum|'_')[ "xor" ] >> xor_) [ _val = phx::construct<binop<op_xor>>(_1, _2) ] | and_   [ _val = _1 ];
and_ = (not_ >> qr::distinct(alnum|'_')[ "and" ] >> and_) [ _val = phx::construct<binop<op_and>>(_1, _2) ] | not_   [ _val = _1 ];
not_ = (qr::distinct(alnum|'_')[ "not" ] > simple       ) [ _val = phx::construct<unop <op_not>>(_1)     ] | simple [ _val = _1 ];

Next up, and also unrelated, you apparently wish to treat "xor_in1" etc. as valid identifiers. 接下来 ,也是不相关的,您显然希望将“ xor_in1”等视为有效标识符。 Change the rule accordingly: 相应地更改规则:

var_ = qi::lexeme[ alpha >> *(alnum | char_("_")) ];

Now all cases pass: 现在所有情况都通过了:

Live On Coliru 生活在Coliru

result: ((a & b) ^ ((c & d) | (a & b)))
result: (((a & b) ^ (c & d)) | (a & b))
result: (a & b)
result: (a | b)
result: (xor_in1 ^ xor_in2)
result: (xorin1 ^ xorin2)
result: (and1 ^ xor)
result: (!a)
result: ((!a) & b)
result: (!(a & b))
result: (a | (b | c))

BONUS 奖金

You mentioned something about implicit and? 您提到了有关隐式的东西吗?

and_ = (not_ >> -qr::distinct(alnum|'_')[ "and" ] >> and_) [ _val = phx::construct<binop<op_and>>(_1, _2) ] | not_   [ _val = _1 ];

Some nice test cases: 一些不错的测试用例:

Live On Coliru 生活在Coliru

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/repository/include/qi_distinct.hpp>
#include <boost/variant/recursive_wrapper.hpp>

namespace qi    = boost::spirit::qi;
namespace qr    = boost::spirit::repository::qi;
namespace phx   = boost::phoenix;

struct op_or  {};
struct op_and {};
struct op_xor {};
struct op_not {};

typedef std::string var;
template <typename tag> struct binop;
template <typename tag> struct unop;

typedef boost::variant<var,
    boost::recursive_wrapper<unop <op_not> >,
    boost::recursive_wrapper<binop<op_and> >,
    boost::recursive_wrapper<binop<op_xor> >,
    boost::recursive_wrapper<binop<op_or> >
    > expr;

template <typename tag> struct binop
{
    explicit binop(const expr& l, const expr& r) : oper1(l), oper2(r) { }
    expr oper1, oper2;
};

template <typename tag> struct unop
{
    explicit unop(const expr& o) : oper1(o) { }
    expr oper1;
};

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

template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, expr(), Skipper>
{
    parser() : parser::base_type(expr_)
    {
        using namespace qi;

        expr_  = or_.alias();

        or_  = (xor_ >>  qr::distinct(alnum|'_')[ "or" ]  >> or_ ) [ _val = phx::construct<binop<op_or >>(_1, _2) ] | xor_   [ _val = _1 ];
        xor_ = (and_ >>  qr::distinct(alnum|'_')[ "xor" ] >> xor_) [ _val = phx::construct<binop<op_xor>>(_1, _2) ] | and_   [ _val = _1 ];
        and_ = (not_ >> -qr::distinct(alnum|'_')[ "and" ] >> and_) [ _val = phx::construct<binop<op_and>>(_1, _2) ] | not_   [ _val = _1 ];
        not_ = (qr::distinct(alnum|'_')[ "not" ] > simple       )  [ _val = phx::construct<unop <op_not>>(_1)     ] | simple [ _val = _1 ];

        simple = (('(' > expr_ > ')') | var_);
        var_   = 
            !qr::distinct(alnum|'_') [ lit("or")|"xor"|"and"|"not" ] >>
            qr::distinct(alnum|'_') [ alpha >> *(alnum | char_("_")) ]
            ;

        BOOST_SPIRIT_DEBUG_NODES((expr_) (or_) (xor_) (and_) (not_) (simple) (var_))
    }

    private:
    qi::rule<It, var()> var_;
    qi::rule<It, expr(), Skipper> not_, and_, xor_, or_, simple, expr_;
};

int main()
{
    for (auto& input : std::list<std::string> {
#if 0
            "a or b or c;",
            "(a and b);",
            "a xor b;",
            "a or b;",
            "(a) or (b);",
            "((c and d) or (a and b));",
#endif
            "(a and b) xor ((c and d) or (a and b));",
            "(a b) xor ((c d) or (a b));",

            "a and b xor c and d or a and b;",
            "a b xor c d or a b;",

            /// Simpler tests:
            "a and b;",
            "a b;",
            "not a and b;",
            "not a b;",

            "not (a and b);",
            "not (a b);",
            })
    {
        auto f(std::begin(input)), l(std::end(input));
        parser<decltype(f)> p;

        try
        {
            expr result;
            bool ok = qi::phrase_parse(f,l,p > ';',qi::space,result);

            std::cout << "\n======= input '" << input << "'\n";

            if (!ok)
                std::cerr << "invalid input\n";
            else
                std::cout << "result: " << result << "\n";

        } catch (const qi::expectation_failure<decltype(f)>& e)
        {
            std::cerr << "expectation_failure at '" << std::string(e.first, e.last) << "'\n";
        }

        if (f!=l) std::cerr << "unparsed: '" << std::string(f,l) << "'\n";
    }
    return 0;
}

Prints 打印

======= input '(a and b) xor ((c and d) or (a and b));'
result: ((a & b) ^ ((c & d) | (a & b)))

======= input '(a b) xor ((c d) or (a b));'
result: ((a & b) ^ ((c & d) | (a & b)))

======= input 'a and b xor c and d or a and b;'
result: (((a & b) ^ (c & d)) | (a & b))

======= input 'a b xor c d or a b;'
result: (((a & b) ^ (c & d)) | (a & b))

======= input 'a and b;'
result: (a & b)

======= input 'a b;'
result: (a & b)

======= input 'not a and b;'
result: ((!a) & b)

======= input 'not a b;'
result: ((!a) & b)

======= input 'not (a and b);'
result: (!(a & b))

======= input 'not (a b);'
result: (!(a & b))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM