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

ANTLR4 JAVA -Is it possible to extract fragments from the lexer at the Parser Listener point?

我的 Lexer 规则如下:

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

TCHAN           :   PREFIX EXTRA? DIGIT+ SUFFIX?;

和解析器规则:

tpin            :   TCHAN
                ;

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

或者我应该删除 TCHAN 作为令牌并将该规则移动到 tpin(即)

tpin : PREFIX EXTRA? DIGIT+ SUFFIX?

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

我的猜测是,当 TOKEN 被呈现给解析器时,解构它已经太晚了……但我想知道是否有 ANTLR 大师知道某种技术。

如果我重写我的 TOKENIZER,INT/ID 个令牌可能会丢失 TCHAN 令牌(我认为这就是我最终解析的原因)。

我总是可以在侦听器方法中做一些正则表达式工作...但这似乎是错误的形式...因为我之前有单独的组件。我只是懒惰,想知道除了重构解析语法之外的其他技术是否可行。

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