如何从默认和隐藏通道中读取Antlr Parser规则

时间:2011-04-21 08:59:57

标签: antlr antlr3 lexer parser-generator

我将正常的空白分隔用于隐藏的通道,但我有一条规则,我希望包含任何空格以便以后处理,但我找到的任何示例都需要一些非常奇怪的手动编码。

没有简单的选项可以从多个频道中读取,例如从一开始就把空白放在那里的选项。

实施例。这是WhiteSpace词法分析器

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

这是我的规则,我希望包含空格

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

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

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

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

3 个答案:

答案 0 :(得分:4)

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

假设你要解析(并显示)大写或小写字母。大写字母放在HIDDEN通道上,因此通过deafult,只会解析小写字母。但是,当解析器偶然发现小写"q"时,我们希望更改为HIDDEN频道。在HIDDEN频道进行解析后,我们希望"Q"再次将我们带回DEFAULT_CHANNEL

因此,在解析来源"aAbBcqCdDQeE"时,会先打印"a""b""c",然后更改频道,然后"C"和{{ 1}}打印,然后再次更改频道,最后"D"打印到控制台。

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

ChannelDemo.g

"e"

这是自定义令牌流类:

ChangeableChannelTokenStream.java

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

还有一个小型的主类来测试它:

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

最后,生成一个词法分析器/解析器(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

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

a
b
c
C
D
e

修改

您可以在语法文件中包含该类,如下所示:

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

从上面的语法生成的解析器将在遇到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"

打印以下内容:

a
b
c
C
d
D
e
E

答案 1 :(得分:0)

也许你应该考虑将空白作为你的gramar的一部分。但是为什么要用这些不重要的信息来混淆你的gramar呢?好吧,因为它并不重要。换行符在某些情况下有所改善。当您需要IDE支持时,例如从visual studio语言服务器,你需要指定一个语言语法,没有所有低级ANTLR定制的花哨。

答案 2 :(得分: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();
    }