ANTLR3在2个不同域值中的常见值

时间:2011-09-13 15:05:23

标签: c# antlr antlr3

我需要为以下搜索条件定义语言解析器:

CRITERIA_1=<values-set-#1> AND/OR CRITERIA_2=<values-set-#2>;

其中<values-set-#1>可以包含1-50和<values-set-#2>的值,可以来自以下集合(5,A,B,C) - 这里的情况并不重要。

我已经决定在C#(CSharp3)中使用带有输出的ANTLR3(v3.4),到目前为止它的工作非常流畅。问题是当我从两个数据集(即本例中为'5')提供值时,它无法解析字符串。例如,如果我提供以下字符串

CRITERIA_1=5;

它返回以下错误,其中值节点应为:

<unexpected: [@1,11:11='5',<27>,1:11], resync=5>

语法定义文件如下:

grammar ZeGrammar;

options {
    language=CSharp3;
    TokenLabelType=CommonToken;
    output=AST;
    ASTLabelType=CommonTree;
    k=3;
}

tokens 
{
    ROOT;
    CRITERIA_1;
    CRITERIA_2;
    OR = 'OR';
    AND = 'AND';
    EOF = ';';
    LPAREN = '(';
    RPAREN = ')';
}

public
start
  : expr EOF -> ^(ROOT expr)
  ;

expr
  : subexpr ((AND|OR)^ subexpr)*
  ;

subexpr
  :   grouppedsubexpr
    | 'CRITERIA_1=' rangeval1_expr -> ^(CRITERIA_1 rangeval1_expr)
    | 'CRITERIA_2=' rangeval2_expr -> ^(CRITERIA_2 rangeval2_expr)
  ;

grouppedsubexpr
  :  LPAREN! expr RPAREN!
  ;

rangeval1_expr
  :   rangeval1_subexpr
    | RANGE1_VALUES
  ;

rangeval1_subexpr
  : LPAREN! rangeval1_expr (OR^ rangeval1_expr)* RPAREN!
  ;

RANGE1_VALUES
  : (('0'..'4')? ('0'..'9') | '5''0')
  ;

rangeval2_expr
  :   rangeval2_subexpr
    | RANGE2_VALUES
  ;

rangeval2_subexpr
  : LPAREN! rangeval2_expr (OR^ rangeval2_expr)* RPAREN!
  ;

RANGE2_VALUES
  : '5' | ('a'|'A') | ('b'|'B') | ('c'|'C')
  ;

如果我从RANGE2_VALUES删除值'5',它就可以了。任何人都可以暗示我做错了吗?

1 个答案:

答案 0 :(得分:3)

您必须意识到词法分析器根据解析器尝试匹配的内容生成令牌。因此,在您的情况下,输入"5"将始终标记为RANGE1_VALUES,而不是RANGE2_VALUES,因为RANGE1_VALUESRANGE2_VALUES都可以匹配此输入但RANGE1_VALUES首先出现(因此RANGE1_VALUES优先于RANGE2_VALUES)。

可能的解决方法是删除RANGE1_VALUESRANGE2_VALUES规则,并使用以下词法规则替换它们:

D0_4
  :  '0'..'4'
  ;

D5
  :  '5'
  ;

D6_50
  :  '6'..'9'           // 6-9
  |  '1'..'4' '0'..'9'  // 10-49
  |  '50'               // 50
  ;

A_B_C
  :  ('a'|'A') 
  |  ('b'|'B') 
  |  ('c'|'C')
  ;

并介绍这些新的解析器规则:

range1_values
  :  D0_4
  |  D5
  |  D6_50
  ;

range2_values
  :  A_B_C
  |  D5
  ;

并分别使用RANGE1_VALUESRANGE2_VALUES更改解析器规则中的所有range1_valuesrange2_values次调用。

修改

您可以简单地匹配任何整数值,而不是尝试在 lexer-level 中解决此问题,如果值是正确的(或正确的范围),则使用< EM> semantic predicate

range1_values
  :  INT {Integer.valueOf($INT.text) <= 50}?
  ;

range2_values
  :  A_B_C
  |  INT {Integer.valueOf($INT.text) == 5}?
  ;

INT
  :  '0'..'9'+
  ;

A_B_C
  :  'a'..'c'
  |  'A'..'C'
  ;