在C++标准中,它在哪里指示用它所代表的源代码替换类别描述符的间隔协议



冒着问一个被认为过于挑剔的问题的风险,我花了很长时间试图证明(作为整个标准在不同上下文中发生的事情的一个例子)C++11标准§2.14.2中integer literal的以下定义,特别是关于一个细节,即语法符号本身中存在空格。

(请注意,这个例子-整数字面的定义-不是我问题的重点。我问题的要点是询问C++标准本身使用的语法描述符号,特别是语法类别名称之间的空格。我在这里给出的例子-整数文本的定义-是专门选择,因为它是一个简单明了的例子。)

(简洁的缩写,来自§2.14.2):

integer-literal:
decimal-literal integer-suffix_opt
decimal-literal:
nonzero-digit
decimal-literal digit

(具有预期的nonzero-digitdigit,[0]1…9)。(注:以上文本均为标准中的斜体字。)

这一切对我来说都是有意义的,假设语法类别描述符decimal-literaldigit之间的空格被理解为不存在于实际源代码中,而仅存在于第2.14.2节中出现的语法描述本身中。

这种约定——在符号中的类别描述符之间放置一个空格,可以理解该空格不存在于源代码中——在规范的其他地方也使用。这里的例子只是一个明确的例子,其中空间显然不应该出现在源代码中。(有关标准中的反例,请参阅本问题的附录,其中当源代码中的类别描述符被实际标记替换时,类别描述符之间必须存在空格或其他分隔符/s,或者是可选的。)

同样,冒着挑剔的风险,我在标准中找不到任何约定声明,即在解释本例中的符号时,源代码中不应存在空格。

该标准确实在§1.6.1(及其后)中讨论了符号惯例。我能找到的关于这方面的唯一相关文本是:

在本国际标准中使用的语法表示法中类别由斜体表示,文字单词和等宽类型的字符。备选方案单独列出行,除非在少数情况下标记了一长串备选方案

我不会那么挑剔;然而,我发现标准中使用的符号有点棘手,所以我想弄清楚所有的细节。我很感激任何愿意花时间向我介绍这件事的人。

附录针对类似于">很明显,最终源代码中不应包含空白,因此标准没有必要明确说明这一点"的评论:我在这个问题中选择了一个微不足道的例子,其中是明显的。在标准中有许多情况下,如果没有对语言的先验知识(在我看来),它就不明显,例如§8.0.4讨论"const"one_answers"volatile":

cv-qualifier-seq:
cv-qualifier cv-qualifier-seq_opt

注意这里的相反的假设(在最终源代码中,空格或另一个或多个分隔符是所必需的),但这不可能从语法表示法本身推导出来。

也有空间可选的情况,例如:

noptr-abstract-declarator:
noptr-abstract-declarator_opt parameters-and-qualifiers

(在这个例子中,为了表明一点,我不会给出章节编号或解释正在讨论的内容;我只会问,从语法符号本身来看,在这个上下文中,最终源代码中的空白在标记之间是可选的。)

我怀疑,这些评论——"这很明显,所以这一定是"——是我选择的例子如此明显的结果。这正是我选择这个例子的原因。

§2.7.1

有五种令牌:标识符、关键字、文本、,运算符和其他分隔符。空白,水平和垂直标签,换行符、表单提要和注释(统称为"空白"),作为下面描述的被忽略,除非它们用于分隔标记

因此,如果一个文字是一个标记,而空白用于分隔标记,则文字数字之间的空格将被解释为两个单独的标记,因此不能是同一文字的一部分。

我有理由相信,在标准中没有更直接的解释来解释这一事实。

所使用的表示法与典型的BNF非常相似,它们认为许多相同的通用约定是理所当然的,包括表示法中的空白除了分隔BNF本身的标记之外没有任何意义——如果/当空白在源代码中具有超出分隔标记的意义时,它们将包括直接指定它的符号(例如,对于大多数预处理指令,直接指定new-line

#ifdef标识符新行组opt

或:

#包括<h-char-sequence>新行

的责任可能要追溯到Algol 68标准,该标准在精确指定语法方面做得太过火了,如果没有几周的全职学习,任何人基本上都不可能阅读1。从那时起,对语法描述语言的任何粗略解释都会被拒绝,因为它太像Algol 68了,而且毫无疑问会失败,因为它过于正式,没有人会阅读或理解它


1你问的怎么会这么糟糕?基本上是这样的:他们从语法描述语言的正式英语描述开始。不过,这并没有被用来定义Algol 68——它被用来指定(甚至更准确地说)另一种语法描述语言。然后使用第二个语法描述语言来指定Algol 68本身的语法。因此,您必须学习两种独立的语法描述语言,然后才能开始阅读Algol68语法本身。毫无疑问,你可以猜到,几乎没有人这样做过。

正如您所说,标准规定:

常宽型中的文字和字符

因此,如果要在规则中包含文字空间,则必须以恒定宽度类型呈现。仔细检查该标准会发现,您所指的生产中的空间比恒定宽度类型窄。(此外,您试图引用该标准是一种错误陈述,因为它以等宽类型呈现,而该等宽类型应以斜体呈现,从而导致语义变化。)


好的,这就是";有抱负的语言律师;答复此外,它并没有真正起作用,因为它在所有形式的产品上都失败了:

One of:
0 1 2 3 4 5 6 7 8 9

我认为,事实上,答案是空白不是形式语法的一部分,因为它只用于分隔标记;此外,这句话主要适用于语法本身,语法的标记由空白分隔,而空白不是标记,只是语法中的缩进很重要,这与程序中的缩进不同。


回答补遗的补遗

实际上,constvolatile需要用空格分隔并不是真的。它们只需要是单独的令牌。示例:

#define A(x)x
A(const)A(volatile)A(int)A(x)A(;)

同样,更重要的是,第2章(特别参考2.2和2.5,但您必须阅读全文)描述了如何处理程序文本以生成令牌流。所有声称必须忽略空白的规则都在语法的这一部分中,而所有声称可能需要空白的规则则不是。

这实际上是两种独立的语法,但词法语法必然是不完整的,因为你需要考虑预处理器的操作才能应用它

我相信我所说的每一句话都可以从标准中得到。以下是一些摘录:

2.2(3)源文件被分解为预处理标记(2.5)和空白字符序列(包括注释)…将源文件的字符划分为预处理令牌的过程取决于上下文。

2.2(7)分隔标记的空白字符不再有效。每个预处理令牌都被转换为一个令牌。(2.7)对生成的令牌进行语法和语义分析,并将其翻译为翻译单元。

我认为所有这些都清楚地表明,有两种语法,一种是词法语法,也就是说,它从一系列字形(字符)中产生一个字形(标记),另一种是句法语法,也即,它从一连串的字形(符号)中产生抽象语法树。在这两种情况下(除了一个小的例外,我稍后会讨论),如果词汇语法允许的话,除了阻止两个词元相互碰撞的东西之外,空白不被认为是其他任何东西。(参见2.5(3)中的算法。)

C++在语法上并不漂亮,所以几乎总是有例外。其中一个继承自C,是之间的区别

#define A(X)(X)

#define A (X)(X)

预处理指令有自己的解析规则,这一规则由以下定义代表:

lparen
前面不是空白的(字符

我想说,这是证明规则的例外[注1]。事实上,有必要说这个(前面没有空白,这表明在句法规则中标记(的正常使用并不能说明它的空白上下文。

因此,借用雷·卡明斯(而不是阿尔伯特·爱因斯坦,正如有时所说的那样)的话,";时间和空白都是将一个令牌与另一个令牌分隔开的"[注2]


[注1]我在这里使用了这个短语的原始法律意义,就像perCicero一样。

[注2]:

"时间,";乔治说:";为什么我可以给你一个时间的定义。这就是阻止一切同时发生的原因">

那一小群人发出阵阵笑声。

"确实如此;化学家同意了"先生们,这并不像听起来那么好笑。事实上,这确实是一个不错的科学定义。时间和空间将一个事件与另一个事件分隔开来…

摘自《掌握时间的人》,雷·卡明斯著,1929年,王牌图书。见第一页,在谷歌图书

标准实际上有两个独立的语法。

第2节和第16节中描述的预处理器语法定义了在翻译阶段1-6中如何将源字符序列转换为预处理标记和空白字符序列。在这个语法的某些阶段和部分中,空白是非常重要的。

在翻译阶段4之后,不属于预处理标记的空白字符不再重要。该标准在翻译阶段7开始时明确规定,在预处理标记之间丢弃空白字符。

语言语法定义了在翻译阶段7中如何对一系列标记(从预处理标记转换而来)进行语法和语义解释。在这个语法中没有空白这回事。(此时,' '字符文字,就像'c'一样。)

在这两种语法中,标准中可见的语法组件之间的空间与源或执行空白字符无关,它只是为了使标准清晰可见。当预处理器语法依赖于空白时,它会用单词拼写出来,例如:

c-char:

源字符集的任何成员,单引号'、反斜杠或换行符除外

转义序列

通用字符名

控制线:

# define标识符lparen标识符列表[opt])替换列表换行

lparen

不紧跟空白的(字符

因此,整数文本的数字之间可能没有空格,因为预处理器语法不允许。

这里的另一个重要规则来自C++11 2.5p3:

如果输入流已被解析为预处理标记,最大可达给定字符:

  • 如果下一个字符以一系列字符开头,这些字符可能是原始字符串文字的前缀和初始双引号,例如R",则下一个预处理标记应为原始字符串文字。。。

  • 否则,如果接下来的三个字符是<::,并且随后的字符既不是:也不是>,则<本身被视为预处理器令牌,而不是替代令牌<:的第一个字符。

  • 否则,下一个预处理令牌是可能构成预处理令牌的最长字符序列,即使这会导致进一步的词汇分析失败。

因此,constvolatile令牌之间必须有空格,否则,最长的令牌规则将把它转换为单个标识符令牌constvolatile

最新更新