【问题标题】:ANTLR4: Matching an identifier but NOT a keywordANTLR4:匹配标识符但不匹配关键字
【发布时间】:2022-01-01 05:55:22
【问题描述】:

我正在使用 ANTLR4 来分析和解析字符串。字符串是这样的:

alpha at 3

语法是这样的:

access: IDENTIFIER 'at' INT;
IDENTIFIER: [A-Za-z]+;
INT: '-'? ([1-9][0-9]* | [0-9]);

但是,这个 ANTLR 给了我line 1:6 mismatched input 'at' expecting 'at'。我发现这是因为IDENTIFIER'at' 的超集,如this answer 所示。所以,我试着把语法改成这样:

access: identifier AT INT;
identifier: NAME | ~AT;
NAME: [A-Za-z]+;
INT: '-'? ([1-9][0-9]* | [0-9]);
AT: 'at';

但是我得到一个相同的错误。

我如何匹配alpha at 3,其中alpha[A-Za-z]+,而at 也在[A-Za-z]+ 中?

【问题讨论】:

  • 你的第一个语法版本没有给我错误(但你的第二个版本有)。
  • 如果您在第二版语法中将AT 移到IDENTIFIER 之前,我认为这也应该有效。

标签: regex parsing antlr4


【解决方案1】:

我在使用 ANTLR4 的工作中发现,将我的语法分为单独的词法分析器和解析器更容易。这有它自己的学习曲线。但结果是我考虑将“令牌”提供给解析器。我可以使用 grun -tokens 来查看我的标记在到达解析器之前是否已被词法分析器识别。我仍然是 ANTLR4 新手,所以在玩了几年的 ANTLR4 之后,在学习曲线上可能比你早 2 周。

所以在我的 Grammer 文件中,我会 myLexer.g4:

AT: 'at';
IDENTIFIER: [a-ZA-Z]+;
INT:      -?[0-9]+;

myParser.g4:

 access: IDENTIFIER AT INT;

做完之后要小心:

 antlr4 myLexer.g4
 antlr4 myParser.g4
 javac *.java

运行解析器的 GRUN 命令不是:

 grun myParser -tokens access  infile

但是

 grun my -tokens access infile

当我将我的语法分割成单独的词法分析器/解析器 g4 文件时,在名称中添加“解析器”总是会让我感到沮丧。我通常在使用 ANTLR4 时表现平庸,然后在 8-12 个月内不使用它并遇到同样的问题,我来到 Stack Overflow 让自己重回正轨。

这将在 grun -tokens 中特别显示为“AT”令牌。但正如 cmets 中提到的,AT 需要排在第一位。

任何两个规则可以匹配“AT:'at'”的情况也是合法的标识符:[a-ZA-Z]+ 将较小的匹配放在第一位。 ALSO 我倾向于避免 * 贪婪匹配并使用非贪婪 ?匹配,即使我不太了解 ANTLR4 如何区分 '' 和 '*?' 的具体机制。该学生的未来学习。

您可以使用的另一个技巧是使用解析器模式。我认为解析器模式的维护开销和复杂性有点高,但它们可以提供一种变通方法来解决问题,直到您能够理解“正确”的解析解决方案。这就是我今天使用它们的方式。解决我的问题的拐杖,我有 //TODO -我需要在我的语法中修复这个 cmets。 因此,如果您的解析变得更复杂,您可以尝试词法分析器模式,但我认为它们是一个有风险的拐杖……而且您可以用它们远离时间沉没的兔子洞。 (我想我现在已经下降了一半)。

但我发现 ANTLR4 是一个很棒的解析工具……虽然我认为我可能只对“C”/Perl 解析器进行硬编码比学习 ANTLR4 更好。我发现的最终结果是一种语法,我认为它比我回退到旧的“C”/“Perl”强力令牌阅读器更强大。而且它比过去尝试 Lexx/Yacc 的效率高出几个数量级。我从来没有走得足够远,无法将它们视为有用的工具。 ANTLR4 更有用了。

【讨论】:

    【解决方案2】:

    你提到的第一个语法运行良好,结果如下:

    第二个:

    access: identifier AT INT;
    identifier: NAME | ~AT;
    NAME: [A-Za-z]+;
    INT: '-'? ([1-9][0-9]* | [0-9]);
    AT: 'at';
    

    确实产生了错误。这是因为NAMEAT 都匹配文本"at"。而且因为NAME 是在AT 之前定义的,所以会创建一个NAME 令牌。

    始终小心此类重叠标记:始终将关键字放在 NAME 或标识符标记上方:

    access: IDENTIFIER AT INT;
    AT: 'at';
    IDENTIFIER: [A-Za-z]+;
    INT: '-'? ([1-9][0-9]* | [0-9]);
    

    请注意,当规则匹配相同数量的字符时,ANTLR 只会查看首先定义的规则。因此对于像"atat" 这样的输入,将创建一个IDENTIFIER不是 2 个AT 令牌!)。

    【讨论】:

      猜你喜欢
      • 2014-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多