ISO/IEC 14882:2011之条款2——词法协定
2、词法协定
2.1 单独翻译
1、在此国际标准中,程序的文本被存放在被称为源文件的单元中。一个源文件与通过预处理指示符#include所包含的所有头文件(17.6.1.2)和源文件(16.2)一起,排除通过任一条件包含(16.1)预处理指示符所跳过的源代码行,被称为一个翻译单元。[译者注:即,一个翻译单元基于一个源文件。它所包含的所有头文件和源文件,把无效的预编译条件排除掉后的所有源代码形成一个翻译单元。][注:一个C++程序不需要在同一时刻被全部翻译。 ——注结束]
2、[注:先前被翻译的翻译单元以及实例单元可以被单独保留或保存在库里。一个程序的单独的翻译单元通过对其标识符具有外部连接的函数的调用,对其标识符具有外部连接的对象的操作,或对数据文件的操作进行通信。翻译单元可以被分别翻译,然后稍后被连接以产生一个可执行程序(3.5) ——注结束]
2.2 翻译阶段
在翻译的语法规则之间的优先权由以下阶段指定:[注:实现必须表现得就似乎这些单独的阶段发生了一样,即使在实践上不同阶段可能被一起合拢处理。]
1、物理源文件字符以一种实现定义的方式被映射到基本的源字符集(为行结束指示符引入新行字符),如果有必要。被接受的物理源文件字符的集合是实现定义的。三字符序列(2.4)被相应的单字符内部表示替换。任何不在基本源字符集(2.3)的源文件字符用指代那个字符的通用字符名代替。(一个实现可以使用任一内部编码,只要在源文件中遇到一个实际被扩展的字符,并且同一被扩展的字符在源文件中被表达为一个通用字符名(即,使用\uXXXX符记法),被等价处理,除了这个替换被还原为原始字符串字面量的地方。)
2、后面跟着一个新行字符的一个倒斜杠字符(\)的每个实例都被删除,它胶接物理源代码行以形成逻辑源代码行。在物理源代码行上只有最后的倒斜杠有资格作为这么一个胶接的一部分。[译者注:比如,
int main(int argc, const char * argv[]) { const char *str = "1234\ 5678"; puts(str); }
上述第三行最后加了倒斜杠字符,而倒斜杠后紧跟新行,那么第三行与下面的第四行进行胶接,形成一个逻辑行。因此,对于物理行3、4行在翻译之后就变成了
const char *str = "12345678";
]
如果,作为一个结果,匹配一个通用字符名的语法的一个字符序列产生,那么行为是未定义的。[译者注:比如,
int main(int argc, const char * argv[]) { const wchar_t c = '\u\ 1234'; putchar(c); puts(""); }
上述代码中,第三、第四行胶接后正好形成一个通用字符名——'\u1234',此时行为是未定义的。(在Apple LLVM3.0中仍然能显示出通用字符名所表示的字符)
]
一个不空以及不以一个新行字符结束的源文件,或者以一个新行字符结束,而前面有一个倒斜杠字符,在任一这种胶接发生之前,应该被处理,就好像一个额外的新行字符被添加到文件后面。[译者注:对于使用过GCC的朋友对此应该很熟悉了。如果你的源文件末尾不是以新行字符结束的,那么编译器会发出警告,要求你添加上新行。而此时,编译器在翻译时会自动添加一个新行字符。]
3、源文件被分解为预处理符记(2.5)以及空白字符序列(包括注释)。一个源文件不应该在一个部分预处理符记或在一个部分注释中结束。[注:一个部分预处理符记会发生在源文件结束处,此位置作为需要一个字符的终结序列的一个多字符符记的第一个部分,比如缺少闭合的"或>的header-name。一个部分注释会发生在带有一个未闭合的/*注释的源文件结束处。]每个注释用一个空格字符代替。新行字符被保留。除了新行字符以外的每个非空空白字符序列是被保留还是用一个空格字符代替并未指定。将一个源文件的字符划分为预处理符记是依赖于上下文的。[例:见#include预处理指示符内的<的处理。 ——例结束]
4、预处理指示符被执行,宏调用被展开,并且_Pragma单目操作符表达式被执行。如果匹配一个通用字符名的语法的一个字符序列通过符记连接(16.3.3[译者注:即宏里的##符记])产生,那么行为是未定义的。一个#include预处理指示符使得命名的头文件和源文件从阶段1到阶段4被分别处理。所有的预处理指示符然后被删除。
5、在一个字符字面量或字符串字面量中的每个源字符集成员,以及在一个字符字面量或一个非原生的字符串字面量中的每个转义序列和通用字符名,被转为执行字符集(2.14.3, 2.14.5);如果没有相应的成员,那么它被转为一个实现定义的成员而不是空(宽)字符[注:一个实现不需要将所有非相对应的源字符转为同一个执行字符]。
6、相邻的字符串字面量符记被联结。[译者注:比如,"1234" "5678"会被联结为"12345678"]
7、分割符记的空白字符不再重要。每个预处理符记被转换为一个符记。(2.7)结果符记在语法上和语义上被分析,并作为一个翻译单元而被翻译。[注:分析和翻译符记的过程可能偶尔导致一个符记正被一序列其它符记代替。(14.2) ——注结束][注:源文件、翻译单元、以及被翻译的翻译单元不需要被存储为文件,也不需要在这些实体与任一外部表示之间有任一的一对一的对应关系。这个描述仅仅是在概念上,而并不指定任一特定的实现。 ——注结束]
8、被翻译的翻译单元以及实例化单元如下结合:[注:所有这些中的某些可以从一个库中被提供。 ——注结束]每个被翻译的翻译单元被检查以产生所需要的实例化的一个列表。[注:这可以包括已经被显式请求的实例。(14.7.2) ——注结束]所需要的模板的定义被定位。包含这些定义的翻译单元的源是否要求是可用的,是由实现定义的。[注:一个实现可以将充足的信息编码到已被翻译的翻译单元,以至于源在这里是不需要的。 ——注结束]所有所需要的实例化被执行以产生实例化单元。[注:这些类似于被翻译的翻译单元,但并不包含对未被实例化的模板的引用以及不包含模板定义。 ——注结束]如果有任一实例化失败,那么程序是不良定义的。
9、所有外部实体引用被解决。库组件被连接以满足对在当前翻译中未被定义的实体的引用。所有这样的翻译器的输出被搜集在一个程序镜像中,程序镜像包含了在其执行环境中为执行所需要的信息。
2.5 预处理符记
preporcessing-token:
header-name
identifier
pp-number
character-literal
user-defined-character-literal
string-literal
user-defined-string-literal
preprocessing-op-or-punc
无法为上述之一的每个非空白字符
1、每个被转换为一个符记的预处理符记应该具有一个关键字、标识符、字面量、操作符、或标点符的词法形式。
2、在翻译阶段3到6中,一个预处理符记是语言的最小词法元素。预处理符记的类别有:头文件名、标识符、预处理数字、字符字面量(包括用户自定义的字符字面量)、字符串字面量(包括用户自定义的字符串字面量)、预处理操作符和标点符,以及不在词法上匹配其它预处理符记类别的单个非空白字符。如果一个'或一个"字符匹配了最后那个类别,那么行为是未定义的。预处理符记可以用空白符分割;这由注释(2.8),或空白字符(空格、水平制表符、新行、垂直制表符、以及换页),或两者共同组成。正如在条款16中所描述的,在翻译阶段4中的某些情况下,空白符(或其中缺省的)不仅仅充当预处理分割。空白符可以出现在一个预处理符记内,仅作为一个头文件名的一部分,或在一个字符字面量或字符串字面量中的引号字符之间。
3、如果输入流根据一个给定的字符已被解析为预处理符记,那么:
——如果下一个字符以一个字符序列开始,可能是一个原生字符串字面量的前缀和初始双引号,诸如R",那么下一个预处理符记应该是一个原生字符串字面量。在原生字符串的起始和末尾双引号之间,任何在阶段1和阶段2中执行的转换(三字符、通用字符名、以及行拼接)被还原;这个还原应该在任一d-char、r-char、或划定圆括号被标识之前应用。原生字符串字面量被定义为匹配原生字符串模式最短的字符序列
encoding-prefixoptR raw-string
——否则,如果接下去的三个字符是<::并且后面的字符既不是:也不是>,那么<单独被对待为预处理符记,而并非作为可选<:的首字符。
——否则,下一个符记是可能构成一个预处理符记最长的字符序列,即使那可能会导致进一步词法分析失败。
[例:
#define R "x" const char* s = R"y"; // 不良形式的原生字符串,并不是"x" "y"
——例结束]
4、[例:程序片段1Ex被解析为一个预处理数字符记(它并不是一个有效的浮点或整型字面量符记),即使一个解析为预处理符记1和Ex对可能产生一个有效的表达式(比如,如果Ex是一个被定义为+1的宏)。类似的,程序片段1E1被解析为一个预处理数字(它是一个有效的浮点字面量符记),不管E是否为一个宏名。 ——例结束]
5、[例:程序片段x+++++y被解析为x ++ ++ + y,这里,如果x和y具有整型类型,那么违背了递增操作符的限制,即使解析为x++ + ++y可能产生一个正确的表达式。 ——例结束]