是否可以在Boost.Spirit中创建自定义分析器?

时间:2010-08-17 12:52:50

标签: c++ boost-spirit-qi

我试图在Boost.Spirit(2.3)中创建一个自定义的Parser类,但它没有成功。代码是:

template <class Iter>
class crule : public boost::spirit::qi::parser<crule<Iter> >
{
  rule<Iter> r_;
public:
  crule(const rule<Iter>& r) : r_(r) {}
  template <class T>
  crule(const T& t) : r_(t) {}
  template<class Ctx, class Skip>
  bool parse(Iter& f, const Iter& l, Ctx& context, Skip& skip, typename rule<Iter>::template attribute<Ctx, Iter>::type& attr) const {
    return r_.parse(f, l, context, skip, attr);
  }
  template <class Ctx>
  boost::spirit::info what(Ctx& context) const {
    return r_.what(context);
  }
  template <class Context, class It>
  struct attribute {
    typedef typename rule<Iter>::template attribute<Context, It>::type type;
  };
};

虽然我(至少我认为我已经)完成了所有requirements,但当我尝试在解析表达式中使用此类时,我会收到错误:

shell_grammar.h:134: error: no match for 'operator!' in '!shell_grammar<Iter>::token(boost::spirit::qi::rule<Iter, boost::fusion::unused_type, boost::fusion::unused_type, boost::fusion::unused_type>) [with Iter = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >](boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::fusion::unused_type, boost::fusion::unused_type, boost::fusion::unused_type>(((const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::fusion::unused_type, boost::fusion::unused_type, boost::fusion::unused_type>&)((const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::fusion::unused_type, boost::fusion::unused_type, boost::fusion::unused_type>*)(&((shell_grammar<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*)this)->shell_grammar<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::reserved_words)))))'

shell_grammar.h:134: note: candidates are: operator!(bool) <built-in>

我试着看看其他解析器的实现(例如。not_predicate),但是无法弄清楚它有什么不同之处。

Motvation

我这样做的原因与this question有关。我想解析POSIX shell语言,它具有特殊的词法规则。特别是,“船长解析器”必须在词汇中应用,但它必须与“词组级别”船长解析器不同。这是lexeme指令不能执行的操作,而skip不会预先跳过(AFAIK),这也是我需要的。所以我想创建一个函数

something token(std::string);

将返回与令牌匹配的规则。一种方法是创建我自己的rule包装器作为终端(因为rule单独不能用于它的引用语义),另一种方法是创建一个新的解析器(在{中是非终结符号) {1}}),并在其中实现shell的令牌解析。

3 个答案:

答案 0 :(得分:3)

这很有可能,但是我发现它只需要手工编写自己的词法分析器和递归下降解析器一样多(并且更难调试)。即使是相当小的Spirit语法也可能需要花费数周时间与编译器进行摔跤。

您收到此错误消息会显示您遇到的问题类型。任何时候你得到一个错误,它是一个模板实例化的错误,深入精神的内部,添加了许多模板实例的层,以混淆问题。为了有解密错误消息的希望,您几乎必须了解整个设施的代码。

我讨厌批评,因为精神是值得的。我完成了关于实现object-oriented compiler-generator的硕士论文,所以我是这个概念的粉丝。我真的很想喜欢它,但除了认真的C ++专家之外,Spirit对于任何人来说都太难了。

要与可以做的事情进行比较,请查看Ada OpenToken项目。 Spirit可能更灵活,但编译错误在OpenToken中更为明智,浏览该页面上的版本历史记录显示,他们的努力中有很大一部分用于帮助用户调试错误。

答案 1 :(得分:3)

您提供的代码看起来不错(至少就实际解析器的界面而言)。但是为了将自定义解析器与Spirit集成,您需要做更多的工作。 Spirit的网站有一个自定义解析器组件的示例,解释了所有必需的步骤here

在我看来,好像你不必要地试图以艰难的方式做事。但我并不完全明白你想要达到的目标,所以我可能错了。如果你解释了你的用例,我相信我们可以提出一个更简单的解决方案。

答案 2 :(得分:1)

BTW这就是我的目标:

您需要在boost::proto中将该类注册为文字:

template <class T>
struct crulexx : public boost::proto::literal<rule<T> >
{
  template <class U>
  crulexx(const U& u) : boost::proto::literal<rule<T> >(rule<T>(u)) {}
};

这对我test有用。但是,我在其他代码段中使用了segfaults,我将不得不进行调试。