【问题标题】:Using Boost.Spirit.Lex and stream iterators使用 Boost.Spirit.Lex 和流迭代器
【发布时间】:2014-05-17 10:01:21
【问题描述】:

我想使用 Boost.Spirit.Lex 来 lex 一个二进制文件;为此,我编写了以下程序(这是摘录):

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <fstream>
#include <iterator>
#include <string>

namespace spirit = boost::spirit;
namespace lex = spirit::lex;

#define X 1
#define Y 2
#define Z 3

template<typename L>
class word_count_tokens : public lex::lexer<L>
{
    public:
        word_count_tokens () {
            this->self.add
                ("[^ \t\n]+", X)
                ("\n", Y)
                (".", Z);
        }
};

class counter
{
    public:
        typedef bool result_type;

        template<typename T>
        bool operator () (const T &t, size_t &c, size_t &w, size_t &l) const {
            switch (t.id ()) {
               case X:
                   ++w; c += t.value ().size ();
                    break;
               case Y:
                   ++l; ++c;
                    break;
                case Z:
                    ++c;
                    break;
            }

            return true;
        }
};

int main (int argc, char **argv)
{
    std::ifstream ifs (argv[1], std::ios::in | std::ios::binary);
    auto first = spirit::make_default_multi_pass (std::istream_iterator<char> (ifs));
    auto last = spirit::make_default_multi_pass (std::istream_iterator<char> ());
    size_t w, c, l;
    word_count_tokens<lex::lexertl::lexer<>> word_count_functor;

    w = c = l = 0;

    bool r = lex::tokenize (first, last, word_count_functor, boost::bind (counter (), _1, boost::ref (c), boost::ref (w), boost::ref (l)));

    ifs.close ();

    if (r) {
        std::cout << l << ", " << w << ", " << c << std::endl;
    }

    return 0;
}

构建返回以下错误:

lexer.hpp:390:46: error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type

现在,错误是由于具体词法分析器lex::lexer&lt;&gt;的定义造成的;事实上,它的第一个参数默认为const char *。如果我使用spirit::istream_iteratorspirit::make_default_multi_pass (.....),也会出现同样的错误。
但是,如果我指定lex::lexer&lt;&gt; 的正确模板参数,我会得到大量错误!

解决方案?

更新

我已经把所有的源文件;这是 word_counter 网站的例子。

【问题讨论】:

  • 当然,firstlast 的确切类型很重要。很多。
  • 我已经修复了您修改后问题的代码。如果你发布一个新问题,我会解释修复方法。
  • 我的问题很简单,如果我的英语不是很清楚请见谅! :-( 现在,如果我想使用类似于Boost.Spirit.Lex 的示例但使用istream_iterator 而不是const char *,则该示例不会编译报告指定的错误。在我的更新中,我输入了我的实际代码。
  • 我看到了你的更新。我的意思是让您发布一个新问题,因为您更新的问题会使现有答案无效。没关系,我刚刚posted a new answer

标签: c++ c++11 boost boost-spirit


【解决方案1】:

好的,由于问题已更改,这里有一个新答案,用完整的代码示例解决了一些问题。

  1. 首先,您需要使用自定义令牌类型。 IE。

    word_count_tokens<lex::lexertl::lexer<lex::lexertl::token<boost::spirit::istream_iterator>>> word_count_functor;
    // instead of:
    // word_count_tokens<lex::lexertl::lexer<>> word_count_functor;
    

    显然,习惯上 typedef lex::lexertl::token&lt;boost::spirit::istream_iterator&gt;

  2. 您需要使用 min_token_id 而不是令牌 ID 1、2、3。另外,为了便于维护,将其设为枚举:

    enum token_ids {
        X = lex::min_token_id + 1,
        Y,
        Z,
    };
    
  3. 您不能再仅在默认令牌 value() 上使用 .size(),因为迭代器范围不再是 RandomAccessRange。相反,请使用专门用于iterator_rangeboost::distance()

            ++w; c += boost::distance(t.value()); // t.value ().size ();
    

结合这些修复:Live On Coliru

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/bind.hpp>
#include <fstream>

namespace spirit = boost::spirit;
namespace lex    = spirit::lex;

enum token_ids {
    X = lex::min_token_id + 1,
    Y,
    Z,
};

template<typename L>
class word_count_tokens : public lex::lexer<L>
{
    public:
        word_count_tokens () {
            this->self.add
                ("[^ \t\n]+", X)
                ("\n"       , Y)
                ("."        , Z);
        }
};

struct counter
{
    typedef bool result_type;

    template<typename T>
    bool operator () (const T &t, size_t &c, size_t &w, size_t &l) const {
        switch (t.id ()) {
            case X:
                ++w; c += boost::distance(t.value()); // t.value ().size ();
                break;
            case Y:
                ++l; ++c;
                break;
            case Z:
                ++c;
                break;
        }

        return true;
    }
};

int main (int argc, char **argv)
{
    std::ifstream ifs (argv[1], std::ios::in | std::ios::binary);
    ifs >> std::noskipws;
    boost::spirit::istream_iterator first(ifs), last;
    word_count_tokens<lex::lexertl::lexer<lex::lexertl::token<boost::spirit::istream_iterator>>> word_count_functor;

    size_t w = 0, c = 0, l = 0;
    bool r = lex::tokenize (first, last, word_count_functor, 
            boost::bind (counter (), _1, boost::ref (c), boost::ref (w), boost::ref (l)));

    ifs.close ();

    if (r) {
        std::cout << l << ", " << w << ", " << c << std::endl;
    }
}

在自身运行时,打印

65, 183, 1665

【讨论】:

  • 好的!谢谢!这是我预期的反应! :-) 但是...... Boost 文档不是很解释......也就是说,关于定义令牌类型很清楚,但关于 min_token_id 没有!谢谢!
【解决方案2】:

我认为真正的问题没有显示出来。你没有显示firstlast,我觉得你可能在那里有临时人员。

这是我想出的一个示例来验证,也许你可以看到你在做什么---错误---不同:)

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
#ifdef MEMORY_MAPPED
#   include <boost/iostreams/device/mapped_file.hpp>
#endif

namespace /*anon*/
{
    namespace qi =boost::spirit::qi;
    namespace lex=boost::spirit::lex;

    template <typename Lexer>
        struct mylexer_t : lex::lexer<Lexer>
    {
        mylexer_t()
        {
            fileheader = "hello";

            this->self = fileheader
                | space [ lex::_pass = lex::pass_flags::pass_ignore ];
        }

        lex::token_def<lex::omit>
            fileheader, space;
    };

    template <typename Iterator> struct my_grammar_t
        : public qi::grammar<Iterator>
    {
        template <typename TokenDef>
            my_grammar_t(TokenDef const& tok) 
                : my_grammar_t::base_type(header)
        {
            header = tok.fileheader;
            BOOST_SPIRIT_DEBUG_NODE(header);
        }

      private:
        qi::rule<Iterator> header;
    };
}

namespace /* */ {

    std::string safechar(char ch) {
        switch (ch) {
            case '\t': return "\\t"; break;
            case '\0': return "\\0"; break;
            case '\r': return "\\r"; break;
            case '\n': return "\\n"; break;
        }
        return std::string(1, ch); 
    }

    template <typename It>
        std::string showtoken(const boost::iterator_range<It>& range)
        {
            std::ostringstream oss;
            oss << '[';
            std::transform(range.begin(), range.end(), std::ostream_iterator<std::string>(oss), safechar);
            oss << ']';
            return oss.str();
        }
}

bool parsefile(const std::string& spec)
{
#ifdef MEMORY_MAPPED
    typedef char const* It;
    boost::iostreams::mapped_file mmap(spec.c_str(), boost::iostreams::mapped_file::readonly);
    char const *first = mmap.const_data();
    char const *last = first + mmap.size();
#else
    typedef char const* It;
    std::ifstream in(spec.c_str());
    in.unsetf(std::ios::skipws);

    std::string v(std::istreambuf_iterator<char>(in.rdbuf()), std::istreambuf_iterator<char>());
    It first = &v[0];
    It last = first+v.size();
#endif

    typedef lex::lexertl::token<It  /*, boost::mpl::vector<char, unsigned int, std::string> */> token_type;
    typedef lex::lexertl::actor_lexer<token_type> lexer_type;

    typedef mylexer_t<lexer_type>::iterator_type iterator_type;
    try
    {
        static mylexer_t<lexer_type> mylexer;
        static my_grammar_t<iterator_type> parser(mylexer);

        auto iter = mylexer.begin(first, last);
        auto end  = mylexer.end();

        bool r = qi::parse(iter, end, parser);

        r = r && (iter == end);

        if (!r)
            std::cerr << spec << ": parsing failed at: \"" << std::string(first, last) << "\"\n";
        return r;
    }
    catch (const qi::expectation_failure<iterator_type>& e)
    {
        std::cerr << "FIXME: expected " << e.what_ << ", got '";
        for (auto it=e.first; it!=e.last; it++)
            std::cerr << showtoken(it->value());
        std::cerr << "'" << std::endl;
        return false;
    }
}

int main()
{
    if (parsefile("input.bin"))
        return 0;
    return 1;
}

对于变体:

typedef boost::spirit::istream_iterator It;
std::ifstream in(spec.c_str());
in.unsetf(std::ios::skipws);

It first(in), last;

【讨论】:

  • 您的回复有助于理解 tokenize(...) 不能与其他迭代器一起使用,而不是 'const char *';这是正确的?我必须使用 actor_lexer 才能使用自定义迭代器?
  • actor_lexer 在词法分析器中启用了语义操作(和状态,IIRC)。那是无关的AFAICS。我确实展示了 boost::istream_iterator in the alternative 的用法,所以可以。如果您有此 SSCCE 的修改版本显示您遇到的问题,您可能希望将 that 作为问题发布。 编辑 我看到您在我回答 7 小时后更新了问题 (...)。稍后我会看看你修改后的问题
猜你喜欢
  • 2021-12-29
  • 2018-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 2018-07-28
  • 2019-03-26
相关资源
最近更新 更多