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

时间:2012-12-06 23:52:55

标签: c++ c++11 standards

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

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

(缩写为简明,来自§2.14.2):

integer literal

(按预期integer-literal: decimal-literal integer-suffix_opt decimal-literal: nonzero-digit decimal-literal digit nonzero-digit,[0] 1 ... 9)。 (注意:上面的文字在标准中都是斜体。)

这一切都对我有意义,假设语法类别描述 digit decimal-literal 之间的空格被理解为不存在在实际的源代码中,但仅出现在语法描述本身中,如第2.12.2节中所示。

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

再次,冒着被挑剔的风险,我无法在标准的任何地方找到一个约定声明,在解释符号时,例如在本例中,空格不会出现在源代码中。

该标准确实讨论了§1.6.1(及其后)中的符号约定。我能找到的唯一相关文字是:

  

在本国际标准中使用的语法表示法中,使用句法   类别用斜体字和字面词表示   恒定宽度类型的字符。替代品单独列出   除了少数情况下标记了一长串备选方案的行   用短语“之一。”

我不会那么挑剔;但是,我觉得标准中使用的符号有些棘手,所以我想清楚所有的细节。我感谢任何愿意花时间来填补这一点的人。

ADDENDUM 为了回应与“”相似的评论,显然最终源代码中不应包含空格,因此不需要明确标准说明这个“:我在这个问题中选择了一个简单的例子,其中 显而易见。标准中有很多情况,如果没有a, 不明显。先验的语言知识(在我看来),如§8.0.4讨论“const”和“volatile”:

digit

...请注意相反的假设(空格,或其他分隔符或分隔符,在最终源代码中必需),但这不可能从语法符号本身。

还有一些空间可选,例如:

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

(在这个例子中,为了说明一点,我不会给出部分编号或者解释正在讨论的内容;我只是问一下,从这个本身的语法符号本身是否显而易见em> context,最终源代码中的空格在令牌之间是可选的。)

我怀疑沿着这些方向发表的评论 - “很明显,这就是它必须是什么” - 是我所选择的例子非常明显的结果。这正是我选择这个例子的原因。

4 个答案:

答案 0 :(得分:8)

§2.7.1

  

有五种令牌:标识符,关键字,文字,   操作员和其他分隔符。空白,水平和垂直标签,   新行,换页和评论(统称为“白色空间”),如   如下所述,将被忽略,除非它们用于分隔令牌

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

答案 1 :(得分:6)

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

使用的符号类似于典型的BNF,它们将许多相同的一般约定视为理所当然,包括符号中的空格除了分离BNF本身的标记之外没有任何意义 - 如果/当空白时除了分隔标记之外,它们在源代码中具有重要意义,它们将包括直接指定它的符号(例如,对于大多数预处理指令,new-line是直接指定的:

#ifdef 标识符换行组 opt

或:

#include < H-炭序列GT;新行

可能的责任归咎于Algol 68标准,该标准在精确指定语法的过程中远远落后于其他人在没有数周全职工作的情况下阅读基本上是不可能的研究 1 。从那以后,语法描述语言的最粗略的解释导致拒绝,因为它太像Algol 68,并且无疑会失败,因为它太正式,没有人会阅读或理解它。


1 你怎么会这么糟糕?它基本上是这样的:他们从语法描述语言的正式英语描述开始。但这并不是用来定义Algol 68的 - 它用于指定(甚至更精确地)另一种语法描述语言。然后使用 second 语法描述语言来指定Algol 68本身的语法。因此,您必须在之前学习两种单独的语法描述语言,您可以开始阅读Algol 68语法本身。你无疑可以猜测,几乎没有人做过。

答案 2 :(得分:3)

正如你所说,标准说:

  

恒定宽度类型的文字和字符

因此,如果规则中包含文字空间,则必须以恒定宽度类型呈现。仔细检查标准将显示您所指的生产空间比恒定宽度类型窄。 (此外,您引用标准的尝试是一种误传,因为它呈现为恒定宽度类型,应以斜体显示,随后会发生语义更改。)


好的,那是“有抱负的语言律师”的答案;此外,它并没有真正起作用,因为它失败了所有形式的作品:

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)。由此产生的标记在语法和语义上进行分析并翻译为翻译单元。

我认为所有这一切都清楚地表明有两个语法,一个词汇 - 也就是说,它从一系列字形(字符)中产生一个词法(标记) - 另一个语法 - 也就是说,它从一系列lexemes(标记)生成一个抽象语法树。在这两种情况下(除了一个小的例外,我将在一分钟内得到)是空白被认为是除了阻止两个词汇相互碰撞的东西,如果词法语法否则允许的话。 (参见2.5(3)中的算法。)

C++在语法上并不漂亮,因此几乎总会有例外。继承自C的其中一个是:

之间的区别
#define A(X)(X)

#define A (X)(X)

预处理指令有自己的解析规则,这个规则的定义是:

  

LPAREN 的:
  (字符前面没有空格

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

因此,用Ray Cummings(不是有时声称的阿尔伯特爱因斯坦)来解释,“时间和空白都是将一个令牌与另一个令牌分开的。” [注2]


[注1]我按照Cicero的原始法律意义使用此处的短语。

[注2]:

  “时间,”乔治说,“为什么我能给你一个时间的定义。这就是让一切都不能立刻发生的原因。”

     

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

     

“很好,”化学家同意。 “而且,先生们,这听起来并不是那么有趣。事实上,它确实不是一个糟糕的科学定义。时间和空间都是将一个事件与另一个事件分开......

- 来自掌握时间的人,作者:Ray Cummings,1929年,Ace Books。见first page, in Google books

答案 3 :(得分:3)

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

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

在翻译阶段4之后,不属于预处理令牌的空白字符停止显着。标准明确指出在翻译阶段7开始时丢弃预处理令牌之间的空白字符。

语言语法定义了如何在翻译阶段7中对语法序列(从预处理标记转换)进行语法和语义解释。在该语法中没有空格这样的东西。 (到目前为止,' '是一个字符 - 文字,就像'c'一样。)

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

  

C-炭

     

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

除外      

逸出序列

     

通用字符名称

  

控制线

     

...

     

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

     

...

     

LPAREN

     

(字符前面没有空格

因此, integer-literal 的数字之间可能没有空格,因为预处理程序语法不允许它。

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

  

如果输入流已被解析为预处理标记,直到给定字符:

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

  •   
  • 否则,如果接下来的三个字符为<::且后续字符既不是:也不是>,则<将被视为预处理程序令牌本身而不是备用标记<:的第一个字符。

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

  •   

因此,constvolatile令牌之间必须有空格,否则,最长令牌可能的规则会将其转换为单个标识符令牌{{1} }。