srossi
srossi

Reputation: 132

Defining a symbol which may be part of a literal function in boost spirit

I am trying to read a mathematical function which depends on the symbol t with boost::spirit.

In the example below, I'm trying evaluate the function "tan(t)" in t=1.2. Instead of having

Exit: 1, value = 2.5721 

I get

Exit: 1, value = 1.2 

I understand that when I try to read the function "tan(t)", instead of computing the tangent of t, the value of t is assigned to the first letter in the word tan. Is it possible to circumvent this behavior, without changing the symbol t? Moreover, shouldn't the parse fail?

#include <string>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi_symbols.hpp>
#include <boost/phoenix/stl/cmath.hpp>

namespace qi = boost::spirit::qi;
namespace ascii=boost::spirit::ascii;
using boost::spirit::ascii::space;
using boost::spirit::qi::symbols;


template< typename Iterator >
struct Grammar : public qi::grammar<  Iterator, double(), ascii::space_type >
{

    Grammar() : Grammar::base_type(expression)
    {
        using qi::double_;
        using qi::_val;
        using qi::_1;

        expression = double_                   [_val = _1]
                    | symbol                   [_val = _1]
                    | function                 [_val = _1]
                    | group                    [_val = _1];

        function =  qi::lit("tan")  >> group   [_val = boost::phoenix::tan(_1)];
        group = '(' >> expression   [_val = _1] >> ')' ;
  }

    qi::rule<Iterator, double(), ascii::space_type> expression, function, group;
    qi::symbols<char, double > symbol;
};


int main()
{
    typedef std::string::iterator iterator;
    Grammar<iterator> grammar;
    std::string function = "tan(t)"; //it would work if function = "tan(x)"
    grammar.symbol.add("t",1.2);     // and add("x",1.2)
    double value;
    bool r = qi::phrase_parse(function.begin(), function.end(), grammar, space, value);
    std::cout << "Exit: " << r << ", value = " << value << std::endl;

    return 0;
}

Upvotes: 2

Views: 277

Answers (1)

sehe
sehe

Reputation: 393114

You have to reorder you rules. Your symbol (t) is eating the first letter of tan. So, you're not actually parsing all input at all!

If you enable debugging you'd see this output:

<expression>
<try>tan(t)</try>
<success>an(t)</success>
<attributes>[1.2]</attributes>
</expression>
Exit: 1, value = 1.2

The "Royal Way" to fix this is using the Qi Distinct Keyword directive from the Spirit Repository: boost::spirit::qi keywords and identifiers

Live On Coliru

#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi_symbols.hpp>
#include <boost/spirit/repository/include/qi_distinct.hpp>
#include <boost/phoenix/stl/cmath.hpp>

namespace qi = boost::spirit::qi;
namespace ascii=boost::spirit::ascii;
using boost::spirit::ascii::space;
using boost::spirit::qi::symbols;


template< typename Iterator >
struct Grammar : public qi::grammar<  Iterator, double(), ascii::space_type >
{

    Grammar() : Grammar::base_type(expression)
    {
        using qi::double_;
        using qi::_val;
        using qi::_1;
        using boost::spirit::repository::qi::distinct;

        expression = double_
                   | distinct(qi::char_("a-zAZ09_")) [ symbol ]
                   | function
                   | group;

        function =  "tan"  >> group [_val = boost::phoenix::tan(_1)];
        group = '(' >> expression >> ')' ;

        BOOST_SPIRIT_DEBUG_NODES((expression)(function)(group));
  }

    qi::rule<Iterator, double(), ascii::space_type> expression, function, group;
    qi::symbols<char, double > symbol;
};


int main()
{
    typedef std::string::iterator iterator;
    Grammar<iterator> grammar;
    std::string function = "tan(t)";
    grammar.symbol.add("t",1.2);
    double value;
    bool r = qi::phrase_parse(function.begin(), function.end(), grammar, space, value);
    std::cout << "Exit: " << r << ", value = " << value << std::endl;

    return 0;
}

The output with debug information is:

<expression>
  <try>tan(t)</try>
  <function>
    <try>tan(t)</try>
    <group>
      <try>(t)</try>
      <expression>
        <try>t)</try>
        <success>)</success>
        <attributes>[1.2]</attributes>
      </expression>
      <success></success>
      <attributes>[1.2]</attributes>
    </group>
    <success></success>
    <attributes>[2.57215]</attributes>
  </function>
  <success></success>
  <attributes>[2.57215]</attributes>
</expression>
Exit: 1, value = 2.57215

Upvotes: 1

Related Questions