我有以下代码来评估基于字符串输入的布尔字符串。
代码应该像这样工作:
Boolean string: "((0|1)&3);"
Sting input: "101"
它是如何工作的?输入字符串中的每个字符都应该被布尔字符串中的相应字符替换。
例如:
输入字符串中的1,布尔字符串中的0
0,布尔字符串
1,布尔字符串
我知道这很令人困惑,我的问题是代码被用于许多情况,但我不明白为什么它不适用于上面的例子。
我添加了实时版本以进行编辑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 :(得分:3)
所以你想要变量。它们是隐含的。 你用表达式中的整数表示它们。是的,那令人困惑,但为什么不呢,我想。
语法表明变量可以是任何长度的字母数字字符。让我们这样做,并将样本修复为:
bool res = string2BooleanExe("((a|b)&c);", {
{ "a", true }, { "b", false }, { "c", true } }); // was: 101
现在,在您的实施中存在两个大问题:
您使用名称0
,1
,2
作为源表达式中的占位符但忽略了这些(这意味着((0|1)&2)
在功能上等效到((1|2)&0)
......我怀疑那是谁想要的)
您的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);
}
现在让我们看一下完整的演示
<强> 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
函数中执行魔术控制台输出。