【问题标题】:Implementing a lexer identifying tokens after creating DFA创建 DFA 后实现词法分析器识别令牌
【发布时间】:2014-04-27 21:47:17
【问题描述】:

我想了解一些关于实现词法分析器的知识,但我不想使用扫描仪生成器。从我所读到的,我用正则表达式来识别语言的规范,每个正则表达式都用于不同的标记。然后我应该做一个大的正则表达式 ORing 所有令牌的表达式,对吧?!然后创建NFA然后是这个大正则表达式的DFA,对吧?!如果是这样,那么当最终 DFA 匹配到一个词时,我怎么知道这个词代表哪个标记?!

【问题讨论】:

  • 一旦匹配了一个单词,您就知道从哪里开始,也知道从哪里结束,对吧?

标签: compiler-construction lexer


【解决方案1】:

基于 FSM 的词法分析器

手动编写有限状态机 (FSM) 词法分析器,循环输入中的字符并使用两级 switch 语句进行处理:

  • 外层switch语句是开启状态;
  • 内部 switch 语句正在打开读取的字符。

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

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

非 FSM 词法分析器

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

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

正则表达式

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

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

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

【讨论】:

  • 我想我将使用转换表,虽然我有一个关于关键字的问题,在我的 DFA 中我没有关键字的状态,所以我想知道我是否在标识符状态下完成我应该检查如果它是一个关键字,通过在一个表中包含所有关键字然后循环查找表中的变量或创建一个具有所有关键字 ORed 的正则表达式,哪个会更快?
  • @Doggynub 如果要使用 DFA,则需要使用令牌类型标记每个接受状态。这与教科书的 DFA 不太一样,但实际上并没有那么难。在原始模式中,每个接受状态都是不同的,所以没有问题;在闭包结构中,当您组合两组状态(包括两种不同的接受状态)时,您只需保留数字较小的一组(假设您按顺序对模式进行编号)。在扫描期间,您还需要记住最后接受状态的位置和令牌类型,因为您可能需要回退。
【解决方案2】:

您在此处描述的是手动实现生成的扫描仪。这不是你的做法。只需编写一个包含大型 switch 语句的循环,其 case 是每个令牌类型的首字母,每个 case 都是一个循环,用于消耗令牌的其余部分并返回其类型。空格大小写是相同的,只是它不返回。标识符的大小写也需要查找关键字表。

【讨论】:

    猜你喜欢
    • 2013-01-03
    • 2021-06-29
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-05
    • 1970-01-01
    相关资源
    最近更新 更多