Antlr 规则优先级

标签 antlr antlr3

首先,我知道这个语法没有意义,但它是为了测试 ANTLR 规则优先级行为而创建的

grammar test;

options 
{

output=AST;
backtrack=true;
memoize=true;

}

rule_list_in_order :
    (
    first_rule
    | second_rule
    | any_left_over_tokens)+
    ;


first_rule
    :
     FIRST_TOKEN
    ;


second_rule:     
    FIRST_TOKEN NEW_LINE SECOND_TOKEN NEW_LINE;


any_left_over_tokens
    :
    NEW_LINE
    | FIRST_TOKEN
    | SECOND_TOKEN;



FIRST_TOKEN
    : 'First token here'
    ;   

SECOND_TOKEN
    : 'Second token here';

NEW_LINE
    : ('\r'?'\n')   ;

WS  : (' '|'\t'|'\u000C')
    {$channel=HIDDEN;}
    ;

当我向此语法提供输入“此处第一个标记\n此处第二个标记”时,它与第二个规则匹配。

我希望它匹配第一个规则,然后匹配任何_left_over_tokens,因为第一个规则出现在作为起点的规则顺序列表中的第二个规则之前。谁能解释为什么会发生这种情况?

干杯

最佳答案

首先,ANTLR 的词法分析器将从上到下对输入进行标记。因此首先定义的标记比其下面的标记具有更高的优先级。如果规则有重叠的标记,则匹配最多字符的规则将优先(贪婪匹配)。

同样的原则也适用于解析器规则。首先定义的规则也将首先匹配。例如,在规则 foo 中,子规则 a 将在 b 之前先尝试:

foo
  :  a
  |  b
  ;

请注意,在您的情况下,第二个nd规则不匹配,但尝试这样做,但由于没有尾随换行符而失败,从而产生错误:

line 0:-1 mismatched input '<EOF>' expecting NEW_LINE

所以,根本没有匹配的东西。但这很奇怪。因为您已经设置了 backtrack=true,所以它至少应该回溯并匹配:

  1. first_rule (“这里是第一个 token ”)
  2. any_left_over_tokens (“换行符”)
  3. any_left_over_tokens (“这里是第二个 token ”)

如果首先不匹配 first_rule,甚至不尝试匹配 second_rule

手动执行谓词(并在选项 { ... } 部分中禁用回溯)时的快速演示如下所示:

grammar T;

options {
  output=AST;
  //backtrack=true;
  memoize=true;
}

rule_list_in_order
  :  ( (first_rule)=>  first_rule  {System.out.println("first_rule=[" + $first_rule.text + "]");}
     | (second_rule)=> second_rule {System.out.println("second_rule=[" + $second_rule.text + "]");}
     | any_left_over_tokens        {System.out.println("any_left_over_tokens=[" + $any_left_over_tokens.text + "]");}
     )+ 
  ;

first_rule
  :  FIRST_TOKEN
  ;

second_rule
  :  FIRST_TOKEN NEW_LINE SECOND_TOKEN NEW_LINE
  ;

any_left_over_tokens
  :  NEW_LINE
  |  FIRST_TOKEN
  |  SECOND_TOKEN
  ;

FIRST_TOKEN  : 'First token here';   
SECOND_TOKEN : 'Second token here';
NEW_LINE     : ('\r'?'\n');
WS           : (' '|'\t'|'\u000C') {$channel=HIDDEN;};

可以使用以下类进行测试:

import org.antlr.runtime.*;

public class Main {
    public static void main(String[] args) throws Exception {
        String source = "First token here\nSecond token here";
        ANTLRStringStream in = new ANTLRStringStream(source);
        TLexer lexer = new TLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        TParser parser = new TParser(tokens);
        parser.rule_list_in_order();
    }
}

产生预期的输出:

first_rule=[First token here]
any_left_over_tokens=[
]
any_left_over_tokens=[Second token here]

请注意,如果您使用:

rule_list_in_order
  :  ( (first_rule)=>  first_rule 
     | (second_rule)=> second_rule
     | any_left_over_tokens
     )+ 
  ;

rule_list_in_order
  :  ( (second_rule)=> second_rule // <--+--- swapped
     | (first_rule)=>  first_rule  // <-/
     | any_left_over_tokens
     )+ 
  ;

,两者都会产生预期的输出。

所以,我的猜测是您可能发现了一个错误。

如果您想要明确的答案,您可以尝试 ANTLR 邮件列表(Terence Parr 比他在这里更常去那里)。

祝你好运!

PS。我用 ANTLR v3.2 对此进行了测试

关于Antlr 规则优先级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4899538/

相关文章:

parsing - Antlr3:无法匹配词法分析器规则中使用的解析器规则中的标记

ANTLR3:匹配所有内容,直到特定关键字

java - 如何使用antlr生成Java CFG(控制流图)?

compiler-construction - ANTLR 中的 bool 和算术表达式语法

python-3.x - 如何将 ANTLR 用于 python 目标?

antlr - 如何解决 "The following alternatives can never be matched"

antlr - 使用 ANTLR3 解析换行符、EOF 作为语句结束标记

c++ - 处理 C++ 目标中的 ANTLR3 错误

c - ANTLR3 C 目标 - 解析器返回 'misses' 根元素

java - 如何从 .erb 文件中的函数调用中提取函数名称?