您的方法显然存在一些问题:
你正在跳过WS
(这很好),但是您仍在其他规则中使用它。但你在词法分析器中,这导致我们......
词法分析器正在识别您的组。我认为你不希望它们成为一个单一的令牌。您的组属于解析器。
正如所写的,您的语法将为以结尾的事物创建特定的标记类型roup
, so croup
例如可能永远不会匹配ID
。这不好。
STRING_CONSTANT_ERROR
太宽泛了。它能够全局化整个输入。看我的UNTERMINATED_STRING
below.
我不太确定会发生什么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
: .
;
请注意,这些未终止的标记代表单个原子值,它们不跨越逻辑结构。
Also, UNKNOWN_CHAR
无论如何,如果您将其定义为,则将是单个字符.+?
无论如何,它总是会精确匹配一个字符,因为它将尝试匹配尽可能少的字符,而最少是一个字符。
当有东西跟随时,非贪婪量词才有意义。例如在表达式中.+? '#'
, the .+?
将被迫消耗字符,直到遇到#
符号。如果.+?
表达式是单独的,它不必消耗多个字符来匹配,因此相当于.
.
我在词法分析器(.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 将在发现意外标记的位置插入所谓的错误节点(在本例中是在GROUP
and ID
)。然后,为了解析的目的,这些节点将被忽略,但您可以使用访问者/侦听器检索它们来处理它们(您可以使用访问者的VisitErrorNode
方法为例)。