// Define a grammar called Hello
grammar Hello;
r : element* ;
element
: number Whitespace
| string Whitespace
;
string
: '(' Charactor* ')'
;
Charactor
: [a-zA-Z] |'!' | '"'| '#' | '$' | '%' | '&' | '\'' | '\\(' | '\\)' | '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '[' | '\\' | ']' | '^' | '`' | '{' | '|' | '}' | '~' | '_'
;
number
: '-'? integer ('.' integer)?
;
integer
: digit+
;
digit
: D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9
;
Whitespace
: ' ' | '\n'
;
D1 : '1';
D2 : '2';
D3 : '3';
D4 : '4';
D5 : '5';
D6 : '6';
D7 : '7';
D8 : '8';
D9 : '9';
D0 : '0';
//WS : [ \t\r\n]+ -> skip ; skip spaces, tabs, newlines
我使用上面的.g4语法文件来解析以下字符序列。
➜ Hello antlr4 Hello.g4 ➜ Hello javac Hello*.java ➜ Hello grun Hello r -tree 1.1 -1.2 333 -222 (((*&^%$#@!~
<>,?"'\|[[]]{}~) (r (element (number (integer (digit 1)) . (integer (digit 1))) ) (element (number - (integer (digit 1)) . (integer (digit 2))) ) (element (number (integer (digit 3) (digit 3) (digit 3))) ) (element (number - (integer (digit 2) (digit 2) (digit 2))) ) (element (string ( \( \( * & ^ % $ # @ ! ~
< > , ? " ' \ | [ [ ] ] { } ~ )) ))
这个案例效果很好。
但是当我输入字符串“1.1 -1.2 333 -222 (-.#$?)”时,它无法正确解析。
➜ Hello grun Hello r -tree 1.1 -1.2 333 -222 (-.#$?) line 1:19 mismatched input '-' expecting {')', Charactor} line 1:20 mismatched input '.' expecting {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} line 1:21 mismatched input '#' expecting {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} (r (element (number (integer (digit 1)) . (integer (digit 1))) ) (element (number - (integer (digit 1)) . (integer (digit 2))) ) (element (number (integer (digit 3) (digit 3) (digit 3))) ) (element (number - (integer (digit 2) (digit 2) (digit 2))) ) (element (string () ) (element (number - integer . (integer # $ ? ))) )) ➜ Hello
这个特殊的字符序列是一个包含数字或字符串的数组。 数字可以用以下方式表示:1.1、-1.2、-222、222 字符串以“(”开头,以“)”结尾,如果其中出现“(”或“)”,则可以进行转义。注意,该字符串可以包含字符“-”或“.”。所以当'-'或'.'时数字和字符串同时出现,看来Antlr无法正确解析。
有谁知道如何解决这个问题吗?谢谢!
最佳答案
ANTLR 在解析器规则和词法分析器规则之间有严格的分离。每当您在解析器规则中使用文字标记(例如 number
规则中的 '-'
和 '.'
)时,ANTLR 都会在为你准备的场景。所以语法:
number
: '-'? integer ('.' integer)?
;
Charactor
: [a-zA-Z] | ... | '-' | '.' | ...
;
实际上看起来像这样:
number
: T_0? integer (T_1 integer)?
;
T_0 : '-';
T_1 : '.';
Charactor
: [a-zA-Z] | ... | '-' | '.' | ...
;
由于严格分离,ANTLR 独立于解析器创建标记。这意味着对于字符 -
和 .
,它们永远不会成为 Charactor
标记。它们将始终成为 T_0
和 T_1
token 。这是没有办法解决的。如果您想在任何规则中随意使用任何字符/标记,请查找“无扫描器解析”或“PEG 解析器”,而不是使用 ANTLR。
要使当前语法在尽可能少的更改下工作,请执行以下操作:
grammar Hello;
r : element* ;
element
: number Whitespace
| string Whitespace
;
string
: '(' (Charactor | Minus | Dot)* ')'
;
Minus : '-';
Dot : '.';
Charactor
: [a-zA-Z] |'!' | '"'| '#' | '$' | '%' | '&' | '\'' | '\\(' | '\\)' | '*' | '+' | ',' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '[' | '\\' | ']' | '^' | '`' | '{' | '|' | '}' | '~' | '_'
;
number
: Minus? integer (Dot integer)?
;
integer
: digit+
;
digit
: D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9
;
Whitespace
: ' ' | '\n'
;
D1 : '1';
D2 : '2';
D3 : '3';
D4 : '4';
D5 : '5';
D6 : '6';
D7 : '7';
D8 : '8';
D9 : '9';
D0 : '0';
但理想情况下,您应该更多地使用 ANTLR,因为它本来就是这样的(明确定义标记而不是在解析器规则中构造标记):
grammar Hello;
r : element* EOF;
element
: Number Whitespace
| string Whitespace
;
string
: '(' character* ')'
;
character
: Character
| Minus
| Dot
;
Minus
: '-'
;
Dot
: '.'
;
Character
: [a-zA-Z] | '!' | '"'| '#' | '$' | '%' | '&' | '\'' | '\\(' | '\\)' | '*' | '+' | ',' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '[' | '\\' | ']' | '^' | '`' | '{' | '|' | '}' | '~' | '_'
;
Number
: '-'? Integer ('.' Integer)?
;
Integer
: Digit+
;
Whitespace
: [ \t\r\n]
;
fragment Digit
: [0-9]
;
一些提示:
- 不要混合词法分析器和解析器规则:从解析器规则开始,然后是词法分析器规则
- 不要在解析器规则中使用文字标记(例如
'.'
)
如果你这样做,当选择一个 token 而不是另一个 token 时会更清楚:词法分析器:
- 从上到下匹配规则,始终选择最长的匹配,并且
- 当 2 个(或更多)词法分析器规则匹配相同数量的字符时,第一个定义的规则“获胜”
这就是为什么(在我的第二个语法中)输入“-1”将成为单个Number
标记,而不是Minus
token 后跟一个 Number
token (最长匹配“获胜”)。
关于java - ANTLR4语法无法涵盖所有情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70015538/