Общие значения ANTLR3 в 2 разных значениях домена - PullRequest
2 голосов
/ 13 сентября 2011

Мне нужно определить синтаксический анализатор языка для следующих критериев поиска:

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

Где <values-set-#1> может иметь значения от 1 до 50, а <values-set-#2> может быть из следующего набора (5, A, B, C) - случай здесь не важен.

Я решил использовать ANTLR3 (v3.4) с выводом в C # (CSharp3), и до сих пор он работал довольно гладко. Проблема заключается в том, что он не может проанализировать строку, когда я предоставляю значения из обоих наборов данных (т. Е. В данном случае '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')
  ;

И если я уберу значение '5' из RANGE2_VALUES, оно будет работать нормально. Может кто-нибудь намекнуть на то, что я делаю неправильно?

1 Ответ

3 голосов
/ 13 сентября 2011

Вы должны понимать, что лексер не производит токены на основе того, что анализатор пытается сопоставить. Таким образом, в вашем случае вход "5" всегда будет маркироваться как RANGE1_VALUES, а не как RANGE2_VALUES, потому что и RANGE1_VALUES, и RANGE2_VALUES могут соответствовать этому входу, но RANGE1_VALUES стоит первым (поэтому RANGE1_VALUES имеет приоритет над RANGE2_VALUES).

Возможное исправление - удалить правила RANGE1_VALUES и RANGE2_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_VALUES и RANGE2_VALUES вызовы в правилах вашего синтаксического анализатора с range1_values и range2_values соответственно.

EDIT

Вместо того, чтобы пытаться решить эту проблему на уровне лексера , вы можете просто сопоставить любое целочисленное значение и проверить внутри правила синтаксического анализатора, является ли значение правильным (или правильным диапазоном), используя семантический предикат :

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'
  ;
...