antlr - 如何获取 Antlr 解析器规则以从默认和隐藏 channel 读取

标签 antlr antlr3 lexer parser-generator

我在隐藏 channel 中使用正常的空格分隔,但我有一个规则,我想包含任何空格以供以后处理,但我发现的任何示例都需要一些非常奇怪的手动编码。

从多个 channel 读取是否没有简单的选项,例如从一开始就将空格放在那里的选项。

前任。这是 WhiteSpace 词法分析器规则

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

这是我想包含空格的规则
raw :   '{'? (~('{'))*;

基本上,它是一个捕获所有规则,用于捕获与要由另一个模式处理的其他规则不匹配的任何内容,因此我需要原始流。

我希望有一个 {$channel==DEFAULT || $channel==HIDDEN}语法示例,但找不到任何。

我的目标是 C#,但如果需要,我可以重写 Java 示例。

最佳答案

AFAIK,这是不可能的。但是,您可以扩展 UnbufferedTokenStream更改 channel在解析过程中。您不能使用 CommonTokenStream因为它缓冲了可变数量的 token (并且缓冲区中可能有 token 位于错误的 channel 上!)。请注意,您至少需要 ANTLR 3.3:在以前的版本中 UnbufferedTokenStream尚未包括在内。

假设您要解析(并显示)小写或大写字母。大写字母放在 HIDDEN 上 channel ,因此默认情况下,只会解析小写字母。但是,当解析器偶然发现小写字母 "q" 时,我们要改成 HIDDEN channel 。一旦在 HIDDEN 上解析 channel ,我们想要 "Q"让我们回到 DEFAULT_CHANNEL再次。

所以在解析源码时"aAbBcqCdDQeE" , 第一 "a" , "b""c"被打印,然后 channel 改变,然后"C""D"打印出来,然后再换 channel ,最后"e"打印到控制台。

这是执行此操作的 ANTLR 语法:

channel 演示.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;}
  ;

这是自定义 token 流类:

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 类来测试它:

主程序
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 Main

3 (Windows)

java -cp .;antlr-3.3.jar Main

which will cause the following to be printed to the console:

a
b
c
C
D
e

EDIT

You can include the class in your grammar file like this:

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;}
  ;

从上述语法生成的解析器将在遇到 "*" 时启用从所有 channel 读取。 .所以在解析"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();
  }
}

打印以下内容:

一种

C
C
d
D
电子

关于antlr - 如何获取 Antlr 解析器规则以从默认和隐藏 channel 读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5741829/

相关文章:

java - 是什么导致 Antlr 创建一个大的 token 流导致内存不足

JavaScript : How to do Error Handling in Lexer generated by antlr?

C++ 标记化

antlr - 用 gradle 编译 3.2 Antlr 语法

antlr - 用简单计算器实现互左递归

recursion - 如何消除左递归

java - 正则表达式 - 包含多个下划线的单词

java - 在 ANTLR 中处理 EOF、空格和换行符

ANTLR语法if语句

whitespace -\v和\r是什么意思?他们是空格吗?