ANTLR4 JAVA - 是否可以在Parser Listener点从词法分析器中提取片段?

时间:2017-01-27 19:37:35

标签: java parsing antlr4

我有一个Lexer规则如下:

PREFIX  : [abcd]'_'; 
EXTRA   : ('xyz' | 'XYZ' );
SUFFIX  : [ab];

TCHAN           :   PREFIX EXTRA? DIGIT+ SUFFIX?;

和解析器规则:

tpin            :   TCHAN
                ;

在exit_tpin()Listiner方法中,是否有一种语法可以提取令牌的DIGIT组件?现在我可以得到ctx.TCHAN()元素,但这是一个字符串。我只想要TCHAN的数字部分。

或者我应该删除TCHAN作为TOKEN并将该规则移至tpin(即)

tpin : PREFIX EXTRA? DIGIT+ SUFFIX?

我知道如何从听众中提取DIGIT。

我的猜测是,当TOKEN被呈现给解析器时,解构它已经太晚了......但我想知道是否有一些ANTLR专家知道一种技术。

如果我重写我的TOKENIZER,那么INT / ID令牌可能会错过TCHAN令牌(我想这就是为什么我最终解析了)。

我总是可以在监听器方法中做一些正则表达式的工作......但这看起来很糟糕......因为我之前有各个组件。我只是懒惰,并且想知道除了重构解析语法之外是否还有其他技术。

1 个答案:

答案 0 :(得分:1)

The Definitive ANTLR Reference中,您可以找到复杂词法分析器的示例,其中大部分工作已完成。但是在学习ANTLR时,我建议将词法分析器主要用于将输入流的分裂函数转换为小标记。然后在解析器中做大工作。在目前的情况下,我会这样做:

grammar Question;

/* extract digit */

question
    :   tpin EOF
    ;

tpin
//  :   PREFIX EXTRA? DIGIT+ SUFFIX?
//      {System.out.println("The only useful information is " + $DIGIT.text);}
    :   PREFIX EXTRA? number SUFFIX?
        {System.out.println("The only useful information is " + $number.text);}
    ;

number
    :   DIGIT+
    ;

PREFIX  : [abcd]'_'; 
EXTRA   : ('xyz' | 'XYZ' );
DIGIT   : [0-9] ;
SUFFIX  : [ab];
WS      : [ \t\r\n]+ -> skip ;

输入为d_xyz123456b。随着第一个版本

    :   PREFIX EXTRA? DIGIT+ SUFFIX?

你得到了

$ grun Question question -tokens data.txt
[@0,0:1='d_',<PREFIX>,1:0]
[@1,2:4='xyz',<EXTRA>,1:2]
[@2,5:5='1',<DIGIT>,1:5]
[@3,6:6='2',<DIGIT>,1:6]
[@4,7:7='3',<DIGIT>,1:7]
[@5,8:8='4',<DIGIT>,1:8]
[@6,9:9='5',<DIGIT>,1:9]
[@7,10:10='6',<DIGIT>,1:10]
[@8,11:11='b',<SUFFIX>,1:11]
[@9,13:12='<EOF>',<EOF>,2:0]
The only useful information is 6

因为DIGIT+的解析转换为重用DIGIT的循环

    setState(12); 
    _errHandler.sync(this);
    _la = _input.LA(1);
    do {
        {
        {
        setState(11);
        ((TpinContext)_localctx).DIGIT = match(DIGIT);
        }
        }
        setState(14); 
        _errHandler.sync(this);
        _la = _input.LA(1);
    } while ( _la==DIGIT );

$DIGIT.text转换为((TpinContext)_localctx).DIGIT.getText(),只保留最后一位数字。这就是我定义一个子规则number

的原因
:   PREFIX EXTRA? number SUFFIX?

可以轻松捕获值:

[@0,0:1='d_',<PREFIX>,1:0]
[@1,2:4='xyz',<EXTRA>,1:2]
[@2,5:5='1',<DIGIT>,1:5]
[@3,6:6='2',<DIGIT>,1:6]
[@4,7:7='3',<DIGIT>,1:7]
[@5,8:8='4',<DIGIT>,1:8]
[@6,9:9='5',<DIGIT>,1:9]
[@7,10:10='6',<DIGIT>,1:10]
[@8,11:11='b',<SUFFIX>,1:11]
[@9,13:12='<EOF>',<EOF>,2:0]
The only useful information is 123456

你甚至可以简化:

tpin
    :   PREFIX EXTRA? INT SUFFIX?
        {System.out.println("The only useful information is " + $INT.text);}
    ;

PREFIX  : [abcd]'_'; 
EXTRA   : ('xyz' | 'XYZ' );
INT     : [0-9]+ ;
SUFFIX  : [ab];
WS      : [ \t\r\n]+ -> skip ;

$ grun Question question -tokens data.txt
[@0,0:1='d_',<PREFIX>,1:0]
[@1,2:4='xyz',<EXTRA>,1:2]
[@2,5:10='123456',<INT>,1:5]
[@3,11:11='b',<SUFFIX>,1:11]
[@4,13:12='<EOF>',<EOF>,2:0]
The only useful information is 123456

在侦听器中,您可以通过规则上下文TpinContext直接访问这些值:

public static class TpinContext extends ParserRuleContext {
    public Token INT;
    public TerminalNode PREFIX() { return getToken(QuestionParser.PREFIX, 0); }
    public TerminalNode INT() { return getToken(QuestionParser.INT, 0); }
    public TerminalNode EXTRA() { return getToken(QuestionParser.EXTRA, 0); }
    public TerminalNode SUFFIX() { return getToken(QuestionParser.SUFFIX, 0); }