字符串到布尔表达式不起作用c ++

时间:2016-12-18 21:35:21

标签: c++ boost-spirit boost-variant boost-phoenix

我有以下代码来评估基于字符串输入的布尔字符串。

代码应该像这样工作:

Boolean string: "((0|1)&3);"

Sting input: "101"  

它是如何工作的?输入字符串中的每个字符都应该被布尔字符串中的相应字符替换。

例如:

  • 输入字符串中的1,布尔字符串中的0

  • 输入字符串中的
  • 0,布尔字符串

  • 中的1 输入字符串中的
  • 1,布尔字符串

  • 中的3

我知道这很令人困惑,我的问题是代码被用于许多情况,但我不明白为什么它不适用于上面的例子。

我添加了实时版本以进行编辑here

#include <iostream>
#include <fstream>
#include <vector>

#include <boost/lexical_cast.hpp>
#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_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_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 eval2 : boost::static_visitor<bool> 
{
    eval2(const std::string& pk): pkey(pk) { iter = 0; }

    //
    bool operator()(const var& v) const 
    { 
      std:: cout << "**** " << v << "\titer: " << iter << std::endl;
      iter ++;
      return boost::lexical_cast<bool>(pkey[iter-1]); 
    }

    bool operator()(const binop<op_and>& b) const
    {
        return recurse(b.oper1) && recurse(b.oper2);
    }
    bool operator()(const binop<op_or>& b) const
    {
        return recurse(b.oper1) || recurse(b.oper2);
    }
    bool operator()(const unop<op_not>& u) const
    {
        return !recurse(u.oper1);
    } 

    private:
    mutable int iter;
    const std::string pkey;
    template<typename T>
        bool recurse(T const& v) const 
        { return boost::apply_visitor(*this, v); }
};

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

bool evaluate2(const expr& e, const std::string s) 
{ 
  return boost::apply_visitor(eval2(s), e); 
}

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_  = (and_ >> '|'  >> or_ ) [ qi::_val = phx::construct<binop<op_or > >(qi::_1, qi::_2) ] | and_   [ qi::_val = qi::_1 ];
            and_ = (not_ >> '&' >> and_)  [ qi::_val = phx::construct<binop<op_and> >(qi::_1, qi::_2) ] | not_   [ qi::_val = qi::_1 ];
            not_ = ('!' > simple       )  [ qi::_val = phx::construct<unop <op_not> >(qi::_1)     ] | simple [ qi::_val = qi::_1 ];

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

            BOOST_SPIRIT_DEBUG_NODE(expr_);
            BOOST_SPIRIT_DEBUG_NODE(or_);
            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_, or_, simple, expr_; 
};

bool string2BooleanExe(std::string bStatement, std::string bKey)
{
  typedef std::string::const_iterator It;
  It f(bStatement.begin()), l(bStatement.end());
  parser<It> 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:\t" << result << "\n";
      bool returnResult = evaluate2(result, bKey);
      std::cout << "evaluated:\t" << returnResult << "\n";
      return returnResult;
      }

  } catch (const qi::expectation_failure<It>& 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 false;
}

int main()
{
    bool res = string2BooleanExe("((0|1)&3);", "101");
    std::cout << "res: " << res << std::endl;
    return 0;
}

请注意我只能使用C ++ 03。

1 个答案:

答案 0 :(得分:3)

所以你想要变量。它们是隐含的。 你用表达式中的整数表示它们。是的,那令人困惑,但为什么不呢,我想。

语法表明变量可以是任何长度的字母数字字符。让我们这样做,并将样本修复为:

bool res = string2BooleanExe("((a|b)&c);", {
        { "a", true }, { "b", false }, { "c", true } }); // was: 101

现在,在您的实施中存在两个大问题:

  1. 您使用名称012作为源表达式中的占位符但忽略了这些(这意味着((0|1)&2)在功能上等效到((1|2)&0) ......我怀疑那是谁想要的)

  2. 您的eval2¹访客是有状态的。如果要保留状态,则需要通过引用传递和使用它。或者,请确保您的复制构造函数实际上复制了iter

  3. 的值

    这是我对事物的看法,使用

    typedef std::map<std::string, bool> VarMap;
    

    让我们在evaluator访问者中使用它:

    struct evaluator : boost::static_visitor<bool> 
    {
        evaluator(VarMap const& pk) : pk(pk) { }
    
        bool operator()(const var& v) const { return pk.at(v); }
        bool operator()(const binop<op_and>& b) const { return recurse(b.oper1) && recurse(b.oper2); }
        bool operator()(const binop<op_or>&  b) const { return recurse(b.oper1) || recurse(b.oper2); }
        bool operator()(const unop<op_not>&  u) const { return !recurse(u.oper1); }
    
      private:
        template<typename T> bool recurse(T const& v) const { return boost::apply_visitor(*this, v); }
        const VarMap pk;
    };
    

    拆分evaluateparse函数:

    static const parser<std::string::const_iterator> s_parser_instance;
    expr parse(std::string const& bStatement) {
        std::string::const_iterator f = bStatement.begin(), l = bStatement.end();
    
        expr parsed;
        qi::parse(f, l, s_parser_instance, parsed);
    
        return parsed;
    }
    
    bool evaluate(expr const& e, VarMap const& vars) {
        return boost::apply_visitor(evaluator(vars), e); 
    }
    

    现在让我们看一下完整的演示

    完整演示

    <强> Live On Coliru

    //#define BOOST_SPIRIT_DEBUG
    #include <iostream>
    #include <fstream>
    #include <vector>
    
    #include <boost/lexical_cast.hpp>
    #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;
    
    typedef std::map<std::string, bool> VarMap;
    
    struct op_or  {};
    struct op_and {};
    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_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 evaluator : boost::static_visitor<bool> 
    {
        evaluator(VarMap const& pk) : pk(pk) { }
    
        bool operator()(const var& v) const { return pk.at(v); }
        bool operator()(const binop<op_and>& b) const { return recurse(b.oper1) && recurse(b.oper2); }
        bool operator()(const binop<op_or>&  b) const { return recurse(b.oper1) || recurse(b.oper2); }
        bool operator()(const unop<op_not>&  u) const { return !recurse(u.oper1); }
    
      private:
        template<typename T> bool recurse(T const& v) const { return boost::apply_visitor(*this, v); }
        const VarMap pk;
    };
    
    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 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>
    struct parser : qi::grammar<It, expr()>
    {
        parser() : parser::base_type(start) {
            using namespace qi;
    
            start  = skip(space) [expr_ > ';' > eoi];
    
            expr_  = or_.alias();
            or_    = (and_ >> '|'  >> or_ ) [ _val = phx::construct<binop<op_or > >(_1, _2) ] | and_   [ _val = _1 ];
            and_   = (not_ >> '&' >> and_)  [ _val = phx::construct<binop<op_and> >(_1, _2) ] | not_   [ _val = _1 ];
            not_   = ('!' > simple       )  [ _val = phx::construct<unop <op_not> >(_1) ]     | simple [ _val = _1 ];
    
            simple = ('(' > expr_ > ')') | var_;
            var_   = lexeme[ +(alpha|digit) ];
    
            BOOST_SPIRIT_DEBUG_NODES((expr_) (or_) (and_) (not_) (simple) (var_));
        }
    
      private:
        qi::rule<It, expr()> start;
        qi::rule<It, var() , qi::space_type> var_;
        qi::rule<It, expr(), qi::space_type> not_, and_, or_, simple, expr_; 
    };
    
    static const parser<std::string::const_iterator> s_parser_instance;
    expr parse(std::string const& bStatement) {
        std::string::const_iterator f = bStatement.begin(), l = bStatement.end();
    
        expr parsed;
        qi::parse(f, l, s_parser_instance, parsed);
    
        return parsed;
    }
    
    bool evaluate(expr const& e, VarMap const& vars) {
        return boost::apply_visitor(evaluator(vars), e); 
    }
    
    void test(std::string const& expression, VarMap const& vars, bool expected) {
        try {
            std::cout << "'" << expression << "'";
    
            expr parsed = parse(expression);
            std::cout << " -> " << parsed;
    
            bool actual = evaluate(parsed, vars);
            std::cout 
                << " - evaluates to " << std::boolalpha << actual
                << (expected == actual? " Correct." : " INCORRECT!!!")
                << "\n";
    
        } catch(std::exception const& e) {
            std::cout << " EXCEPTION(" << e.what() << ")\n";
        }
    }
    
    int main() {
        VarMap vars;
        vars["a"] = true;
        vars["b"] = false;
        vars["c"] = true;
    
        test("a;", vars, true);
        test("b;", vars, false);
        test("c;", vars, true);
    
        test("((a|b)&c);", vars, true);
    
        vars["c"] = false;
        test("((a|b)&c);", vars, false);
    
        // let's use an undefined variable - should throw
        test("((z|y)&x);", vars, false|true);
    
        // you CAN still use confusing numeric placeholders:
        vars["0"] = true;
        vars["1"] = false;
        vars["2"] = true;
        test("((0|1)&2);", vars, true);
        test("((2|0)&1);", vars, false);
        test("((1|0)&2);", vars, true);
    
        // note you can also have "special variables"; no need for single-letter names
        vars["TRUE"] = true;
        vars["FALSE"] = false;
        test("TRUE | FALSE;", vars, true);
        test("TRUE & FALSE;", vars, false);
    }
    

    打印:

    'a;' -> a - evaluates to true Correct.
    'b;' -> b - evaluates to false Correct.
    'c;' -> c - evaluates to true Correct.
    '((a|b)&c);' -> ((a | b) & c) - evaluates to true Correct.
    '((a|b)&c);' -> ((a | b) & c) - evaluates to false Correct.
    '((z|y)&x);' -> ((z | y) & x) EXCEPTION(map::at)
    '((0|1)&2);' -> ((0 | 1) & 2) - evaluates to true Correct.
    '((2|0)&1);' -> ((2 | 0) & 1) - evaluates to false Correct.
    '((1|0)&2);' -> ((1 | 0) & 2) - evaluates to true Correct.
    'TRUE | FALSE;' -> (TRUE | FALSE) - evaluates to true Correct.
    'TRUE & FALSE;' -> (TRUE & FALSE) - evaluates to false Correct.
    

    ¹FIX BAD NAMING。另外,单一责任。创建parse函数和evaluate函数。将';'和船长放在语法中。检查语法中的qi::eoi。传播异常而不是在parse/evaluate函数中执行魔术控制台输出。