Boost Spirit X3的上下文是啥?

Posted

技术标签:

【中文标题】Boost Spirit X3的上下文是啥?【英文标题】:What are contexts in boost spirit X3?Boost Spirit X3的上下文是什么? 【发布时间】:2021-02-26 22:54:33 【问题描述】:

短版

上下文 (boost::spirit::x3::context) 用于什么,它们代表什么,为什么需要它们并且不对最终用户隐藏, 以及它们应该满足哪些要求才能顺利编译?

尤其是当解析器位于不同的翻译单元时,例如单元测试。

加长版

在 X3 文档中,当人们想要准确了解需要上下文时的预期内容时, 不是很清楚。

Documentation 说:

    在semantic actions 的上下文中 :

我们可以从上下文中提取相关信息

    BOOST_SPIRIT_DEFINE 提到了上下文 但是什么也没说

    在解释预期的program structures 时也提到了上下文:

我们还需要提供初始上下文类型。这是上下文 X3 将用于启动解析。拨打phrase_parse,您将 需要phrase_parse_context,就像我们在下面做的那样,传入船长类型

    在annotation tutorial 上下文再次被提及和操纵,但没有明确解释它们是什么以及如何使用它们。

如果您阅读了之前的程序结构教程,其中我们将各种逻辑 解析器的模块到单独的 cpp 和头文件中,你想知道如何 提供上下文配置信息(见配置部分),我们需要补充 像这样的上下文:

    以此类推,我们在阅读中越深入,就会认为已知的上下文越多,解释的越少。

所以最后,我们选择一个(文档中提供的那个开始)

x3::phrase_parse_context<x3::ascii::space_type>::type

只要它编译...一切都很好。

尽管很快,编译器就会大喊大叫。这并不罕见:编译器在做它的工作。但在这种情况下,很难看出如何从错误消息中获取有用的信息。

我看到链接器报告了一个缺少符号,但我需要更多信息。很难看出符号的使用位置或类型是如何推断的。

如果只有文档告诉我们上下文用于什么以及真正期望什么

我尝试阅读context.hpp,它不长而且很容易阅读。但它是如此抽象,以至于我仍然不知道上下文模型或表示什么。

在SO 上,有几个关于上下文、不匹配的问题, 以及出色的答案。我并没有真正找到更多关于询问为什么需要上下文、应该如何使用它们以及何时需要关心它们的信息。参见例如

    Mixing non-terminal rules from separeted translation unit Embedding a parser from a separate translation unit into another parser Splitting Boost.Spirit.X3 parsers into several TUs。

在攀登了学习精神/X3的第一座山之后,我认为我已经完成了最艰难的部分。 那是没有掌握上下文的本质,这在链接时变得很重要。

问题

上下文 (boost::spirit::x3::context) 用于什么,它们代表什么,为什么最终用户需要它们,以及它们应该满足哪些要求才能顺利编译?

【问题讨论】:

“这些话被认为是一个受挫的人的话”——我很感激。然而,在这个网站上,你被认为是在为手头的任务寻求帮助,而不是寻求同情或安慰。我认为你应该降低问题的咆哮程度以改进它(而不是浪费太多时间)。 您的问题的答案可能取决于上下文。撇开玩笑不谈,您不会在context-sensitive grammars 中找到上下文的定义,因为它是特定语法的属性,即使grammar is context-free 可能不是该语法的解析器。对于 X3,当您省略空格跳过(通过使用短语解析)或规则定义(通过使用范围规则)或要求错误处理时,它会在上下文中查找该信息。 C++20 模块应该可以拯救你。 感谢 Nikita,这很好理解。我也想到了 C++20 的概念。 【参考方案1】:

上下文包含解析器运行所需的运行时状态的每一位以及以下通用实例化参数:

船长类型 迭代器类型

确实,在跨翻译单元拆分解析器时,这是链接器错误的常见来源。至少不是因为

正如您所注意到的,某些指令可以修饰上下文 上下文有时可以包含完整的解析器表达式模板(参见例如Mixing non-terminal rules from separeted translation unit)(*)。

文档基本上说明了所有这些。但他们专注于一条幸福的道路,不会让您轻易意识到跨翻译单元维护规则会变得多么繁琐。

扮演你的教练

我建议一个简单的指南

我不会在单个翻译单元之外传播/分享规则

这样它们的定义可以与它们的声明/实例一起存在。

这样做有一些缺点:

该 TU 的编译时间可能更长

如果语法复杂,可能会导致源文件很大

最后,由于上面观察到的效果(*),在简单场景中可能会出现过多的模板实例化,因为当局部规则递归实例化时;它们将导致许多“不同”的实例化。

如果部分语法发生变化,例如船长(例如使用 x3::skip/x3::no_skip)。

推论:

如果你的语法真的很大,考虑一个独立的解析器生成器,而不是 Spirit

尽管这些似乎都暗示了这些限制,但 X3 仍然是一个出色的工具,可以极大地提高我的工作效率。我被积极授权正确一致编写小型解析器。

附言/旁白

我认为链接的答案也应该有助于更广泛的关于什么是上下文的问题。除此之外,我还可以向您指出一些 [其他] 我帮助人们在 X3 的上下文 [原文如此] 中调试链接器错误的地方。

Recursive rule in Spirit.X3(装饰“状态”的其他指令 - 添加到上下文中:x3::with&lt;&gt;

Boost spirit x3 example calculator (calc8, calc9) linker error

X3: Linker Error (unresolved external symbol "parse_rule") on nonterminal parser

【讨论】:

“我不会传播,共享规则超出单个翻译单元”。如果我们想要单元测试怎么办?考虑到解析器工作的困难,我认为这并不过分。我们应该在同一个翻译单元中混合单元测试吗? “上下文有时可以包含完整的解析器表达式模板”。但是为什么不对最终用户隐藏呢?我们不想/不需要管理这个,是吗? “我们是否应该在同一个 TU 中混合单元测试” - 这取决于。白盒,当然!当然,一如既往。黑盒子?不,当然不是,因为根据定义,您测试的是公共接口,而不是实现。我认为那里没有问题。确实感觉就像是为了激怒而发明更多的问题。 “我们不想 [...] 是吗?” - 只有当您选择退出 SPIRIT_DEFINE 机制时才会发生这种情况。这意味着无论如何您都没有任何理由关心上下文细节。同样,这不是最大的问题。只需选择您的偏好。是否使用 DEFINE。也许,根本不使用 X3。有利有弊。 “我不会在另一个 TU 中传播/共享规则”。我的经验是,如果您在 TU 之外“单独”使用规则,那很好。但是不好的是将规则嵌入到另一个解析器中,无论它是否是规则,在规则的 TU 之外。因为在这种情况下,您需要使用这个新解析器的上下文来实例化规则。但是为了简化建议,您只需说:不要在其 TU 之外传播/共享规则。对吗?

以上是关于Boost Spirit X3的上下文是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::spirit::x3 解析成向量<boost::string_view>

无法使用 Boost Spirit X3 解析空的 C++ 结构

(如何)我可以在不安装完整的 boost 库的情况下使用 boost::spirit X3 吗?

Boost Spirit x3 -- 使用其他解析器参数化解析器

Boost Spirit X3:跳过啥都不做的解析器

是啥导致了 boost C++ asio 中的 asio.misc.3 错误