7

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

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

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

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

这是我想包含空格的规则

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

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

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

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

4

3 回答 3

5

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

假设您要解析(并显示)小写或大写字母。大写字母放在HIDDEN通道上,因此默认情况下,只会解析小写字母。但是,当解析器偶然发现小写时"q",我们想更改为HIDDEN通道。HIDDEN一旦在频道上解析,我们希望"Q"将我们带回到另一端DEFAULT_CHANNEL

所以在解析 source 时"aAbBcqCdDQeE",首先打印"a", ,然后改变频道,然后"b"打印and ,然后再次改变频道,最后打印到控制台。"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 类来测试这一切:

主.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 (*尼克斯)

java -cp .:antlr-3.3.jar 主要

3(窗口)

java -cp .;antlr-3.3.jar 主要

这将导致以下内容打印到控制台:

一种
b
C
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();
  }
}

打印以下内容:

一种
b
C
C
d
D
e
乙
于 2011-04-21T12:21:40.450 回答
0

也许您应该考虑将空格作为语法的一部分。但是,为什么要用这些不重要的信息把你的语法弄得一团糟呢?好吧,因为它不是不重要的。换行符在某些情况下有意义。当您需要 IDE 支持时,例如来自 Visual Studio 语言服务器,您需要指定一个语言语法,而不需要低级 ANTLR 自定义的所有花里胡哨。

于 2012-08-13T08:43:29.637 回答
0

Antler 4我使用一个简单的解决方案。我没有在Antlr 3. 它是 C#,但您可以轻松地将其转换为 Java。

  1. 更改parser1.g4如下:

    parser grammar Parser1;
    
    options { tokenVocab=Lexer1; }
    
    startRule
    @init { SetWhiteSpacesAcceptence(false); } 
        : (componentWithWhiteSpaces | componentWithoutWhiteSpaces)* EOF
    ;
    
    componentWithWhiteSpaces : { SetWhiteSpacesAcceptence(true); } 
                                component1 component2 component3 
                                { SetWhiteSpacesAcceptence(false); } 
    ;
    
    componentWithoutWhiteSpaces : component4 component5 component6 
    
  2. 更改lexer1.g4如下:

    lexer grammar Lexer1;
    WS : [ \t\r\n] { if( this.IsWhiteSpacesAccepted() ) Skip(); };
    
  3. 扩展Parser1类如下:

    class MyParser : Parser1
    {
        public void SetWhiteSpacesAcceptence(bool isAccept)
        {
            if (_input != null && _input.TokenSource != null)
            {
                if (_input.TokenSource is MyLexer)
                {
                    MyLexer lexer = _input.TokenSource as MyLexer;
                    if (lexer != null)
                        lexer.SetWhiteSpacesAcceptence(isAccept);
                }
            }
        }
    
        public bool IsWhiteSpacesAccepted()
        {
            if (_input != null && _input.TokenSource != null)
            {
                if (_input.TokenSource is MyLexer)
                {
                    MyLexer lexer = _input.TokenSource as MyLexer;
                    if (lexer != null)
                        return lexer.IsWhiteSpacesAccepted();
                }
            }
    
            return false;
        }
    }
    
  4. 扩展Lexer1类如下:

    class MyLexer : Lexer1
    {
        private bool isWhiteSpacesAccepted;
    
        public void SetWhiteSpacesAcceptence(bool isAccept) { isWhiteSpacesAccepted = isAccept }
    
        public bool IsWhiteSpacesAccepted() { return isWhiteSpacesAccepted; }
    }
    
  5. 现在Main功能如下:

    static void Main()
    {
        AntlrFileStream input = new AntlrFileStream("pathToInputFile");
        MyLexer lexer = new MyLexer(input);
        UnbufferedTokenStream tokens = new UnbufferedTokenStream(lexer);
        MyParser parser = new MyParser(tokens);
    
        parser.startRule();
    }
    
于 2013-09-24T11:08:11.850 回答