2
votes

I use boost spirit to parse mathematical expressions and have run into a problem that I extracted into the following code.

There is a simple lexer with one token, having an attribute holding the matched string. The parser defines a single rule which is meant to take the token's attribute and call a function with it. The result of the function call should be the attribute value of the rule.

This fails to compile (calc_something: cannot convert parameter 1 from const boost::spirit::_1_type to const std::string &) - clearly because the type of qi::_1 is not inferred correctly. However, changing the action to a simple "cout << qi::_1" works.

I am fairly new to boost spirit, but have managed to get my grammar to behave correctly. Now that I need to get at parsed values, I am stuck here and would appreciate any help I can get.

// spiritTest.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include <tchar.h>

#include <string>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>

namespace qi = boost::spirit::qi;
namespace lex = boost::spirit::lex;
namespace phoenix = boost::phoenix;

template <typename Lexer>
class TestLexer : public lex::lexer<Lexer>
{
public:
    TestLexer()
    {
        number = "(\\d*\\.)?\\d+([eE][-+]?\\d+)?";      
        self = number;
    }

    lex::token_def<std::string> number;
};

int calc_something(const std::string & s)
{
    return 5;
}

template <typename Iterator>
class Parser : public qi::grammar<Iterator, int>
{
public:
    template <typename TokenDef>
    Parser(const TokenDef& tok) : Parser::base_type(value)
    {   
        // the following line causes error C2664: 'calc_something' : cannot convert parameter 1 from 'const boost::spirit::_1_type' to 'const std::string &'    
        value = tok.number [qi::_val = calc_something(qi::_1)];         

        // the following line works as expected
        //value = tok.number [std::cout << qi::_1 << std::endl];            
    }

    qi::rule<Iterator, int> value;
};

int _tmain(int argc, _TCHAR* argv[])
{
    typedef const char* base_iterator_type;
    typedef lex::lexertl::token<base_iterator_type> token_type;
    typedef lex::lexertl::lexer<token_type> lexer_type;
    typedef TestLexer<lexer_type> TestLexer;
    typedef TestLexer::iterator_type iterator_type;
    typedef Parser<iterator_type> Parser;

    TestLexer lexer;
    Parser parser(lexer);

    const char * formula = "530";
    bool result = lex::tokenize_and_parse(formula, formula + strlen(formula), lexer, parser);

    return 0;
}
2

2 Answers

2
votes

I don't have experience with spirit lex, but I assume it's similar to qi and so you'd want to use a phoenix function to do that:

#include <boost/spirit/include/phoenix_function.hpp>

struct calc_something_impl
{
  template <typename T1>
  struct result { typedef int type; };

  int operator()(const std::string & s) const
  {
    return 5;
  }
};

boost::phoenix::function<calc_something_impl> calc_something;
1
votes

eddi has identified part of the problem; I managed to get this working with another two changes:

  • The signature of your grammar and rule must use function call syntax for the synthesized attribute, i.e., <Iterator, int()>
  • Although I was unable to get the lazy Phoenix function eddi detailed to work (it compiles, but never gets called), switching to Phoenix V3 makes it work. Add before your first Spirit include:

#define BOOST_SPIRIT_USE_PHOENIX_V3 1