【问题标题】:Is it possible to create custom Parsers in Boost.Spirit?是否可以在 Boost.Spirit 中创建自定义解析器?
【发布时间】:2010-08-17 12:52:49
【问题描述】:

我试图在 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),但无法弄清楚它的工作原理是什么。

动机

我这样做的原因与this question有关。我想解析具有特殊词汇规则的 POSIX shell 语言。特别是,即使在词位中也必须应用“skipper parser”,但它必须不同于“phrase level”skipper parser。这是lexeme 指令不能做的,skip 不会预先跳过(AFAIK),这也是我需要的。所以我想创建一个函数

something token(std::string);

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

【问题讨论】:

  • 哇!!!!第一个错误行超过 550 个字符...哇...哇...
  • @Stephane Rolland:实际上,这并不是什么特别的事情。我经常得到更长的行,这可以解释为“运算符!不能应用于crule”,关键是,它可以应用于精神规则和解析器,我不知道它们是怎么做的它(他们没有实现operator!,但使用了一些 Boost.Proto 魔法)。
  • 您是否尝试过仅在满月和新月时编译和链接?
  • 遗憾的是,该错误行与您从 Spirit 规范中非常简单的错误中得到的结果完全不同。正如我在下面提到的,如果您对这些事情(或更糟)感到不舒服,那么您可能不应该使用 Spirit。经过几个项目后,我决定我属于那个类别。

标签: c++ boost-spirit-qi


【解决方案1】:

这很有可能,但我发现它比仅仅手动编写我自己的词法分析器和递归下降解析器需要更多的工作(并且更难调试)。即使是相当小的 Spirit 语法也会让我花费数周时间与编译器搏斗。

您收到的此错误消息显示了您遇到的问题类型。任何时候你得到一个错误,它都是来自 Spirit 深处的一些模板实例化的错误,并添加了许多进一步的模板实例化层来混淆问题。为了有希望破译错误消息,您几乎必须了解整个设施的代码。

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

要与可以做的事情进行比较,请查看 Ada OpenToken 项目。 Spirit 可能更灵活,但在 OpenToken 中编译错误更加敏感,浏览该页面上的版本历史可以看出他们的很大一部分工作都用于帮助用户调试错误。

【讨论】:

  • 我真的希望 Boost.MPL、Spirit、StateChart、Prototype 等能够使用 static_assert(或 BOOST_MPL_ASSERT_MSG)来传达更好的错误消息。当然,如果 c++ 本身有办法在出现问题时替换您的错误消息,那就太好了。 (我会满足于 gcc 有一些不错的#pragmas 来做到这一点)
【解决方案2】:

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

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

【讨论】:

  • 感谢您的链接;我注意到了 Proto 的魔力,并尝试了 Proto 的一些东西,但是,编译需要 aaaaaggggggeeeeeeessssssss 并且我遇到了段错误,所以我必须进一步研究它。如果我碰巧调试它,我可能会发布我来到这里的内容。
  • 请注意,我发现我发布的代码中存在一些问题;我会对其进行编辑,以免造成混乱。
【解决方案3】:

顺便说一句,这就是我的想法:

您需要像这样在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 中对我有用。但是,我在使用它的其他代码段中遇到了段错误,我将不得不对其进行调试。

【讨论】:

  • 看看我给你链接的例子,那么所有的谜团都应该消失了。但我仍然不确定你在做什么——敢解释吗?
  • @hkaiser:是的,见编辑。顺便说一句,您建议使新的解析器成为精神 proto 语法中的非终结符,这比使其成为终端更有好处吗?
  • 我在哪里提到了非终端?通常,如果这些是终端(并且大多数情况下就足够了),那么编写新的解析器会更容易。如果你觉得你需要一个新的非终端,请先尝试直接使用 rule。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
相关资源
最近更新 更多