【问题标题】:Boost.Spirit: Setup sub-grammar during parsingBoost.Spirit:在解析过程中设置子语法
【发布时间】:2013-07-22 14:35:47
【问题描述】:

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

我的问题是,如何在保持子语法之间松散耦合的同时有效地解决这个问题?

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

  • SETUP 语法的 on_success 处理程序可以完成这项工作,但这会引入一些耦合。
  • 在 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;
}

SetupRealWork 本身就是解析器(我上面的子解析器)。在设置部分,可能会更改语法的某些关键字,因此设置部分有一个qi::symbols&lt;char, keywords&gt;规则。在开始时,这些符号将包含#task1#task2。解析文件的第一部分后,它们包含$$task1$$task2

由于关键字已经改变并且RealWork需要解析I,它需要知道新的关键字。所以我必须在文件配对期间将符号从Setup转移到RealWork

我看到的两种方法是:

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

    start = setup >> unparsed_rest
    

    MainParser 之后会有第二个解析器。示意图:

    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一样。

【问题讨论】:

  • 我在一些通用的 cmets 上已经尽力了。希望这些东西能帮助你走上正轨。如果没有,我建议你回来提出一个具体的问题。我更喜欢代码而不是“想法”,因为想法通常对你来说意味着其他东西,而不是对我。
  • @sehe:非常感谢您的努力。我试图更具体一点,语法是如何划分的以及解析应该如何工作。

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


【解决方案1】:

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

关注点分离

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

解析器组合

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

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

但是,如果你发现

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

你可以考虑

  1. Nabialek 把戏(我在我的[tag:boost-spirit] answers on this site 中多次展示/提到过这个
  2. 您可以动态构建规则(不建议这样做,因为您将在与复制 Proto 表达式树有关的致命陷阱中运行,这会导致悬空引用)。我有时也展示了一些答案:

    REPEAT:除非您知道如何检测 UB 并使用 Proto 修复问题,否则不要尝试此操作

希望这些内容能帮助您走上正轨。如果没有,我建议您返回一个具体 问题。我更喜欢代码而不是“想法”,因为想法通常对你来说意味着其他东西,而不是对我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多