7

Antlr は、解析するテキストの直後に EOF がないストリームからのデータの解析に適していますか? 私の観察によると、レクサーは次のトークンの最初の文字が受信されるまで現在のトークンを発行しません。その上、次のルールの最初のトークンが受信されるまで、パーサーはルールを発行しないようです。これが私が試した簡単な文法です:

fox: 'quick' 'brown' 'fox' '\r'? '\n' ;

次に、生成されたパーサーを UnbufferedCharStream と UnbufferedTokenStream で使用しました。

  CharStream input = new UnbufferedCharStream(is);
  MyLexer lex = new MyLexer(input);
  lex.setTokenFactory(new CommonTokenFactory(true));
  TokenStream tokens = new UnbufferedTokenStream(lex);
  MyParser parser = new MyParser(tokens);
  MyParser.FoxContext fox = parser.fox();

ストリームが「クイック」になると、何も起こりません。

' b ' が入ったとき - 入力ルール ' fox '

then ' roun ' - 何もない (2 つのトークンがストリームにある - それらのどれもまだ leser することが知られていない!)

' f ' の後でのみ、リスナーは最初のトークン ' quick 'にアクセスします。

then - ' ox 'には何もありません

新しい行 (unix): トークン ' brown 'にアクセスしてください

これで、ストリームにはすべてのデータ (4 つのトークン) が含まれていますが、認識されるのは 2 つのトークンのみです。

これらのトークンをシステムにプッシュするために、ストリームは 2 つのトークンを発行できることがわかりました。これは、文法で認識されている任意のトークンです。2 つの余分な新しい行、または ' fox ' と ' Brown ' としましょう。その後、トークン ' fox ' と ' \n ' が訪問され、パーサーはルール ' fox ' を終了し、解析が終了します。

それはバグですか、それとも機能ですか?そのラグをなくす方法はありますか?

ありがとう!

4

3 に答える 3

7

ANTLR 4 ブックには、もともとストリーミング入力を解析する例が含まれる予定でしたが、このようなものに適応無制限先読みパーサーを使用すると必然的に発生する深刻な複雑さのために、私はそれに反対しました。

ANTLR 4には保証された先読みバウンドがありません(また、それを探すように指示したり、強制しようとする方法さえありません)。そのため、ブロッキングストリームで動作する実装は、その時点までの解析に関する情報を返さずにデッドロックする可能性があります. 最初に中間バッファーが配置されていることを確認しない限り、ストリーミング入力を解析する可能性さえありません。

  1. 利用可能な (または以前に解析されていない) 入力をすべて取得し、Stringまたはに配置しchar[]ます。
  2. ANTLRInputStreamバッファの を作成します。
  3. 最後に暗黙の EOF を持つこのストリームを lex/parse しようとします。

解析の結果は、その時点までの結果を破棄するか、より多くのデータが利用可能になったときに再試行するために保持するかを示します。

  • 構文エラーが発生しない場合、入力は正常に解析されており、入力の次のセクションが後で使用可能になったときに解析できます。

  • EOF トークンが消費される前に構文エラーが報告された場合は、実際の入力に構文エラーが表示されるため、それを処理する必要があります (ユーザーに報告するなど)。

  • EOF トークンが消費された時点で構文エラーが報告された場合は、追加の入力によって問題が解決される可能性があります。現在の解析の結果を無視し、入力ストリームからデータが利用可能になったら再試行してください。

于 2013-02-18T03:58:55.007 に答える
4

バッファリングされていないストリームを正しく使用していると思います。表示されるのは、これらのストリームを使用した場合に期待される望ましい結果です。しかし、あなたは彼らが会う義務がないという彼らの期待を持っているかもしれないと思います。

以下は、スティックで突くためのテストコードです。入力に使用System.inしているので、単語トークン間の改行文字を考慮して文法を変更しました。

Streaming.g

grammar Streaming;

fox   : 'quick' NL 'brown' NL 'fox' NL DONE NL;
DONE  : 'done';
NL    : '\r'? '\n';

StreamingTest.java

import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.CommonTokenFactory;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.UnbufferedCharStream;
import org.antlr.v4.runtime.UnbufferedTokenStream;
import org.antlr.v4.runtime.tree.TerminalNode;

public class StreamingTest {
    public static void main(String[] args) throws Exception {
        lex();
        parse();
    }

    private static void lex() {
        System.out.println("-> Reading from lexer:");
        UnbufferedCharStream input = new UnbufferedCharStream(System.in);
        StreamingLexer lexer = new StreamingLexer(input);
        lexer.setTokenFactory(new CommonTokenFactory(true));

        Token t;

        //read each token until hitting input "done"
        while ((t = lexer.nextToken()).getType() != StreamingLexer.DONE){
            if (t.getText().trim().length() == 0){
                System.out.println("-> " + StreamingLexer.tokenNames[t.getType()]);
            } else { 
                System.out.println("-> " + t.getText());
            }
        }
    }

    private static void parse() {
        System.out.println("-> Reading from parser:");
        UnbufferedCharStream input = new UnbufferedCharStream(System.in);
        StreamingLexer lexer = new StreamingLexer(input);
        lexer.setTokenFactory(new CommonTokenFactory(true));

        StreamingParser parser = new StreamingParser(new UnbufferedTokenStream<CommonToken>(lexer));
        parser.addParseListener(new StreamingBaseListener(){
            @Override
            public void visitTerminal(TerminalNode t) {
                if (t.getText().trim().length() == 0){
                    System.out.println("-> " + StreamingLexer.tokenNames[t.getSymbol().getType()]);
                } else { 
                    System.out.println("-> " + t.getText());
                }
            }
        });

        parser.fox();
    }
}

以下は、上記のプログラムのレクサーとパーサーに提供/受信される入力と出力の組み合わせです。出力の各行の前には。が付いてい->ます。その後、どうしてそうなるのかを説明します。

入出力

-> Reading from lexer:
quick
-> quick
brown
-> NL
-> brown
fox
-> NL
-> fox
done
-> NL
-> Reading from parser:
quick
brown
-> quick
-> NL
fox
-> brown
-> NL
done
-> fox
-> NL

-> done

-> NL

私が最初に気付くのは、レクサーquick NLが入力をすぐに受け取ったが、のトークンしか提供しなかったことですquick。この不一致の理由は、空の先読み文字バッファーに配置されないためUnbufferedCharStream、(完全に優れたトークンが用意されていても)もう1文字先読みするためです。NL残念ながら、バッファリングされていないストリームはバッファリングされます。クラス自体のJavadocコメントによると:

ここでの「バッファなし」とは、すべてのデータをバッファリングするわけではなく、charのオンデマンドロードではないという事実を指します。

この余分な読み取りは、ストリームでさらに入力を待機することを意味します。これは、レクサーが残りの入力に対して1トークン遅れている理由を説明しています。

次に、パーサーに移ります。なぜそれはレクサーのものに2つのトークンより遅れているのですか?単純:UnbufferedTokenStream空の先読みバッファーにも配置されないため。ただし、次のトークンは、a)レクサーからのスペアトークンがあり、b)レクサーUnbufferedCharStreamが独自の先読み文字を読み取るまで作成できません。事実上、これはレクサーの1文字の「ラグ」と1トークンの「ラグ」です。

ANTLR v4で「ラグのない」データオンデマンドストリームを取得することは、独自のストリームを作成することを意味するようです。しかし、既存のストリームは期待どおりに機能しているように見えます。


Antlrは、解析するテキストの直後にEOFがないストリームからのデータを解析するのに適していますか?

ANTLR4についてはまだ自信を持って答えることはできません。必要になるまで先にバッファリングしないトークンストリームを作成するのは簡単なようですが(呼び出しをスキップするためにUnbufferedTokenStreamオーバーライドします)、文字ストリームは、他の人のバッファリングに関係なく、独自の読み取りを行うクラスによって呼び出されます。またはそう思われます。私はできる限りこれを掘り下げていきますが、これを行うための公式の方法を学ぶ必要があるかもしれません。consumesync

于 2013-02-15T07:21:06.063 に答える
2

どうやら問題の根源は Unbuffered*Streams ではありません。LexerATNSimulator.execATN() メソッドのように、インタープリターにあります。そのメソッドは、レクサーをステート マシンとして解釈し、次の最初の文字で 1 つのタグから別のタグに移動します。タグが消費されます。同様のアルゴリズムが、レクサーによって認識されたトークンを処理する ParserATNSimulator で使用されます。それがダブルラグの原因です。したがって、現在実装されている Antlr 4 は、継続的な対話型データの解析には使用できないと確信しています。Flex/Bison とは異なり、レクサーは最後の文字がタグに一致する可能性があるときにタグを返します。その結果、文法に一致するデータ部分が到着すると、 parse() 関数が終了します。これにより、サイズが別の方法で定義されていない場合に、データ構造によって決定される正確な量のデータを読み取る優れた機能が提供されます。

于 2013-02-18T03:19:35.823 に答える