首页 > 解决方案 > 在词法分析器规则中使用标记类型的积极前瞻

问题描述

我正在迁移我最初使用GrammarKit编写的语法(GrammarKit 使用 Flex 作为其词法分析器)。

我正在努力寻找在词法分析器规则中使用标记类型编写积极前瞻的最佳方法。

在我的第一个实验(非常)简化版本的问题下,使用基于流中字符的前瞻:

grammar PossitiveLookAheadCharacters;

@header {
    package lookahead;
}

@lexer::members {
    private boolean isChar(int charPosition, char testChar) {
        return _input.LA(charPosition) == testChar;
    }
}

r : CONS | DOT | LEFT_PAR | RIGHT_PAR;

LEFT_PAR : '(';
RIGHT_PAR : ')';
CONS : DOT {isChar(1, '(')}? {isChar(2, ')')}?;
DOT : '.';
WS : [ \t\r\n]+ -> skip ;

这可以正常工作,因为前瞻只是基于字符比较。如果我使用测试台对此进行测试,我将获得以下预期输出:

> grun lookahead.PossitiveLookAheadCharacters r -tokens
.()
[@0,0:0='.',<CONS>,1:0]
[@1,1:1='(',<'('>,1:1]
[@2,2:2=')',<')'>,1:2]
[@3,4:3='<EOF>',<EOF>,2:0]

但是,如果我想基于标记类型而不是流中的字符(正如我在 Flex 中可以轻松做到的那样)编写前瞻,我无法使这项工作正常工作。经过一些试验和错误,这是我到达的最接近的:

grammar PossitiveLookAheadTokenType;


@header {
    package lookahead;
}

@lexer::members {
    private boolean isToken(int tokenPosition, int tokenId) {
        int tokenAtPosition = new UnbufferedTokenStream(this).LA(tokenPosition);
        System.out.println("LA(" + tokenPosition + ") = " + tokenAtPosition);
        return tokenAtPosition == tokenId;
    }
}

r : CONS | DOT | LEFT_PAR | RIGHT_PAR;

LEFT_PAR : '(';
RIGHT_PAR : ')';
CONS : DOT {isToken(1, LEFT_PAR)}? {isToken(2, RIGHT_PAR)}?;
DOT : '.';
WS : [ \t\r\n]+ -> skip ;

如果我使用测试台对此进行测试,我会看到测试表达式被正确评估(简而言之,这个谓词是真的:)LA(1) == LEFT_PAR && LA(2) == RIGHT_PAR。但是第一个识别的令牌并不[@0,0:0='.',<CONS>,1:0]像预期的那样,而是[@0,2:2=')',<')'>,1:2]相反。在我的测试的完整输出下方:

? grun lookahead.PossitiveLookAheadTokenType r -tokens
.()
LA(1) = 1
LA(2) = 2
[@0,2:2=')',<')'>,1:2]
[@1,1:1='(',<'('>,1:1]
[@2,2:2=')',<')'>,1:2]
[@3,4:3='<EOF>',<EOF>,2:0]

我认为问题可能是输入流不再处于正确的位置,所以我尝试重置它的位置,如图所示的这个新版本的isToken方法:

    private boolean isToken(int tokenPosition, int tokenId) {
        int streamPosition = _input.index();
        int tokenAtPosition = new UnbufferedTokenStream(this).LA(tokenPosition);
        _input.seek(streamPosition);
        return tokenAtPosition == tokenId;
    }

但这并没有帮助。

所以我的 ANTLR4 问题是:在词法分析器规则中使用标记类型而不是普通字符编写正向前瞻的推荐方法是什么?

在 Flex 中这是完全可能的,它就像写这样的东西一样简单:

{MY_MATCH}/{TOKEN_TO_THE_RIGHT}

我喜欢 Flex 方法的地方在于它是完全声明性的,并且基于令牌类型,而不是字符。我想知道在 ANTLR4 中是否有类似的可能。

标签: javaantlr4grammar

解决方案


这不能像您想象的那样工作,因为您要做的是在正在进行的词法分析器规则中使用标记(这是词法分析器规则的结果)。这意味着词法分析器正在确定当前标记,因此无法同时确定另一个标记。

您可能想要的是解析器规则。在这种情况下,词法分析器已经完成了所有工作,您可以轻松地四处寻找其他标记。

cons: DOT {isToken(1, LEFT_PAR) && isToken(2, RIGHT_PAR)}?;
r : cons | DOT | LEFT_PAR | RIGHT_PAR;
@parser::members {
    private boolean isToken(int position, int tokenType) {
        return _input.LT(position).getType() == tokenType;
    }
}

推荐阅读