【问题标题】:Difficulty with my lexical analyzer我的词法分析器有困难
【发布时间】:2014-12-11 05:45:56
【问题描述】:

我正在尝试将词法分析器编程为标准 C 翻译单元,因此我将可能的标记分为 6 组;每个组都有一个正则表达式,它将被转换为 DFA:

  1. 关键字 - (将有一个包含“goto”、“int”的符号表....)

  2. 标识符 - [a-zA-z][a-zA-Z0-9]*

  3. 数字常量 - [0-9]+/.?[0-9]*

  4. 字符串常量 - ""[EVERY_ASCII_CHARACTER]*""

  5. 特殊符号 - (将有一个包含“;”、“(”、“{”....)的符号表

  6. 运算符 - (将有一个包含“+”、“-”...的符号表)

My Analyzer 的输入是字节/ASCII 字符流。我的算法如下:

assuming there's a stream of characters, x1...xN
 foreach i=1, i<=n, i++
    if x1...xI accepts one or more of the 6 group's DFA
    {
       take the longest-token
       add x1...xI to token-linked-list
       delete x1...xI from input
    }

但是,该算法将假定给定的每个字节(一个字母)都是一个标识符,因为在输入 1 个字符后,它接受标识符令牌的 DFA([a-zA-Z][ a-zA-Z0-9]*)。

另一个可能的问题是输入“intx;”,我的算法会将这个流标记为“int”、“x”、“;”这当然是一个错误。

我正在尝试考虑一种新算法,但一直失败。有什么建议吗?

【问题讨论】:

  • 为什么? gnu-flex 已经存在。
  • 我正在编写一个编译器。
  • “我正在编写一个编译器。” ——嗯,是吗?这与使用 flex 并不矛盾。但是如果你必须自己动手......关于如何编写词法分析器已经写了很多,我建议你阅读其中的一些,因为你的方法很糟糕,速度非常慢,并且会产生错误的结果。
  • @RonPinkas Gee,Ron,我当然想知道我的方法很糟糕,而且我无法理解为什么不理解。
  • @RonPinkas 也许您没有阅读我的评论,与您的这些 cmets 相比,我的评论更有用。

标签: c regex compiler-construction lexical-analysis


【解决方案1】:

对您的扫描仪进行编码,使其在读取完成之前将标识符和关键字视为相同。

当您拥有完整的标记时,请在关键字表中查找它,如果找到它,则将其指定为关键字,如果找不到,则将其指定为标识符。这会立即处理intx 问题;扫描仪读取intx,这不是关键字,所以它必须是标识符。

我注意到您的标识符不允许使用下划线。这不一定是个问题,但许多语言确实允许在标识符中使用下划线。

【讨论】:

    【解决方案2】:

    您似乎缺少竞争 DFA 的 greediness 方面。 greedy 匹配通常是最有用的(最左边最长的匹配),因为它解决了如何在竞争的 DFA 之间进行选择的问题。匹配int 后,IDENTIFIER DFA 中有另一个节点前进到intx。您的最终自动机不会退出,直到它到达它不能消费的东西,如果它在输入结束时不处于有效的接受状态,或者在另一个 DFA 正在接受的点,它被修剪,另一个DFA 已匹配。

    例如,Flex 默认为贪婪匹配。

    也就是说,你提出的intx的问题不是问题……

    如果您有 2 条规则竞争 int

    1. 规则 1 是标记“int”
    2. 规则 2 是标识符

    当我们到达时

    int

    我们不会立即接受 int,因为我们看到另一个规则(规则 2),进一步输入 x 会使自动机进入 NEXT 状态:

    intx

    如果此时规则 2 处于 ACCEPT 状态,则根据定义丢弃规则 1。但是如果规则 2 仍然不处于 ACCEPT 状态,我们必须保留规则 1,同时检查更多输入,看看我们最终是否可以在规则 2 中达到比规则 1 更长的 ACCEPT 状态。如果我们收到其他匹配的字符没有规则,我们检查规则 2 自动机是否处于intx 的接受状态,如果是,则匹配。如果不是,则丢弃它,并接受最长的先前匹配(规则 1),但是在这种情况下,规则 2 处于 ACCEPT 状态并匹配intx

    如果 2 个规则同时达到 ACCEPT 或 EXIT 状态,则使用优先级(语法中规则的顺序)。通常,您将关键字放在第一位,因此 IDENTIFIER 不会首先匹配。

    【讨论】:

      【解决方案3】:

      标记器通常首先将输入流拆分为标记,基于指示什么构成标记的 END 的规则,然后才确定它是什么类型的标记(否则为错误)。标记的典型结尾是空格(当不是文字字符串的一部分时)、运算符、特殊分隔符等。

      【讨论】:

      • FWIW 我确实开发了一个通用词法分析器引擎,它可能有助于理解许多常见问题。它应该是相当易读的纯 C 代码。 sourceforge.net/projects/simplex
      • 这不是问题的答案。它通常也不正确,因为构成令牌结尾的内容可能取决于上下文。不考虑这一点可能会导致一些不幸的结果;例如,在 C 中,0xe + 1 是合法的,但 0xe+1 不是,因为“预处理器令牌”的定义。 C 标准委员会试图修补规则来解决这个问题,但时间不够,投票决定将这个缺陷留在语言中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-17
      相关资源
      最近更新 更多