2

Antlr を使用して単純なテキスト ファイルを処理しようとしていますが、主に文法設計を再学習するためです。

テキスト ファイルの各行は、キーワード「BY:」と EOL で終了する文字列で構成されます。ファイルは一連の「-」で終わります。そのようです:

BY: abc123@gmail.com
BY: myCrazy@#$%ID
BY: first_name second_name
-------------------

文法を次のように定義しました。

grammar authors;

prog    :   author+ DASHES;
author  :   BY STRING NEWLINE;

BY  :   'BY: ';
STRING  :   ('!'..'~')*;
NEWLINE :   '\r'? '\n' ;
DASHES  :   '-'+ NEWLINE;

この文法は、最初の著者と 2 番目の著者を認識しますが、スペースのために 3 番目の著者を認識できません。そのため、STRINGにスペースを含めるように変更しましたSTRING:('!'..'~'|' ')*が、すべて一緒に機能しなくなりました(スローされMisstingTokenExceptionます)。

BYが一致する前に、STRINGルールが行全体に一致するためだと思います。しかし、STRING からスペースを除外すると、なぜ機能するのでしょうか? lexer を強制的に BY ルールに最初に一致させる方法はありますか?

一般に、自由形式の Unicode 改行で終了する文字列を使用するにはどうすればよいですか (名前にはアクセント付き文字も含めることができます)。

ありがとう!PS私は、Java、perl、awkなどでこれが簡単であることを知っています.

4

1 に答える 1

4

ANTLRでは、レクサーは文字を扱い、パーサーは抽象トークンを扱います。したがって、「文字ABCから始めて、文字XYZまですべての文字を無差別に読む」と言うときはいつでも、「すべての文字」はレクサーには意味がありますが、パーサー。

authorこれらの線に沿って、パーサー規則の英語の定義とC++スタイルの単一行コメントの定型句解析規則の類似性を検討してください。

  • Anauthorは、「BY:」で始まり、行末まですべての文字が続くテキストです。
  • 1行のコメントは、「//」で始まり、行の終わりまですべての文字が続くテキストです。

この種の単一行コメントの字句解析規則は、通常、次の形式に従います。

SINGLE_LINE_COMMENT : '//' ~('\r'|'\n')*;

著者行のレクサールールは次のようになります。

AUTHOR : 'BY: ' ~('\r'|'\n')*;

AUTHORただし、生成されるトークンは「BY:」で始まり、それに続くものだけが必要なため、これは正しく機能しません。次のように、最初の文字を削除するか、できればテキストを最初に区切ることができます。

AUTHOR: BY RESTOFLINE; //TODO ignore BY

この分離は、レクサーフラグメントを使用して実行できます。

AUTHOR  : BY RESTOFLINE; //TODO ignore BY

fragment BY :   'BY: ';
fragment RESTOFLINE  
        :   ~('\r'|'\n')*;

レクサーフラグメントは、プライベートレクサーレベルマクロのように動作します。レクサールールで参照されている場合にのみ「アクティブ」であり、レクサールールのみがアクティブにできます。(パーサーは名前でフラグメントを参照できますが、通常は参照しないでください...しかし、それは別のトピックです。)

これで、のテキストAUTHORのみを含むトークンが必要になります。RESTOFLINEこれは、レクサーアクションを使用すれば簡単です。

    AUTHOR  : BY RESTOFLINE {setText($RESTOFLINE.text);};

AUTHORルールがRESTOFLINEフラグメントの読み取りを終了した後、setTextが呼び出されて、送信AUTHORトークンのテキストがフラグメントのみからのテキストに変更されRESTOFLINEます。

したがって、新しいレクサールールに対応するようにパーサールールを適応させた後、次のような文法になります。

grammar authors;

prog    :   author+ DASHES;
author  :   AUTHOR NEWLINE;


NEWLINE :   '\r'? '\n' ;
DASHES  :   '-'+ NEWLINE;

AUTHOR  : BY RESTOFLINE {setText($RESTOFLINE.text);};

fragment BY       
        :   'BY: ';
fragment RESTOFLINE  
        :   ~('\r'|'\n')*;

簡単なテストケースは次のとおりです。

入力

BY: abc123@gmail.com
BY: myCrazy@#$%ID
BY: first_name second_name
-------------------

生成されたトークン

[AUTHOR : abc123@gmail.com] [NEWLINE : ] [AUTHOR : myCrazy@#$%ID] [NEWLINE : ] [AUTHOR : first_name second_name] [NEWLINE : ] [DASHES : -------------------] 

これが一般的な文法設計にどの程度役立つかはわかりませんが、トークンパーサーと文字パーサー/レクサーの違いと、それぞれの制限の少しを示すのに役立つことを願っています。

于 2012-12-26T18:59:29.757 に答える