我正在开发一个解析器,它将包含一个人全名的字符串拆分为组件(first,middle,last,title,suffix,...)。当我在ANTLRWorks中尝试一个基本的例子“J. A. Doe”时,它匹配fname和lname规则,但是忽略了“A.”。如何解决此类问题?
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
规则按预期工作,但我不确定为什么
答案 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