【问题标题】:ANTRL4 grammar cannot cover all casesANTLR4 语法不能涵盖所有情况
【发布时间】:2021-12-29 02:07:32
【问题描述】:
// 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语法文件来解析下面的字符序列。

➜ 你好 antlr4 Hello.g4 ➜ 你好 javac Hello*.java ➜ 你好 grun 你好 r 树 1.1 -1.2 333 -222 (((*&^%$#@!~&lt;&gt;,?"'\|[[]]{}~) (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 ( \( \( * &amp; ^ % $ # @ ! ~ , ? " ' \ | [ [ ] ] { } ~ )) ))

这个案例效果很好。

但是当我输入字符串“1.1 -1.2 333 -222 (-.#$?)”时,它没有正确解析。

➜ Hello grun Hello r -tree 1.1 -1.2 333 -222 (-.#$?) 第 1:19 行不匹配的输入 '-' 需要 {')',Charactor} 行 1:20 不匹配的输入 '.'期待 {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} line 1:21 mismatched input '#' 期待 {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} (r (元素(数字(整数(数字 1))。(整数(数字 1)))) (元素(数字 - (整数(数字 1))。(整数(数字 2)))) (元素 (数字 (整数 (数字 3) (数字 3) (数字 3)))) (元素 (数字 - (整数(数字 2)(数字 2)(数字 2))))(元素(字符串 () ) (element (number - integer . (integer # $ ? ))) )) ➜ 你好

这个特殊的字符序列是一个包含数字或字符串的数组。 该数字可以通过以下方式获得,例如 1.1、-1.2、-222、222 字符串以'('开头,以')'结尾,如果其中出现'('或')',则可以转义。请注意,字符串可以包含字符“-”或“.”。所以当'-'或'.'同时出现在数字和字符串中,好像 Antlr 无法正确解析。

有人知道如何解决这个问题吗?谢谢!

【问题讨论】:

    标签: java antlr4 context-free-grammar


    【解决方案1】:

    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 令牌。没有办法解决这个问题。如果您想在任何规则中随意使用任何字符/标记,请寻找“无扫描器解析”或“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]
        ;
    

    一些提示:

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

    如果你这样做了,当一个标记被选择而不是另一个标记时会更清楚:词法分析器:

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

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

    【讨论】:

    • 谢谢,Bart 这是一个非常详细的解决方案。现在可以了。但我也有更特殊的需要。如果词法分析器字符还包括 0-9 位数字,如以下序列。 (4/) 6 (5) -1 (.-124() -1.22 上面的语法不起作用。有什么提示吗?谢谢!
    • 字符序列为(4/) 6 (5) -1 (.-124\() -1.22,不起作用。这次怎么解决?
    • character解析器规则中包含任何词法分析器规则(令牌)作为替代:character : Character | Minus | Dot | Integer | ... ;(其中...是其他令牌)
    • ➜ Hello grun Hello r -gui 1.1 -2.1 -666 (12,.-) 1.1001 line 1:15 extraneous input '12' expecting {')', '-', '.', Character, Integer},我按照你说的做了,但是弹出了上面的信息。
    • 12 被标记为 Number,而不是 Integer(它们都匹配 12Number 我们首先定义)。还要在character 规则中包含Number(因此我在之前的评论中说“(其中...是其他标记)”)
    猜你喜欢
    • 1970-01-01
    • 2016-09-05
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 2018-11-23
    • 2021-07-14
    • 2018-08-29
    相关资源
    最近更新 更多