C++ 编译器:词法分析的局限性

C++ 编译器:词法分析的局限性,c++,parsing,compiler-construction,C++,Parsing,Compiler Construction,在经典编译器理论中,前两个阶段是词法分析和解析。他们正在准备中。词法分析将标记识别为解析的输入 但是我遇到了一些在词汇分析中很难正确识别的案例。例如,下面的C++模板代码: 地图 >在“常规”词法分析中会被识别为按位右移,但这是不正确的。我的感觉是很难将这类语法的处理分为两个阶段,词法分析工作必须在解析阶段完成,因为正确解析>依赖于语法,而不仅仅是简单的词法规则 我想知道关于这个问题的理论和实践。另外,我想知道C++编译器是如何处理这个情况的?P> > P> C++标准要求在语法分析之前,执

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

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


地图

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


我想知道关于这个问题的理论和实践。另外,我想知道C++编译器是如何处理这个情况的?P> > P> C++标准要求在语法分析之前,执行程序执行词法分析以产生令牌流。根据词法分析规则,两个连续的
字符(后面不跟
=
)将始终被解释为一个
>
标记。C++标准提供的语法是根据这些令牌定义的。 语法中没有规定在某些上下文中(例如在模板id中需要一个
时),实现应将
>
解释为两个
。相反,该规则被指定为特殊情况:

14.2模板专业名称[临时名称]### 名称查找后(3.4)发现名称是模板名称,或者运算符函数id或文本运算符id指的是一组重载函数,其中的任何成员都是函数模板(如果是) 后跟一个
被视为两个连续但 不同的
标记,其中第一个标记作为模板参数列表的结尾,并完成 template-id.[注意:此替换规则生成的第二个
令牌可能会终止封闭 模板id构造,或者它可能是不同构造的一部分(例如铸造)。-结束注释]

请注意前面的规则,即在某些上下文中,
p可以使用相同的技术将代码标记为emtemplate参数列表/em的开头,或将代码标记为结尾。用两个
替换代码>
的上下文敏感规则本质上提出了相同的问题,可以使用相同的方法解决。

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

C++中的<代码> >代码>,我不知道编译器实际上是做什么的。willj引用了规范中的措辞,但允许实现在内部以不同的方式进行操作,只要可见结果相同。下面是我试图解决这个问题的方法:在读取

时,lexer将发出标记
更大的
,但也会切换到一种状态,在这种状态下,没有空格的每个后续
将被lexed到
更大的位置。任何其他符号都会将状态切换回正常状态。除了状态开关之外,您还可以通过对正则表达式
+
进行词法分析,并从该规则发出多个标记来实现这一点。在解析器中,您可以使用如下规则:

rightAngleBracket: GREATER | GREATER_REPEATED;
rightShift: GREATER GREATER_REPEATED;
如果运气好的话,可以让模板参数规则使用RightAngle括号,而表达式使用rightShift。根据解析器的前瞻性,可能有必要引入额外的非终端来保存较长的不明确内容序列,直到遇到某种上下文,允许您最终在这两种情况之间做出决定。

我认为许多(C++0x之前)确实将其解释为按位右移,事实上,这是一个特例,