【问题标题】:How do you deal with keywords in Lex?您如何处理 Lex 中的关键字?
【发布时间】:2018-09-11 12:40:55
【问题描述】:

假设您有一种允许像这样进行生产的语言:optional optional = 42,其中第一个“可选”是关键字,第二个“可选”是标识符。

一方面,我想要一个像optional { return OPTIONAL; } 这样的 Lex 规则,以后会像这样在 YACC 中使用它,例如:

optional : OPTIONAL identifier '=' expression ;

如果我将identifier 定义为:

identifier : OPTIONAL | FIXED32 | FIXED64 | ... /* couple dozens of keywords */ 
    | IDENTIFIER ;

感觉很糟糕...此外,我需要两种标识符,一种用于允许关键字作为标识符的情况,另一种用于不允许关键字的情况...

有没有一种惯用的方法来解决这个问题?

【问题讨论】:

    标签: parsing yacc lex


    【解决方案1】:

    有没有一种惯用的方法来解决这个问题?

    除了您已经找到的解决方案,没有。半保留关键字绝对不是 lex/yacc 语法的预期用例。

    柠檬解析器生成器有一个为此类情况设计的回退声明,但据我所知,该有用的功能从未添加到野牛中。

    您可以使用 GLR 语法来避免计算 identifier 的所有不同子集。但当然会有性能损失。

    【讨论】:

    • 先生,我有一个问题。在词法分析的情况下(自定义,没有正则表达式引擎)。你如何使用像'RETURNA'这样的标识符?如果我运行词法分析器,它将检测到“RETURN”作为关键字,最后将“A”检测为标识符。它将这个词分成关键字和标识符,但是,我确信它是一个标识符。顺序是 ... -> 关键字 -> 标识符 -> ..
    • @DickWilliams:那么你需要修复词法分析器。 (或者使用词法分析器生成器,这对您和您的可执行文件都更有效。)如果您想手动构建一个高效的词法分析器,请构建一个 trie(这实际上是生成器将为您做的,为您省去麻烦确保您涵盖所有案例)。如果您不想这样做,那么每个关键字模式都需要检查第一个不匹配的字符是否不是有效的标识符字符(字母、数字、_,无论您允许什么。)
    • 谢谢先生!我正在构建自己的词法分析器,因为我想了解它是如何工作的。当然,使用现有工具也很棒!
    • @DickWilliams:你不会通过构建自己的词法分析器来了解词法分析器的工作原理,因为构建表驱动的状态机——编写词法分析器的最有效方法——太难了,而且手工操作容易出错。你最好学习如何使用词法分析器,恕我直言。我通常使用的类比是:你会坚持使用泰勒展开式编写自己的sincos 函数来学习三角学吗?这可能是一个有趣的数学练习,但它不会帮助你写出更好的图形。
    • 编写非臃肿软件的关键是制作一个好的 API,丢弃不再需要(或从不需要)的功能。编写好的界面可能是最困难的设计任务之一;它需要在满足用例(除了您自己的)和避免过度设计之间取得微妙的平衡。第一步,始终是尝试不同的 API,以及各种不同的用例。另外,不要立即拒绝现有的 API;尝试找出动机以及它们是否有效。实施是最后的事情,而不是第一个。
    【解决方案2】:

    您已经在 lex/yacc 中发现了处理这个问题的最常用方法,虽然不漂亮,但也不算太糟糕。通常,您将匹配标识符或(一组)关键字的规则称为whateverName,并且您可能有多个它们 - 因为不同的上下文可能有不同的关键字集,它们可以接受作为名称。

    如果您的关键字仅在易于识别的位置(例如在行首)被识别,则另一种可能有效的方法是使用 lex 开始状态,以便仅在关键字是在这种情况下。在任何其他上下文中,关键字将仅作为标识符标记返回。您甚至可以使用 yacc 操作来为有些复杂的上下文设置词法分析器状态,但是您需要注意解析器可能执行的单令牌词法分析器前瞻(规则可能在操作已经读取后的标记之后才会运行)。

    【讨论】:

    • 先生,我有一个问题。在词法分析的情况下(自定义,没有正则表达式引擎)。你如何使用像returna 这样的标识符?如果我运行词法分析器,它将检测到“return”作为关键字,并将末尾的“a”检测为标识符。它将这个词分成关键字和标识符,但是,我确信它是一个标识符。顺序是 ... -> 关键字 -> 标识符 -> ...
    • @DickWilliams:lex 将始终匹配匹配模式的 longest 词位——仅当两个模式匹配相同长度时,顺序才重要。因此,如果您有一个匹配 return 的模式和一个(后来的)匹配 returna 的模式,则将匹配更长的匹配项。
    【解决方案3】:

    这是不保留关键字的情况。一些编程语言允许这样做:PL/I、FORTRAN。这不是词法分析器的问题,因为词法分析器应该始终知道哪些 IDENTIFIER 是关键字。这是一个解析器问题。它通常会导致语言规范中的歧义过多,解析成为一场噩梦。语法应该是这样的:

    标识符:关键字|标识符;

    关键字:可选 |固定32 |固定64 | ... ;

    如果您在语法上没有冲突,那么您就可以了。如果您有冲突,那么您需要更强大的解析器生成器,例如 LR(k) 或 GLR。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-09
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多