Parsing 词法分析器与语法分析器

Parsing 词法分析器与语法分析器,parsing,antlr,lexer,pygments,Parsing,Antlr,Lexer,Pygments,词法分析器和语法分析器在理论上真的有那么大的不同吗 讨厌正则表达式似乎很时髦: 然而,流行的基于词法分析的工具:,或,都使用正则表达式。他们似乎什么都不知道 什么时候词汇量足够,什么时候需要EBNF 是否有人将这些lexer生成的令牌用于bison或antlr解析器生成器?是的,它们在理论和实现上都有很大的不同 词法分析器用于识别构成语言元素的“单词”,因为这些单词的结构通常很简单。正则表达式非常擅长处理这种简单的结构,并且有非常高性能的正则表达式匹配引擎用于实现lexer 解析器用于识别语言短

词法分析器和语法分析器在理论上真的有那么大的不同吗

讨厌正则表达式似乎很时髦:

然而,流行的基于词法分析的工具:,或,都使用正则表达式。他们似乎什么都不知道

什么时候词汇量足够,什么时候需要EBNF


是否有人将这些lexer生成的令牌用于bison或antlr解析器生成器?

是的,它们在理论和实现上都有很大的不同

词法分析器用于识别构成语言元素的“单词”,因为这些单词的结构通常很简单。正则表达式非常擅长处理这种简单的结构,并且有非常高性能的正则表达式匹配引擎用于实现lexer

解析器用于识别语言短语的“结构”。这种结构通常远远超出“正则表达式”所能识别的范围,因此需要 “上下文敏感”解析器来提取这种结构。上下文敏感解析器 很难构建,因此工程上的折衷方案是使用“上下文无关”语法 并向解析器(“符号表”等)添加hack来处理上下文敏感部分

词法分析和解析技术都不可能很快消失


它们可以通过决定使用“解析”技术来识别“单词”来统一,正如目前所谓的无扫描GLR解析器所探索的那样。这有一个运行时成本,因为您正在将更通用的机器应用于通常不需要它的问题,并且通常您会在开销中为此付费。在有大量空闲周期的地方,开销可能并不重要。如果您处理大量文本,那么开销确实很重要,经典正则表达式解析器将继续使用。

解析器和词法分析器的共同点:

  • 他们从输入中读取一些字母表的符号。
    • 提示:字母表不一定是字母表。但是它 必须是语言的原子符号 解析器/词法分析器可以理解
    • lexer的符号:ASCII字符
    • 解析器的符号:特定的标记,它们是语法的终端符号
  • 他们分析这些符号,并试图将它们与他们所理解的语言语法相匹配。
    • 这就是真正的区别所在。详见下文
    • 词汇学习者理解的语法:常规语法(乔姆斯基的第3级)
    • 语法分析器理解的语法:上下文无关语法(乔姆斯基的第2级)
  • 他们将语义(意义)附加到他们找到的语言片段上。
    • 词法者通过将词素(输入的符号串)分类为特定的标记来附加意义。例如,所有这些词素:
      *
      ==
      
      什么时候词汇量足够,什么时候需要EBNF

      EBNF并没有给语法增加太多功能。这只是一个方便/快捷的符号/“语法糖”,而不是标准的乔姆斯基范式(CNF)语法规则。例如,EBNF替代方案:

      S --> A | B
      
      您只需单独列出每个备选产品,即可在CNF中实现:

      S --> A      // `S` can be `A`,
      S --> B      // or it can be `B`.
      
      EBNF中的可选元素:

      S --> X?
      
      S --> A*
      
      S --> A+
      
      您可以在CNF中通过使用可为空的产生式来实现,也就是说,可以用空字符串替换的产生式(此处仅用空产生式表示;其他使用epsilon或lambda或交叉圆):

      类似于上面最后一个
      B
      形式的产品称为“擦除”,因为它可以擦除它在其他产品中代表的任何内容(产品是空字符串而不是其他内容)

      EBNF的零次或多次重复:

      S --> X?
      
      S --> A*
      
      S --> A+
      
      您可以通过使用递归产生(recursiveproduction)来实现,也就是说,将自身嵌入其中某个位置的产生(recursiveproduction)。这可以通过两种方式实现。第一个是左递归(通常应该避免,因为自上而下的递归下降解析器无法解析它):

      知道它只生成一个空字符串(最终)后跟零个或多个
      A
      s,相同的字符串(但不是相同的语言!)可以使用正确的递归表示:

      对于EBNF的一次或多次重复,当涉及到
      +
      时:

      S --> X?
      
      S --> A*
      
      S --> A+
      
      可以通过分解出一个
      A
      并像前面一样使用
      *
      来完成:

      S --> A A*
      
      您可以用CNF这样表示(我在这里使用右递归;作为练习,尝试自己找出另一个):

      知道了这一点,您现在可能可以将正则表达式的语法(即正则语法)识别为可以在仅由终端符号组成的单个EBNF产品中表达的语法。更一般地说,当您看到类似以下内容的产品时,您可以识别常规语法:

      A -->        // Empty (nullable) production (AKA erasure).
      B --> x      // Single terminal symbol.
      C --> y D    // Simple state change from `C` to `D` when seeing input `y`.
      E --> F z    // Simple state change from `E` to `F` when seeing input `z`.
      G --> G u    // Left recursion.
      H --> v H    // Right recursion.
      
      也就是说,只使用空字符串、终端符号、简单的非终端进行替换和状态更改,并且只使用递归实现重复(迭代,它只是线性递归,不象分支树那样)。没有比这些更高级的了,那么你肯定这是一个常规语法,你可以使用lexer来实现

      但是,当您的语法以一种非平凡的方式使用递归时,会产生类似树的、自相似的嵌套结构,如下所示:

      S --> a S b    // `S` can be itself "parenthesized" by `a` and `b` on both sides.
      S -->          // or it could be (ultimately) empty, which ends recursion.
      
      然后您可以很容易地看到,正则表达式无法实现这一点,因为您无法以任何方式将其解析为单个EBNF产品;您最终将无限期地替换
      S
      ,这将始终在两侧添加另一个
      a
      S和
      b
      S。词法分析器(更具体地说:词法分析器使用的有限状态自动机)不能计数到任意数(它们是有限的,记得吗?),所以它们不知道有多少个
      a
      s与这么多
      b
      s均匀匹配。这样的语法称为上下文无关语法(至少是这样),它们需要解析器

      上下文无关语法是众所周知的语法分析,因此它们被广泛用于描述