BoostSpirit:在解析过程中设置子语法

Boost.Spirit: Setup sub-grammar during parsing

本文关键字:设置 语法 过程中 BoostSpirit      更新时间:2023-10-16

为了处理大量的编译时间和语法的重用,我将语法组成了几个按顺序调用的子语法。其中一个子语法(称之为SETUP语法)提供了解析器的一些配置(通过符号解析器),因此后面的子语法在逻辑上依赖于该子语法(同样通过不同的符号解析器)。因此,在解析SETUP之后,需要更改以下子语法的符号解析器。

我的问题是,如何有效地处理这一问题,同时保持子语法之间的松散耦合?

目前我只看到两种可能性:

  • SETUP语法的on_ccess处理程序可以完成这项工作,但这会引入一些耦合
  • SETUP之后,将所有内容解析为一个字符串,构建一个新的解析器(根据修改后的符号),并在第二步中解析该字符串。这将留下相当多的开销

我想要的是一个on_before_parse处理程序,它可以由任何需要在每次解析之前做一些工作的语法来实现。从我的角度来看,这将减少耦合,并且解析器的一些设置在其他情况下也可以派上用场。这样的事情可能发生吗?

更新:

抱歉说得太粗略了,那不是我的本意。

任务是用一些关键字(如#task1#task2)解析输入I。但在某些情况下,这些关键字需要不同,比如$$task1$$task2

因此,解析后的文件将以开头

setup {
  #task1=$$task1
  #task2=$$task2
}
realwork {
  ...
}

一些代码草图:Given是一个主解析器,由几个(至少两个)解析器组成。

template<typename Iterator>
struct MainParser: qi::grammar<Iterator, Skipper<Iterator>> {
  MainParser() : MainParser::base_type(start) {
    start = setup >> realwork;
  }
  Setup<Iterator>    setup;
  RealWork<Iterator> realwork;
  qi::rule<Iterator, Skipper<Iterator> > start;
}

CCD_ 5和CCD_。在设置部分,语法中的一些关键字可能会被更改,因此设置部分具有qi::symbols<char, keywords>规则。在开头,这些符号将包含#task1#task2。在解析文件的第一部分之后,它们包含$$task1$$task2

由于关键字已经更改,并且RealWork需要解析I,因此它需要了解新的关键字。因此,在文件的配对过程中,我必须将符号从Setup传输到RealWork

我看到的两种方法是:

  • 使Setup知道RealWork,并在Setupqi::on_success处理程序中将符号从Setup传输到RealWork。(坏,耦合)
  • 切换到两个解析步骤。MainParserstart看起来像

    start = setup >> unparsed_rest
    

    并且在CCD_ 23之后将存在第二解析器。示意图:

    SymbolTable Table;
    string Unparsed_Rest;
    MainParser.parse(Input, (Unparsed_Rest, Table));
    RealWordParser.setupFromAlteredSymbolTable(Table);
    RealWorkParser.parse(Unparsed_Rest);
    

    几个解析步骤的开销。

因此,到目前为止,属性还没有发挥作用。只是在解析时更改解析器来处理几种类型的输入文件。

我希望是一个像qi::on_success一样的qi::on_before_parse处理程序。从这个想法来看,每当解析器开始解析输入时,就会触发这个处理程序。从理论上讲,这只是解析开始时的拦截,就像我们的拦截on_successon_error一样。

遗憾的是,您没有显示任何代码,并且您的描述有点。。。粗略的因此,这里有一个相当通用的答案,它解决了我能够从你的问题中提取的一些要点:

关注点分离

听起来很像是需要将AST构建与转换/处理步骤分开。

分析器组成

当然你可以编写语法。只需按照规则编写语法,并以任何传统方式隐藏这些语法的实现(pImpl习语、const静态内部规则,任何合适的)。

然而,合成通常不需要"事件"驱动的元素:如果你觉得需要分两个阶段进行解析,在我看来,你只是在努力保持概述,但递归下降或PEG语法自然非常适合一次性描述类似的语法(如果你愿意的话,也可以一次性)。

但是,如果你发现

(a) 你的语法变得复杂
(b) 或者您希望能够根据运行时功能选择性地插件子语法

你可以考虑

  1. Nabialek技巧(我在这个网站上的[标签:提升精神]回答中多次展示/提到过这一点
  2. 您可以动态地构建规则(不建议这样做,因为您将在与复制Proto表达式树有关的致命陷阱中运行,这会导致悬空引用)。我偶尔也会展示一些这样做的答案:

    • 从替代语法分析器表达式的可变列表生成Spirit语法分析器表达式
    • C++Boost qi递归规则构造
    • BoostSpirit.Qi:动态创造"差异";解析时的解析器

    重复:除非你知道如何检测UB并用Proto 修复问题,否则不要尝试这个

希望这些东西能帮助你走上正轨。如果没有,我建议你提出一个具体的问题。我更喜欢代码而不是"想法",因为想法对你来说往往意味着比我更重要的东西。