在ANTLR4

时间:2017-10-30 07:04:57

标签: antlr antlr4

我的语法如下

Identifier
    : [a-zA-Z0-9_.]+
    | '`' Identifier '`'
    ;

当我匹配一个标识符时,例如“某人”,我想剥去反引号并产生一个不同的标记,即someone

当然,我可以遍历最终的令牌数组,但是在令牌解析期间是否可以这样做?

1 个答案:

答案 0 :(得分:0)

如果我很好理解,给定输入(文件t.text):

one `someone`
two `fred`
tree `henry`

您希望自动生成令牌,就像语法具有词法分析器规则一样:

SOMEONE : 'someone' ;
FRED    : 'fred' ;
HENRY   : 'henry' ;
ID  : [a-zA-Z0-9_.]+ ;

但令牌由type标识,即整数,而不是词法分析器的名称。您可以使用setType():

更改此type
grammar Question;

/* Change `someone` to SOMEONE, `fred` to FRED, etc. */

@lexer::members { int next_number = 1001; }

question
@init {System.out.println("Question last update 1117");}
    :   expr+ EOF
    ;

expr
    :   ID BACKTICK_ID
    ;

ID  : [a-zA-Z0-9_.]+ ;

BACKTICK_ID : '`' ID '`' { setType(next_number); next_number+=1; } ;

WS : [ \r\n\t] -> skip ;

执行:

$ grun Question question -tokens -diagnostics t.text 
[@0,0:2='one',<ID>,1:0]
[@1,4:12='`someone`',<1001>,1:4]
[@2,14:16='two',<ID>,2:0]
[@3,18:23='`fred`',<1002>,2:4]
[@4,25:28='tree',<ID>,3:0]
[@5,30:36='`henry`',<1003>,3:5]
[@6,38:37='<EOF>',<EOF>,4:0]
Question last update 1117
line 1:4 mismatched input '`someone`' expecting BACKTICK_ID
line 2:4 mismatched input '`fred`' expecting BACKTICK_ID
line 3:5 mismatched input '`henry`' expecting BACKTICK_ID

基本类型来自词法分析器规则:

$ cat Question.tokens 
ID=1
BACKTICK_ID=2
WS=3

另一个来自setType。您可以编写表中找到的标记,而不是为每个标记增加一个数字,在创建新标记之前,访问该表以检查它是否已存在并避免重复标记接收不同的数字。

无论如何,你在解析器中无能为力,因为解析器规则需要知道type数字。

如果您事先知道一组名称,可以在tokens声明中列出:

grammar Question;

/* Change `someone` to SOMEONE, `fred` to FRED, etc. */

@lexer::header {
    import java.util.*;
}

tokens { SOMEONE, FRED, HENRY }

@lexer::members {
    Map<String,Integer> keywords = new HashMap<String,Integer>() {{
        put("someone", QuestionParser.SOMEONE);
        put("fred", QuestionParser.FRED);
        put("henry", QuestionParser.HENRY);
    }};
}

question
@init {System.out.println("Question last update 1746");}
    :   expr+ EOF
    ;

expr
    :   ID SOMEONE
    |   ID FRED
    |   ID HENRY
    ;

ID  : [a-zA-Z0-9_.]+ ;

BACKTICK_ID : '`' ID '`'
        { String textb = getText();
          String texta = textb.substring(1, textb.length() - 1);
          System.out.println("text before=" + textb + ", text after="+ texta);
          if ( keywords.containsKey(texta)) {
              setType(keywords.get(texta)); // reset token type
              setText(texta); // remove backticks
          }
        }
    ;

WS : [ \r\n\t] -> skip ;

执行:

$ grun Question question -tokens -diagnostics t.text 
text before=`someone`, text after=someone
text before=`fred`, text after=fred
text before=`henry`, text after=henry
[@0,0:2='one',<ID>,1:0]
[@1,4:12='someone',<4>,1:4]
[@2,14:16='two',<ID>,2:0]
[@3,18:23='fred',<5>,2:4]
[@4,25:28='tree',<ID>,3:0]
[@5,30:36='henry',<6>,3:5]
[@6,38:37='<EOF>',<EOF>,4:0]
Question last update 1746
$ cat Question.tokens 
ID=1
BACKTICK_ID=2
WS=3
SOMEONE=4
FRED=5
HENRY=6

正如您所看到的,不再有错误,因为expr规则对识别良好的令牌感到满意。即使没有

SOMEONE : 'someone' ;
FRED    : 'fred' ;
HENRY   : 'henry' ;

IDBACKTICK_IDtypes已在tokens语句后面的场景中定义:

    public static final int
        ID=1, BACKTICK_ID=2, WS=3, SOMEONE=4, FRED=5, HENRY=6;

我担心如果你想要一个免费的名单,那是不可能的,因为解析器使用的是types,而不是词法规则的名称:

    public static class ExprContext extends ParserRuleContext {
        public TerminalNode ID() { return getToken(QuestionParser.ID, 0); }
        public TerminalNode SOMEONE() { return getToken(QuestionParser.SOMEONE, 0); }
        public TerminalNode FRED() { return getToken(QuestionParser.FRED, 0); }
        public TerminalNode HENRY() { return getToken(QuestionParser.HENRY, 0); }
...
public final ExprContext expr() throws RecognitionException {
    try { ...
        setState(17);
        case 1:
            enterOuterAlt(_localctx, 1);
            {
            setState(11);
            match(ID);
            setState(12);
            match(SOMEONE);
            }
            break;

match(SOMEONE);

SOMEONE是一个代表数字4的常量。

如果您没有已知名称列表,emit将无法解决您的问题,因为它会创建一个最重要字段为_type的令牌:

public Token emit() {
    Token t = _factory.create(_tokenFactorySourcePair, _type, _text, _channel, _tokenStartCharIndex, getCharIndex()-1,
                              _tokenStartLine, _tokenStartCharPositionInLine);
    emit(t);
    return t;
}