为了处理大型编译时间和语法重用,我将语法编写成几个按顺序调用的子语法。其中之一(称之为:SETUP语法)提供了解析器的一些配置(通过符号解析器),因此后来的子语法在逻辑上依赖于那个(再次通过不同的符号解析器)。因此,在解析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;
}
Setup
和RealWork
本身就是解析器(我上面的子解析器)。在设置部分期间,可能会更改语法的某些关键字,因此设置部分具有qi::symbols<char, keywords>
规则。在开始时,这些符号将包含#task1
和#task2
。解析文件的第一部分后,它们包含$$task1
和$$task2
。
由于关键字已更改且由于RealWork
需要解析I,因此需要了解新关键字。因此,在文件配对过程中,我必须将符号从Setup
转移到RealWork
。
我看到的两种方法是:
Setup
了解RealWork
,并将符号从Setup
转移到RealWork
的{{1}}处理程序qi::on_success
。 (坏,耦合)切换到两个解析步骤。 Setup
的{{1}}看起来像
start
并且将会有MainParser
的第二个解析器。示意性地:
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_before_parse
和qi::on_success
一样。
答案 0 :(得分:3)
可悲的是,你没有显示任何代码,你的描述有点......粗略。所以这是一个相当通用的答案,它解决了我能够从你的问题中提炼出来的一些观点:
这听起来非常像您需要将AST构建与转换/处理步骤分开。
当然你可以编写语法。简单地按照规则编写语法,并以任何传统的方式隐藏这些语法的实现(pImpl
成语,const静态内部规则,无论什么符合条件)。
然而,构图通常不需要“事件”驱动元素:如果您觉得需要分两个阶段解析,听起来我只是在努力保持概述但是,递归下降或PEG语法自然非常适合在一个 swoop 中描述类似的语法(或者如果你愿意,可以一次传递)。
但是,如果你找到了
(a)你的语法变得复杂了 (b)或者您希望能够根据运行时功能选择性地插入子程序
你可以考虑
您可以动态构建规则(这不是很容易推荐的,因为您将运行与复制Proto表达式树相关的致命陷阱,这会导致悬空引用)。我偶尔也会给出一些答案:
REPEAT:除非你知道如何检测UB并使用Proto解决问题,否则不要尝试这个
希望这些东西可以帮助你走上正轨。如果没有,我建议你回来一个具体的问题。我在家里的代码比“想法”要多得多,因为经常对你而言意味着别的东西而不是我。