为什么这个ANTLR语法报告错误?



我设计了一个相当简单的语法来解析uri。它是在antlr4-maven-plugin的帮助下编译的。编译不会产生警告或错误。我写了一个简单的测试。

Uri.g4:

/**
* Uniform Resource Identifier (RFC 3986).
*
* @author Oliver Yasuna
* @see <a href="https://www.rfc-editor.org/rfc/rfc3986.html">RFC 3986</a>
* @since 1.0.0
*/
grammar Uri;
options {
tokenVocab = Common;
}
@header {
package com.oliveryasuna.http.antlr;
}
// Parser
//--------------------------------------------------
pctEncoded
: '%' HEXDIG HEXDIG
;
reserved
: genDelims | subDelims
;
genDelims
: ':' | '/' | '?' | '#' | '[' | ']' | '@'
;
subDelims
: '!' | '$' | '&' | ''' | '(' | ')' | '*' | '+' | ',' | ';' | '='
;
unreserved
: ALPHA | DIGIT | '-' | '.' | '_' | '~'
;
uri
: scheme ':' hierPart ('?' query)? ('#' fragment_)?
;
hierPart
: '//' authority pathAbEmpty
| pathAbsolute
| pathRootless
| pathEmpty
;
scheme
: ALPHA (ALPHA | DIGIT | '+' | '-' | '.')*
;
authority
: (userinfo '@')? host (':' port)?
;
userinfo
: (unreserved | pctEncoded | subDelims | ':')*
;
host
: ipLiteral
| ipv4Address
| regName
;
ipLiteral
: '[' (ipv6Address | ipvFuture) ']'
;
ipvFuture
: 'v' HEXDIG+ '.' (unreserved | subDelims | ':')+
;
ipv6Address
:                                                                            '::' (h16 ':') (h16 ':') (h16 ':') (h16 ':') (h16 ':') (h16 ':') ls32
|                                                                            '::'           (h16 ':') (h16 ':') (h16 ':') (h16 ':') (h16 ':') ls32
|                                                                    h16?  '::'                     (h16 ':') (h16 ':') (h16 ':') (h16 ':') ls32
|                                                        ((h16 ':')? h16)? '::'                               (h16 ':') (h16 ':') (h16 ':') ls32
|                                             ((h16 ':')? (h16 ':')? h16)? '::'                                         (h16 ':') (h16 ':') ls32
|                                  ((h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'                                                    h16 ':'  ls32
|                       ((h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'                                                             ls32
|            ((h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'                                                             h16
| ((h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'
;
ls32
: (h16 ':' h16)
| ipv4Address
;
h16
: HEXDIG HEXDIG? HEXDIG? HEXDIG?
;
ipv4Address
: decOctet '.' decOctet '.' decOctet '.' decOctet
;
decOctet
: DIGIT
| ('1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') DIGIT
| '1' DIGIT DIGIT
| '2' ('0' | '1' | '2' | '3' | '4') DIGIT
| '2' '5' ('0' | '1' | '2' | '3' | '4' | '5')
;
regName
: (unreserved | pctEncoded | subDelims)*
;
port
: DIGIT*
;
path
: pathAbEmpty
| pathAbsolute
| pathNoScheme
| pathRootless
| pathEmpty
;
pathAbEmpty
: ('/' segment)*
;
pathAbsolute
: '/' (segmentNz ('/' segment)?)?
;
pathNoScheme
: segmentNzNc ('/' segment)?
;
pathRootless
: segmentNz ('/' segment)?
;
pathEmpty
: // TODO: 0<pchar>.
;
segment
: pchar*
;
segmentNz
: pchar+
;
segmentNzNc
: (unreserved | pctEncoded | subDelims | '@')+
;
pchar
: unreserved | pctEncoded | subDelims | ':' | '@'
;
query
: (pchar | '/' | '?')*
;
fragment_
: (pchar | '/' | '?')*
;
uriReference
: uri
| relativeRef
;
relativeRef
: relativePart ('?' query)? ('#' fragment_)?
;
relativePart
: '//' authority pathAbEmpty
| pathAbEmpty
| pathNoScheme
| pathEmpty
;
absoluteUri
: scheme ':' hierPart ('?' query)?
;

Common.g4:

lexer grammar Common;
// ASCII
//--------------------------------------------------
BANG                  : '!'  ;
//DOUBLE_QUOTE          : '"'  ;
HASH                  : '#'  ;
DOLLAR                : '$'  ;
PERCENT               : '%'  ;
AND                   : '&'  ;
SINGLE_QUOTE          : ''' ;
LEFT_PARENTHESES      : '('  ;
RIGHT_PARENTHESES     : ')'  ;
STAR                  : '*'  ;
PLUS                  : '+'  ;
COMMA                 : ','  ;
MINUS                 : '-'  ;
DOT                   : '.'  ;
SLASH                 : '/'  ;
COLON                 : ':'  ;
SEMICOLON             : ';'  ;
LEFT_ANGLE_BRACKET    : '<'  ;
EQUAL                 : '='  ;
RIGHT_ANGLE_BRACKET   : '>'  ;
QUESTION              : '?'  ;
AT                    : '@'  ;
LEFT_SQUARE_BRACKET   : '['  ;
BACKSLASH             : '\' ;
RIGHT_SQUARE_BRACKET  : ']'  ;
CARROT                : '^'  ;
UNDERSCORE            : '_'  ;
BACKTICK              : '`'  ;
LEFT_CURLY_BRACKET    : '{'  ;
BAR                   : '|'  ;
RIGHT_CURLY_BRACKET   : '}'  ;
TILDE                 : '~'  ;
// Core
//--------------------------------------------------
// Taken from ABNF.
ALPHA   : [a-zA-Z]              ;
DIGIT   : [0-9]                 ;
HEXDIG  : [0-9a-fA-F]           ;
DQUOTE  : '"'                   ;
SP      : ' '                   ;
HTAB    : 't'                  ;
WSP     : SP | HTAB             ;
//LWSP    : (WSP | CRLF WSP)*     ;
VCHAR   : [u0021-u007F]       ;
CHAR    : [u0001-u007F]       ;
OCTET   : [u0000-u00FF]       ;
CTL     : [u0000-u001Fu007F] ;
CR      : 'r'                  ;
LF      : 'n'                  ;
CRLF    : CR LF                 ;
BIT     : '0' | '1'             ;
// Miscellaneous
//--------------------------------------------------
DOUBLE_SLASH  : '//' ;
DOUBLE_COLON  : '::' ;
LOWER_V       : 'v'  ;
ZERO          : '0'  ;
ONE           : '1'  ;
TWO           : '2'  ;
THREE         : '3'  ;
FOUR          : '4'  ;
FIVE          : '5'  ;
SIX           : '6'  ;
SEVEN         : '7'  ;
EIGHT         : '8'  ;
NINE          : '9'  ;

测试方法:

@Test
final void google() {
final String uri = "https://www.google.com/";
final UriLexer lexer = new UriLexer(new ANTLRInputStream(uri));
final UriParser parser = new UriParser(new CommonTokenStream(lexer));
parser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line, final int charPositionInLine, final String msg, final RecognitionException e) {
throw new IllegalStateException("[" + line + ":" + charPositionInLine + "] Symbol [" + offendingSymbol + "] produced error: " + msg + ".", e);
}
});
Assertions.assertDoesNotThrow(parser::uri);
}

当我输入https://www.google.com/时,我得到以下错误:

我完全不知道是什么导致了这些解析错误。有人知道吗?

输出:

line 1:0 token recognition error at: 'h'
line 1:1 token recognition error at: 't'
line 1:2 token recognition error at: 't'
line 1:3 token recognition error at: 'p'
line 1:4 token recognition error at: 's'
line 1:5 missing '6' at ':'

ANTLR的词法分析器在解析和标记化/词法分析之间有严格的分离。词法分析器也独立于解析器工作,并基于2个简单规则创建令牌:

  1. 尝试为单个词法分析器规则消耗尽可能多的字符
  2. 当两个或多个词法分析器规则匹配相同的字符时,让先定义的那个"win">

如果我们现在看看你的规则:

ALPHA   : [a-zA-Z]              ;
DIGIT   : [0-9]                 ;
HEXDIG  : [0-9a-fA-F]           ;

很明显,词法分析器规则HEXDIG永远不会匹配,因为ALPHADIGIT将匹配HEXDIG匹配的内容,并且在HEXDIG之前定义。切换顺序:

HEXDIG  : [0-9a-fA-F]           ;
ALPHA   : [a-zA-Z]              ;
DIGIT   : [0-9]                 ;

将无法工作,因为任何数字现在都不会变成DIGIT令牌,并且F现在也不会变成ALPHA

请注意,这只是一个单独的例子:在您的lexer语法中还有更多这样的情况。

一种解决方案是将部分责任转移给解析器而不是词法分析器:

A : [aA];
B : [bB];
C : [cC];
D : [dD];
E : [eE];
F : [fF];
G : [gG];
H : [hH];
I : [iI];
J : [jJ];
K : [kK];
L : [lL];
M : [mM];
N : [nN];
O : [oO];
P : [pP];
Q : [qQ];
R : [rR];
S : [sS];
T : [tT];
U : [uU];
V : [vV];
W : [wW];
X : [xX];
Y : [yY];
Z : [zZ];
D0 : '0';
D1 : '1';
D2 : '2';
D3 : '3';
D4 : '4';
D5 : '5';
D6 : '6';
D7 : '7';
D8 : '8';
D9 : '9';

,然后在解析器中:

alpha
: A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
;
digit
: D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9
;
hexdig
: A | B | C | D | E | F | digit
;

同时,删除所有从解析器中删除像'6'这样的文字标记,并使用适当的词法分析器规则(在本例中是D6)。每当解析器看到这样一个没有在词法分析器中定义的文字标记时,它就会"神奇地"为它创建一个新的令牌,导致神秘的错误/警告消息。最好从解析器中删除所有(我的意思是所有!)这样的文字标记。

除了Bart对语法的回答之外——都是正确的——这不是如何编写拆分语法!

你必须有"语法分析器"在UriParser。g4(重命名Uri。g4到UriParser.g4),以及"词法分析器语法UriLexer;"在UriLexer。g4(重命名普通。(4).

如果您尝试为原始的"split"生成解析器语法中,您将得到由Antlr工具生成的三个.token文件,它们的大小和内容都不同。这表明词法分析器和解析器之间可能没有标记类型的协调。这与"标记识别错误"没有任何关系。因为正如Bart所说,词法分析器的操作完全独立于解析器。但是,当您开始使用其他输入测试语法结果时,它将产生影响。

同样,您不应该在语法中包含@header { package ...; }。您需要使用-package选项。使用@header使语法完全无法移植到其他目标,并且如果在一个目录中有多个语法,有些有@header,有些没有,则会产生问题。

如果您修复了这些问题,代码将解析您的输入—并警告您的词法分析器规则不正确(参见Bart的回答)。

不清楚你为什么要拆分语法。

UriParser.g4:

/**
* Uniform Resource Identifier (RFC 3986).
*
* @author Oliver Yasuna
* @see <a href="https://www.rfc-editor.org/rfc/rfc3986.html">RFC 3986</a>
* @since 1.0.0
*/
parser grammar UriParser;
options {
tokenVocab = UriLexer;
}
// Parser
//--------------------------------------------------
pctEncoded
: '%' HEXDIG HEXDIG
;
reserved
: genDelims | subDelims
;
genDelims
: ':' | '/' | '?' | '#' | '[' | ']' | '@'
;
subDelims
: '!' | '$' | '&' | ''' | '(' | ')' | '*' | '+' | ',' | ';' | '='
;
unreserved
: ALPHA | DIGIT | '-' | '.' | '_' | '~'
;
uri
: scheme ':' hierPart ('?' query)? ('#' fragment_)?
;
hierPart
: '//' authority pathAbEmpty
| pathAbsolute
| pathRootless
| pathEmpty
;
scheme
: ALPHA (ALPHA | DIGIT | '+' | '-' | '.')*
;
authority
: (userinfo '@')? host (':' port)?
;
userinfo
: (unreserved | pctEncoded | subDelims | ':')*
;
host
: ipLiteral
| ipv4Address
| regName
;
ipLiteral
: '[' (ipv6Address | ipvFuture) ']'
;
ipvFuture
: 'v' HEXDIG+ '.' (unreserved | subDelims | ':')+
;
ipv6Address
:                                                                            '::' (h16 ':') (h16 ':') (h16 ':') (h16 ':') (h16 ':') (h16 ':') ls32
|                                                                            '::'           (h16 ':') (h16 ':') (h16 ':') (h16 ':') (h16 ':') ls32
|                                                                    h16?  '::'                     (h16 ':') (h16 ':') (h16 ':') (h16 ':') ls32
|                                                        ((h16 ':')? h16)? '::'                               (h16 ':') (h16 ':') (h16 ':') ls32
|                                             ((h16 ':')? (h16 ':')? h16)? '::'                                         (h16 ':') (h16 ':') ls32
|                                  ((h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'                                                    h16 ':'  ls32
|                       ((h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'                                                             ls32
|            ((h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'                                                             h16
| ((h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? (h16 ':')? h16)? '::'
;
ls32
: (h16 ':' h16)
| ipv4Address
;
h16
: HEXDIG HEXDIG? HEXDIG? HEXDIG?
;
ipv4Address
: decOctet '.' decOctet '.' decOctet '.' decOctet
;
decOctet
: DIGIT
| ('1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') DIGIT
| '1' DIGIT DIGIT
| '2' ('0' | '1' | '2' | '3' | '4') DIGIT
| '2' '5' ('0' | '1' | '2' | '3' | '4' | '5')
;
regName
: (unreserved | pctEncoded | subDelims)*
;
port
: DIGIT*
;
path
: pathAbEmpty
| pathAbsolute
| pathNoScheme
| pathRootless
| pathEmpty
;
pathAbEmpty
: ('/' segment)*
;
pathAbsolute
: '/' (segmentNz ('/' segment)?)?
;
pathNoScheme
: segmentNzNc ('/' segment)?
;
pathRootless
: segmentNz ('/' segment)?
;
pathEmpty
: // TODO: 0<pchar>.
;
segment
: pchar*
;
segmentNz
: pchar+
;
segmentNzNc
: (unreserved | pctEncoded | subDelims | '@')+
;
pchar
: unreserved | pctEncoded | subDelims | ':' | '@'
;
query
: (pchar | '/' | '?')*
;
fragment_
: (pchar | '/' | '?')*
;
uriReference
: uri
| relativeRef
;
relativeRef
: relativePart ('?' query)? ('#' fragment_)?
;
relativePart
: '//' authority pathAbEmpty
| pathAbEmpty
| pathNoScheme
| pathEmpty
;
absoluteUri
: scheme ':' hierPart ('?' query)?
;

UriLexer.g4:

lexer grammar UriLexer;
// ASCII
//--------------------------------------------------
BANG                  : '!'  ;
//DOUBLE_QUOTE          : '"'  ;
HASH                  : '#'  ;
DOLLAR                : '$'  ;
PERCENT               : '%'  ;
AND                   : '&'  ;
SINGLE_QUOTE          : ''' ;
LEFT_PARENTHESES      : '('  ;
RIGHT_PARENTHESES     : ')'  ;
STAR                  : '*'  ;
PLUS                  : '+'  ;
COMMA                 : ','  ;
MINUS                 : '-'  ;
DOT                   : '.'  ;
SLASH                 : '/'  ;
COLON                 : ':'  ;
SEMICOLON             : ';'  ;
LEFT_ANGLE_BRACKET    : '<'  ;
EQUAL                 : '='  ;
RIGHT_ANGLE_BRACKET   : '>'  ;
QUESTION              : '?'  ;
AT                    : '@'  ;
LEFT_SQUARE_BRACKET   : '['  ;
BACKSLASH             : '\' ;
RIGHT_SQUARE_BRACKET  : ']'  ;
CARROT                : '^'  ;
UNDERSCORE            : '_'  ;
BACKTICK              : '`'  ;
LEFT_CURLY_BRACKET    : '{'  ;
BAR                   : '|'  ;
RIGHT_CURLY_BRACKET   : '}'  ;
TILDE                 : '~'  ;
// Core
//--------------------------------------------------
// Taken from ABNF.
ALPHA   : [a-zA-Z]              ;
DIGIT   : [0-9]                 ;
HEXDIG  : [0-9a-fA-F]           ;
DQUOTE  : '"'                   ;
SP      : ' '                   ;
HTAB    : 't'                  ;
WSP     : SP | HTAB             ;
//LWSP    : (WSP | CRLF WSP)*     ;
VCHAR   : [u0021-u007F]       ;
CHAR    : [u0001-u007F]       ;
OCTET   : [u0000-u00FF]       ;
CTL     : [u0000-u001Fu007F] ;
CR      : 'r'                  ;
LF      : 'n'                  ;
CRLF    : CR LF                 ;
BIT     : '0' | '1'             ;
// Miscellaneous
//--------------------------------------------------
DOUBLE_SLASH  : '//' ;
DOUBLE_COLON  : '::' ;
LOWER_V       : 'v'  ;
ZERO          : '0'  ;
ONE           : '1'  ;
TWO           : '2'  ;
THREE         : '3'  ;
FOUR          : '4'  ;
FIVE          : '5'  ;
SIX           : '6'  ;
SEVEN         : '7'  ;
EIGHT         : '8'  ;
NINE          : '9'  ;

相关内容

  • 没有找到相关文章

最新更新