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


103

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


3
セマンティックな述語が何であるかを知りたい人のために投稿するための適切なオンラインリソースが見つからなかったため、ここに質問を投稿することにしました(これについてもすぐに回答します)。
Bart Kiers、2010年

1
これをしてくれてありがとう。人々が自分の質問に答えるとき、特にこの方法で答えるために具体的に質問するときは、私はいつもそれが好きです。
Daniel H

1
本を読む。The Definitive ANTLR 4 Referenceの11章は、セマンティック述語に関するものです。本を持っていないのですか?それを得る!すべての価値があります。
james.garriss 2015年

回答:


169

ANTLR 4

ANTLR 4の述語については、これらのスタックオーバーフローに関する Q&Aを確認してください。


ANTLR 3

セマンティック述語はプレーンなコードを使用して文法アクションの際に余分な(セマンティック)ルールを強制する方法です。

セマンティック述語には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
  ;

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


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

有効セマンティック述語は疑問符が続くコードのブロック以外の何ものでもありません。

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

2.ゲート付きセマンティック述語

ゲートされたセマンティック述語はに似ている、検証セマンティック述語のみ、ゲートされたバージョンの代わりに、構文エラーを生成します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");

最後にエラーがスローされることがわかります。


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

述語の最後のタイプは、明確化する意味述語です。これは、検証述語({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

12
ANTLRへの初心者向けガイドの作成を検討する必要がある人:P
Yuri Ghensev

5
@Bart Kiers:ANTLRについて本を書いてください
santosh

2
ANTLR v4のために、input.LT(1)であるgetCurrentToken()今:-)
シャオ嘉

素晴らしい...これは、ドキュメントにあるべき徹底的な説明と例です!
エゼキエルビクター

+1。この答えは、The Definitive ANTLR 4 Referenceブックよりはるかに優れています。この回答は、コンセプトにスポットを当てて、良い例を示しています。
asyncwait 14

11

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


6
はい、素晴らしいリンクです!しかし、あなたが言及しているように、ANTLRを(比較的)初めて使う人にとっては、少し難しいかもしれません。私の答えがANTLR-grass-hopperに対して(少し)友好的であることを願っています。:)
Bart Kiers
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.