ANTLR 4 无法解析一个类似于另一个的规则

ANTLR 4 can't parse one rule similar to another

我正在为 C# 编写一个基于 ANTLR 4 的规则引擎(我使用的是 Sam Harwell alternative),但我无法正确解析关于两个非常相似的规则的输入的语法。

可能还有其他几个错误,但我暂时坚持这个。另外,我是 ANTLR 的新手(除了我在学校的旧时光 ^^),所以不要犹豫给我一些 tips/explanations :)。

我的目标是解释二进制和数字赋值,例如:

在幕后,我希望它符合 bin_assign 规则。

在幕后,我希望它符合 num_assign 规则。

但是我的解析器总是匹配 num_assign 并且匹配运算符失败(因为它期望 NUM_OP 或 EOF)。

您会看到这两条规则以相同的方式以 FACT 令牌开始。但我的直觉告诉我这不是问题的原因(正如我所说,我是 ANTLR 的新手,这只是我的直觉)。我写这两条规则是为了避免 Fact := 1 + true 成为正确的陈述。

这是我的语法:

  1. 常用词法分析器规则:

    lexer grammar Common;
    
    /*
     * Lexer Rules
     */
    FACT: [a-zA-Z](([a-zA-Z0-9] | '.' | '_')*[a-zA-Z0-9])?;
    ASSIGN: ':=';
    LITERAL: '\'' .*? '\'' | '"' .*? '"';
    
    WS : [ \t\r\n]+ -> skip;
    
  2. 数值语法:

    grammar NumericGrammar;
    import Common;
    
    /*
     * Parser Rules
     */
    num_stat: num_stat NUM_OP num_stat          # ArithmeticStat
        | '(' num_stat ')'                      # ArithmeticBrakedStat
        | DIGIT                                 # DigitStat
        | FACT                                  # ArithmeticFactStat
        ;
    
    num_assign: 
        FACT NUM_ASSIGN num_stat                # ArithmeticAssign
        ;
    
    /*
     * Lexer Rules
     */
    DIGIT: INT | FLOAT;
    INT: ([1-9][0-9]*)?[0-9]; 
    FLOAT: INT(','|'.')[0-9]+;
    
    NUM_OP: MULT | DIV | ADD | SUB;
    MULT: '*';
    DIV: '/';
    ADD: '+';
    SUB: '-';
    
    NUM_ASSIGN: MULT_ASSIGN | DIV_ASSIGN | ADD_ASSIGN | SUB_ASSIGN | ASSIGN;
    MULT_ASSIGN: '*=';
    DIV_ASSIGN: '\=';
    ADD_ASSIGN: '+=';
    SUB_ASSIGN: '-=';
    
  3. 二进制语法:

    grammar BinaryGrammar;
    import NumericGrammar;
    
    /*
     * Parser Rules
     */
    bin_stat: bin_stat BIN_OP bin_stat          # BinaryStat
        | '(' bin_stat ')'                      # BinaryBrakedStat
        | NOT bin_stat                          # NegateStat
        | num_stat COMPARE_OP num_stat          # CompareStat
        | FACT EQUALITY_OP (LITERAL | DIGIT)    # LiteralComparisonStat
        | num_stat IN SET                       # IntervalComparisonStat
        | BOOL                                  # BoolStat
        | FACT                                  # BinaryFactStat
        ;
    
    bin_assign: 
        FACT ASSIGN bin_stat                    # BinAssign
        ;
    
    /*
     * Lexer Rules
     */
    BOOL: TRUE | FALSE;
    TRUE: '1' | 'true' | 'True' | 'YES' | 'Yes' | 'yes';
    FALSE: '0' | 'false' | 'False' | 'NO' | 'No' | 'no';
    
    BIN_OP: AND | OR | EQUALITY_OP;
    COMPARE_OP: EQUALITY_OP | GT | GTE | LT | LTE;
    AND: 'AND' | 'And' | 'and' | '&' | '&&';
    OR: 'OR' | 'Or' | 'or' | '|' | '||';
    EQUALITY_OP: EQUALITY | UNEQUALITY;
    EQUALITY: '=' | '==';
    UNEQUALITY: '<>' | '!=' | '=/=' | '=\=';
    GT: '>';
    GTE: '>=';
    LT: '<';
    LTE: '<=';
    NOT: '!' | 'NOT' | 'not';
    IN: 'IN' | 'in';
    SET: ('[' | ']') (INT | FLOAT) ';' (INT | FLOAT) ('[' | ']');
    
  4. 最终语法:

    grammar Test;
    import BinaryGrammar;
    
    /*
     * Parser Rules
     */
    r: (IF bin_stat THEN)? assign EOF;
    assign: bin_assign | num_assign;
    
    /*
     * Lexer Rules
     */
    IF: 'if' | 'IF' | 'If';
    THEN: 'then' | 'THEN' | 'Then';
    

如果需要,这里是执行的屏幕截图:

FACT 词法分析器规则将不匹配 Fact1

FACT: [a-zA-Z](([a-zA-Z0-9] | '.' | '_')*[a-zA-Z0-9])?;

中间的 kleene star 名义上是一个贪心算子。因此,将捕获所有终端号码,不留下任何内容来完成规则。

让它成为非贪婪的:

FACT: [a-zA-Z](([a-zA-Z0-9] | '.' | '_')*?[a-zA-Z0-9])?;

使用不一致的标记命名会使您更难分析规则(parser/lexer 不会介意)。因此,这只是关于更容易(手动)阅读规则的建议——当我阅读我的语法时,当我看到两条带有不同标记的规则都在大写(终端)中时,我确信它们在这一点上是不同的。在您的情况下,您必须检查该令牌是否真的是终端,如果不是,它如何展开。