编译器构造-在创建DFA之后实现一个识别令牌的lexer



我想了解一些关于实现lexer的内容,并且我不想使用scanner生成器。根据我所读到的内容,我用正则表达式来识别语言的规范,每个正则表达式用于不同的标记。然后我应该制作一个大的正则表达式,对所有令牌的表达式进行OR运算,对吧?!然后创建这个大正则表达式的NFA,然后创建DFA,对吧?!如果是这样的话,那么当一个单词被最终的DFA匹配时,我怎么知道这个单词代表哪个令牌?!

基于FSM的Lexer

手动编写一个有限状态机(FSM)lexer,您可以循环输入中的字符,并使用两个级别的切换语句进行处理:

  • 外部switch语句正在打开状态
  • 内部switch语句正在打开已读取的字符

这是一个用于处理令牌的有限状态机的实现。这样做的不利之处在于,维护起来可能会变得复杂,尤其是在有许多状态要处理每个令牌的情况下。好处是更容易重构以利用有限状态机(例如,使用转换表而不是切换语句)。

转换表类似于switch语句:行定义状态,列定义数据值,单元格定义下一个要转换到的状态(使用类似-1的信号来停止处理)。通过这种方法,可以使用结束状态来确定令牌类型。在这里,您将有一个token_type tokens[N_STATES];数组,然后您可以执行token = tokens[current_state]来获取令牌。

非FSM Lexer

另一种方法是打开第一个字符,然后读取该令牌中的其余字符,作为case语句的一部分。这可能更容易阅读和编写。

您还可以将字符划分为不同的类别(例如,数字、字母、减号和小于),可以将其定义为256项查找表。这可以简化案例陈述。

正则表达式

正如您所指出的,使用大型正则表达式是有问题的,因为您无法获得令牌类型。这里的一种方法是拥有一个与令牌匹配的正则表达式列表,并将其与令牌类型相关联。例如,在python中:

_tokens = [
    (re.compile('\s+'), WhiteSpace),
    (re.compile('[a-zA-Z_][a-zA-Z0-9_]*'), Identifier),
    (re.compile('[0-9]+'), Integer),
]

您需要正确排序(例如,将关键字匹配放在标识符之前)。

您在这里描述的是手工实现生成的扫描仪。这不是你要做的。只需编写一个包含一个大型switch语句的循环,该语句的大小写是每个令牌类型的首字母,每个大小写都是一个循环,用于消耗令牌的其余部分并返回其类型。除了不返回外,空白大小写完全相同。标识符的情况还需要查找关键字表。

相关内容

最新更新