【发布时间】:2019-02-28 09:38:14
【问题描述】:
我正在为 Markdown 编写一个词法分析器。在这个过程中,我意识到我并没有完全理解它的核心职责应该是什么。
词法分析器最常见的定义是将字符的输入流转换为标记的输出流。
Input → Output
(characters) (tokens)
一开始听起来很简单,但这里出现的问题是 词法分析器在将其标记输出交给解析器之前应该做多少语义解释。
以 Markdown 语法为例:
### Headline
*This* is an emphasized word.
它可能被词法分析器翻译成以下一系列标记:
词法分析器 1 输出
.headline("Headline")
.emphasis("This")
.text"(" is an emphasized word.")
但也可以根据所使用的语法(或词汇集)进行更细化的翻译:
词法分析器 2 输出
.controlSymbol("#")
.controlSymbol("#")
.controlSymbol("#")
.text(" Headline")
.controlSymbol("*")
.text("This")
.controlSymbol("*")
.text"(" is an emphasized word.")
让词法分析器产生类似于 Lexer 1 的输出似乎更实用,因为这样解析器的工作就会更轻松。但这也意味着词法分析器需要在语义上理解代码的含义。它不仅仅是将一系列字符映射到一个标记。它需要向前看并识别模式。 (例如,它需要能够区分**Hey* you* 和**Hey** you。它不能简单地将双星号** 转换为.openingEmphasis,因为这取决于以下上下文。)
根据this Stackoverflow post和CommonMark definition的说法,先将Markdown输入分解成若干块(代表一行或多行),然后在第二步分析每个块的内容,似乎是有意义的.对于上面的示例,这将意味着以下内容:
.headlineBlock("Headline")
.paragraphBlock("*This* is an emphasized word.")
但这不能算作有效的标记序列,因为一些词位(“*”)尚未被解析,将这个paragraphBlock 传递给解析器是不正确的。
这是我的问题:
你在哪里画线?
词法分析器应该做多少语义工作?词法分析器的定义中是否有一些我不知道的硬切?
为词法分析器定义语法的最佳方法是什么?
【问题讨论】:
-
'词法分析器应该做多少语义解释':无。零。纳达。纳拉。里恩。
-
所以你的意思是即使词法分析器在编程语言的源代码中读取了左括号
(,它也应该总是返回.openingParenthesis,不管它是否是字符串的一部分/评论与否?因为这已经是一种语义解释,所以要区分在代码中有意义的括号和没有意义的括号。它已经在说:“这个括号比那个括号有另一个含义。”即使词法分析器不知道什么是什么意思,它确实知道有有两种不同的意思,需要区分它们。 -
是的,这正是我要说的。由解析器来解析,而不是词法分析器。扫描器处理常规语言,解析器处理 CFL。注意这是语法,而不是语义。 NB 2 由扫描器决定删除 cmets,而不是以任何方式将它们返回给 oarssrparser。到目前为止,您定义的语言可能是模棱两可的。
-
@user207421 注释和字符串文字作为单个标记进行词法分析,因此由于标记不能位于其他标记中,因此注释或字符串文字中的
(将不 产生一个(令牌。我从未见过任何词法分析器将 cmets 和字符串文字的处理留给解析器。 -
@Mischa 如果您选择第一种方式,那么
### Head *line*的结果应该是什么?.headline(.text("Head"), .emphasis("line"))?然后你的词法分析器将生成一棵树,因此实际上是一个解析器(当然,你很可能决定像 Markdown 这样简单的东西不需要词法分析器,而只需编写一个无词法分析器的解析器)。通常,任何可以嵌套的东西都不能是单个标记。因此,词法分析器的结果应该更像您的第二个版本(尽管我个人会将###视为单个标记)。
标签: parsing compiler-construction markdown tokenize lexer