2016-12-18 10 views
2

У меня есть следующий код для вычисления булевой строки на основе ввода строки.Строка в булево выражение не работает C++

код должен работать так:

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.

+0

Это довольно запутанно. Вы каким-то образом перепутали итерацию строк и выражение. Это было бы намного лучше, если бы вы избавились от 'iter' и вместо этого использовали' v'. – melpomene

+0

^what @melpomene сказал. Я показываю образец за несколько минут. – sehe

+0

Вот еще более простой пример, который также терпит неудачу: 'string2BooleanExe (« 0 & 0; »,« 10 »)' – melpomene

ответ

3

Итак, вы хотите переменные. И они неявные. И вы обозначаете их целыми числами в выражении. Да, это сбивает с толку, но почему бы и нет.

Грамматика предполагает, что переменные могут быть любой длиной буквенно-цифровых символов. Давайте сделаем это, и зафиксировать образец быть:

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

Сейчас в вашей реализации есть две большие проблемы:

  1. вы используете имена 0, 1, 2 для заполнителей в источнике но это игнорируется (это означает, что ((0|1)&2) функционально эквивалентен ((1|2)&0) ... Я сомневаюсь, что это то, что хотели)

  2. eval2 ¹ посетитель с сохранением состояния. Вам нужно передать и использовать его по ссылке, если вы собираетесь сохранить состояние.Кроме того, убедитесь, что ваш конструктор копирования фактически копирует значение iter

Вот мой взгляд на вещи, используя

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

Разделительный функции evaluate и 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); 
} 

Теперь давайте посмотрим на полную демо

Full Demo

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.

 Смежные вопросы

  • Нет связанных вопросов^_^