【问题标题】:Does context-sensitive tokenisation require multiple goal symbols in the lexical grammar?上下文相关的标记化是否需要词汇语法中的多个目标符号?
【发布时间】:2021-12-27 04:08:26
【问题描述】:

根据ECMAScript spec

词法输入的识别有几种情况 元素对句法语法上下文敏感,即 消耗输入元素。 这需要多个目标符号 词汇语法。

两个这样的符号是InputElementDivInputElementRegExp

在 ECMAScript 中,/ 的含义取决于它出现的上下文。根据上下文,/ 可以是除法运算符、正则表达式文字的开头或注释分隔符。词法分析器无法单独区分除法运算符和正则表达式文字,因此它必须依赖来自解析器的上下文信息。

我想了解为什么这需要在词汇语法中使用多个目标符号。我对语言设计了解不多,所以我不知道这是由于语法的某种形式要求还是只是约定。

问题

  • 为什么不像这样只使用一个目标符号:
InputElement ::
     [...]
     DivPunctuator
     RegularExpressionLiteral
     [...]

让解析器告诉词法分析器使用哪个产生式(DivPunctuator vs RegExLiteral),而不是使用哪个目标符号(InputElementDiv vs InputElementRegExp)?

  • 还有哪些其他语言在其词汇语法中使用多个目标符号?

  • 我们如何对 ECMAScript 词法文法进行分类?就 CSG 的正式定义而言,它不是上下文敏感的(即其产生式的 LHS 没有被终端和非终端符号的上下文包围)。

【问题讨论】:

  • 语法的“目标”和“开始”符号是等价的术语。词法分析器应该独立于解析器工作以提高性能。但是,实际上,大多数主要语言的解析器都可以!词法分析器是一个识别器,它有自己的包含字母、规则和开始符号的语法。在 Antlr4 中,这使用“词法分析器语法”语法是显式的,除了开始符号不是显式的,因为它是一个可以派生任何标记的规则。在 Antlr4 中,您可以使用“模式”切换开始符号。 [grammars-v4](github.com/antlr/grammars-v4) 中“模式”的 grep。

标签: parsing programming-languages grammar context-free-grammar context-sensitive-grammar


【解决方案1】:

在该术语的正式语言定义中,说词法产生“对消耗输入元素的句法语法上下文敏感”并不会使语法上下文敏感。事实上,在几乎所有重要的语法中都有“对句法语法上下文敏感”的产生式。这是解析的本质:句法上下文有效地提供了一组潜在可扩展的非终结符,并且它们在不同的句法上下文中会有所不同,这意味着,例如,在大多数语言中,不能在需要表达式的地方输入语句(尽管通常情况下,表达式是语句的一种表现形式)。

但是,差异不涉及相同的非终结符的不同扩展。 “无上下文”语言所要求的是,非终结符的可能派生集合是相同的集合,无论该非终结符出现在哪里。因此上下文可以提供不同的非终结符选择,但是每个非终结符都可以扩展而不考虑其上下文。这就是语法与上下文无关的意义。

正如您所注意到的,上下文敏感性通常在语法中由左侧具有模式而不是单个非终结符的语法抽象出来。在最初的定义中,上下文——除了要扩展的非终结符之外的所有东西——都需要原封不动地通过产生式;只能扩展一个非终结符,但可能的扩展取决于上下文,如产生式所示。上面隐含的是,有些语法可以用 BNF 编写,它们甚至不符合上下文敏感的规则(或其他等效规则)。所以它不是一个二进制除法,无论是上下文无关的还是上下文敏感的。语法可能两者都不是(并且,由于空上下文仍然是上下文,因此任何上下文无关语法也是上下文敏感的)。底线是,当数学家说话时,他们使用单词的方式有时是出乎意料的。但它总是有一个明确的基本定义。

在形式语言理论中,没有词汇和句法产生;只是制作。如果词汇产生和句法产生都没有上下文,那么整个语法就是没有上下文的。但是,从实际的角度来看,组合语法更难解析,原因有很多,我不打算在这里讨论。事实证明,为一种语言编写语法并对其进行解析会更容易一些,只需区分词法解析器和句法解析器。

在经典模型中,首先进行词法分析,因此解析器看不到单个字符。相反,句法分析是通过“词汇标记”的“字母表”(在非常扩展的意义上)完成的。这非常方便——例如,这意味着词法分析可以简单地删除空格和 cmets,这极大地简化了句法语法的编写。但它也降低了通用性,正是因为句法解析器不能“指导”词法分析器做任何事情。在语法分析器意识到它的需求之前,词法分析器已经完成了它要做的事情。

如果解析器能够引导词法分析器,它会以与引导自身相同的方式这样做。在某些产品中,令牌非终端将包括 InputElementDiv,而在其他产品中,InputElementRegExp 将是可接受的非终端。正如我所指出的,这不是上下文敏感性——它只是上下文无关语法的正常功能——但它确实需要对程序的组织进行修改,以允许词法分析器考虑解析器的目标.这通常被(实践者,而不是理论家)称为“词汇反馈”,有时也被称为价值中立性较低的术语;它有时被认为是语言设计中的一个弱点,因为它违反了整齐分离的词法分析器/解析器架构。 C++ 是一个非常强烈的例子,确实有人类难以解析的 C++ 程序,这是某种迹象。但是 ECMAScript 并没有真正遇到这个问题。人类通常无需付出任何明显的智力努力就能区分除法运算符和正则表达式分隔符。而且,虽然实现 ECMAScript 解析器所需的词法反馈确实使架构不那么整洁,但这也不是一项艰巨的任务。

无论如何,词汇语法中的“目标符号”只是 ECMAScript 参考的作者决定使用的短语。那些“目标符号”只是普通的词汇非终结符,就像任何其他产生式一样,所以说有“多个目标符号”和说“解析器指示词法分析器使用不同的产生式”没有区别,我希望能解决您提出的问题。

注意事项

  1. 这两种语境的词汇差异不仅仅在于/ 具有不同的含义。如果仅此而已,则根本不需要词汇反馈。问题是标记化本身发生了变化。如果一个运算符是可能的,那么/= in

    a /=4/gi;
    

    是单个标记(复合赋值运算符),gi 是单个标识符标记。但是,如果此时可以使用正则表达式文字(但事实并非如此,因为正则表达式文字不能跟随标识符),那么 /= 将是单独的标记,gi 也是如此。

  2. 一些程序员更喜欢从一组产品构建的解析器(但不是写这个的人:-));它们通常被称为“无扫描解析器”。在 ECMAScript 的无扫描仪解析器中,不会有词法反馈,因为没有单独的词法分析。

  3. 在形式语言理论的理论纯粹性与编写真实编程语言的工作解析器的实际细节之间确实存在差异。理论模型非常有用,如果不了解它们,就很难编写解析器。但是很少有解析器严格遵守模型,这没关系。类似地,通常称为“正则表达式”的东西在正式语言意义上根本不是正则;一些“正则表达式”运算符甚至不是上下文无关的(反向引用)。因此,假设某些理论结果(“正则表达式可以在线性时间和恒定空间中识别”)实际上适用于“正则表达式”库,那将是一个巨大的错误。我不认为解析理论是唯一表现出这种二分法的计算机科学分支。

【讨论】:

  • (说有“多个目标符号”和说“解析器指示词法分析器使用不同的产生式”没有区别):如果有问题的产生式是那些目标符号,但在原始问题中并非如此。
  • @michaelDyck:好的,这很公平。我会重写它以更精确。
  • rici,@michaelDyck,为了澄清,我的主要问题是为什么我们需要多个目标符号。它们可能只是规范作者的“词汇状态”版本吗? article 提到,当我们需要对接受来自递归子语言(如正则表达式或模板文字)的字符串的语言结构进行标记时,词法状态特别有用。
  • @user51462:是的,没错。但与“词汇状态”的完全一般性不同,目标符号可以通过在解析器的状态机中为每个状态构造一组可能的初始词汇非终结符来从句法语法中导出。 (您必须这样做才能编写解析器;该标准只提供了一种简化的算法。)所以我坚持我的基本主张,即没有提供额外的解析能力;目标符号是语法中固有的。但它们确实为组织工作提供了一个方便的模型。
  • 我会将所有内容编辑到答案中。但不是这一刻。
【解决方案2】:

为什么不像这样使用一个单一的目标符号:

InputElement ::
  ...
  DivPunctuator
  RegularExpressionLiteral
  ...

让解析器告诉词法分析器使用哪个产生式(DivPunctuator vs RegExLiteral),而不是使用哪个目标符号(InputElementDiv vs InputElementRegExp)?

请注意,DivPunctuator 和 RegExLiteral 本身并不是产生式,而是非终结符。在这种情况下,它们是您为 InputElement 提议的产品中的右侧(替代方案)。所以我将你的问题改写为:为什么不让句法解析器告诉词法解析器使用这两个替代方案中的哪一个? (或者等效地,这两个中的哪一个要压制。)

在 ECMAScript 规范中,有一种机制可以实现这一点:语法参数(在 section 5.1.5 中解释)。

例如,您可以定义参数Div,其中:

  • +Div 表示“斜线应被识别为 DivPunctuator”,并且
  • ~Div 表示“斜杠应被识别为 RegExLiteral 的开头”。

那么你的作品就会变成

InputElement[Div] ::
  ...
  [+Div] DivPunctuator
  [~Div] RegularExpressionLiteral
  ...

但请注意,语法解析器仍然必须告诉词法解析器使用 InputElement[+Div]InputElement[~Div] 作为目标符号,因此您回到规范的当前解决方案,即模重命名。

还有哪些其他语言在其词汇语法中使用多个目标符号?

我认为大多数人不会尝试定义派生所有标记(或输入元素)的单个符号,更不用说必须将其划分为 ECMAScript 的 InputElementFoo 之类的变体,因此可能很难找到具有类似功能的另一种语言在其规范中。

相反,简单地为不同类型的标记(例如标识符、NumericLiteral)定义语法规则,然后从语法产生中引用它们是很常见的。所以这有点像有多个词汇目标符号,但不是(我会说)你问的那种意义上的。

我们如何对 ECMAScript 词法文法进行分类?

它基本上是上下文无关的,加上一些扩展。

【讨论】:

  • 词汇语法的哪一部分不是上下文无关的? (就此而言,提供的句法语法的哪一部分,不包括叙述中列出的许多上下文相关的约束)?
  • 语法参数可以通过宏替换来消除。它们都是有限的(甚至不是不切实际的大)。可选性(如重复)也可以被宏替换。前瞻约束可用于创建上下文相关的语法,但它们在任何地方都没有这样使用。 (CFG 和常规语法的交集是 CFG。)“但不是”和“但仅当”也是如此。 (可能是 CS 但不是那样使用)
  • 自动分号插入肯定是有界上下文;事实上,除非最近发生了变化,否则只需要一次前瞻。
  • 我注意到现在对在正则表达式中使用编号反向引用有一个上下文相关的要求,其中反向引用不能指定大于正则表达式中捕获数的数字。所以我会给你那个。
  • 如果“覆盖语法”是指第 5.2.4 节规定的第二次解析,那么其中一些可能是上下文相关的。但正如我上面所说,如果它所做的只是消除歧义,那么该语言仍然是上下文无关的。无论如何,这不是词汇语法的一部分。
猜你喜欢
  • 2014-06-07
  • 1970-01-01
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 2021-12-01
  • 1970-01-01
相关资源
最近更新 更多