编译器:词法分析的限制

时间:2013-10-01 01:40:17

标签: c++ parsing compiler-construction

在经典的编译器理论中,前两个阶段是词法分析和解析。他们在管道中。词法分析将标记视为解析的输入。

但是我遇到了一些在词法分析中很难被正确识别的案例。例如,以下有关C ++模板的代码:

map<int, vector<int>>

>>将被识别为“常规”词法分析中的按位右移,但这不正确。我的感觉是很难将这种语法的处理分为两个阶段,lexing工作必须在解析阶段完成,因为正确解析>>依赖于语法,而不仅仅是简单的词法规则

我想知道关于这个问题的理论和实践。另外,我想知道C ++编译器如何处理这种情况?

2 个答案:

答案 0 :(得分:6)

C ++标准要求实现在解析阶段之前执行词法分析以生成令牌流。根据词法分析规则,两个连续的>个字符(后面跟=)将始终被解释为一个>>标记。 C ++标准提供的语法是根据这些标记定义的。

在某些上下文中(例如在模板ID中期望>时)实现应该将>>解释为在语法中未指定>的要求。而是将规则指定为特例:

  

14.2模板特化名称[temp.names] ###

     

名称查找(3.4)后发现名称是模板名称 operator-function-id literal-operator-id 是指一组重载函数,如果是这样的话,其中任何成员都是函数模板   后跟<<始终作为 template-argument-list 的分隔符,而不是小于   运营商。解析模板参数列表时,第一个非嵌套>被视为结束分隔符   而不是一个大于运营商。同样,第一个非嵌套>>被视为两个连续但是   不同的>令牌,第一个作为 template-argument-list 的结尾,并完成   模板id 。 [注意:此替换规则生成的第二个>令牌可以终止封闭    template-id 构造或它可能是不同构造的一部分(例如演员).-尾注]

请注意前面的规则,在某些上下文中<应该被解释为 template-argument-list 中的<。这是需要上下文以消除解析歧义的构造的另一个示例。

C ++语法包含许多这样的歧义,如果没有有关上下文的信息,在解析过程中无法解决这些歧义。其中最着名的是Most Vexing Parse,其中标识符可能会被解释为类型名称,具体取决于上下文。

在C ++中跟踪上述上下文需要一个实现来与解析阶段并行执行一些语义分析。这通常以语义动作的形式实现,当在给定的上下文中识别特定的语法构造时,该语义动作被调用。然后,这些语义操作构建一个表示上下文的数据结构,并允许有效的查询。这通常被称为symbol table,但C ++所需的结构几乎就是整个AST

这些上下文敏感的语义动作也可用于解决歧义。例如,在 namespace-body 的上下文中识别标识符时,语义操作将检查该名称是否先前被定义为模板。然后将其结果反馈给解析器。这可以通过使用结果标记标识符标记,或者将其替换为与不同语法规则匹配的特殊标记来完成。

可以使用相同的技术将<标记为 template-argument-list 的开头,或将>标记为结尾。上下文敏感的>>替换为>的规则带来了基本相同的问题,可以使用相同的方法解决。

答案 1 :(得分:1)

你是对的,词法分析器和解析器之间的理论上的清晰区别并不总是可行的。我记得我作为一名学生做过的一个项目。我们要实现一个C编译器,我们用作基础的语法在某些情况下将类型定义的名称视为类型,在其他情况下作为标识符。因此词法分析者必须在这两种模式之间切换。我当时实现它的方式是使用特殊的空规则,它根据上下文重新配置词法分析器。要做到这一点,至关重要的是要知道解析器总是只使用一个前瞻标记。因此,对词法分析器行为的任何更改都必须在受影响的位置之前至少发生一个lexiacal令牌。最后,这很有效。

在你提到的>>的C ++案例中,我不知道编译器实际上做了什么。 willj引用了规范如何对此进行短语,但只要可见结果相同,就允许实现在内部做不同的事情。所以这就是我试图解决的问题:阅读>后,词法分析器将发出令牌GREATER,但也会切换到每个后续> 的状态之间的空格将被刻录到GREATER_REPEATED。任何其他符号都会将状态切换回正常状态。您也可以通过激活正则表达式>+并从此规则发出多个令牌来代替状态切换。在解析器中,您可以使用以下规则:

rightAngleBracket: GREATER | GREATER_REPEATED;
rightShift: GREATER GREATER_REPEATED;

运气好的话,你可以使模板参数规则使用rightAngleBracket,而表达式则使用rightShift。根据您的解析器有多少前瞻,可能需要引入额外的非终端来保存更长的模糊内容序列,直到您遇到一些允许您最终在这些情况之间做出决定的上下文。