ANTLR:有关自定义语法示例的Lexing错误的帮助

时间:2014-07-13 15:21:56

标签: parsing antlr antlr4

哪种方法可以让我最大限度地报告lexing错误?

对于一个简单的例子,我想为以下文本编写语法

(为简单起见,忽略空格并且字符串常量不能包含\"

myvariable = 2
myvariable = "hello world"

Group myvariablegroup {
    myvariable = 3
    anothervariable = 4
}

使用词法分析器捕获错误

如何最大限度地提高词法分析器的错误报告潜力?

阅读此帖后:Where should I draw the line between lexer and parser?

我理解词法分析器应该尽可能多地匹配解析器语法,但是词法错误报告策略呢?

捕捉lexing错误的常规策略是什么?

我正在想象一个语法会有以下错误"错误"令牌:

GROUP_OPEN: 'Group' WS ID WS '{';
EMPTY_GROUP: 'Group' WS ID WS '{' WS '}';
EQUALS: '=';
STRING_CONSTANT: '"~["]+"';
GROUP_CLOSE: '}';
GROUP_ERROR: 'Group' .; // the . character is an invalid token
                        // you probably meant '{'
GROUP_ERROR2: .'roup' ; // Did you mean 'group'?
STRING_CONSTANT_ERROR: '"' .+; // Unterminated string constant
ID: [a-z][a-z0-9]+;
WS: [ \n\r\t]* -> skip();
SINGLE_TOKEN_ERRORS: .+?;

1 个答案:

答案 0 :(得分:2)

您的方法显然存在一些问题:

  • 您正在跳过WS(这很好),但您在其他规则中使用它。但是你在词法分析器中,这导致我们......

  • 你的小组被词法分子识别。我认为你不希望它们成为一个单一的标记。您的组属于解析器。

  • 您编写的语法将为以roup结尾的内容创建特定的令牌类型,因此croup可能永远不会与ID匹配。那不好。

  • STRING_CONSTANT_ERROR过于宽泛。它能够整个输入。请参阅下面的UNTERMINATED_STRING

  • 我不太确定SINGLE_TOKEN_ERRORS会发生什么...请参阅下面的替代方案。

现在,以下是我使用的错误令牌的一些示例,这非常适用于错误报告:

UNTERMINATED_STRING
    :   '"' ('\\' ["\\] | ~["\\\r\n])*
    ;

UNTERMINATED_COMMENT_INLINE
    :   '/*' ('*' ~'/' | ~'*')*? EOF -> channel(HIDDEN)
    ;

// This should be the LAST lexer rule in your grammar
UNKNOWN_CHAR
    :   .
    ;

请注意,这些未终止的标记表示单个原子值,它们不跨越逻辑结构。

此外,UNKNOWN_CHAR无论如何都是单个字符,如果将其定义为.+?,它总是会匹配一个字符,因为它会尽可能少地匹配字符,最小值是一个字符 当贪婪量词跟随它们时,非贪婪量词是有意义的。例如,在表达式.+? '#'中,.+?将被强制使用字符,直到遇到#符号为止。如果.+?表达式是单独的,则不必使用多个匹配的字符,因此将等同于.

我在词法分析器(.NET ANTLR)中使用以下代码:

partial class MyLexer
{
    public override IToken Emit()
    {
        CommonToken token;
        RecognitionException ex;

        switch (Type)
        {
            case UNTERMINATED_STRING:
                Type = STRING;
                token = (CommonToken)base.Emit();
                ex = new UnterminatedTokenException(this, (ICharStream)InputStream, token);
                ErrorListenerDispatch.SyntaxError(this, UNTERMINATED_STRING, Line, Column, "Unterminated string: " + GetTokenTextForDisplay(token), ex);
                return token;

            case UNTERMINATED_COMMENT_INLINE:
                Type = COMMENT_INLINE;
                token = (CommonToken)base.Emit();
                ex = new UnterminatedTokenException(this, (ICharStream)InputStream, token);
                ErrorListenerDispatch.SyntaxError(this, UNTERMINATED_COMMENT_INLINE, Line, Column, "Unterminated comment: " + GetTokenTextForDisplay(token), ex);
                return token;

            default:
                return base.Emit();
        }
    }

    // ...
 }

请注意,当词法分析器遇到错误的令牌类型时,它会将其显式更改为有效令牌,因此解析器实际上可以理解它。

现在,解析器的工作就是识别错误的结构。 ANTLR非常智能,可以在尝试使用无效输入重新同步自身时执行单令牌删除和单令牌插入。这也是我让UNKNOWN_CHAR滑到解析器的原因,因此可以通过错误消息将其丢弃。

只需捕获它生成的错误并对其进行更改,以便为用户提供更好的内容。

因此,只需将您的群组变成解析器规则。


一个例子:

考虑以下输入:

Group ,ygroup {

此处,,显然是拼写错误(用户按,而不是m)。

如果您使用UNKNOWN_CHAR: .;,您将获得以下令牌:

    类型Group
  • GROUP 类型,
  • UNKNOWN_CHAR 类型ygroup
  • ID 类型{
  • '{ '

解析器将能够找出需要删除的UNKNOWN_CHAR令牌并正确匹配一个组(定义为GROUP ID '{' ...)

ANTLR会在找到意外令牌的位置插入所谓的错误节点(在这种情况下,在GROUPID之间)。然后,为了解析,会忽略这些节点,但您可以使用访问者/侦听器检索它们以处理它们(例如,您可以使用访问者的VisitErrorNode方法)。