【问题标题】:Lexer tokenises unexpectedly词法分析器意外标记
【发布时间】:2012-12-03 08:38:15
【问题描述】:

以下极其简单的示例语法并没有达到我的预期(完全)。

Declaration :   'VAR';
Letter: ('A'..'Z');

message :   Declaration Letter+;

我所期望的结果是,任何字母序列都将作为单个字母进行词法分析,而“VAR”序列将作为单个标记进行词法分析。

当我查看 ANTLRWorks interperter 时,我看到以下结果:

  • VARA 解析为 message -> "VAR", "A"(预期)
  • VARVA 不解析 (MismatchedTokenException(-1 != 5)。词法分析器命中第二个 VA 并尝试标记 Declaration。预期:message -> "VAR", "V", "A"
  • VARVPP 解析为 message -> "VAR", "V", "P", "P"(预期)
  • VARVALL 解析为 message -> "VAR", "VALL"

我需要一些帮助来理解这种行为,以及如何解决这个问题的建议。

具体来说:

  • 为什么词法分析器会尝试将所有以 VA 开头的字符串标记为声明,如果它后跟一个字母?
  • 为什么词法分析器不尝试对所有以V 开头的字符串执行此操作?
  • 如果那里有额外的字符,为什么词法分析器不尝试这样做?
  • 我应该如何更改此语法以按预期方式解析?

【问题讨论】:

    标签: antlr antlr3 lexer


    【解决方案1】:

    让我们看看你所有的 4 个例子:

    1“VARA”

    一切都好。

    2“VARVA”

    "VAR" (显然)被标记为VAR,但是词法分析器“看到”"VA" 并期望"R" 不存在。它发出以下错误:

    line 1:5 mismatched character '<EOF>' expecting 'R'
    line 1:5 required (...)+ loop did not match anything at input '<EOF>'
    

    并丢弃 "VA" 导致创建单个令牌,正如您在运行 ANTLRWorks 的调试器时看到的那样(忽略解析中的异常,它们实际上并不存在:)):

    您必须意识到,词法分析器将永远放弃它已经匹配的内容。因此,如果词法分析器看到 "VA" 并且无法匹配其后的"R",它将查看其他可以匹配"VA" 的词法分析器规则。但是Letter 不匹配(它只匹配单个字母!)如果您更改Letter 以匹配多个字符,ANTLR 将能够退回到该规则。但不是当它匹配单个字母时:词法分析器不会放弃来自"VA""A" 以让Letter 规则匹配。没办法:这就是 ANTLR 词法分析器的工作原理。

    这通常不是问题,因为当关键字无法匹配时,通常有某种IDENTIFIER 规则可供词法分析器使用。

    3“天花病毒”

    一切正常:"VAR" 变为 VAR,然后词法分析器尝试在 "V" 之后匹配 "A",但这不会发生,因此词法分析器使用 Letter 规则来处理单曲"V"。之后"PP" 都被标记为Letters。

    4 "VARVALL"

    "VAR" 再次变为VAR。然后"VAL" 中的"L" 会导致词法分析器产生以下错误消息:

    line 1:5 mismatched character 'L' expecting 'R'
    

    然后最后一个"L" 变成Letter


    我猜(或希望)前 3 个问题现在已经得到解答,这就是你的最终答案:

    我应该如何改变这个语法来解析我所期望的方式?

    如果前面确实有"VAR",则强制词法分析器首先在字符流中向前看,如果没有,只需匹配一个"V",并将匹配标记的类型更改为Letter,例如这个:

    Declaration
     : ('VAR')=> 'VAR'
     |           'V'   {$type=Letter;}
     ;
    

    如我回答前所述,请参阅此相关问答:ANTLR lexer can't lookahead at all

    【讨论】:

      【解决方案2】:

      词法分析器并不真正执行前瞻,只有解析器执行;您可以在ANTLR lexer can't lookahead at all 中阅读更多相关信息。所以这里的问题是,一旦词法分析器无法匹配VAR,它就会尝试匹配它到目前为止得到的 - VA - 并且没有匹配的标记,因为 Letter 不能匹配两个字符,只能匹配一个。

      至于解决方案,一个简单的方法就是将其改为单个令牌:

      Message :   'VAR' ('A'..'Z')+;
      message :   Message;
      

      不过,它不会为每个字母提供不同的标记。

      【讨论】:

      • 啊,我现在明白你的意思了。是的,您是对的:您的建议确实适用于 OP 发布的 4 个示例。但我猜 OP 的 real 问题是如何做到这一点,以便像 "VA" 这样的输入后跟 "R" 以外的其他内容被标记为 Letters 而不是(失败)@ 987654328@ 令牌。再次对所有的混乱表示抱歉。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多