2016-12-18 5 views
2

J'ai un code suivant pour évaluer une chaîne booléenne basée sur une entrée de chaîne.chaîne à l'expression booléenne ne fonctionne pas C++

Le code censé fonctionner comme ceci:

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

Sting input: "101" 

comment va fonctionner? chaque caractère de la chaîne d'entrée est supposé être remplacé par le caractère correspondant dans la chaîne booléenne.

par exemple:

  • 1 dans la chaîne d'entrée par 0 dans la chaîne booléenne

  • 0 dans la chaîne d'entrée par 1 à chaîne booléenne

  • 1 dans la chaîne d'entrée par 3 dans la chaîne booléenne

Je sais que c'est confus, Mon problème est que le code a été utilisé pour travailler dans de nombreux cas, mais je ne comprends pas pourquoi il ne fonctionne pas pour l'exemple ci-dessus.

J'ai ajouté la version en direct pour l'édition 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; 
} 

Veuillez noter que je ne peux utiliser que C++ 03.

+0

C'est assez déroutant. Vous avez mélangé l'itération des chaînes et l'évaluation de l'expression. Cela fonctionnerait beaucoup mieux si vous vous débarrassiez de 'iter' et que vous utilisiez plutôt' v'. – melpomene

+0

^ce que @melpomene a dit. Je montre un exemple en quelques minutes – sehe

+0

Voici un exemple plus simple qui échoue également: 'string2BooleanExe (" 0 & 0; "," 10 ")' – melpomene

Répondre

3

Vous voulez donc des variables. Et ils sont implicites. Et vous les notez avec des entiers dans l'expression. Oui, c'est confus, mais pourquoi pas, je suppose.

La grammaire suggère que les variables pourraient être n'importe quelle longueur de caractères alphanumériques, cependant. Faisons cela, et fixer l'échantillon soit:

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

Maintenant, dans votre mise en œuvre il y a deux grands problèmes:

  1. vous utilisez des noms 0, 1, 2 pour les espaces réservés à la source expression mais ceux-ci sont ignorés (cela signifie que ((0|1)&2) est fonctionnellement équivalent à ((1|2)&0) ... Je doute que c'est ce que tout le monde voulait)

  2. votre eval2 ¹ visiteur est stateful. Vous devez passer et l'utiliser par référence si vous allez conserver l'état.Sinon, assurez-vous que votre constructeur de copie en fait une copie de la valeur de iter

Voici mon avis sur les choses, en utilisant

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

Utilisons dans le evaluator visiteur:

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

Fractionnement les fonctions evaluate et parse:

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

Maintenant, nous allons voir la démo complète

pleine démonstration

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

Prints:

'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. 

¹ FIXER NOM MAUVAIS. Aussi, une seule responsabilité. Créez une fonction parse et une fonction evaluate. Mettez ';' et le skipper dans la grammaire. Vérifiez qi::eoi dans la grammaire. Propagez des exceptions au lieu de faire une sortie de console magique à l'intérieur de votre fonction parse/evaluate.