Rust 的词法语法是规则的、上下文无关的还是上下文敏感的?

Is Rust's lexical grammar regular, context-free or context-sensitive?

为了快速词法化,大多数编程语言的词法语法都没有表达能力。我不确定 Rust 的词法语法属于哪一类。大多数看起来很正常,可能 raw string literals:

除外
let s = r##"Hi lovely "\" and "#", welcome to Rust"##;
println!("{}", s);

打印:

Hi lovely "\" and "#", welcome to Rust

因为我们可以任意加很多#,好像不能正则吧?但是语法至少是上下文无关的吗?或者关于 Rust 的 词法 语法有什么非上下文无关的东西吗?


相关

原始字符串文字语法不是上下文无关的。

如果把它看成是被r#<sup>k</sup>"…"#<sup>k</sup>[=包围的字符串72=](使用上标 <code>k 作为计数运算符),那么您可能希望它是上下文无关的:

raw_string_literal
   : 'r' delimited_quoted_string
delimited_quoted_string
   : quoted_string
   | '#' delimited_quoted_string '#'

但这实际上不是正确的语法,因为 quoted_string 不允许包含 "#<sup>k</sup> 虽然对于任何 j<k

,它可以包含 "#<sup>j</sup>

排除终止序列而不排除任何其他不同长度的相似序列不能用上下文无关语法来完成,因为它涉及三个(或更多)使用k-单个产生式中的重复,堆栈自动机只能处理两个。 (证明语法不是上下文无关的非常复杂,所以我不打算在这里尝试,因为缺少 MathJax。我能想到的最好的证明使用 Ogden 的引理和不常见的引用(但非常有用) 属性上下文无关文法在有限状态转换器的应用下是封闭的。)

C++ 原始字符串文字也是上下文相关的[或者如果分隔符长度不受限制,请参阅注释 1],而且所有对空格敏感的语言(如 Python 和 Haskell) 是上下文相关的。 None 这些词法分析任务特别复杂,因此上下文敏感性不是一个大问题,尽管大多数标准扫描器生成器并没有提供人们希望的那么多帮助。但就是这样。

Rust 的词法语法为扫描器生成器提供了一些其他的复杂性。一个问题是 ' 的双重含义,它既用于创建字符文字,又用于标记生命周期变量和循环标签。显然,可以通过考虑先前识别的令牌来确定其中哪些适用。这可以通过能够从单个模式生成两个连续标记的词法扫描器来解决,或者可以通过无扫描器解析器来完成;后一种解决方案是上下文无关的,但不是常规的。 (C++'s use of ' 作为数字文字的一部分不会导致同样的问题;C++ 标记可以用正则表达式识别,因为 ' 不能用作数字文字的第一个字符。)

另一个稍微依赖于上下文的词法问题是范围运算符 .. 优先于浮点值,因此 2..3 必须作为三个标记进行词法分析:2 .. 3, 而不是两个浮点数 2. .3,这是大多数使用最大咀嚼规则的语言的分析方式。同样,这可能会或可能不会被视为与正则表达式标记化的偏差,因为它取决于尾随上下文。但由于前瞻最多是一个字符,所以它当然可以用 DFA 来实现。

后记

反思一下,我不确定问一个“词法文法”是否有意义。或者,至少,它是模棱两可的:“词法语法”可能是指所有语言“标记”的组合语法,或者它可能是指将一个句子分成标记的行为。后者实际上是一个转换器,而不是解析器,并提出了语言是否可以用有限状态转换器标记化的问题。 (答案再次是否定的,因为 FSA 甚至 PDA 都无法识别原始字符串。)

识别单个标记和标记输入流不一定等同。可以想象一种语言,其中各个标记都由正则表达式识别,但输入流不能用有限状态转换器处理。如果有两个正则表达式 TU 这样一些字符串匹配 T 是最长的标记,它是 [=19= 中无限字符串集的严格前缀,就会发生这种情况].作为一个简单(且无意义)的例子,以一种带有标记的语言为例:

a
a*b

这两个标记显然是规则的,但是输入流不能用有限状态转换器标记化,因为它必须在决定是否回退到首先 a 或接受由所有 a 和随后的 b (如果存在)组成的令牌。

很少有语言表现出这种病态(据我所知,Rust 不是其中之一),但它在技术上存在于某些关键字是多词短语的语言中。

备注

  1. 实际上,从技术意义上讲,C++ 原始字符串文字是常规的(因此是上下文无关的),因为它们的分隔符被限制为从 88 个字符的字母表中提取的最大长度为 16 的字符串。这意味着(理论上)可以创建一个由 13,082,362,351,752,551,144,309,757,252,761 模式组成的正则表达式,每个模式匹配不同的可能的原始字符串定界符。