我有一个类似以下的语法
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' ;
只有ID
和BACKTICK_ID
,types
是由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;
}