web-dev-qa-db-ja.com

ANTLRの「セマンティック述語」とは何ですか?

ANTLRのsemantic predicateとは何ですか?

99
Bart Kiers

ANTLR 4

ANTLR 4の述語については、これらのstackをチェックアウトしてくださいオーバーフローQ&A:


ANTLR 3

semantic predicateは、プレーンコードを使用して、文法アクションに追加の(semantic)ルールを適用する方法です。

セマンティック述語には3つのタイプがあります。

  • validatingセマンティック述語;
  • gatedセマンティック述語;
  • 曖昧さ回避セマンティック述語。

文法の例

カンマで区切られた数字のみで構成されるテキストブロックがあり、空白を無視するとします。この入力を解析して、数字が最大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
  ;
_

面倒になります。セマンティック述語は、このタイプのルールを簡素化するのに役立ちます。


1.セマンティック述語の検証

検証セマンティック述語は、疑問符が後に続くコードブロックにすぎません。

_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");
_

2.ゲーテッドセマンティック述語

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");
_

最後にエラーがスローされます。


3.セマンティック述語の明確化

述語の最後のタイプは曖昧さを解消するセマンティック述語です。これは検証述語(_{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
_
161
Bart Kiers

私は常に、wincent.comの ANTLR述語 への簡潔な参照をガイドとして使用しました。

11
Kaleb Pederson