antlr - 当有更通用的标记可用时,如何在词法分析期间将连接的标记分开

标签 antlr token lexer greedy

我正在使用的语言允许将某些标记粘在一起(例如“intfloat”),我正在寻找一种方法让词法分析器不会将它们变成 ID,以便它们在解析时单独可用.我能想到的最简单的语法是(省略 WS):

B: 'B';
C: 'C';
ID: ('a'..'z')+;
doc : (B | C | ID)* EOF;

反对:

bc
abc
bcd

我想从词法分析器中得到什么:

B C
ID (starts with not-a-keyword so it's an ID)
<error> (cannot concat non-keywords)

但正如预期的那样,我得到的是 3 个 ID。

我一直在考虑让 ID 不贪心,但会退化为每个角色的单独标记。我想我可以稍后将它们粘在一起,但感觉应该有更好的方法。

有什么想法吗?

谢谢

最佳答案

这是解决方案的开始,使用词法分析器将文本分解为标记。这里的技巧是规则 ID 可以在每次调用时发出多个标记。这是非标准的词法分析器行为,因此有一些注意事项:

  • 我相信这不会在 ANTLR4 中工作。

  • 此代码假设所有 token 都在 tokenQueue 中排队。

  • 规则 ID 不会阻止关键字重复,因此 intintint 生成标记 INT INT 整数。如果这很糟糕,您将希望在词法分析器或解析器端处理它,具体取决于哪个在您的语法中更有意义。

  • 关键字越短,这个解决方案就越脆弱。输入 internal 是无效的 ID,因为它以关键字 int 开头,但后跟一个非关键字字符串。

  • 语法会产生我尚未删除的警告。如果您使用此代码,我建议您尝试删除它们。

语法如下:

多 token .g

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

测试用例 1:混合关键字

输入

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] 

测试用例 2:包含关键字的 ID

输入

aintfloat
bint
cfloat
dintfloatintfloat

输出(代币)

[ID : aintfloat] 
[ID : bint] 
[ID : cfloat] 
[ID : dintfloatintfloat] 

测试用例 3:错误 ID #1

输入

internal

输出( token 和词法分析器错误)

[INT : int] [ID : rnal] 
line 1:3 rule ID failed predicate: {!keyword}?

测试用例 4:错误 ID #2

输入

floatation

输出( token 和词法分析器错误)

[FLOAT : float] [ID : tion] 
line 1:5 rule ID failed predicate: {!keyword}?

测试用例 5:非 ID 规则

输入

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] 

关于antlr - 当有更通用的标记可用时,如何在词法分析期间将连接的标记分开,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14227901/

相关文章:

ANTLR4:实现 C 语言(如包含文件处理)的最佳方法是什么?

ios - 无法使用 Java 使用 ionic 3 FCM token 将 iOS 设备 token 注册到 azure 通知中心

java - 从 GooglePlus 登录获取访问 token

parsing - 树结构 : propagate a subtree to child

c - 如何为 C 常量结构设置 ANTLR 语法?

java - 错误 : java. lang.NoClassDefFoundError:antlr/RecognitionException

swift - 使用 Locksmith 和 Swift 4 删除 token

javascript - 如何获得Jison代币?

autocomplete - python中自定义词法分析器上的QScintilla自动完成功能

scala - scala StdLexical 中的 Lexing 换行符?