9

まず、この文法は意味がないことを知っていますが、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;}
    ;

この文法に「最初のトークンはここに\n2番目のトークンはここに」という入力を与えると、second_ruleと一致します。

開始点であるrule_order_listのsecond_ruleの前にfirst_ruleが表示されるため、最初のルール、次にany_left_over_tokensに一致すると予想していました。なぜこれが起こるのか誰かが説明できますか?

乾杯

4

1 に答える 1

19

まず第一に、ANTLRのレクサーは入力を上から下にトークン化します。したがって、最初に定義されたトークンは、その下のトークンよりも優先されます。また、ルールに重複するトークンがある場合、ほとんどの文字に一致するルールが優先されます(欲張り一致)。

同じ原則がパーサールールにも当てはまります。最初に定義されたルールも最初に照合されます。たとえば、ルールfooでは、サブルールaは最初に次の前に試行されbます。

foo
  :  a
  |  b
  ;

あなたの場合、2番目のルールは一致していませんが、一致しようとしますが、末尾の改行がないために失敗し、エラーが発生することに注意してください。

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

したがって、何も一致しません。しかし、それは奇妙です。を設定したのでbacktrack=true、少なくともバックトラックして一致する必要があります。

  1. first_rule (「ここに最初のトークン」)
  2. any_left_over_tokens (「改行」)
  3. any_left_over_tokens (「ここに2番目のトークン」)

そもそも一致first_ruleしていなくても、そもそも一致しようとしない場合second_rule

述語を手動で実行する場合(およびオプション{...}セクションでを無効にする場合)の簡単なデモは、backtrack次のようになります。

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。私はこれをANTLRv3.2でテストしました

于 2011-02-04T18:57:28.723 に答える