ANTLRのsemantic predicateとは何ですか?
ANTLR 4の述語については、これらのstackをチェックアウトしてくださいオーバーフローQ&A:
semantic predicateは、プレーンコードを使用して、文法アクションに追加の(semantic)ルールを適用する方法です。
セマンティック述語には3つのタイプがあります。
カンマで区切られた数字のみで構成されるテキストブロックがあり、空白を無視するとします。この入力を解析して、数字が最大3桁の「長い」(最大999)であることを確認します。次の文法(_Numbers.g
_)はそのようなことをします:
_grammar Numbers;
// entry point of this parser: it parses an input string consisting of at least
// one number, optionally followed by zero or more comma's and numbers
parse
: number (',' number)* EOF
;
// matches a number that is between 1 and 3 digits long
number
: Digit Digit Digit
| Digit Digit
| Digit
;
// matches a single digit
Digit
: '0'..'9'
;
// ignore spaces
WhiteSpace
: (' ' | '\t' | '\r' | '\n') {skip();}
;
_
文法は、次のクラスでテストできます。
_import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
NumbersLexer lexer = new NumbersLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
NumbersParser parser = new NumbersParser(tokens);
parser.parse();
}
}
_
レクサーとパーサーを生成し、すべての_.Java
_ファイルをコンパイルし、Main
クラスを実行してテストします。
Java -cp antlr-3.2.jar org.antlr.Tool Numbers.g javac -cp antlr-3.2.jar * .Java java -cp。:antlr-3.2 .jar Main
その場合、コンソールには何も印刷されず、何も問題がなかったことを示します。変更してみてください:
_ANTLRStringStream in = new ANTLRStringStream("123, 456, 7 , 89");
_
に:
_ANTLRStringStream in = new ANTLRStringStream("123, 456, 7777 , 89");
_
そして、再度テストを実行します。文字列_777
_の直後にコンソールにエラーが表示されます。
これにより、セマンティック述語がわかります。 1〜10桁の数字を解析するとします。次のようなルール:
_number
: Digit Digit Digit Digit Digit Digit Digit Digit Digit Digit
| Digit Digit Digit Digit Digit Digit Digit Digit Digit
/* ... */
| Digit Digit Digit
| Digit Digit
| Digit
;
_
面倒になります。セマンティック述語は、このタイプのルールを簡素化するのに役立ちます。
検証セマンティック述語は、疑問符が後に続くコードブロックにすぎません。
_RULE { /* a boolean expression in here */ }?
_
validatingセマンティック述語を使用して上記の問題を解決するには、number
ルールを変更します文法で:
_number
@init { int N = 0; }
: (Digit { N++; } )+ { N <= 10 }?
;
_
_{ int N = 0; }
_と_{ N++; }
_の部分は単純なJavaステートメントで、パーサーがnumber
ルールを「入力」するときに最初のステートメントが初期化されます。実際の述語is:_{ N <= 10 }?
_。これにより、数値の長さが10桁を超えると、パーサーは FailedPredicateException
をスローします。
次のANTLRStringStream
を使用してテストします。
_// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
_
例外は発生しませんが、次の例では例外が発生します。
_// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
_
gatedセマンティック述語はvalidatingセマンティック述語に似ており、gatedバージョンはFailedPredicateException
の代わりに構文エラーを生成します。
gatedセマンティック述語の構文は次のとおりです。
_{ /* a boolean expression in here */ }?=> RULE
_
代わりにgated述語を使用して上記の問題を解決し、最大10桁の数字に一致させます。 :
_number
@init { int N = 1; }
: ( { N <= 10 }?=> Digit { N++; } )+
;
_
両方でもう一度テストします。
_// all equal or less than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,1234567890");
_
そして:
_// '12345678901' is more than 10 digits
ANTLRStringStream in = new ANTLRStringStream("1,23,12345678901");
_
最後にエラーがスローされます。
述語の最後のタイプは曖昧さを解消するセマンティック述語です。これは検証述語(_{boolean-expression}?
_)に少し似ていますが、ゲートセマンティック述語のように機能します(例外なし)ブール式がfalse
と評価されるとスローされます。ルールの開始時にこれを使用して、ルールのプロパティをチェックし、パーサーがそのルールに一致するかどうかを確認できます。
例の文法が、0..999の範囲の数値に一致するNumber
トークン(パーサールールではなくレクサールール)を作成するとします。パーサーで、低音数と高音数を区別したいと思います(低:0..500、高:501..999)。これは、ストリームの次のトークン(input.LT(1)
)を調べて低位か高位かを確認する曖昧性解消セマンティック述語を使用して実行できます。
デモ:
_grammar Numbers;
parse
: atom (',' atom)* EOF
;
atom
: low {System.out.println("low = " + $low.text);}
| high {System.out.println("high = " + $high.text);}
;
low
: {Integer.valueOf(input.LT(1).getText()) <= 500}? Number
;
high
: Number
;
Number
: Digit Digit Digit
| Digit Digit
| Digit
;
fragment Digit
: '0'..'9'
;
WhiteSpace
: (' ' | '\t' | '\r' | '\n') {skip();}
;
_
文字列_"123, 999, 456, 700, 89, 0"
_を解析すると、次の出力が表示されます。
_low = 123
high = 999
low = 456
high = 700
low = 89
low = 0
_
私は常に、wincent.comの ANTLR述語 への簡潔な参照をガイドとして使用しました。