C++ 字符串到布尔表达式不起作用c++;
我有一个基于字符串输入计算布尔字符串的代码 代码应该是这样工作的:C++ 字符串到布尔表达式不起作用c++;,c++,boost-spirit,boost-variant,boost-phoenix,C++,Boost Spirit,Boost Variant,Boost Phoenix,我有一个基于字符串输入计算布尔字符串的代码 代码应该是这样工作的: Boolean string: "((0|1)&3);" Sting input: "101" 工作怎么样?输入字符串中的每个字符都应该被布尔字符串中相应的字符替换 例如: 输入字符串中的1乘以布尔字符串中的0 输入字符串中的0乘以布尔字符串中的1 输入字符串中的1乘以布尔字符串中的3 我知道这很让人困惑,我的问题是代码在很多情况下都可以使用,但我不明白为什么上面的例子中它不起作用 我添加了实时版本进行编辑
Boolean string: "((0|1)&3);"
Sting input: "101"
工作怎么样?输入字符串中的每个字符都应该被布尔字符串中相应的字符替换
例如:
- 输入字符串中的1乘以布尔字符串中的0
- 输入字符串中的0乘以布尔字符串中的1
- 输入字符串中的1乘以布尔字符串中的3
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
名称空间qi=boost::spirit::qi;
名称空间phx=boost::phoenix;
结构op_或{};
结构op_和{};
结构op_not{};
typedef std::string变量;
模板结构binop;
模板结构unop;
typedef boost::variant expr;
模板结构binop
{
显式binop(const-expr&l,const-expr&r):oper1(l),oper2(r){}
expr oper1、oper2;
};
模板结构unop
{
显式unop(const expr&o):oper1(o){}
expr-oper1;
};
结构eval2:boost::static\u visitor
{
eval2(const std::string&pk):pkey(pk){iter=0;}
//
布尔运算符()
{
std::cout所以您需要变量,并且它们是隐式的。
在表达式中用整数表示它们。是的,这很混乱,但我想为什么不呢
语法建议变量可以是任意长度的字母数字字符。让我们这样做,并将示例修改为:
bool res = string2BooleanExe("((a|b)&c);", {
{ "a", true }, { "b", false }, { "c", true } }); // was: 101
现在在您的实现中有两个大问题:
您正在使用名称0
,1
,2
作为源表达式中的占位符,但这些占位符被忽略了(这意味着((0 | 1)&2)
在功能上等同于((1 | 2)&0)
。我怀疑这是谁想要的)
您的eval2
imk visitor是有状态的。如果要保留状态,您需要通过引用传递并使用它。或者,请确保您的复制构造函数实际复制了iter
以下是我对事物的看法,使用
typedef std::map<std::string, bool> VarMap;
拆分求值
和解析
函数:
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);
}
“”“修复错误的命名。此外,单一责任。创建一个解析
函数和一个求值
函数。将';”
和跳过程序放在语法中。检查语法中的qi::eoi
。传播异常,而不是在解析/求值
函数中执行神奇的控制台输出。这非常令人困惑。不知何故,您混淆了字符串迭代和表达式求值。如果您摆脱了iter
,而使用了@melpomene所说的v
^,那么效果会更好。我将在几分钟后展示一个示例这里有一个简单的示例也失败了:string2BooleanExe(“0&0;”,“10”)
@melpomene对于string2BooleanExe(“0&0;”,“10”)=0@H'H期望“0&0;”
在使用0
的两次时都替换为true更有意义。请参阅我的答案。
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;
};
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);
}
//#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.