Antlr 当一个更通用的令牌可用时,如何在词法分析期间保持连接的令牌分离
我正在使用的语言允许某些标记粘在一起(例如“intfloat”),我正在寻找一种方法,使lexer不将它们转换为ID,以便它们在解析时可以单独使用。我能想到的最简单的语法是(WS省略): 反对:Antlr 当一个更通用的令牌可用时,如何在词法分析期间保持连接的令牌分离,antlr,token,lexer,greedy,Antlr,Token,Lexer,Greedy,我正在使用的语言允许某些标记粘在一起(例如“intfloat”),我正在寻找一种方法,使lexer不将它们转换为ID,以便它们在解析时可以单独使用。我能想到的最简单的语法是(WS省略): 反对: bc abc bcd 我想从lexer中得到什么: B C ID (starts with not-a-keyword so it's an ID) <error> (cannot concat non-keywords) bc ID(以非关键字开头,因此它是一个ID) (无法搜索非关键
bc
abc
bcd
我想从lexer中得到什么:
B C
ID (starts with not-a-keyword so it's an ID)
<error> (cannot concat non-keywords)
bc
ID(以非关键字开头,因此它是一个ID)
(无法搜索非关键字)
但我得到的是3个ID,正如预期的那样
我一直在寻找使ID不贪婪,但退化为每个字符的单独标记。我想我以后可以把它们粘在一起,但感觉应该有更好的办法
有什么想法吗
谢谢这是解决方案的开始,使用lexer将文本分解为标记。这里的技巧是规则
ID
可以在每次调用中发出多个令牌。这是非标准lexer行为,因此有一些警告:
- 我相信这在ANTLR4中不会起作用
- 此代码假定所有令牌都排队到
中tokenQueue
- 规则
不阻止关键字重复,因此ID
生成令牌INT
INT
INT
。如果这不好,您将希望在lexer或parser端处理它,这取决于在语法中哪个更有意义INT
- 关键字越短,此解决方案就越脆弱。输入
是无效的internal
,因为它以关键字ID
开头,但后跟非关键字字符串int
- 语法产生了我没有删除的警告。如果您使用此代码,我建议尝试删除它们
以下是ANTLR 4的几乎所有语法解决方案(在目标语言中只需要一个小谓词):
该语法在ANTLRWorks 2 lexer解释器(即将推出!)中也适用于除单字符标识符之外的所有内容。由于lexer解释器无法计算
ID\u START
中的谓词,像a
这样的输入将(在解释器中)在隐藏的通道上生成一个带有WS
类型的的单个令牌。如果令牌队列为空,nextToken不应该只调用super.nextToken吗?这应该可以解决普通令牌消失的问题。@TroyDanielssuper.nextToken
管理对lexer规则的调用。如果它没有被调用,tokenQueue
将不会被填充,并且nextToken
将不会返回任何内容。@TroyDaniels我更新了语法(并添加了一个测试用例)以处理其他lexer规则:nextToken
仅从tokenQueue
中提取令牌,emit(int,int)
调用emit(Token)
,并重写emit(Token)
,将所有令牌推入队列。没有我想象的那么糟糕,但是如果我错过了什么,试试看。医生说模式只适用于lexer-only语法。如果我想将其用于组合语法,我是否需要制作单独的lexer和parser语法,并在调用应用程序中桥接它们?p、 为什么只有lexer?重写中的“更多”是做什么的?(link to doc?)more
命令表示与当前标记匹配的文本不是完整的标记,因此请尝试匹配另一个标记,并将这两个标记组合在一起以获得结果。
B C
ID (starts with not-a-keyword so it's an ID)
<error> (cannot concat non-keywords)
grammar MultiToken;
@lexer::members{
private java.util.LinkedList<Token> tokenQueue = new java.util.LinkedList<Token>();
@Override
public Token nextToken() {
Token t = super.nextToken();
if (tokenQueue.isEmpty()){
if (t.getType() == Token.EOF){
return t;
} else {
throw new IllegalStateException("All tokens must be queued!");
}
} else {
return tokenQueue.removeFirst();
}
}
public void emit(int ttype, int tokenIndex) {
//This is lifted from ANTLR's Lexer class,
//but modified to handle queueing and multiple tokens per rule.
Token t;
if (tokenIndex > 0){
CommonToken last = (CommonToken) tokenQueue.getLast();
t = new CommonToken(input, ttype, state.channel, last.getStopIndex() + 1, getCharIndex() - 1);
} else {
t = new CommonToken(input, ttype, state.channel, state.tokenStartCharIndex, getCharIndex() - 1);
}
t.setLine(state.tokenStartLine);
t.setText(state.text);
t.setCharPositionInLine(state.tokenStartCharPositionInLine);
emit(t);
}
@Override
public void emit(Token t){
super.emit(t);
tokenQueue.addLast(t);
}
}
doc : (INT | FLOAT | ID | NUMBER)* EOF;
fragment
INT : 'int';
fragment
FLOAT : 'float';
NUMBER : ('0'..'9')+;
ID
@init {
int index = 0;
boolean rawId = false;
boolean keyword = false;
}
: ({!rawId}? INT {emit(INT, index++); keyword = true;}
| {!rawId}? FLOAT {emit(FLOAT, index++); keyword = true;}
| {!keyword}? ('a'..'z')+ {emit(ID, index++); rawId = true;}
)+
;
WS : (' '|'\t'|'\f'|'\r'|'\n')+ {skip();};
intfloat a
int b
float c
intfloatintfloat d
[INT : int] [FLOAT : float] [ID : a]
[INT : int] [ID : b]
[FLOAT : float] [ID : c]
[INT : int] [FLOAT : float] [INT : int] [FLOAT : float] [ID : d]
aintfloat
bint
cfloat
dintfloatintfloat
[ID : aintfloat]
[ID : bint]
[ID : cfloat]
[ID : dintfloatintfloat]
internal
[INT : int] [ID : rnal]
line 1:3 rule ID failed predicate: {!keyword}?
floatation
[FLOAT : float] [ID : tion]
line 1:5 rule ID failed predicate: {!keyword}?
int x
float 3 float 4 float 5
5 a 6 b 7 int 8 d
[INT : int] [ID : x]
[FLOAT : float] [NUMBER : 3] [FLOAT : float] [NUMBER : 4] [FLOAT : float] [NUMBER : 5]
[NUMBER : 5] [ID : a] [NUMBER : 6] [ID : b] [NUMBER : 7] [INT : int] [NUMBER : 8] [ID : d]
lexer grammar PackedKeywords;
INT : 'int' -> pushMode(Keywords);
FLOAT : 'float' -> pushMode(Keywords);
fragment ID_CHAR : [a-z];
ID_START : ID_CHAR {Character.isLetter(_input.LA(1))}? -> more, pushMode(Identifier);
ID : ID_CHAR;
// these are the other tokens in the grammar
WS : [ \t]+ -> channel(HIDDEN);
Newline : '\r' '\n'? | '\n' -> channel(HIDDEN);
// The Keywords mode duplicates the default mode, except it replaces ID
// with InvalidKeyword. You can handle InvalidKeyword tokens in whatever way
// suits you best.
mode Keywords;
Keywords_INT : INT -> type(INT);
Keywords_FLOAT : FLOAT -> type(FLOAT);
InvalidKeyword : ID_CHAR;
// must include every token which can follow the Keywords mode
Keywords_WS : WS -> type(WS), channel(HIDDEN), popMode;
Keywords_Newline : Newline -> type(Newline), channel(HIDDEN), popMode;
// The Identifier mode is only entered if we know the current token is an
// identifier with >1 characters and which doesn't start with a keyword. This is
// essentially the default mode without keywords.
mode Identifier;
Identifier_ID : ID_CHAR+ -> type(ID);
// must include every token which can follow the Identifiers mode
Identifier_WS : WS -> type(WS), channel(HIDDEN), popMode;
Identifier_Newline : Newline -> type(Newline), channel(HIDDEN), popMode;