最初的回答:
我正在编写一个Markdown解析器。在这个过程中,我意识到我并没有完全理解它的核心职责。
最常见的词法分析器定义是将输入流中的字符转换为输出流中的令牌。
似乎让词法分析器生成类似于 Lexer 1 的输出更加实用,因为解析器将更容易处理。但这也意味着词法分析器需要在语义上理解代码的含义。它不仅仅是将一系列字符映射到标记上。它需要向前查看并识别模式。(例如,它需要能够区分
根据此Stackoverflow帖子和CommonMark定义,似乎先将Markdown输入拆分为多个块(表示一个或多个行),然后在第二步中分析每个块的内容是有意义的。对于上面的示例,这意味着以下内容:
最初的回答:
这不能算作有效的标记序列,因为其中一些词元("*")尚未被解析,将此
所以我的问题是:
你会在哪里划线呢?
词法分析器应该做多少语义工作?在词法分析器的定义中是否有一些硬性的限制我不知道?
为词法分析器定义文法的最佳方法是什么?
我正在编写一个Markdown解析器。在这个过程中,我意识到我并没有完全理解它的核心职责。
最常见的词法分析器定义是将输入流中的字符转换为输出流中的令牌。
Input → Output
(characters) (tokens)
一开始听起来很简单,但在此产生的问题是词法分析器在将其令牌输出交给解析器之前应该进行多少语义解释。
拿Markdown语法举例:
### Headline
*This* is an emphasized word.
这可能会被词法分析器翻译成以下一系列标记:
词法分析器1的输出
.headline("Headline")
.emphasis("This")
.text"(" is an emphasized word.")
但是根据语法(或词素集)的不同,它也可以更细致地进行翻译:
Lexer 2 输出
.controlSymbol("#")
.controlSymbol("#")
.controlSymbol("#")
.text(" Headline")
.controlSymbol("*")
.text("This")
.controlSymbol("*")
.text"(" is an emphasized word.")
似乎让词法分析器生成类似于 Lexer 1 的输出更加实用,因为解析器将更容易处理。但这也意味着词法分析器需要在语义上理解代码的含义。它不仅仅是将一系列字符映射到标记上。它需要向前查看并识别模式。(例如,它需要能够区分
** Hey * you *
和** Hey ** you
。它不能简单地将双星号**
翻译成.openingEmphasis
,因为那取决于后面的上下文。)根据此Stackoverflow帖子和CommonMark定义,似乎先将Markdown输入拆分为多个块(表示一个或多个行),然后在第二步中分析每个块的内容是有意义的。对于上面的示例,这意味着以下内容:
.headlineBlock("Headline")
.paragraphBlock("*This* is an emphasized word.")
最初的回答:
这不能算作有效的标记序列,因为其中一些词元("*")尚未被解析,将此
paragraphBlock
传递给解析器是错误的。
所以我的问题是:
你会在哪里划线呢?
词法分析器应该做多少语义工作?在词法分析器的定义中是否有一些硬性的限制我不知道?
为词法分析器定义文法的最佳方法是什么?
(
,它也应该始终返回.openingParenthesis
,而不管它是否是字符串/注释的一部分?因为这已经是一种语义解释,用于区分在代码中具有含义和没有含义的括号。它已经在说:“这个括号与那个括号的含义不同。” 即使词法分析器不知道 什么 是含义,它也知道有两个不同的含义,并且需要区分它们。 - Mischa(
不会成为注释或字符串字面量的一部分。我所见过的任何词法分析器都不会将注释和字符串字面量的处理委托给解析器。 - sepp2k### Head *line*
的结果应该是什么呢?.headline(.text("Head"), .emphasis("line"))
?那么你的词法分析器将会生成一棵树,因此实际上它就是一个解析器(当然,你也可以决定像Markdown这样简单的东西不需要词法分析器,而直接编写无词法分析器的解析器)。通常,任何可以嵌套的内容都不能成为单个标记。因此,词法分析器的结果更像是你的第二个版本(尽管我个人会将###
视为单个标记)。 - sepp2k