I wanted to extend the calc example in the boost spirit documentation to add a relational operator to check whether 2 strings are equal like "abc" = "abc" . Facing some issues while extending the calc to compare 2 strings.
Any help in fixing this would be appreciated..
The code I'm trying to implement:
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST. The AST,
// once created, is traversed, 1) To print its contents and
// 2) To evaluate the result.
//
// [ JDG April 28, 2008 ] For BoostCon 2008
// [ JDG February 18, 2011 ] Pure attributes. No semantic actions.
//
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include <iostream>
#include <string>
#include <list>
#include <boost/config/warning_disable.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/foreach.hpp>
#include <boost/spirit/include/qi.hpp>
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST
///////////////////////////////////////////////////////////////////////////
struct nil {};
struct signed_;
struct program;
typedef boost::variant<
nil
, unsigned int
, bool
, boost::recursive_wrapper<signed_>
, boost::recursive_wrapper<program>
>
operand;
struct signed_
{
char sign;
operand operand_;
};
struct operation
{
char operator_;
operand operand_;
};
struct program
{
operand first;
std::list<operation> rest;
};
}}
BOOST_FUSION_ADAPT_STRUCT(
client::ast::signed_,
(char, sign)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::operation,
(char, operator_)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::program,
(client::ast::operand, first)
(std::list<client::ast::operation>, rest)
)
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST Printer
///////////////////////////////////////////////////////////////////////////
struct printer
{
typedef void result_type;
void operator()(nil) const {}
void operator()(unsigned int n) const { std::cout << n; }
void operator()(const bool &n) const { std::cout << n; }
void operator()(operation const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case ' ': std::cout << " add"; break;
case '-': std::cout << " subt"; break;
case '*': std::cout << " mult"; break;
case '/': std::cout << " div"; break;
case '|': std::cout << " or"; break;
case '&': std::cout << " and"; break;
}
}
void operator()(signed_ const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': std::cout << " neg"; break;
case ' ': std::cout << " pos"; break;
case '!': std::cout << " not"; break;
}
}
void operator()(program const& x) const
{
boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
std::cout << ' ';
(*this)(oper);
}
}
};
///////////////////////////////////////////////////////////////////////////
// The AST evaluator
///////////////////////////////////////////////////////////////////////////
struct eval
{
typedef int result_type;
int operator()(nil) const { BOOST_ASSERT(0); return 0; }
int operator()(unsigned int n) const { return n; }
int operator()(const bool &n) const { return n; }
int operator()(operation const& x, int lhs) const
{
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case ' ': return lhs rhs;
case '-': return lhs - rhs;
case '*': return lhs * rhs;
case '/': return lhs / rhs;
case '|': return lhs | rhs;
case '&': return lhs & rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(signed_ const& x) const
{
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': return -rhs;
case ' ': return rhs;
case '!': return !rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(program const& x) const
{
int state = boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
state = (*this)(oper, state);
}
return state;
}
};
}}
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// The calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ast::program(), ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::uint_type uint_;
qi::char_type char_;
qi::bool_type bool_;
qi::_val_type _val;
qi::_1_type _1;
qi::_2_type _2;
qi::lexeme_type lexeme;
qi::as_string_type as_string;
// qi::string_type string;
expression =
comparison
|
term
>> *( (char_(' ') >> term)
| (char_('-') >> term)
| (char_('|') >> term)
// | (string("or") >> term)
)
;
term =
factor
>> *( (char_('*') >> factor)
| (char_('/') >> factor)
| (char_('&') >> term)
// | (string("and") >> term)
)
;
str = lexeme[ (char_ - '=')] [_val = _1];
comparison = str >> '=' >> '=' >> str [_val = (as_string(_1) == as_string(_2))];
factor =
uint_
| bool_
| '(' >> expression >> ')'
| (char_('-') >> factor)
| (char_(' ') >> factor)
| (char_('!') >> factor)
// | (string("not") >> factor)
;
}
qi::rule<Iterator, ast::program(), ascii::space_type> expression;
qi::rule<Iterator, ast::program(), ascii::space_type> term;
qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
qi::rule<Iterator , bool() > comparison;
qi::rule<Iterator , std::string()> str;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
typedef client::ast::program ast_program;
typedef client::ast::printer ast_print;
typedef client::ast::eval ast_eval;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
calculator calc; // Our grammar
ast_program program; // Our program (AST)
ast_print print; // Prints the program
ast_eval eval; // Evaluates the program
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
boost::spirit::ascii::space_type space;
bool r = phrase_parse(iter, end, calc, space, program);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
print(program);
std::cout << "\nResult: " << eval(program) << std::endl;
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \" " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
Error generated:
In file included from /usr/include/boost/phoenix/core/actor.hpp:20,
from /usr/include/boost/phoenix/core.hpp:12,
from /usr/include/boost/spirit/include/phoenix_core.hpp:11,
from /usr/include/boost/spirit/home/support/make_component.hpp:15,
from /usr/include/boost/spirit/home/support/meta_compiler.hpp:20,
from /usr/include/boost/spirit/home/qi/meta_compiler.hpp:14,
from /usr/include/boost/spirit/home/qi/action/action.hpp:14,
from /usr/include/boost/spirit/home/qi/action.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:14,
from /usr/include/boost/spirit/include/qi.hpp:16,
from integrate3.cpp:38:
/usr/include/boost/phoenix/core/is_nullary.hpp: In instantiation of ‘struct boost::phoenix::result_of::is_nullary<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2>, void>’:
/usr/include/boost/phoenix/core/actor.hpp:119:13: required from ‘struct boost::phoenix::result_of::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2> >’
/usr/include/boost/phoenix/core/actor.hpp:154:9: required from ‘struct boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2> >’
integrate3.cpp:238:48: required from ‘client::calculator<Iterator>::calculator() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
integrate3.cpp:285:20: required from here
/usr/include/boost/phoenix/core/is_nullary.hpp:115:16: error: base type ‘boost::phoenix::evaluator::impl<const boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2>&, boost::phoenix::vector2<mpl_::bool_<true>, boost::phoenix::is_nullary>, boost::proto::envns_::empty_env>::result_type’ {aka ‘const boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::phoenix::actor<boost::spirit::argument<0> > >, 2>&’} fails to be a struct or class type
115 | struct is_nullary
| ^~~~~~~~~~
In file included from /usr/include/boost/phoenix/core.hpp:12,
from /usr/include/boost/spirit/include/phoenix_core.hpp:11,
from /usr/include/boost/spirit/home/support/make_component.hpp:15,
from /usr/include/boost/spirit/home/support/meta_compiler.hpp:20,
from /usr/include/boost/spirit/home/qi/meta_compiler.hpp:14,
from /usr/include/boost/spirit/home/qi/action/action.hpp:14,
from /usr/include/boost/spirit/home/qi/action.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:14,
from /usr/include/boost/spirit/include/qi.hpp:16,
from integrate3.cpp:38:
/usr/include/boost/phoenix/core/actor.hpp: In instantiation of ‘struct boost::phoenix::result_of::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2> >’:
/usr/include/boost/phoenix/core/actor.hpp:154:9: required from ‘struct boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2> >’
integrate3.cpp:238:48: required from ‘client::calculator<Iterator>::calculator() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
integrate3.cpp:285:20: required from here
/usr/include/boost/phoenix/core/actor.hpp:119:13: error: ‘value’ is not a member of ‘boost::phoenix::result_of::is_nullary<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::phoenix::actor<boost::spirit::argument<0> > >, 2>, void>’
119 | type;
| ^~~~
In file included from /usr/include/boost/phoenix/core/actor.hpp:20,
from /usr/include/boost/phoenix/core.hpp:12,
from /usr/include/boost/spirit/include/phoenix_core.hpp:11,
from /usr/include/boost/spirit/home/support/make_component.hpp:15,
from /usr/include/boost/spirit/home/support/meta_compiler.hpp:20,
from /usr/include/boost/spirit/home/qi/meta_compiler.hpp:14,
from /usr/include/boost/spirit/home/qi/action/action.hpp:14,
from /usr/include/boost/spirit/home/qi/action.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:14,
from /usr/include/boost/spirit/include/qi.hpp:16,
from integrate3.cpp:38:
/usr/include/boost/phoenix/core/is_nullary.hpp: In instantiation of ‘struct boost::phoenix::result_of::is_nullary<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, boost::proto::exprns_::expr<boost::proto::tagns_::tag::equal_to, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::function, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::as_string>, 0>&, const boost::phoenix::actor<boost::spirit::argument<0> >&>, 2>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::function, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::as_string>, 0>&, const boost::phoenix::actor<boost::spirit::argument<1> >&>, 2>&>, 2> >, 2>, void>’:
/usr/include/boost/phoenix/core/actor.hpp:119:13: required from ‘struct boost::phoenix::result_of::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::phoenix::actor<boost::spirit::attribute<0> >, integrate3.cpp:218:24: required from ‘client::calculator<Iterator>::calculator() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
integrate3.cpp:285:20: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:153:20: error: no matching function for call to ‘client::ast::program::program(const bool&)’
153 | attr = static_cast<Attribute>(val);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
integrate3.cpp:70:12: note: candidate: ‘client::ast::program::program()’
70 | struct program
| ^~~~~~~
integrate3.cpp:70:12: note: candidate expects 0 arguments, 1 provided
integrate3.cpp:70:12: note: candidate: ‘client::ast::program::program(const client::ast::program&)’
integrate3.cpp:70:12: note: no known conversion for argument 1 from ‘const bool’ to ‘const client::ast::program&’
integrate3.cpp:70:12: note: candidate: ‘client::ast::program::program(client::ast::program&&)’
integrate3.cpp:70:12: note: no known conversion for argument 1 from ‘const bool’ to ‘client::ast::program&&’
I just want expressions like "abc" == "xyz" to be evaluated as bool and further try boolean logic on them. ("abc" == "xyz" or "ab" == "ab") .. Pls help
CodePudding user response:
str = lexeme[ (char_ - '=')] [_val = _1];
That's... not even close to your required grammar. Why not e.g.
str = '"' >> *~char_('"') >> '"';
Where (a) automatic attribute propagation is used and (b) the rule is already lexeme (since it has no skipper).
You hard code a string comparison grammar like:
You probably don't want to special-case it like this:
expression = comparison | ...
Instead, consider a string a factor (like uint) and =
a binary expression (like '/').
Now on the evaluator side you have complicated things. Previously, all values were int. Now, you apparently try to return bools:
int operator()(const bool &n) const { return n; }
And, worse, you should be supporting strings, but your variant can't even hold them.
Fixing the variant:
using operand = boost::variant<
nil
, unsigned int
, bool
, std::string
, boost::recursive_wrapper<signed_>
, boost::recursive_wrapper<program>
>;
Add printing support:
void operator()(bool n) const { std::cout << std::boolalpha << n; }
void operator()(std::string const& s) const { std::cout << std::quoted(s); }
Evaluation becomes a large task because now you have to provide evaluations for all operands between bool/string/int. I won't get into that today.
Simplest Direct Answer
Instead, let's take your question literally (although it doesn't seem all that useful in its own right):
expression = term >> *(qi::char_("- |&") >> term);
term = factor >> *(qi::char_("*/!") >> factor);
str = '"' >> *~qi::char_('"') >> '"';
comparison = (str >> '=' >> str)[_val = (_1 == _2)];
factor = comparison | qi::uint_ | qi::bool_ | qi::char_("- !") >> factor |
'(' >> expression >> ')';
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <iomanip>
#include <list>
namespace client { namespace ast {
struct signed_;
struct program;
using operand = boost::variant<unsigned int, bool, //
boost::recursive_wrapper<signed_>,
boost::recursive_wrapper<program>>;
struct signed_ { char sign; operand operand_; };
struct operation { char operator_; operand operand_; };
struct program { operand first; std::list<operation> rest; };
}} // namespace client::ast
BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_, sign, operand_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, operator_, operand_)
BOOST_FUSION_ADAPT_STRUCT(client::ast::program, first, rest)
namespace client { namespace ast
{
struct printer {
using result_type = void;
void operator()(auto const& v) const { std::cout << v; }
void operator()(operation const& x) const {
boost::apply_visitor(*this, x.operand_);
switch (x.operator_) {
case ' ': std::cout << " add"; break;
case '-': std::cout << " subt"; break;
case '*': std::cout << " mult"; break;
case '/': std::cout << " div"; break;
case '|': std::cout << " or"; break;
case '&': std::cout << " and"; break;
}
}
void operator()(signed_ const& x) const {
boost::apply_visitor(*this, x.operand_);
switch (x.sign) {
case '-': std::cout << " neg"; break;
case ' ': std::cout << " pos"; break;
case '!': std::cout << " not"; break;
}
}
void operator()(program const& x) const {
boost::apply_visitor(*this, x.first);
for (auto& oper : x.rest) {
std::cout << ' ';
(*this)(oper);
}
}
};
///////////////////////////////////////////////////////////////////////////
// The AST evaluator
///////////////////////////////////////////////////////////////////////////
struct eval {
using result_type = int;
int operator()(auto const& n) const { return n; }
int operator()(operation const& x, int lhs) const {
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.operator_) {
case ' ': return lhs rhs;
case '-': return lhs - rhs;
case '*': return lhs * rhs;
case '/': return lhs / rhs;
case '|': return lhs | rhs;
case '&': return lhs & rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(signed_ const& x) const {
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.sign) {
case '-': return -rhs;
case ' ': return rhs;
case '!': return !rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(program const& x) const {
int state = boost::apply_visitor(*this, x.first);
for (auto& oper : x.rest) {
state = (*this)(oper, state);
}
return state;
}
};
}} // namespace client::ast
namespace client
{
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ast::program(), qi::space_type> {
calculator() : calculator::base_type(expression) {
using namespace qi::labels;
expression = term >> *(qi::char_("- |&") >> term);
term = factor >> *(qi::char_("*/!") >> factor);
str = '"' >> *~qi::char_('"') >> '"';
comparison = (str >> '=' >> str)[_val = (_1 == _2)];
factor = comparison | qi::uint_ | qi::bool_ | qi::char_("- !") >> factor |
'(' >> expression >> ')';
}
private:
qi::rule<Iterator, ast::program(), qi::space_type> expression, term;
qi::rule<Iterator, ast::operand(), qi::space_type> factor, comparison;
qi::rule<Iterator, std::string()> str;
};
}
int main() {
using iterator_type = std::string::const_iterator;
using calculator = client::calculator<iterator_type>;
using ast_program = client::ast::program;
using ast_print = client::ast::printer;
using ast_eval = client::ast::eval;
calculator const calc; // Our grammar
ast_print const print; // Prints the program
ast_eval const eval; // Evaluates the program
for (std::string const str : {
"42",
"3 (7*8)",
"7*8 3",
"7 8*3",
"\"abc\" = \"abc\"",
"\"abc\" = \"ABC\"",
}) {
ast_program program;
auto f = str.begin(), l = str.end();
bool r = phrase_parse(f, l, calc, client::qi::space, program);
if (r && f == l) {
std::cout << "\nResult: " << quoted(str) << " -> " << eval(program) << std::endl;
print(program);
}
else
std::cout << "\nFailed at: " << quoted(std::string (f, l)) << "\n";
}
}
Prints
Result: "42" -> 42
42
Result: "3 (7*8)" -> 59
3 7 8 mult add
Result: "7*8 3" -> 59
7 8 mult 3 add
Result: "7 8*3" -> 31
7 8 3 mult add
Result: "\"abc\" = \"abc\"" -> 1
1
Result: "\"abc\" = \"ABC\"" -> 0
0