How to write a 'c like if' parser with boost spirit - c++

I want to write a rule that parses something like:
if (1==1) {do something}
My problem is how to 'disable' a semantic action bases on the result of another rule's output.
For demonstration in my example I use a int_ parser and simply use that value as its result. I want to bypass that action if the ifrule returns false.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
int main() {
qi::symbols<char, std::function<bool(int, int)>> sym;
sym.add
("==", std::equal_to<>())
("!=", std::not_equal_to<>());
using namespace qi::labels;
int result;
auto bin_eval = [](auto const& lhs, auto const& op, auto const& rhs) {
return op(lhs, rhs);
};
qi::rule<std::string::const_iterator, bool(), qi::space_type> ifrule;
ifrule = (qi::lit("if") >> '(' >> qi::int_ >> sym >> qi::int_ >> ')')
[qi::_val = px::bind(bin_eval, _1, _2, _3)];
qi::rule<std::string::const_iterator, int(), qi::space_type> rule;
rule = ifrule >> '{' >> qi::int_[_val = _1] >> '}';
for (std::string const s : {"if (1==2) {1}", "if (1==1) {1}"}) {
std::cout << std::quoted(s) << " -> ";
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << std::boolalpha << result << "\n";
} else {
std::cout << "parse failed\n";
}
}
}

Are you looking for shortcut evaluation?
If so, there is literally nothing you have to do to get that effect, nor is there anything you COULD do, short of modifying the input.
As long as you are combining the parsing and the evaluation, you will necessarily be visiting the entire expression tree (because you have to parse it).
Only if you pruduce an AST tree you could do optimizations:
static optimizations that simplify the tree (removing double negations, removing tautologies, resolving contradictions like x xor not x -> not (x and not x) -> not (false) -> true
This is a rich topic and not as easy as it seems (see e.g. Boost Spirit: parse boolean expression and reduce to canonical normal form)
runtime optimization, this is where shortcut evaluation comes in. E.g. a && (b | (c ^ d) | (!a & (b ^ c))) would be
[![enter image description here][1]][1]. Ifais false, the entire evaluation can be skipped because the result will befalse`.
Is your grammar an expression grammar or a statement parser? What should be the value of result if the condition is false? – sehe just now
Assuming the answer is "expression" not "statement" then this is arguably just an expression parser with some binary operators and one ternary (with implicit else branch).
In that case you could cheat and just return an optional value.
I'll show you a more complete AST-based example because likely you're trying to parse something less trivial than what you're showing anyways.
An AST
As always, think of what your Abstract Syntax Tree looks like fist:
namespace Ast {
struct BinOp;
struct Conditioinal;
using Expr = boost::variant<
int,
bool,
BinOp,
Conditional
>;
struct BinOp {
BinOpF op;
Expr lhs, rhs;
};
struct Conditional {
Expr condition, true_part;
};
}
I've taken the function definition for operators from your code:
using BinOpF = std::function<bool(int, int)>;
The variant wouldn't actually compiler with the recursive use incomplete types, so let's use the wrapper:
using Expr = boost::variant<
int,
bool,
boost::recursive_wrapper<BinOp>,
boost::recursive_wrapper<Conditional>
>;
Parsing
The parser construction is usually a 1:1 mapping of rules:
expr
= '(' >> expr >> ')'
| conditional_
| int_
| bool_
| binop_
;
Let's put it in a grammar (that also hides the skipper).
Note I also shuffled the binop parser around splitting expr_ into simple_ and using semantic actions to create the BinOp Ast nodes for efficiency. This stems from experience that grammars get horrifically inefficient with backtracking otherwise.
template <typename It>
struct Expr : qi::grammar<It, Ast::Expr()> {
Expr() : Expr::base_type(start) {
using namespace qi;
start = qi::skip(space) [ expr_ ];
simple_
= '(' >> expr_ >> ')'
| conditional_
| int_
| bool_
;
auto make_bin = [](auto lhs, auto op, auto rhs) {
return Ast::BinOp { op, lhs, rhs };
};
expr_ %= simple_
>> *(binop_ >> expr_) [ _val = px::bind(make_bin, _val, _1, _2) ];
;
conditional_
= lexeme["if"]
>> '(' >> expr_ >> ')'
>> '{' >> expr_ >> '}'
;
binop_.add
("==", std::equal_to<>())
("!=", std::not_equal_to<>());
BOOST_SPIRIT_DEBUG_NODES((start)(expr_)(conditional_))
}
private:
qi::rule<It, Ast::Expr()> start;
qi::rule<It, Ast::Expr(), qi::space_type> simple_, expr_;
qi::rule<It, Ast::Conditional(), qi::space_type> conditional_;
qi::symbols<char, Ast::BinOpF> binop_;
};
That's it. Note that it will parse a multitude of expressions that your parser could not, previously. E.g.:
Live On Coliru
int main() {
Expr<std::string::const_iterator> const parser;
for (std::string const s : {
"false",
"1==2",
"-3!=3",
"if (true) {42}",
"if (false) {43}",
"if (1==2) {44}",
"if (false == (1 == 2)) { ((((45)))) }",
"if (false == (1 == 2)) { if (true) {46} }",
"if (true == (1 == 2)) {47} else { 48 };",
}) {
std::cout << std::quoted(s) << " -> ";
auto f = begin(s);
auto l = end(s);
Ast::Expr expr;
if (qi::parse(f, l, parser, expr)) {
std::cout << "result: " << expr << "\n";
} else {
std::cout << "parse failed\n";
}
if (f!=l) {
std::cout << "Remaining unparsed input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Prints
"false" -> result: 0
"1==2" -> result: (1 (opfun) 2)
"-3!=3" -> result: (-3 (opfun) 3)
"if (true) {42}" -> result: if(1) {42}
"if (false) {43}" -> result: if(0) {43}
"if (1==2) {44}" -> result: if((1 (opfun) 2)) {44}
"if (false == (1 == 2)) { ((((45)))) }" -> result: if((0 (opfun) (1 (opfun) 2))) {45}
"if (false == (1 == 2)) { if (true) {46} }" -> result: if((0 (opfun) (1 (opfun) 2))) {if(1) {46}}
"if (true == (1 == 2)) {47} else { 48 };" -> result: if((1 (opfun) (1 (opfun) 2))) {47}
Remaining unparsed input: " else { 48 };"
Evaluation
All that's left is actual evaluation. The result will be an optional value which could be optional<int> for your examples, but I'll make it variant<Nil, int, bool> so we can represent conditions as well:
namespace Evaluation {
struct Nil {};
using Value = boost::variant<Nil, int, bool>;
Now the engine is a function object that will be visiting our AST nodes:
struct Engine {
template <typename... T> auto operator()(T const&... v) const {
return eval(v...);
}
private:
Delegating any call to operator() to private eval methods makes things easier to read, and implementation is pretty straightforward:
Value eval(Ast::Expr const& e) const { return boost::apply_visitor(*this, e); }
Value eval(int e) const { return e; }
Value eval(bool e) const { return e; }
Value eval(Ast::BinOp const& e) const { return e.op(as_int(e.lhs), as_int(e.rhs)); }
Here we see the first bit of logic creep in. Since BinUpF is bool(int,int) we have to coerce the arguments to int.
Value eval(Ast::Conditional const& e) const {
Value True = true;
if (eval(e.condition) == True) {
return eval(e.true_part);
}
return Nil{};
}
This is where we finally answer the meat of your question: the true_part is never evaluated if the condition evaluates to false¹
Test #1
Testing the current state of affairs:
Evaluation::Engine const eval;
// ...
if (qi::parse(f, l, parser, expr)) {
std::cout << "expr: " << expr << "\n";
try {
auto result = eval(expr);
std::cout << "result: " << result << "\n";
} catch(boost::bad_get const&) {
std::cout << "result: Type mismatch\n";
}
} else {
std::cout << "parse failed\n";
}
Prints Live On Coliru
result: 0
result: 0
result: 1
result: 42
result: Nil
result: Nil
"false" -> expr: 0
"1==2" -> expr: (1 (opfun) 2)
"-3!=3" -> expr: (-3 (opfun) 3)
"if (true) {42}" -> expr: if(1) {42}
"if (false) {43}" -> expr: if(0) {43}
"if (1==2) {44}" -> expr: if((1 (opfun) 2)) {44}
The following expressions fail because the operands aren't integer:
"if (false == (1 == 2)) { ((((45)))) }" -> expr: if((0 (opfun) (1 (opfun) 2))) {45}
result: Type mismatch
"if (false == (1 == 2)) { if (true) {46} }" -> expr: if((0 (opfun) (1 (opfun) 2))) {if(1) {46}}
result: Type mismatch
"if (true == (1 == 2)) {47} else { 48 };" -> expr: if((1 (opfun) (1 (opfun) 2))) {47}
result: Type mismatch
Remaining unparsed input: " else { 48 };"
Three Improvevents
Let's allow mixed-type evaluation (true != false should also work, right? As well as true == (1!=9).
Changing the function type from bool(int,int) to Value(Value,Value) allows for the full gamut.
namespace Evaluation {
struct Nil { bool operator==(Nil) const { return true; } };
using Value = boost::variant<Nil, int, bool>;
using BinOpF = std::function<Value(Value, Value)>;
For Mixed-Type evaluation we need some help because std::equal_to and friends don't know how to do (binary) variant visitation. So, let's make a wrapper for that:
template <typename Op> struct MixedOp {
Value operator()(Value const& lhs, Value const& rhs) const {
return boost::apply_visitor(Dispatch{}, lhs, rhs);
}
private:
struct Dispatch {
template <typename T, typename U>
Value operator()(T const& lhs, U const& rhs, decltype(Op{}(T{}, U{}))* = nullptr) const
{ return Op{}(lhs, rhs); }
template <typename... T>
Value operator()(T const&...) const
{ throw std::logic_error("Type mismatch " + std::string(__PRETTY_FUNCTION__)); }
};
};
Yeah. That's ugly, but note how it will naturally work for most of the operator functions (like std::plus<>, std::multiplies<> etc.).
// wrap std functionals
using equal_to = detail::MixedOp<std::equal_to<> >;
using not_equal_to = detail::MixedOp<std::not_equal_to<> >;
using plus = detail::MixedOp<std::plus<> >;
using minus = detail::MixedOp<std::minus<> >;
using multiplies = detail::MixedOp<std::multiplies<> >;
using divides = detail::MixedOp<std::divides<> >;
Let's also make it so truthy-ness of values is better (0 should probably just mean false like in C, as would Nil).
Let's get the truthyness:
namespace detail {
struct truthy {
Value operator()(Value const& v) const { return boost::apply_visitor(*this, v); }
Value operator()(int v) const { return static_cast<bool>(v); }
Value operator()(bool v) const { return static_cast<bool>(v); }
Value operator()(Nil) const { return Nil{}; }
};
And then define a free function to make it easy to call from our Engine:
static inline bool truthy(Value const& v) { return Value{true} == detail::truthy{}(v); }
We can now drop the complications from the Evaluation::Engine:
Value eval(Ast::BinOp const& e) const { return e.op(eval(e.lhs), eval(e.rhs)); }
Value eval(Ast::Conditional const& e) const {
if (truthy(eval(e.condition))) {
return eval(e.true_part);
}
return Nil{};
}
No more as_inst or True comparison.
Let's make else supported
This is very simple extending from what we had above. But it will nicely demonstrate that only one branch is actually evaluated, e.g. when one branch contains a division by zero.
Value eval(Ast::Conditional const& e) const {
if (truthy(eval(e.condition))) {
return eval(e.true_part);
}
if (e.false_part) {
return eval(*e.false_part);
}
return Nil{};
}
Full Demo
Live On Coliru
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace Evaluation {
struct Nil { bool operator==(Nil) const { return true; } };
using Value = boost::variant<Nil, int, bool>;
using BinOpF = std::function<Value(Value, Value)>;
namespace detail {
struct truthy {
Value operator()(Value const& v) const { return boost::apply_visitor(*this, v); }
Value operator()(int v) const { return static_cast<bool>(v); }
Value operator()(bool v) const { return static_cast<bool>(v); }
Value operator()(Nil) const { return Nil{}; }
};
template <typename Op> struct MixedOp {
Value operator()(Value const& lhs, Value const& rhs) const {
return boost::apply_visitor(Dispatch{}, lhs, rhs);
}
private:
struct Dispatch {
template <typename T, typename U>
Value operator()(T const& lhs, U const& rhs, decltype(Op{}(T{}, U{}))* = nullptr) const
{ return Op{}(lhs, rhs); }
template <typename... T>
Value operator()(T const&...) const
{ throw std::logic_error("Type mismatch " + std::string(__PRETTY_FUNCTION__)); }
};
};
}
static inline bool truthy(Value const& v) { return Value{true} == detail::truthy{}(v); }
// wrap std functionals
using equal_to = detail::MixedOp<std::equal_to<> >;
using not_equal_to = detail::MixedOp<std::not_equal_to<> >;
using plus = detail::MixedOp<std::plus<> >;
using minus = detail::MixedOp<std::minus<> >;
using multiplies = detail::MixedOp<std::multiplies<> >;
using divides = detail::MixedOp<std::divides<> >;
}
namespace Ast {
struct BinOp;
struct Conditional;
using Expr = boost::variant<
int,
bool,
boost::recursive_wrapper<BinOp>,
boost::recursive_wrapper<Conditional>
>;
struct BinOp {
Evaluation::BinOpF op;
Expr lhs, rhs;
};
struct Conditional {
Expr condition, true_part;
boost::optional<Expr> false_part;
};
}
BOOST_FUSION_ADAPT_STRUCT(Ast::Conditional, condition, true_part, false_part)
namespace Parsing {
template <typename It>
struct Expr : qi::grammar<It, Ast::Expr()> {
Expr() : Expr::base_type(start) {
using namespace qi;
start = qi::skip(space) [ expr_ ];
simple_
= '(' >> expr_ >> ')'
| conditional_
| int_
| bool_
;
auto make_bin = [](auto lhs, auto op, auto rhs) {
return Ast::BinOp { op, lhs, rhs };
};
expr_ %= simple_
>> *(binop_ >> expr_) [ _val = px::bind(make_bin, _val, _1, _2) ];
;
conditional_
= lexeme["if"]
>> '(' >> expr_ >> ')'
>> '{' >> expr_ >> '}'
>> -(lit("else") >> '{' >> expr_ >> '}')
;
binop_.add
("==", Evaluation::equal_to())
("!=", Evaluation::not_equal_to())
("+", Evaluation::plus())
("-", Evaluation::minus())
("*", Evaluation::multiplies())
("/", Evaluation::divides())
;
BOOST_SPIRIT_DEBUG_NODES((start)(expr_)(conditional_))
}
private:
qi::rule<It, Ast::Expr()> start;
qi::rule<It, Ast::Expr(), qi::space_type> simple_, expr_;
qi::rule<It, Ast::Conditional(), qi::space_type> conditional_;
qi::symbols<char, Evaluation::BinOpF> binop_;
};
}
namespace Evaluation {
struct Engine {
template <typename... T> auto operator()(T const&... v) const {
return eval(v...);
}
private:
Value eval(Ast::Expr const& e) const { return boost::apply_visitor(*this, e); }
Value eval(int e) const { return e; }
Value eval(bool e) const { return e; }
Value eval(Ast::BinOp const& e) const { return e.op(eval(e.lhs), eval(e.rhs)); }
Value eval(Ast::Conditional const& e) const {
if (truthy(eval(e.condition))) {
return eval(e.true_part);
}
if (e.false_part) {
return eval(*e.false_part);
}
return Nil{};
}
};
}
namespace Ast { // for debug output only
static inline std::ostream& operator<<(std::ostream& os, BinOp const& b) {
return os << "(" << b.lhs << " (opfun) " << b.rhs << ")";
}
static inline std::ostream& operator<<(std::ostream& os, Conditional const& c) {
os << "if(" << c.condition << ") {" << c.true_part << "}";
if (c.false_part)
os << "else{" << *c.false_part << "}";
return os;
}
}
namespace Evaluation { // for debug output only
static inline std::ostream& operator<<(std::ostream& os, Nil) {
return os << "Nil";
}
}
int main() {
Parsing::Expr<std::string::const_iterator> const parser;
Evaluation::Engine const eval;
for (std::string const s : {
"false",
"1==2",
"-3!=3",
"if (true) {42}",
"if (false) {43}",
"if (1==2) {44}",
"if (false == (1 == 2)) { ((((45)))) }",
"if (false == (1 == 2)) { if (true) {46} }",
"if (true == (1 == 2)) {47} else { 48 }",
// cherry on top:
"if (false) { 3*3 / 0 } else { 7*7 }", // note division by zero
}) {
std::cout << std::quoted(s) << " -> ";
auto f = begin(s);
auto l = end(s);
Ast::Expr expr;
if (qi::parse(f, l, parser, expr)) {
std::cout << "expr: " << expr << "\n";
try {
std::cout << "result: " << eval(expr) << "\n";
} catch(std::exception const& e) {
std::cout << "result: " << e.what() << "\n";
}
} else {
std::cout << "parse failed\n";
}
if (f!=l) {
std::cout << "Remaining unparsed input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Prints
"false" -> expr: 0
result: 0
"1==2" -> expr: (1 (opfun) 2)
result: 0
"-3!=3" -> expr: (-3 (opfun) 3)
result: 1
"if (true) {42}" -> expr: if(1) {42}
result: 42
"if (false) {43}" -> expr: if(0) {43}
result: Nil
"if (1==2) {44}" -> expr: if((1 (opfun) 2)) {44}
result: Nil
"if (false == (1 == 2)) { ((((45)))) }" -> expr: if((0 (opfun) (1 (opfun) 2))) {45}
result: 45
"if (false == (1 == 2)) { if (true) {46} }" -> expr: if((0 (opfun) (1 (opfun) 2))) {if(1) {46}}
result: 46
"if (true == (1 == 2)) {47} else { 48 }" -> expr: if((1 (opfun) (1 (opfun) 2))) {47}else{48}
result: 48
"if (false) { 3*3 / 0 } else { 7*7 }" -> expr: if(0) {(3 (opfun) (3 (opfun) 0))}else{(7 (opfun) 7)}
result: 49
¹ Something other than true, actually

Related

n-ary boolean grammar conversion from infix to prefix with Boost::Spirit?

I need to convert infix notations like the one below to n-ary prefix notation with Boost::Spirit, but I am failing at building on the answers from https://stackoverflow.com/a/8707598/1816477 et al.
This is what I am trying to parse:
not (xyz='a' or xyz='b' or xyz='c') and abc='s' xor (pqr ='v' and xyz='d')
and this LISP-styled format is what I am trying to provide as output (do not mind the indentation):
(xor (and (= pqr 'v') (= xyz 'd'))
(and (= abc 's')
(not (or (= xyz 'a')
(= xyz 'b')
(= xyz 'c')))))
So, the terms I try to parse consist of prefixed (not <expression>) and infix expressions (<expression> and <expression> and ... etc.), i.e.: assignments, negations and n-ary ands, ors, xors etc., implying operator precedence (or < xor < and < assignment < negation).
What I am failing at is getting the grammar right. Outputting to a suitable boost::variant representing the parsed boolean expression I think I am able to accomplish. I am thinking of an output structure like this one:
struct prefixExpr;
struct infixExpr;
typedef boost::variant<
std::string, // identifiers, values etc.
boost::recursive_wrapper<prefixExpr>, // e.g. negation
boost::recursive_wrapper<infixExpr> // assignment, and, or, xor etc.
> expression;
struct prefixExpr {
std::string op; // currently only "not"
expression expr;
};
BOOST_FUSION_ADAPT_STRUCT(prefixExpr, op, expr)
struct infixExpr {
std::string op; // "and", "or", "xor", "="
std::vector<expression> exprs;
};
BOOST_FUSION_ADAPT_STRUCT(infixExpr, op, exprs)
What do I need to do to be able to parse expressions like the one mentioned above and convert them to a prefix notation?
I am using the boost 1.67.0 (the latest at the time of writing) and Visual Studio 15.7.3 (also the latest at the time of writing).
The code is not perfect but should be simple to understand:
#include <boost/variant.hpp>
#include <boost/spirit/home/x3.hpp>
#include <vector>
#include <string>
#include <iostream>
struct id : std::string {};
struct value : std::string {};
struct nary_expr;
using expr = boost::variant<
id, value,
boost::recursive_wrapper<nary_expr>
>;
struct nary_expr
{
std::string op;
std::vector<expr> exprs;
};
namespace x3 = boost::spirit::x3;
auto compose_nary_expr = [](auto& ctx)
{
//auto&& [left, tail] = x3::_attr(ctx);
auto&& left = boost::fusion::at_c<0>(x3::_attr(ctx));
auto&& tail = boost::fusion::at_c<1>(x3::_attr(ctx));
if (tail.size() == 0) {
x3::_val(ctx) = left;
return;
}
// left associativity
auto op = boost::fusion::at_c<0>(tail[0]);
std::vector<expr> exprs = { left, boost::fusion::at_c<1>(tail[0]) };
for (std::size_t i = 1; i < tail.size(); ++i) {
// same priority but different operator
auto&& next_op = boost::fusion::at_c<0>(tail[i]);
if (op != next_op) {
exprs = std::vector<expr>{ nary_expr{ op, std::move(exprs) } };
op = next_op;
}
exprs.push_back(boost::fusion::at_c<1>(tail[i]));
}
x3::_val(ctx) = nary_expr{ op, std::move(exprs) };
};
x3::rule<class prec4_expr_rule, expr> const prec4_expr("prec4_expr");
x3::rule<class prec3_expr_rule, expr> const prec3_expr("prec3_expr");
x3::rule<class prec2_expr_rule, expr> const prec2_expr("prec2_expr");
x3::rule<class prec1_expr_rule, expr> const prec1_expr("prec1_expr");
x3::rule<class prec0_expr_rule, expr> const prec0_expr("prec0_expr");
auto const prec4_expr_def = prec4_expr = (
prec3_expr
>> *( (x3::string("or") > prec3_expr)
)
)[compose_nary_expr];
auto const prec3_expr_def = prec3_expr = (
prec2_expr
>> *( (x3::string("xor") > prec2_expr)
)
)[compose_nary_expr];
auto const prec2_expr_def = prec2_expr = (
prec1_expr
>> *( (x3::string("and") > prec1_expr)
)
)[compose_nary_expr];
auto compose_binary_expr = [](auto& ctx)
{
auto&& rhs = boost::fusion::at_c<0>(x3::_attr(ctx));
auto&& tail = boost::fusion::at_c<1>(x3::_attr(ctx));
if (tail.size() > 0) {
auto&& op = boost::fusion::at_c<0>(tail[0]);
auto&& lhs = boost::fusion::at_c<1>(tail[0]);
x3::_val(ctx) = nary_expr{ op, { rhs, lhs } };
}
else {
x3::_val(ctx) = rhs;
}
};
// should use optional, but something wrong with spirit
auto const prec1_expr_def = prec1_expr = (
prec0_expr >> *(x3::string("=") > prec0_expr)
)[compose_binary_expr];
x3::rule<class not_expr_rule, expr> const not_expr("not_expr");
auto compose_unary_expr = [](auto& ctx)
{
//auto&& [op, expr] = x3::_attr(ctx);
auto&& op = boost::fusion::at_c<0>(x3::_attr(ctx));
auto&& expr = boost::fusion::at_c<1>(x3::_attr(ctx));
x3::_val(ctx) = nary_expr{ op, { expr } };
};
auto const not_expr_def = not_expr = (x3::string("not") > prec0_expr)[compose_unary_expr];
auto const id_term = x3::rule<class id_r, id>{} = x3::lexeme[x3::alpha >> *x3::alnum];
auto const value_term = x3::rule<class value_r, value>{} = x3::lexeme["'" > +~x3::char_('\'') >> "'"];
auto const prec0_expr_def =
value_term
| ( '(' > prec4_expr >> ')' )
| not_expr
| id_term
;
BOOST_SPIRIT_DEFINE(
prec0_expr
, prec1_expr
, prec2_expr
, prec3_expr
, prec4_expr
, not_expr
);
struct indent
{
std::size_t cur;
};
indent operator+(indent lhs, std::size_t rhs)
{
return { lhs.cur + rhs };
}
std::ostream& operator<<(std::ostream& os, indent const& v)
{
for (unsigned i = 0; i < v.cur; ++i) os << ' ';
return os;
}
struct is_simple
{
template <typename T>
bool operator()(T const&) const
{
return std::is_same<T, id>::value || std::is_same<T, value>::value;
}
};
struct printer
{
indent indent_;
void operator()(id const& v)
{
std::cout << v;
}
void operator()(value const& v)
{
std::cout << '\'' << v << '\'';
}
void operator()(nary_expr const& v)
{
std::cout << '(' << v.op << ' ';
printer p{ indent_ + 2 + v.op.size() };
boost::apply_visitor(p, v.exprs[0]);
for (std::size_t i = 1; i < v.exprs.size(); ++i) {
if (boost::apply_visitor(is_simple{}, v.exprs[i])) {
std::cout << ' ';
}
else {
std::cout << '\n' << p.indent_;
}
boost::apply_visitor(p, v.exprs[i]);
}
std::cout << ')';
}
};
int main()
{
std::string s = "not (xyz='a' or xyz='b' or xyz='c') and abc='s' xor (pqr ='v' and xyz='d')";
expr expr;
auto iter = s.cbegin();
if (phrase_parse(iter, s.cend(), prec4_expr_def, x3::space, expr) && iter == s.cend()) {
boost::apply_visitor(printer{}, expr);
}
return 0;
}
It prints:
(xor (and (not (or (= xyz 'a')
(= xyz 'b')
(= xyz 'c')))
(= abc 's'))
(and (= pqr 'v')
(= xyz 'd')))

boost.spirit x3 move_to and list ast member

the BNF I implement has a funny rule where, depending on operator, the terms can be chained or event not at this production rule. Hence I use the same AST data structure since only the enumeration changes:
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <list>
namespace ast
{
struct identifer {
int name;
};
struct expression {
struct chunk {
char operator_;
ast::identifer identifer;
};
ast::identifer identifer;
std::list<chunk> chunk_list;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::identifer,
name
)
BOOST_FUSION_ADAPT_STRUCT(ast::expression::chunk,
operator_, identifer
)
BOOST_FUSION_ADAPT_STRUCT(ast::expression,
identifer, chunk_list
)
namespace boost { namespace spirit { namespace x3 { namespace traits {
void move_to(ast::expression::chunk&& chunk, std::list<ast::expression::chunk>& chunk_list,
mpl::identity<container_attribute>)
{
chunk_list.emplace(chunk_list.end(), std::move(chunk));
}
} } } }
namespace parser
{
namespace x3 = boost::spirit::x3;
auto const identifier = x3::rule<struct _, int> { "identifier" } =
x3::int_;
auto const operator_1 = x3::rule<struct _, char> { "operator" } =
x3::char_("ABC");
auto const operator_2 = x3::rule<struct _, char> { "operator" } =
x3::char_("XYZ");
auto const expression_chunk_1 = x3::rule<struct _, ast::expression::chunk> { "expression" } =
operator_1 > identifier
;
auto const expression_chunk_2 = x3::rule<struct _, ast::expression::chunk> { "expression" } =
operator_2 > identifier
;
auto const expression = x3::rule<struct _, ast::expression> { "expression" } =
identifier >> *expression_chunk_1 // foo { and foo }
// rule below fails to compile
| identifier >> expression_chunk_2 // foo [ nand foo ]
;
}
struct visitor {
visitor(std::ostream& os) : os{ os } { }
void operator()(ast::expression const& node) {
os << "(";
(*this)(node.identifer);
for(auto const& chunk : node.chunk_list) {
os << "(" << chunk.operator_ << " ";
(*this)(chunk.identifer);
os << ")";
}
os << ")\n";
}
void operator()(ast::identifer const& node) {
os << "(" << node.name << ")";
}
std::ostream& os;
};
int main()
{
namespace x3 = boost::spirit::x3;
for(std::string const str: {
"1 X 2",
"3 A 4 A 5"
}) {
auto iter = str.begin(), end = str.end();
ast::expression attr;
bool r = x3::phrase_parse(iter, end, parser::expression, x3::space, attr);
std::cout << "parse '" << str << "': ";
if (r && iter == end) {
std::cout << "succeeded:\n";
visitor(std::cout)(attr);
} else {
std::cout << "*** failed ***\n";
}
}
return 0;
}
This was the idea - the operator X,Y,Z adds only one chunk to list. Following the compiler errors, I have to specialize x3::traits::move_to, but I don't found any solution to get this to compile. What is the wayy to do? is the list::emplace() and std::move() safe here?
I'd do without the trait. Instead, make the grammar result in a vector<T> artificially using repeat:
auto const expression = x3::rule<struct _, ast::expression> { "expression" } =
identifier >> *expression_chunk_1 // foo { and foo }
| identifier >> x3::repeat(1) [ expression_chunk_2 ] // foo [ nand foo ]
;

string to Boolean expression is not working c++

I have a following code to evaluate a Boolean string based on an string input.
The code supposed to work like this:
Boolean string: "((0|1)&3);"
Sting input: "101"
how's it working? each character in the input string is supposed to be substituted by corresponding character in Boolean string.
for example:
1 in the input string by 0 in Boolean string
0 in the input string by 1 in Boolean string
1 in the input string by 3 in Boolean string
I know it is confusing, My problem is that the code was used to work for many cases, but I don't understand why it is not working for above example.
I added the live version for editing 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;
}
Please note I can only use C++03.
So you want variables. And they are implicit.
And you denote them with integers in the expression. Yes, that's confusing, but why not, I guess.
The grammar suggests that variables could be any length of alphanumeric characters, though. Let's do this, and fix the sample to be:
bool res = string2BooleanExe("((a|b)&c);", {
{ "a", true }, { "b", false }, { "c", true } }); // was: 101
Now in your implementation there are two big problems:
you are using names 0, 1, 2 for the placeholders in the source expression but these are ignored (this means that ((0|1)&2) is functionally equivalent to ((1|2)&0)... I doubt that's what anyone wanted)
your eval2¹ visitor is stateful. You need to pass and use it by reference if you're going to retain state. Alternatively, make sure your copy constructor actually copies the value of iter
Here's my take on things, using
typedef std::map<std::string, bool> VarMap;
Let's use it in the evaluator visitor:
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;
};
Splitting the evaluate and parse functions:
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);
}
Now let's see the full demo
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);
}
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.
¹ FIX BAD NAMING. Also, single-responsibility. Make a parse function and an evaluate function. Put ';' and the skipper inside the grammar. Check for qi::eoi inside the grammar. Propagate exceptions instead of doing magic console output inside your parse/evaluate function.

Convert logic formula to conjunctive normal form in C++

I'm going to implement a CNF generator in C++, using Boots/Spirit. but after finish "the order of precedence" and "eliminating equivalences & implications" these two parts, I can't figure out how to implement "move NOTs inwards" and "distribute ORs inwards over ANDs".
Desired output is documented here:
https://en.wikipedia.org/wiki/Conjunctive_normal_form
Here are more detail description below:
The order of precedence:
NOT > AND > OR > IMP > IFF
Input example:
A iff B imp C
Now the output is:
(A or not ( not B or C)) and ( not A or ( not B or C))
And the code( I implement output at printer part ):
#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;
// Abstract data type
struct op_or {};
struct op_and {};
struct op_imp {};
struct op_iff {};
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> >,
boost::recursive_wrapper<binop<op_imp> >,
boost::recursive_wrapper<binop<op_iff> >
> 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;
};
// Operating on the syntax tree
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(" and ", b.oper1, b.oper2); }
void operator()(const binop<op_or >& b) const { print(" or ", b.oper1, b.oper2); }
void operator()(const binop<op_iff>& b) const { eliminate_iff(b.oper1, b.oper2); }
void operator()(const binop<op_imp>& b) const { eliminate_imp(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 << "( not ";
boost::apply_visitor(*this, u.oper1);
_os << ")";
}
void eliminate_iff(const expr& l, const expr& r) const
{
_os << "(";
boost::apply_visitor(*this, l);
_os << " or not ";
boost::apply_visitor(*this, r);
_os << ") and ( not ";
boost::apply_visitor(*this, l);
_os << " or ";
boost::apply_visitor(*this, r);
_os << ")";
}
void eliminate_imp(const expr& l, const expr& r) const
{
_os << "( not ";
boost::apply_visitor(*this, l);
_os << " or ";
boost::apply_visitor(*this, r);
_os << ")";
}
};
std::ostream& operator<<(std::ostream& os, const expr& e)
{ boost::apply_visitor(printer(os), e); return os; }
// Grammar rules
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, expr(), Skipper>
{
parser() : parser::base_type(expr_)
{
using namespace qi;
expr_ = iff_.alias();
iff_ = (imp_ >> "iff" >> iff_) [ _val = phx::construct<binop<op_iff>>(_1, _2) ] | imp_ [ _val = _1 ];
imp_ = (or_ >> "imp" >> imp_) [ _val = phx::construct<binop<op_imp>>(_1, _2) ] | or_ [ _val = _1 ];
or_ = (and_ >> "or" >> or_ ) [ _val = phx::construct<binop<op_or >>(_1, _2) ] | and_ [ _val = _1 ];
and_ = (not_ >> "and" >> and_) [ _val = phx::construct<binop<op_and>>(_1, _2) ] | not_ [ _val = _1 ];
not_ = ("not" > simple ) [ _val = phx::construct<unop <op_not>>(_1) ] | simple [ _val = _1 ];
simple = (('(' > expr_ > ')') | var_);
var_ = qi::lexeme[ +alpha ];
BOOST_SPIRIT_DEBUG_NODE(expr_);
BOOST_SPIRIT_DEBUG_NODE(iff_);
BOOST_SPIRIT_DEBUG_NODE(imp_);
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_, imp_, iff_, simple, expr_;
};
// Test some examples in main and check the order of precedence
int main()
{
for (auto& input : std::list<std::string> {
// Test the order of precedence
"(a and b) imp ((c and d) or (a and b));",
"a and b iff (c and d or a and b);",
"a and b imp (c and d or a and b);",
"not a or not b;",
"a or b;",
"not a and b;",
"not (a and b);",
"a or b or c;",
"aaa imp bbb iff ccc;",
"aaa iff bbb imp ccc;",
// Test elimination of equivalences
"a iff b;",
"a iff b or c;",
"a or b iff b;",
"a iff b iff c;",
// Test elimination of implications
"p imp q;",
"p imp not q;",
"not p imp not q;",
"p imp q and r;",
"p imp q imp r;",
})
{
auto f(std::begin(input)), l(std::end(input));
parser<decltype(f)> 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: " << result << "\n";
} catch (const qi::expectation_failure<decltype(f)>& 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 0;
}
Compiling command:
clang++ -std=c++11 -stdlib=libc++ -Weverything CNF_generator.cpp
Moving NOT inward should be done before distributing OR across AND:
!(A AND B) ==> (!A OR !B)
!(A OR B) ==> (!A AND !B)
remember to cancel any !!X that occurs while doing that.
Also drop redundant ( )
OR distributes across AND:
A OR (B AND C) ==> (A OR B) AND (A OR C)
You Probably need to reduce some other redundancies that will creep in as you do all that, such as (X OR X)
(A ornot( not B or C)) and ( not A or ( not B or C)) ==>
(A or (notnot B andnotC)) and ( not A or(not B or C)) ==>
(Aor( B and not C)) and ( not A or not B or C) ==>
((​AorB) and (Aornot C))and ( not A or not B or C) ==>
(A or B) and (A or not C) and ( not A or not B or C)
Maybe I misunderstood your question and you already understood all the above transformations, and you are having trouble with the mechanics of doing that inside the structure you have created.
You certainly have made things hard for yourself (maybe impossible) by trying to accomplish all the transformations inside the print routine. I would have parsed, then transformed, then printed.
If you insist on transforming in the print routine, then you likely miss some simplifications and you need print to be more aware of the rules of CNF. An AND node can simply print its two sides recursively with AND in between. But any other node most first inspect its children and conditionally transform enough to pull an AND up to the top before recursively calling.
You had:
void eliminate_iff(const expr& l, const expr& r) const
{
_os << "(";
boost::apply_visitor(*this, l);
_os << " or not ";
boost::apply_visitor(*this, r);
_os << ") and ( not ";
boost::apply_visitor(*this, l);
_os << " or ";
boost::apply_visitor(*this, r);
_os << ")";
}
But you can't recurse all the way into l or r from iff and you can't directly generate any "not" or "or" text until you have recursively reached the bottom. So with the mis design of transforming while printing, the iff routine would need to generate a temp object representing (l or not r) and then call the or processing routine to handle it, then output "AND" then create a temp object representing (not l or r) and call the or processing routine to handle it.
Similarly, the or processing routine would need to look at each operand. If each is simply a final variable or not of a final variable, or can simply emit itself to the stream. But if any operand is more complicated, or must do something more complicated.
In addition to doing transformation before you start printing, there are a couple other things you might change in order to make the code simpler:
First, you could avoid a lot of trouble by having or and and objects each hold a std::set of any number of operands, rather than a pair of operands. The big cost of that is you need a decent comparison function for the objects. But the pay back is worth the trouble of having a comparison function.
Next, you might consider having a single type for all subexpressions, rather than having a type for each operator. So each object must store an operator and a std::set of operands. There are some pretty big and obvious disadvantages to that design choice, but there is one big advantage: A subexpression can transform itself into a different kind.
The more common subexpression transformation scheme (which might still be best, just consider alternatives) is for the owner of a subexpression to ask the subexpression to conditionally generate a transformed clone of itself. That is more efficient than having objects able to directly transform themselves. But getting the coding details right requires more thought.
Another good choice for this grammar is to do all the transformations while parsing. More complicated problems really deserve the full split of parse, transform, print. But in this case transform fits beautifully into parsing if you think through your factory function:
The factory takes an operator and one (for NOT) or two subexpressions that are already CNF. It produces a new CNF expression:
AND:
a) Both inputs are AND's, form the union of their sets.
b) One input is an AND, insert the other input into that one's set.
c) Neither input is an AND, create a new AND with those two inputs.
OR:
a) Both inputs are OR's, form the union of their sets.
b) One input is an OR and the other is primitive or NOT, insert the other input into the OR's set.
c) At least one input is an AND, distribute the other input across that AND (the distribute function must handle the ugly sub cases).
NOT:
Inversion of a primitive is trivial. Inversion of a NOT is trivial. Inversion of an OR is pretty trivial. Inversion of an AND is the ugliest thing in this whole design (you need to turn the whole thing inside out) but is doable. To keep your sanity, you could forget about efficiency and use the factory recursively for the NOT and OR operations that a NOT AND trivially transforms to (but which need further transformation to get back to CNF).
IFF and IMP: Just make the appropriate several calls to the basic factories.
Inspired by what little I know about Boost.Proto I've tried to modify your code to allow for independent ast transformations. This approach uses 4 passes (eliminate_iff, eliminate_imp, distribute_nots and distribute_ors) and in each one it rebuilds the ast. There may be a way to do the same in a single pass, probably with better performance, but I think that approach would be (even) harder to understand.
Explanation of the changes:
The first change is a little gratuitous but I really think that all the phx::construct...s make the grammar harder to read. The grammar I use is:
iff_ = as_iff[imp_ >> "iff" >> iff_] | imp_;
imp_ = as_imp[or_ >> "imp" >> imp_] | or_;
or_ = as_or[and_ >> "or" >> or_] | and_;
and_ = as_and[not_ >> "and" >> and_] | not_;
not_ = as_not["not" > simple] | simple;
In order to be able to use this you need to adapt unop and binop using BOOST_FUSION_ADAPT_TPL_STRUCT and declare as_xxx as:
const as<binop<op_xxx>> as_xxx={};
If you don't like this change your original grammar should also work (if you add a using namespace ast;).
I've put everything related to the AST inside namespace ast and made a few additions:
enum class expr_type: the order of its enumerators needs to be kept in synch with the parameters in the variant. It is used to check whether one of a node's children has a particular type.
get_expr_type: simply returns what is the type of the expression.
printer: now it just prints the expression passed, without making any transformation. Maybe it could be changed to be smarter about the placing of parentheses.
operators !, && and ||: they are used to make the rebuilding of the AST easier.
And finally the transformations. Every transformation uses ast_helper<Transformation> as its base. This struct has several reused member functions:
pass_through: creates a node of the same type that has as members, the result of transforming the original members.
recurse: applies the transformation to the current node.
left: gets the first member of a node independently of the type of the node. Gets used in the more complex transformations to slightly help with readability.
child0: exactly the same as left, but the name makes more sense in unary nodes.
right: gets the second member of a node.
eliminate_imp :
This one is really easy:
If you get a binop<op_imp> return !p || q. Where p and q are the result of applying the transformation to the first and second operands respectively.
If you get anything else return a node of the same kind applying the transformation to its operands(pass_through).
eliminate_iff :
It's basically the same, changing binop<op_iff> with (p || !q)&&(!p || q).
distribute_nots :
If you get anything that is not a unop<op_not> simply pass_through.
If you get a unop<op_not>, first check the type of its operand:
If it's an and, substitute with !p || !q.
If it's an or, substitute with !p && !q.
If it's a not, substitute with p.
distribute_ors :
If it's anything but an or, pass_through.
If it's an or:
Check whether its first operand is an and. If it is distribute the ors and apply the transformation again in case another or->and is there.
Check whether its second operand is an and. Do the analogous work.
If neither direct child is an and, check recursively if there is any and in the subtree starting with this node. If there is it'll end up floating to the top so we'll need to recurse on the pass_through.
If there isn't any and in the subtree, it is already in CNF and simply pass_through.
Running on Ideone
Full Code:
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_wrapper.hpp>
namespace qi = boost::spirit::qi;
// Abstract data type
struct op_or {};
struct op_and {};
struct op_imp {};
struct op_iff {};
struct op_not {};
namespace ast
{
typedef std::string var;
template <typename tag> struct binop;
template <typename tag> struct unop;
enum class expr_type { var = 0, not_, and_, or_, imp, iff };
typedef boost::variant<var,
boost::recursive_wrapper<unop <op_not> >,
boost::recursive_wrapper<binop<op_and> >,
boost::recursive_wrapper<binop<op_or> >,
boost::recursive_wrapper<binop<op_imp> >,
boost::recursive_wrapper<binop<op_iff> >
> expr;
expr_type get_expr_type(const expr& expression)
{
return static_cast<expr_type>(expression.which());
}
template <typename tag> struct binop
{
expr oper1, oper2;
};
template <typename tag> struct unop
{
expr oper1;
};
struct printer : boost::static_visitor<void>
{
printer(std::ostream& os) : _os(os) {}
std::ostream& _os;
mutable bool first{ true };
//
void operator()(const ast::var& v) const { _os << v; }
void operator()(const ast::binop<op_and>& b) const { print(" and ", b.oper1, b.oper2); }
void operator()(const ast::binop<op_or>& b) const { print(" or ", b.oper1, b.oper2); }
void operator()(const ast::binop<op_iff>& b) const { print(" iff ", b.oper1, b.oper2); }
void operator()(const ast::binop<op_imp>& b) const { print(" imp ", b.oper1, b.oper2); }
void print(const std::string& op, const ast::expr& l, const ast::expr& r) const
{
_os << "(";
boost::apply_visitor(*this, l);
_os << op;
boost::apply_visitor(*this, r);
_os << ")";
}
void operator()(const ast::unop<op_not>& u) const
{
_os << "not(";
boost::apply_visitor(*this, u.oper1);
_os << ")";
}
};
std::ostream& operator<<(std::ostream& os, const expr& e)
{
boost::apply_visitor(printer(os), e); return os;
}
expr operator!(const expr& e)
{
return unop<op_not>{e};
}
expr operator||(const expr& l, const expr& r)
{
return binop<op_or>{l, r};
}
expr operator&&(const expr& l, const expr& r)
{
return binop<op_and>{l, r};
}
}
BOOST_FUSION_ADAPT_TPL_STRUCT(
(Tag),
(ast::binop) (Tag),
(ast::expr, oper1)
(ast::expr, oper2)
)
BOOST_FUSION_ADAPT_TPL_STRUCT(
(Tag),
(ast::unop) (Tag),
(ast::expr, oper1)
)
// Grammar rules
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, ast::expr(), Skipper>
{
parser() : parser::base_type(expr_)
{
using namespace qi;
const as<ast::binop<op_iff> > as_iff = {};
const as<ast::binop<op_imp> > as_imp = {};
const as<ast::binop<op_or> > as_or = {};
const as<ast::binop<op_and> > as_and = {};
const as<ast::unop<op_not> > as_not = {};
expr_ = iff_.alias();
iff_ = as_iff[imp_ >> "iff" >> iff_] | imp_;
imp_ = as_imp[or_ >> "imp" >> imp_] | or_;
or_ = as_or[and_ >> "or" >> or_] | and_;
and_ = as_and[not_ >> "and" >> and_] | not_;
not_ = as_not["not" > simple] | simple;
simple = (('(' > expr_ > ')') | var_);
var_ = qi::lexeme[+alpha];
BOOST_SPIRIT_DEBUG_NODE(expr_);
BOOST_SPIRIT_DEBUG_NODE(iff_);
BOOST_SPIRIT_DEBUG_NODE(imp_);
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, ast::var(), Skipper> var_;
qi::rule<It, ast::expr(), Skipper> not_, and_, or_, imp_, iff_, simple, expr_;
};
template <typename Transform>
struct ast_helper : boost::static_visitor<ast::expr>
{
template <typename Tag>
ast::expr pass_through(const ast::binop<Tag>& op) const
{
return ast::binop<Tag>{recurse(op.oper1), recurse(op.oper2)};
}
template <typename Tag>
ast::expr pass_through(const ast::unop<Tag>& op) const
{
return ast::unop<Tag>{recurse(op.oper1)};
}
ast::expr pass_through(const ast::var& variable) const
{
return variable;
}
ast::expr recurse(const ast::expr& expression) const
{
return boost::apply_visitor(Transform{}, expression);
}
struct left_getter:boost::static_visitor<ast::expr>
{
template< template<class> class Op,typename Tag>
ast::expr operator()(const Op<Tag>& op) const
{
return op.oper1;
}
ast::expr operator()(const ast::var&) const
{
return{};//throw something?
}
};
ast::expr left(const ast::expr& expression) const
{
return boost::apply_visitor(left_getter{}, expression);
}
ast::expr child0(const ast::expr& expression) const
{
return left(expression);
}
struct right_getter :boost::static_visitor<ast::expr>
{
template<typename Tag>
ast::expr operator()(const ast::binop<Tag>& op) const
{
return op.oper2;
}
template<typename Expr>
ast::expr operator()(const Expr&) const
{
return{};//throw something?
}
};
ast::expr right(const ast::expr& expression) const
{
return boost::apply_visitor(right_getter{}, expression);
}
};
struct eliminate_imp : ast_helper<eliminate_imp>
{
template <typename Op>
ast::expr operator()(const Op& op) const
{
return pass_through(op);
}
ast::expr operator()(const ast::binop<op_imp>& imp) const
{
return !recurse(imp.oper1) || recurse(imp.oper2);
}
ast::expr operator()(const ast::expr& expression) const
{
return recurse(expression);
}
};
struct eliminate_iff : ast_helper<eliminate_iff>
{
template <typename Op>
ast::expr operator()(const Op& op) const
{
return pass_through(op);
}
ast::expr operator()(const ast::binop<op_iff>& imp) const
{
return (recurse(imp.oper1) || !recurse(imp.oper2)) && (!recurse(imp.oper1) || recurse(imp.oper2));
}
ast::expr operator()(const ast::expr& expression) const
{
return recurse(expression);
}
};
struct distribute_nots : ast_helper<distribute_nots>
{
template <typename Op>
ast::expr operator()(const Op& op) const
{
return pass_through(op);
}
ast::expr operator()(const ast::unop<op_not>& not_) const
{
switch (ast::get_expr_type(not_.oper1)) //There is probably a better solution
{
case ast::expr_type::and_:
return recurse(!recurse(left(not_.oper1))) || recurse(!recurse(right(not_.oper1)));
case ast::expr_type::or_:
return recurse(!recurse(left(not_.oper1))) && recurse(!recurse(right(not_.oper1)));
case ast::expr_type::not_:
return recurse(child0(not_.oper1));
default:
return pass_through(not_);
}
}
ast::expr operator()(const ast::expr& expression) const
{
return recurse(expression);
}
};
struct any_and_inside : boost::static_visitor<bool>
{
any_and_inside(const ast::expr& expression) :expression(expression) {}
const ast::expr& expression;
bool operator()(const ast::var&) const
{
return false;
}
template <typename Tag>
bool operator()(const ast::binop<Tag>& op) const
{
return boost::apply_visitor(*this, op.oper1) || boost::apply_visitor(*this, op.oper2);
}
bool operator()(const ast::binop<op_and>&) const
{
return true;
}
template<typename Tag>
bool operator()(const ast::unop<Tag>& op) const
{
return boost::apply_visitor(*this, op.oper1);
}
explicit operator bool() const
{
return boost::apply_visitor(*this, expression);
}
};
struct distribute_ors : ast_helper<distribute_ors>
{
template <typename Op>
ast::expr operator()(const Op& op) const
{
return pass_through(op);
}
ast::expr operator()(const ast::binop<op_or>& or_) const
{
if (ast::get_expr_type(or_.oper1) == ast::expr_type::and_)
{
return recurse(recurse(left(or_.oper1)) || recurse(or_.oper2))
&& recurse(recurse(right(or_.oper1)) || recurse(or_.oper2));
}
else if (ast::get_expr_type(or_.oper2) == ast::expr_type::and_)
{
return recurse(recurse(or_.oper1) || recurse(left(or_.oper2)))
&& recurse(recurse(or_.oper1) || recurse(right(or_.oper2)));
}
else if (any_and_inside( or_ ))
{
return recurse(recurse(or_.oper1) || recurse(or_.oper2));
}
else
{
return pass_through(or_);
}
}
ast::expr operator()(const ast::expr& expression) const
{
return recurse(expression);
}
};
ast::expr to_CNF(const ast::expr& expression)
{
return distribute_ors()(distribute_nots()(eliminate_iff()(eliminate_imp()(expression))));
}
// Test some examples in main and check the order of precedence
int main()
{
for (auto& input : std::list<std::string>{
// Test the order of precedence
"(a and b) imp ((c and d) or (a and b));",
"a and b iff (c and d or a and b);",
"a and b imp (c and d or a and b);",
"not a or not b;",
"a or b;",
"not a and b;",
"not (a and b);",
"a or b or c;",
"aaa imp bbb iff ccc;",
"aaa iff bbb imp ccc;",
// Test elimination of equivalences
"a iff b;",
"a iff b or c;",
"a or b iff b;",
"a iff b iff c;",
// Test elimination of implications
"p imp q;",
"p imp not q;",
"not p imp not q;",
"p imp q and r;",
"p imp q imp r;"
})
{
auto f(std::begin(input)), l(std::end(input));
parser<decltype(f)> p;
try
{
ast::expr result;
bool ok = qi::phrase_parse(f, l, p > ';', qi::space, result);
if (!ok)
std::cerr << "invalid input\n";
else
{
std::cout << "original: " << result << "\n";
std::cout << "CNF: " << to_CNF(result) << "\n";
}
}
catch (const qi::expectation_failure<decltype(f)>& 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 0;
}

How to calculate boolean expression in Spirit

I found a really good example about boolean translator,
* Boolean expression (grammar) parser in c++
What I am thinking now is to do a further step, translate (!T|F)&T into F or 0, so it is very convenient for calculating a very long boolean expression.
Is there some examples about this using spirit? What I have done is making a calculator first, and then let it calculate '(T+!F*T)', which equal to (T||!F&&T)but when I type (), there is an error. How to modify it? Thanks a lot!
#include <iostream>
#include <stack>
#include <boost/lexical_cast.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
using namespace std;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct calculator
{
bool interpret(const string& s);
void do_neg();
void do_add();
void do_sub();
void do_mul();
void do_div();
void do_number(const char* first, const char* last);
int val() const;
private:
stack<int> values_;
int *pn1_, n2_;
void pop_1();
void pop_2();
};
template <typename Iterator>
struct calc_grammar : qi::grammar<Iterator, ascii::space_type>
{
calc_grammar(calculator& calc)
: calc_grammar::base_type(add_sub_expr)
, calc_(calc)
{
using namespace qi;
using boost::iterator_range;
#define LAZY_FUN0(f) phoenix::bind(&calculator::f, calc_)
#define LAZY_FUN2(f) phoenix::bind(&calculator::f, calc_, phoenix::bind(&iterator_range<Iterator>::begin, qi::_1), phoenix::bind(&iterator_range<Iterator>::end, qi::_1))
add_sub_expr =
(
-lit('+') >> mul_div_expr |
(lit('-') >> mul_div_expr)[LAZY_FUN0(do_neg)]
) >>
*(
lit('+') >> mul_div_expr[LAZY_FUN0(do_add)] |
lit('-') >> mul_div_expr[LAZY_FUN0(do_sub)]
) >> eoi;
mul_div_expr =
basic_expr >>
*(
lit('*') >> basic_expr[LAZY_FUN0(do_mul)] |
lit('/') >> basic_expr[LAZY_FUN0(do_div)]
);
basic_expr =
raw[number][LAZY_FUN2(do_number)] |
lit('(') >> add_sub_expr >> lit(')');
number = lexeme[+digit];
}
qi::rule<Iterator, ascii::space_type> add_sub_expr, mul_div_expr, basic_expr, number;
calculator& calc_;
};
bool calculator::interpret(const string& s)
{
calc_grammar<const char*> g(*this);
const char* p = s.c_str();
return qi::phrase_parse(p, p + s.length(), g, ascii::space);
}
void calculator::pop_1()
{
pn1_ = &values_.top();
}
void calculator::pop_2()
{
n2_ = values_.top();
values_.pop();
pop_1();
}
void calculator::do_number(const char* first, const char* last)
{
string str(first, last);
int n = boost::lexical_cast<int>(str);
values_.push(n);
}
void calculator::do_neg()
{
pop_1();
*pn1_ = -*pn1_;
}
void calculator::do_add()
{
pop_2();
*pn1_ += n2_;
}
void calculator::do_sub()
{
pop_2();
*pn1_ -= n2_;
}
void calculator::do_mul()
{
pop_2();
*pn1_ *= n2_;
}
void calculator::do_div()
{
pop_2();
*pn1_ /= n2_;
}
int calculator::val() const
{
assert(values_.size() == 1);
return values_.top();
}
int main()
{
for(;;){
cout << ">>> ";
string s;
getline(cin, s);
if(s.empty()) break;
calculator calc;
if(calc.interpret(s))
cout << calc.val() << endl;
else
cout << "syntax error" << endl;
}
return 0;
}
Here goes a quick and dirty demo based on my old Boolean Parser answer. This is a visitor that evaluates the AST you pass it:
struct eval : boost::static_visitor<bool>
{
eval() {}
//
bool operator()(const var& v) const
{
if (v=="T" || v=="t" || v=="true" || v=="True")
return true;
else if (v=="F" || v=="f" || v=="false" || v=="False")
return false;
return boost::lexical_cast<bool>(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); }
};
bool evaluate(const expr& e)
{ return boost::apply_visitor(eval(), e); }
I hope I can find some time later to explain. Note that _var is a misnomer now, since you wanted to treat all operands as literals. Also note that the evaluation of a literal is a bit ... quick and dirty right now :)
Full Code
Live On Coliru
#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>
#include <boost/lexical_cast.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 eval : boost::static_visitor<bool>
{
eval() {}
//
bool operator()(const var& v) const
{
if (v=="T" || v=="t" || v=="true" || v=="True")
return true;
else if (v=="F" || v=="f" || v=="false" || v=="False")
return false;
return boost::lexical_cast<bool>(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); }
};
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 evaluate(const expr& e)
{ return boost::apply_visitor(eval(), 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_ ) [ _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_ = 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_;
};
int main()
{
const std::string inputs[] = {
std::string("true & false;"),
std::string("true & !false;"),
std::string("!true & false;"),
std::string("true | false;"),
std::string("true | !false;"),
std::string("!true | false;"),
std::string("T&F;"),
std::string("T&!F;"),
std::string("!T&F;"),
std::string("T|F;"),
std::string("T|!F;"),
std::string("!T|F;"),
std::string("") // marker
};
for (const std::string *i = inputs; !i->empty(); ++i)
{
typedef std::string::const_iterator It;
It f(i->begin()), l(i->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";
std::cout << "evaluated:\t" << evaluate(result) << "\n";
}
} 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 0;
}
Output:
result: (true & false)
evaluated: 0
result: (true & (!false))
evaluated: 1
result: ((!true) & false)
evaluated: 0
result: (true | false)
evaluated: 1
result: (true | (!false))
evaluated: 1
result: ((!true) | false)
evaluated: 0
result: (T & F)
evaluated: 0
result: (T & (!F))
evaluated: 1
result: ((!T) & F)
evaluated: 0
result: (T | F)
evaluated: 1
result: (T | (!F))
evaluated: 1
result: ((!T) | F)
evaluated: 0