ANTLRのセマンティック述語とは何ですか?
ANTLRのセマンティック述語とは何ですか?
回答:
ANTLR 4の述語については、これらのスタックオーバーフローに関する Q&Aを確認してください。
セマンティック述語はプレーンなコードを使用して文法アクションの際に余分な(セマンティック)ルールを強制する方法です。
セマンティック述語には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 */ }?
有効
なセマンティック述語を使用して上記の問題を解決するにはnumber
、文法のルールを次のように変更します。
number
@init { int N = 0; }
: (Digit { N++; } )+ { N <= 10 }?
;
パーツ{ int N = 0; }
と{ N++; }
はプレーンなJavaステートメントで、パーサーがnumber
ルールに「入る」ときに最初のステートメントが初期化されます。実際の述語は次のとおりです{ N <= 10 }?
。これにより、パーサーFailedPredicateException
は、数値が10桁を超える場合は常にをスローし
ます。
以下を使用してテストします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");
ゲートされたセマンティック述語はに似ている、検証セマンティック述語のみ、ゲートされたバージョンの代わりに、構文エラーを生成しますFailedPredicateException
。
ゲーティングされたセマンティック述語の構文は次のとおりです。
{ /* a boolean expression in here */ }?=> RULE
代わりに、ゲート付き述部を使用して上記の問題を解決し、最大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
)。これをルールの最初に使用して、ルールのいくつかのプロパティをチェックし、パーサーにそのルールと一致させるかどうかを指定できます。
例の文法がNumber
、0..999の範囲の数値に一致するトークン(パーサールールではなくレクサールール)を作成するとします。パーサーで、低数値と高数値を区別したいとします(低: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
input.LT(1)
であるgetCurrentToken()
今:-)
私は常にwincent.comのANTLR述語への簡潔な参照をガイドとして使用してきました。