2

具体的には、ANTLR に RegExp パーサーを実装しようとしています。

私の文法の関連部分は次のとおりです。

grammar JavaScriptRegExp;
options {
    language = 'CSharp3';
}

tokens {
    /* snip */
    QUESTION = '?';
    STAR = '*';
    PLUS = '+';
    L_CURLY = '{';
    R_CURLY = '}';
    COMMA = ',';
}

/* snip */

quantifier returns [Quantifier value]
    :   q=quantifierPrefix QUESTION?
        {
            var quant = $q.value;
            quant.Eager = $QUESTION == null;
            return quant;
        }
    ;

quantifierPrefix returns [Quantifier value]
    :   STAR { return new Quantifier { Min = 0 }; }
    |   PLUS { return new Quantifier { Min = 1 }; }
    |   QUESTION { return new Quantifier { Min = 0, Max = 1 }; }
    |   L_CURLY min=DEC_DIGITS (COMMA max=DEC_DIGITS?)? R_CURLY
        {
            var minValue = int.Parse($min.Text);
            if ($COMMA == null)
            {
                return new Quantifier { Min = minValue, Max = minValue };
            }
            else if ($max == null)
            {
                return new Quantifier { Min = minValue, Max = null };
            }
            else
            {
                var maxValue = int.Parse($max.Text);
                return new Quantifier { Min = minValue, Max = maxValue };
            }
        }
    ;

DEC_DIGITS
    :   ('0'..'9')+
    ;

/* snip */

CHAR
    :   ~('^' | '$' | '\\' | '.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '|')
    ;

ここで、中括弧の内側では「,」を COMMA としてトークン化したいのですが、外側では CHAR としてトークン化したいと考えています。

これは可能ですか?

これが起こっている唯一のケースではありません。これが問題になる他の多くのインスタンスがあります (10 進数字、文字クラスのハイフンなど)。

編集:

これが文脈依存の字句解析と呼ばれることを私は知っています。これはANTLRで可能ですか?

4

2 に答える 2

3

これは、コンテキスト依存の字句解析と呼ばれます。これはANTLRで可能ですか?

いいえ、パーサーは、たとえば、解析中に特定の時点で異なる数字を処理する必要があることをレクサーに「伝える」ことはできません。レクサーだけでも状況依存の字句解析が可能ですが、パーサーはレクサーに影響を与えることはできません。

ただし、いくつかのパーサー規則を追加することで簡単に解決できます。たとえば、文字クラス ( [... ]) を照合する場合、文字クラス内で有効なものに一致するパーサー ルールを使用します。

char_class
 : LBRACK char_class_char+ RBRACK
 ;

// ...

char_class_char
 : LBRACK // the '[' is not special inside a character class!
 | LBRACE // the '{' is not special inside a character class!
 | RBRACE // the '}' is not special inside a character class!
 | PLUS   // the '+' is not special inside a character class!
 | STAR   // the '*' is not special inside a character class!
 | QMARK  // the '?' is not special inside a character class!
 | COMMA
 | DIGIT
 | OTHER
 ;

小さなデモ:

grammar T;

parse
 : atom* EOF
 ;

atom
 : unit quantifier?
 ;

unit
 : char_class
 | single_char
 ;

quantifier
 : greedy (PLUS | QMARK)?
 ;

greedy
 : PLUS
 | STAR
 | QMARK
 | LBRACE (number (COMMA number?)?) RBRACE
 ;

char_class
 : LBRACK char_class_char+ RBRACK
 ;

number
 : DIGIT+
 ;

single_char
 : DIGIT
 | COMMA
 | RBRACE
 | RBRACK // this is only special inside a character class
 | OTHER
 ;

char_class_char
 : LBRACK
 | LBRACE
 | RBRACE
 | PLUS
 | STAR
 | QMARK
 | COMMA
 | DIGIT
 | OTHER
 ;

LBRACK : '[';
RBRACK : ']';
LBRACE : '{';
RBRACE : '}';
PLUS   : '+';
STAR   : '*';
QMARK  : '?';
COMMA  : ',';
DIGIT  : '0'..'9';
OTHER  : . ;

"[+*]{5,20}?A*+"次のように入力を解析します。

ここに画像の説明を入力

より完全な PCRE 文法は、 https ://github.com/bkiers/PCREParser にあります(文法はここにあります) 。

編集

つまり、「、」を中括弧内の COMMA としてトークン化することを好みますが、外側では CHAR としてトークン化します。今のところ回避策を使用しますが、それは可能ですか?

いいえ、私が言ったように、レクサーはパーサーの影響を受けません。これが必要な場合は、ANTLR ではなくPEGを使用する必要があります。ANTLR では、字句解析と解析が厳密に分離されています。それについては何もできません。

ただし、パーサー ルールで一致するトークンのタイプを変更することもできます。すべてのパーサー ルールには、一致する最初と最後のトークンを示す$startandトークンがあります。(and ) は常に単一のトークンに一致する$endため、次のようにルールのブロックでトークンのタイプを変更できます。char_class_charsingle_char@after

single_char
@after{$start.setType(CHAR);}
 : DIGIT
 | COMMA
 | RBRACE
 | RBRACK // this is only special inside a character class
 | OTHER
 ;

char_class_char
@after{$start.setType(CHAR);}
 : LBRACK
 | LBRACE
 | RBRACE
 | PLUS
 | STAR
 | QMARK
 | COMMA
 | DIGIT
 | CHAR
 ;

// ...

CHAR : . ;

あなたが求めている動作になります(私は推測します)。

HTH

于 2012-07-15T07:37:53.460 に答える
2

これは、レクサーでゲート付きセマンティック述語を使用して行うことができます。以下のコードでは、',' は isComma が true の場合にのみ COMMA ルールに一致します。それ以外の場合、文法で CHAR が COMMA の後にある場合、CHAR と一致します。私は CSharp を知らないので、完全な例を挙げることはできません。

L_CURLY : '{' {setComma();};
R_CURLY : '}' {clearComma();};
COMMA : {isComma}? => ',';

明らかに、中括弧が異なるコンテキストで使用されている場合、これは機能しない可能性があります。パーサーを本当に混乱させない限り、この方法でレクサーを使用することは避けることをお勧めします。

于 2012-07-16T15:10:45.143 に答える