大多数编程语言的词汇语法都是非常不具有表现力的,以便快速掌握它。我不确定Rust的词汇语法属于哪个类别。大多数似乎是常规的,可能除了raw string literals:
let s = r##"Hi lovely "\" and "#", welcome to Rust"##;
println!("{}", s);
打印哪些:
Hi lovely "\" and "#", welcome to Rust
因为我们可以添加任意多#
,所以它似乎不是常规的,对吧?但语法是否至少没有上下文?或者是否有关于Rust的 lexical 语法的非上下文免费?
相关:Is Rust's syntactical grammar context-free or context-sensitive?
答案 0 :(得分:8)
原始字符串文字语法不是无上下文的。
如果您将其视为由r#k"…"#k
包围的字符串(使用上标k
作为计数运算符),那么您可能希望它是无上下文的:
raw_string_literal
: 'r' delimited_quoted_string
delimited_quoted_string
: quoted_string
| '#' delimited_quoted_string '#'
但这实际上并不是正确的语法,因为quoted_string
不允许包含"#k
,但它可以包含任何"#j
的{{1}}
排除终止序列而不排除任何其他类似的不同长度的序列无法通过无上下文语法完成,因为它涉及j<k
的三个(或更多)用法 - 在单个生产中重复,堆栈自动机只能处理两个。 (语法不是无语境的证据非常复杂,所以我不会因为缺少MathJax而在这里尝试。我能想出的最好的证据是使用Ogden的引理而且非常罕见引用(但非常有用)的属性,即在有限状态传感器的应用下关闭无上下文语法。)
C ++原始字符串文字也是上下文敏感的[或者如果分隔符长度不受限制,请参见注释1],并且所有对空格敏感的语言(如Python和Haskell)都是上下文敏感的。这些词法分析任务都不是特别复杂,因此上下文敏感性不是一个大问题,尽管大多数标准扫描仪生成器不能提供尽可能多的帮助。但它就是。
Rust的词汇语法为扫描仪生成器提供了许多其他复杂功能。一个问题是&#39; 的双重含义,它既用于创建字符文字,也用于标记生命周期变量和循环标签。显然,可以通过考虑先前识别的令牌来确定其中的哪一个适用。这可以通过词法扫描器解决,该词法扫描器能够从单个模式生成两个连续的令牌,或者可以使用无扫描器解析器来完成;后一种解决方案是无背景的,但不是常规的。 (C ++使用&#39; 作为数字文字的一部分不会导致同样的问题; C ++令牌可以用正则表达式识别,因为& #39; 不能用作数字文字的第一个字符。)
另一个稍微依赖于上下文的词汇问题是范围运算符k
优先于浮点值,因此必须将..
作为三个标记进行词法分析: 2 .. 3 ,而不是两个浮点数 2. .3 ,这是怎么回事在大多数使用最大蒙克规则的语言中进行分析。同样,这可能会或可能不会被视为偏离正则表达式标记化,因为它取决于尾随上下文。但由于前瞻最多只有一个字符,因此可以用DFA实现。
经过反思,我不确定询问词汇语法&#34;是否有意义。或者,至少,它是模糊的:&#34;词汇语法&#34;可能是指所有语言&#34;令牌&#34;的组合语法,或者它可能指的是将句子分成令牌的行为。后者实际上是一个传感器,而不是一个解析器,并提出了这个语言是否可以用有限状态传感器进行标记的问题。 (答案是否定的,因为FSA,甚至是PDA都无法识别原始字符串。)
识别单个令牌并对输入流进行标记不一定等效。可以想象一种语言,其中各个令牌都被正则表达式识别,但输入流不能用有限状态换能器处理。如果有两个正则表达式2..3
和T
,那么一些字符串匹配U
是最长的标记,这是T
中无限字符串集的严格前缀,就会发生这种情况。 }。作为一个简单(无意义)的例子,请使用带有标记的语言:
U
这两个令牌都是明确规则的,但输入流不能用有限状态传感器进行标记,因为在决定是否回退到第一个a
a*b
时,它必须检查a
s(任意长度)的任何序列。 {1}}或接受由所有a
和以下a
组成的令牌(如果有)。
很少有语言能够显示这种病症(据我所知,Rust不是其中之一),但从技术上讲,它存在于一些关键词是多字短语的语言中。