antlr词法分析器匹配另一个规则的前缀

时间:2012-04-03 13:40:39

标签: antlr antlr3

我不确定这个问题实际上是前缀,但是这里有。

我的语法中有这两个规则(以及许多其他规则)

DOT_T  : '.' ;
AND_T  : '.AND.'  | '.and.'  ;

我需要解析这样的字符串:

a.eq.b.and.c.ne.d
c.append(b)

这应该被理解为:

ID[a] EQ_T ID[b] AND_T ID[c] NE_T ID[d]
ID[c] DOT_T ID[append] LPAREN_T ID[b] RPAREN_T

我得到的第二行错误是:

line 1:3 mismatched character "p"; expecting "n"

它不会将.作为DOT_T,而是会尝试匹配.and.,因为它会在a之后看到.

关于我需要做些什么才能使这项工作?

更新

我添加了以下规则,并认为我会使用相同的技巧

NUMBER_T
    : DIGIT+
        ( (DECIMAL)=> DECIMAL 
        | (KIND)=>    KIND
        )?
    ;

fragment DECIMAL
    : '.' DIGIT+ ;
fragment KIND
    : '.' DIGIT+ '_' (ALPHA+ | DIGIT+) ;

但是当我尝试解析它时:

lda.eq.3.and.dim.eq.3

它给了我以下错误:

line 1:9 no viable alternative at character "a"

而lexing 3。所以我猜测同样的事情发生在上面,但解决方案在这种情况下不起作用:S现在我对此感到困惑......

1 个答案:

答案 0 :(得分:2)

是的,这是因为带有前缀'.' - s。

每当词法分析器偶然发现".a"时,它会尝试创建AND_T令牌。如果找不到字符"nd",则词法分析器会尝试构造另一个以".a"开头的标记,该标记不存在(并且ANTLR会产生错误)。因此,词法分析器将 回馈角色"a"并回退以创建DOT_T令牌(然后是ID令牌)!这就是ANTLR的工作原理。

您可以选择在AND_T规则中与这些EQ_TDOT_T,...匹配。但是,你仍然需要通过添加一些句法谓词来帮助词法分析器,这些句法强制词法分析器在字符流中向前看以确保它可以匹配这些标记。

演示:

grammar T;  

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

DOT_T  
 : '.' ( (AND_T)=> AND_T {$type=AND_T;}
       | (EQ_T)=>  EQ_T  {$type=EQ_T; }
       | (NE_T)=>  NE_T  {$type=NE_T; }
       )?
 ;

ID
 : ('a'..'z' | 'A'..'Z')+
 ;

LPAREN_T
 : '('
 ;

RPAREN_T
 : ')'
 ;

SPACE
 : (' ' | '\t' | '\r' | '\n')+ {skip();}
 ;

NUMBER_T
 : DIGIT+ ((DECIMAL)=> DECIMAL)?
 ;

fragment DECIMAL : '.' DIGIT+ ;
fragment AND_T   : ('AND' | 'and') '.' ;
fragment EQ_T    : ('EQ'  | 'eq' ) '.' ;
fragment NE_T    : ('NE'  | 'ne' ) '.' ;
fragment DIGIT   : '0'..'9';

如果您为生成的解析器提供输入:

a.eq.b.and.c.ne.d
c.append(b)

将打印以下输出:

ID         'a'
EQ_T       '.eq.'
ID         'b'
AND_T      '.and.'
ID         'c'
NE_T       '.ne.'
ID         'd'
ID         'c'
DOT_T      '.'
ID         'append'
LPAREN_T   '('
ID         'b'
RPAREN_T   ')'

输入:

lda.eq.3.and.dim.eq.3

打印以下内容:

ID         'lda'
EQ_T       '.eq.'
NUMBER_T   '3'
AND_T      '.and.'
ID         'dim'
EQ_T       '.eq.'
NUMBER_T   '3'

修改

DECIMALKIND都以'.' DIGIT+开头的事实并不好。尝试这样的事情:

NUMBER_T
 : DIGIT+ ((DECIMAL)=> DECIMAL ((KIND)=> KIND)?)?
 ;

fragment DECIMAL : '.' DIGIT+;
fragment KIND    : '_' (ALPHA+ | DIGIT+); // removed ('.' DIGIT+) from this fragment

请注意,规则NUMBER_T现在永远不会生成DECIMALKIND令牌。如果您希望这种情况发生,您需要更改类型:

NUMBER_T
 : DIGIT+ ((DECIMAL)=> DECIMAL {/*change type*/} ((KIND)=> KIND {/*change type*/})?)?
 ;