ANTLR4语法不能涵盖所有情况

ANTRL4 grammar cannot cover all cases

// 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_0T_1 代币。没有办法解决这个问题。如果您想在任何规则中随意使用任何 character/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]
    ;

一些提示:

  • 不要混合使用词法分析器规则和解析器规则:从分析器规则开始,然后是词法分析器规则
  • 不要在解析器规则中使用文字标记(如 '.'

如果您这样做,那么何时将一个令牌选择在另一个令牌上:the lexer:

  1. 从上到下匹配规则,始终选择可能的最长匹配,并且
  2. 当 2 个(或更多)词法分析器规则匹配相同数量的字符时,首先定义的规则“获胜”

这就是为什么(在我的第二个语法中)输入“-1”将成为单个 Number 标记,而 而不是 一个 Minus 标记后跟一个 Number 标记(最长匹配“获胜”)。