Parsing 为具有递归结构(如嵌套列表)的上下文敏感标记语言编写lexer

Parsing 为具有递归结构(如嵌套列表)的上下文敏感标记语言编写lexer,parsing,rust,restructuredtext,lexer,Parsing,Rust,Restructuredtext,Lexer,我正在Rust中开发一个StructuredText transpiler,我需要一些关于如何在具有递归结构的语言中构造词法分析的建议。例如,列表中的列表在rST中是可能的: * This is a list item * This is a sub list item * And here we are at the preceding indentation level again. 默认情况下,每次扫描一行输入: StructuredText解析器作为状态机实现,检查其 一次输入

我正在Rust中开发一个StructuredText transpiler,我需要一些关于如何在具有递归结构的语言中构造词法分析的建议。例如,列表中的列表在rST中是可能的:

* This is a list item

  * This is a sub list item

* And here we are at the preceding indentation level again.
默认情况下,每次扫描一行输入:

StructuredText解析器作为状态机实现,检查其 一次输入一行

上述方法基本上是在一组状态上运行的,其形式为
(regex,match\u method,next\u state)
。它尝试根据当前状态将当前行匹配到
regex
,并运行
match_方法
,同时转换到
下一个_状态
,如果匹配成功,则执行此操作直到扫描的行数不足为止

我的问题是,这是扫描诸如rST之类的语言的最佳方法吗?到目前为止,我的方法是创建源代码的迭代器,并在尝试与当前Unicode标量的结构匹配的同时吃掉源代码。当我所做的只是扫描内联内容时,这在某种程度上是可行的,但我现在意识到,处理嵌套列表之类的递归体级结构将是一件痛苦的事情。感觉上我需要一大堆状态,在许多状态下都有重复的正则表达式和相关的方法,以匹配新行之前的缩进等等

简单地使用源代码行的和迭代器,并在每行的基础上进行匹配是否更好

    * this is an indented list item
State::Body
中遇到,只需转换到诸如
State::BulletList
之类的状态,然后根据其中指定的规则开始对行进行词法分析?例如,上面的行可以作为序列进行词法分析

TokenType::Indent,TokenType::Bullet,TokenType::BodyText

对此有什么想法吗?

我对rST了解不多。但你说它有“递归”结构。如果是这样的话,您就不能仅仅使用状态机或正则表达式,甚至是lexer生成器将其完全lex为递归结构

但这是错误的思考方式。词法分析器的工作是识别语言的原子。解析器的工作是识别结构,特别是当它是递归结构时(是的,解析器通常构建树来记录他们发现的递归结构)。 因此,如果可以,构建lexer忽略上下文,如果需要,使用解析器提取递归结构。你可以在我关于解析器和词法分析器的答案中阅读更多关于区别的内容

如果您坚持在lexer中执行所有这些操作,则需要使用下推堆栈来扩展它,以跟踪递归结构。那么你要构建的是一个伪装成lexer的草率解析器。(您可能仍然需要一个真正的解析器来处理这个“lexer”的输出)

如果语言在不同的上下文中有不同的原子,特别是当上下文嵌套时,具有下推堆栈实际上是有用的;在这种情况下,您需要的是模式堆栈,当lexer遇到表示从一种模式切换到另一种模式的令牌时,您可以更改它。这一思想的一个真正有用的扩展是让模式更改选择不同的词素,每个词素产生该模式特有的词素

例如,您可以对包含嵌入式SQL的语言执行此操作。我们为JavaScript构建解析器;我们的lexer使用下推堆栈来处理regexp文本的内容,并跟踪{…}[…]和(…)的嵌套。(这可能有一个缺点:它拒绝包含格式错误正则表达式的JQuery.js版本[是的,它们是存在的]。Javascript不在乎您是否定义了一个错误的正则表达式文字并且从不使用它,但这似乎毫无意义。)


如果只有轨迹单“(“…”)对或等效对,则会出现堆栈的特殊情况。在这种情况下,您可以使用一个计数器来记录在一个实际堆栈上可能进行了多少次“推送”或“弹出”。如果您有两对或更多这样的令牌,计数器将不起作用

您可能想询问有关r/编译器的问题。仅供参考,您似乎混淆了词法分析和语法分析;一般来说,正则表达式不是进行词法分析和语法分析的最佳方法——尽管它们可以用于快速而肮脏的实验。@MatthieuM。我确实知道词法分析和语法分析是什么:词法分析程序生成标记,而语法分析程序根据这些标记构建语法树。docutils“parser”似乎是两者的混合体,我的问题涉及到为rST构建一个完全独立的lexer和parser的想法。一个独立的lexer不应该关心是否有(1)项目符号列表,(2)斜体开头,或者(3)粗体开头。从这个意义上说,lexer是无上下文的。因此,您的lexer不应该需要一个状态机。问题之一是,如果您想将缩进表示为令牌流的一部分。我个人认为这比用行/列信息注释每个标记更干净。如果仅表示缩进级别(x空格/制表符),则该缩进级别仍然不区分上下文;如果表示缩进/删除,则该缩进级别稍微区分上下文,但仍然不需要状态机——仅表示上一行(有效)的缩进级别。@MatthieuM。我想我的困惑源于这样一个事实:我仍然会使用编译正则表达式(又称确定性有限自动机)来检测这些更简单的非上下文标记,所以当有人告诉我我没有使用状态机时,我会产生认知不一致。在任何情况下,我认为这是我希望进入的方向,一行一行地生成代币。解析器可以担心缩进(行首的空白)级别,因为它知道如何作为下推自动机计算