在ANTLR4中生成一个修改后的令牌



我有一个类似以下的语法

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

当我匹配一个标识符时,例如"某人",我想去掉反勾号并生成一个不同的令牌,即someone

当然,我可以遍历最终的令牌数组,但在令牌解析过程中可以这样做吗?

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

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

您希望令牌是自动生成的,就好像语法有lexer规则一样:

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

但是令牌是由type(即整数)标识的,而不是由lexer规则的名称标识的。您可以使用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 : [ rnt] -> 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

基本类型来自lexer规则:

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

另一个来自CCD_ 5。您可以写入表中找到的令牌,而不是为每个令牌递增一个数字,在创建新的令牌之前,访问该表以检查它是否已经存在,并避免重复的令牌接收不同的数字。

无论如何,您在解析器中不能做任何有用的事情,因为解析器规则需要知道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 : [ rnt] -> 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,而不是lexer规则的名称:

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

CCD_ 14是表示数字4的常数。

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

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

相关内容

  • 没有找到相关文章

最新更新