java - ANTLR4语法无法涵盖所有​​情况

标签 java antlr4 context-free-grammar

// 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 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 时会更清楚:词法分析器:

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

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

关于java - ANTLR4语法无法涵盖所有​​情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70015538/

相关文章:

java - 在JavaFX中动态添加元素到窗口

java - 如何操作一串 INSERT 数据并以正确的方式分隔列的值?

java - 如何将 A 类与 B 类关联起来,并从 A 类中的方法返回对 B 类的引用?

c# - Visual Studio 2019 中的 ANTLR4 运行时问题

java - Ant : beginner 's mismatched input expecting ID

c - 为什么 C 的 BNF 语法允许使用空的 init-declarators 序列进行声明?

java - 如何比较任何对象的两个实例并获取 "dirty"字段

ANTLR Verilog @(*) 匹配两个标记

context-free-grammar - EBNF和CFG有什么区别

set - 两种上下文无关语言的集合差异是上下文无关的吗?