Compiler construction 关于构建编译器扫描程序的问题

Compiler construction 关于构建编译器扫描程序的问题,compiler-construction,Compiler Construction,我想从头开始构建一个编译器。所以在第一步中,我想建立一个扫描仪。但我遇到的问题是,我应该如何对待关系运算符,比如“>=,==,然后=,=然后=,”之前解析“>=”。

我想从头开始构建一个编译器。所以在第一步中,我想建立一个扫描仪。但我遇到的问题是,我应该如何对待关系运算符,比如“>=,==,然后=,=然后=, 扫描算法通常设计为匹配最大可能的令牌。为了做到这一点,他们采用了一种“最大munch”算法,该算法在遇到错误时进行回溯,并通过重置状态继续

lexer最简单的实现策略是构造一个DFA,该DFA对每个令牌的正则表达式的并集进行建模。因此,如果您的语言的标记由空格分隔的
=
==
(但不是
=
)组成(我将明确地将此答案建模为
),那么您的词法器基本上将使用接受
===|==|*
(请注意,空格规则是“至少有一个
\
”,
\+

如您所见,存在一个中间状态(您可以在输入时达到该状态,但由于它不是接受状态,所以不能接受)。因此,假设您已经计算了DFA(实际上,有压缩范围和每个输入字符上的其他通用谓词的快捷方式),您可以开始编写状态机

DFA可以写成一个循环,对于每个状态(使用
switch
语句和每个状态的
case
),对照源缓冲区中的当前字符检查当前状态。如果转换有效-即当前状态和当前输入字符在DFA中作为传出箭头存在-则将当前状态设置为该状态(并跟踪其是否接受)

为了最大限度地发挥作用,您需要在输入缓冲区中保留两个偏移量,
previous
current
。最初,这两个偏移量都是0。您可以前进
current
,并跟踪每次转换期间的最后一个接受状态。其思想是,如果在任何一点都不存在这样的转换,您可以确认/回放到最后一个有效的接受状态。因此,如果不存在这样的转换,您可以回放,生成一个令牌,然后更新上一个(以匹配当前、错误、偏移),并将状态重置为初始状态

假设我们正在词法分析
====
。词法分析程序将处于初始状态,请参见
=
,然后进入中间(接受)状态。从那里,它将看到下一个
=
,并决定进入下一个(不接受)状态。然后,它会看到最后的
=
并进入该链上的最终接受状态。一旦它看到
==
,它会看到一个新词素的开始,注意到
==
的接受状态没有有效的转换。因此它会产生
=
(从
prev
开始,取长度
当前prev
的子串,重置机器,然后从初始状态开始检查
的序列。类似的情况也会产生
\uuuuuuuu
=
(使用一个特殊的
EOF
标记,当输入用尽时,所有lexer都会使用该标记)

我在这里描述的是一种简化形式的最大munch算法。这是一种简化形式,因为倒带永远不需要超过一个输入标记。这里描述了您可能希望进一步回溯的情况,以及幼稚算法(执行有限的簿记)可能出现最坏情况O(n^2)的情况由于巧妙地构造回溯场景,时间复杂度较高


长话短说,通过确保lexer能够识别这两个标记,但选择使用尽可能大的标记(接受),您可以区分其他标记的截断输入。你可以在DFA上按照这个算法进行实验,并将两个偏移量保留到你正在词法分析的输入中。你可以阅读更多关于Maximum munch的内容。另一个有用的资源是一篇文章,描述了如何使这个算法线性化,-我上面描述的算法,连同它的改进,都在pa上Ge 5,虽然有一点不同。

不。你应该把它们当作扫描仪级的单个令牌。在扫描器级比在解析器级要容易得多,甚至可能是不可能的。考虑跳过扫描仪步骤,从一开始就进入无故障的方式(例如,使用PEG)。。然后您将拥有非常清晰的有序规则(另外一个好处是,它们将是上下文敏感的)。只需尝试在“>”之前解析“>=”。