跟踪ANTLR解析器忽略文本的问题

时间:2011-09-06 19:15:41

标签: java antlr

我正在开发一个解析器,它将包含一个人全名的字符串拆分为组件(first,middle,last,title,suffix,...)。当我在ANTLRWorks中尝试一个基本的例子“J. A. Doe”时,它匹配fname和lname规则,但是忽略了“A.”。如何解决此类问题?

enter image description here

简化语法:

grammar PersonNamesMinimal;

fullname returns [Name name]
 : (directory_style[name] | standard[name] | proper_initials[name]);

fullname_only returns [Name name]: f=fullname EOF;

standard[Name name]
 : fname[name] ' ' (mname[name] ' ')* lname[name] ;

proper_initials[Name name]: a=INITIAL ' '? b=INITIAL lname[name];

sep: ',' | ', ' | ' ';
dir_sep: ',' | ', ' | ' , ';

directory_style[Name name]
 : lname[name] dir_sep fname[name] (' ' mname[name])*;

fname[Name name] : (f=NAME | f=INITIAL);

mname[Name name] : (m=NAME | m=INITIAL); // Weird bug when mname is "F."

lname[Name name] : a=single_lname (b='-' c=single_lname)?;
single_lname returns [String s]
 : (p=LNAME_PREFIX r=NAME)
 | r=NAME;
LNAME_PREFIX : (V O N | V A N ' ' D E R | V A N ' ' D E N | V A N | D E ' ' L A | D E | B I N) ' ';

O_APOS: ('O'|'o') '\'';
NAME: (O_APOS? LETTER LETTER+) | LETTER;
INITIAL: LETTER '.';

AND: ( ' '+ A N D ' '+ ) | (' '* '&' ' '*);
fragment WORD : LETTER+;
COMMA : ',';
//WS : ( '\t' | ' ' );

fragment DIGIT : '0' .. '9';
fragment LETTER : 'A' .. 'Z' | 'a' .. 'z';

//{{{ fragments for each letter of alphabet
fragment A : 'A' | 'a';
fragment B : 'B' | 'b';
fragment C : 'C' | 'c';
fragment D : 'D' | 'd';
fragment E : 'E' | 'e';
fragment F : 'F' | 'f';
fragment G : 'G' | 'g';
fragment H : 'H' | 'h';
fragment I : 'I' | 'i';
fragment J : 'J' | 'j';
fragment K : 'K' | 'k';
fragment L : 'L' | 'l';
fragment M : 'M' | 'm';
fragment N : 'N' | 'n';
fragment O : 'O' | 'o';
fragment P : 'P' | 'p';
fragment Q : 'Q' | 'q';
fragment R : 'R' | 'r';
fragment S : 'S' | 's';
fragment T : 'T' | 't';
fragment U : 'U' | 'u';
fragment V : 'V' | 'v';
fragment W : 'W' | 'w';
fragment X : 'X' | 'x';
fragment Y : 'Y' | 'y';
fragment Z : 'Z' | 'z';
//}}}

在创建此精简版本时,我发现删除directory_style规则或LNAME_PREFIX规则会导致mname规则按预期工作,但我不确定为什么

1 个答案:

答案 0 :(得分:1)

问题不在于你的解析器规则,至少不是你现在面临的问题...... :)。词法分析器出了问题。

输入A.中的初始"J. A. Doe"未被标记为INITIAL,但词法分析器尝试从中创建AND令牌(请注意前面的空格) 'A'!)。您可以通过解析输入"J. X. Doe"来看到这一点,使用更加修剪的语法:

grammar PersonNamesMinimal;

// just parse zero or more tokens (no matter what) and print their type and text
parse
  :  (t=. {System.out.printf("\%-25s \%s\n", tokenNames[$t.type], $t.text);})* EOF
  ;


LNAME_PREFIX : (V O N | V A N ' ' D E R | V A N ' ' D E N | V A N | D E ' ' L A | D E | B I N) ' ';
O_APOS       : ('O'|'o') '\'';
NAME         : (O_APOS? LETTER LETTER+) | LETTER;
INITIAL      : LETTER '.';
AND          : ( ' '+ A N D ' '+ ) | (' '* '&' ' '*);
COMMA        : ',';

fragment LETTER : 'A' .. 'Z' | 'a' .. 'z';

fragment A : 'A' | 'a';
fragment B : 'B' | 'b';
fragment C : 'C' | 'c';
fragment D : 'D' | 'd';
fragment E : 'E' | 'e';
fragment F : 'F' | 'f';
fragment G : 'G' | 'g';
fragment H : 'H' | 'h';
fragment I : 'I' | 'i';
fragment J : 'J' | 'j';
fragment K : 'K' | 'k';
fragment L : 'L' | 'l';
fragment M : 'M' | 'm';
fragment N : 'N' | 'n';
fragment O : 'O' | 'o';
fragment P : 'P' | 'p';
fragment Q : 'Q' | 'q';
fragment R : 'R' | 'r';
fragment S : 'S' | 's';
fragment T : 'T' | 't';
fragment U : 'U' | 'u';
fragment V : 'V' | 'v';
fragment W : 'W' | 'w';
fragment X : 'X' | 'x';
fragment Y : 'Y' | 'y';
fragment Z : 'Z' | 'z';

SPACE : ' ';

与班级:

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    PersonNamesMinimalLexer lexer = new PersonNamesMinimalLexer(new ANTLRStringStream(args[0]));
    PersonNamesMinimalParser parser = new PersonNamesMinimalParser(new CommonTokenStream(lexer));
    parser.parse();
  }
}

然后生成词法分析器&解析器,将其全部编译,然后使用"J. X. Doe"作为命令行参数运行Main:

java -cp antlr-3.3.jar org.antlr.Tool PersonNamesMinimal.g 
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main "J. X. Doe"

在您的控制台上打印以下内容:

INITIAL                   J.
SPACE                      
INITIAL                   X.
SPACE                      
NAME                      Doe

(。即预期的输出)

但现在提供"J. A. Doe"

java -cp .:antlr-3.3.jar Main "J. A. Doe"

并生成以下输出:

line 1:4 mismatched character '.' expecting set null
INITIAL                   J.
SPACE                      
NAME                      Doe

如果您现在在词法分析器中评论规则AND

...
INITIAL      : LETTER '.';
//AND          : ( ' '+ A N D ' '+ ) | (' '* '&' ' '*);
COMMA        : ',';
...

再次测试"J. A. Doe"

java -cp antlr-3.3.jar org.antlr.Tool PersonNamesMinimal.g 
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main "J. A. Doe"
你会看到这个:

INITIAL                   J.
SPACE                      
INITIAL                   A.
SPACE                      
NAME                      Doe

(即一切顺利!)


如何解决?如果我是你,我首先通过删除所有文字空格并将它们放在HIDDEN频道上来使词法分析器更清晰,这样你就不必在其他解析器和词法分析器规则中考虑它们: / p>

SPACE
  :  (' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;}
  ; 

这至少可以解决您面临的当前问题。但可能会有更多......


修改

  

bemace写道:

     

我如何修改AND规则,以便它只匹配整个单词而不是“stand”之类的东西?

你不需要为此做任何特别的事情。只要您的规则与"stand""andre"匹配,那么它们就不会被标记为AND。在您的情况下,NAME会匹配它们,并且由于NAME匹配的输入AND"stand"的字符数超过"andre",因此它们将成为NAME {1}}令牌。

这是ANTLR的词法分析器的工作方式:选择最长匹配,如果两个规则匹配相同数量的字符,则首先定义的规则优先于另一个规则。

小测试:

grammar PersonNamesMinimal;

parse
  :  (t=. {System.out.printf("\%-25s \%s\n", tokenNames[$t.type], $t.text);})* EOF
  ;

AND
  :  A N D
  |  '&'
  ;

LNAME_PREFIX 
  :  V O N 
  |  V A N SPACES D E R 
  |  V A N SPACES D E N 
  |  V A N 
  |  D E SPACES L A 
  |  D E 
  |  B I N
  ;

INITIAL
  :  LETTER '.'
  ;

NAME
  :  (O '\'')? LETTER+ 
  ;

COMMA
  :  ','
  ;

SPACE 
  :  (' ' | '\t') {$channel=HIDDEN;}
  ;

fragment LETTER : 'A' .. 'Z' | 'a' .. 'z';
fragment SPACES : (' ' | '\t')+;
fragment A : 'A' | 'a';
fragment B : 'B' | 'b';
fragment C : 'C' | 'c';
fragment D : 'D' | 'd';
fragment E : 'E' | 'e';
fragment F : 'F' | 'f';
fragment G : 'G' | 'g';
fragment H : 'H' | 'h';
fragment I : 'I' | 'i';
fragment J : 'J' | 'j';
fragment K : 'K' | 'k';
fragment L : 'L' | 'l';
fragment M : 'M' | 'm';
fragment N : 'N' | 'n';
fragment O : 'O' | 'o';
fragment P : 'P' | 'p';
fragment Q : 'Q' | 'q';
fragment R : 'R' | 'r';
fragment S : 'S' | 's';
fragment T : 'T' | 't';
fragment U : 'U' | 'u';
fragment V : 'V' | 'v';
fragment W : 'W' | 'w';
fragment X : 'X' | 'x';
fragment Y : 'Y' | 'y';
fragment Z : 'Z' | 'z';

如果您现在解析输入:

"Andre and stand van     der"

您将看到正在创建的预期令牌:

java -cp .:antlr-3.3.jar Main "Andre and stand van     der"

NAME                      Andre
AND                       and
NAME                      stand
LNAME_PREFIX              van     der