7

私は隠しチャネルへの通常の空白分離を使用していますが、後で処理するために空白を含めたいルールが 1 つありますが、見つかった例には非常に奇妙な手動コーディングが必要です。

最初から空白を配置するオプションのように、複数のチャネルから読み取る簡単なオプションはありませんか。

元。これは WhiteSpace lexer ルールです

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
    ;

そして、これは空白を含めたい私のルールです

raw :   '{'? (~('{'))*;

基本的に、他のルールに一致しないコンテンツをキャプチャして別のパターンで処理するのは、キャッチ オール ルールであるため、元のストリームが必要です。

構文の例を期待していました{$channel==DEFAULT || $channel==HIDDEN}が、見つかりません。

私のターゲットは C# ですが、必要に応じて Java の例を書き直すこともできます。

4

3 に答える 3

5

私の知る限り、それは不可能です。ただし、 を拡張して、解析中にUnbufferedTokenStreamを変更できます。可変量のトークンをバッファリングするため、channelを使用することはできません(バッファ内に間違ったチャネルのトークンが存在する可能性があります!)。CommonTokenStream少なくとも ANTLR 3.3 が必要であることに注意してください。以前のバージョンでは、UnbufferedTokenStreamはまだ含まれていませんでした。

小文字または大文字のいずれかを解析 (および表示) したいとします。大文字がHIDDENチャネルに配置されるため、デフォルトでは小文字のみが解析されます。ただし、パーサーが小文字の に出くわした場合は、チャネル"q"に変更する必要があります。HIDDENチャネルで解析したら、再び に戻してHIDDENもらいたいと思います。"Q"DEFAULT_CHANNEL

したがって、ソースを解析すると"aAbBcqCdDQeE"、最初"a""b""c"が出力され、次にチャネルが変更され、次に"C""D"が出力され、次にチャネルが再び変更され、最後"e"にコンソールに出力されます。

これを行うANTLR文法は次のとおりです。

ChannelDemo.g

grammar ChannelDemo;

@parser::members {
  private void handle(String letter) {
    if("Q".equals(letter)) {
      ((ChangeableChannelTokenStream)input).setChannel(Token.DEFAULT_CHANNEL);
    }
    else if("q".equals(letter)) {
      ((ChangeableChannelTokenStream)input).setChannel(HIDDEN);
    }
    else {
      System.out.println(letter);
    }
  }
}

parse
  :  any* EOF
  ;

any
  :  letter=(LOWER | UPPER) {handle($letter.getText());}
  ;

LOWER
  :  'a'..'z'
  ;

UPPER
  :  'A'..'Z' {$channel=HIDDEN;}
  ;

カスタム トークン ストリーム クラスは次のとおりです。

ChangeableChannelTokenStream.java

import org.antlr.runtime.*;

public class ChangeableChannelTokenStream extends UnbufferedTokenStream {

    public ChangeableChannelTokenStream(TokenSource source) {
        super(source);
    }

    public Token nextElement() {
        Token t = null;
        while(true) {
            t = super.tokenSource.nextToken();
            t.setTokenIndex(tokenIndex++);
            if(t.getChannel() == super.channel) break;
        }
        return t;
    }

    public void setChannel(int ch) {
        super.channel = ch;
    }
}

そして、すべてをテストするための小さな Main クラス:

Main.java

import org.antlr.runtime.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("aAbBcqCdDQeE");
        ChannelDemoLexer lexer = new ChannelDemoLexer(in);
        ChangeableChannelTokenStream tokens = new ChangeableChannelTokenStream(lexer);
        ChannelDemoParser parser = new ChannelDemoParser(tokens);
        parser.parse();
    }
}

最後に、レクサー/パーサーを生成し (1)、すべてのソース ファイルをコンパイルし (2)、Main クラスを実行します (3)。

1

java -cp antlr-3.3.jar org.antlr.Tool ChannelDemo.g

2

javac -cp antlr-3.3.jar *.java

3 (*nix)

java -cp .:antlr-3.3.jar メイン

3 (ウィンドウズ)

java -cp .;antlr-3.3.jar メイン

これにより、以下がコンソールに出力されます。

a
b
c
ハ
D
e

編集

次のように、文法ファイルにクラスを含めることができます。

grammar ChannelDemo;

@parser::members {
  private void handle(String letter) {
    if("Q".equals(letter)) {
      ((ChangeableChannelTokenStream)input).setChannel(Token.DEFAULT_CHANNEL);
    }
    else if("q".equals(letter)) {
      ((ChangeableChannelTokenStream)input).setChannel(HIDDEN);
    }
    else {
      System.out.println(letter);
    }
  }

  public static class ChangeableChannelTokenStream extends UnbufferedTokenStream {

    private boolean anyChannel;

    public ChangeableChannelTokenStream(TokenSource source) {
      super(source);
      anyChannel = false;
    }

    @Override
    public Token nextElement() {
      Token t = null;
      while(true) {
        t = super.tokenSource.nextToken();
        t.setTokenIndex(tokenIndex++);
        if(t.getChannel() == super.channel || anyChannel) break;
      }
      return t;
    }

    public void setAnyChannel(boolean enable) {
      anyChannel = enable;
    }

    public void setChannel(int ch) {
      super.channel = ch;
    }
  }
}

parse
  :  any* EOF
  ;

any
  :  letter=(LOWER | UPPER) {handle($letter.getText());}
  |  STAR                   {((ChangeableChannelTokenStream)input).setAnyChannel(true);}
  ;

STAR
  :  '*'
  ;

LOWER
  :  'a'..'z'
  ;

UPPER
  :  'A'..'Z' {$channel=HIDDEN;}
  ;

上記の文法から生成されるパーサーは、"*". したがって、解析するとき"aAbB*cCdDeE"

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    ANTLRStringStream in = new ANTLRStringStream("aAbB*cCdDeE");
    ChannelDemoLexer lexer = new ChannelDemoLexer(in);
    ChannelDemoParser.ChangeableChannelTokenStream tokens =
        new ChannelDemoParser.ChangeableChannelTokenStream(lexer);
    ChannelDemoParser parser = new ChannelDemoParser(tokens);
    parser.parse();
  }
}

以下が出力されます。

a
b
c
ハ
d
D
e
え
于 2011-04-21T12:21:40.450 に答える
0

代わりに、空白を文法の一部にすることを検討する必要があるかもしれません。しかし、なぜそれほど重要でない情報で文法を乱雑にするのでしょうか? まあ、それは重要ではないからです。改行は、特定のコンテキストで意味を持ちます。Visual Studio 言語サーバーなどの IDE サポートが必要な場合は、低レベルの ANTLR カスタマイズのさまざまな機能を使用せずに、言語グラマーを指定する必要があります。

于 2012-08-13T08:43:29.637 に答える