如何在ANTLR3词法分析器规则中更改令牌类型?

时间:2012-07-18 00:04:06

标签: action antlr token fragment lexer

我有一个使用一些片段的词法规则(Integer)。在解析器规则(解析)中,我想以不同的方式重写我的树,具体取决于生成相关令牌的片段。我做了一个小语法来证明我正在尝试的东西:

grammar subrange;

options {
    output=AST;
}

tokens {
    NumberNode;
    DecimalNode;
    BinaryNode;
    HexNode;
    OctalNode;
}

parse
    : Integer+ -> ^(NumberNode Integer)+
    ;

Integer
    : DECIMAL_LITERAL
    | BINARY_LITERAL
    | HEX_LITERAL
    | OCTAL_LITERAL
    ;

fragment BINARY_LITERAL
    : '2#' ('0' | '1')+
    ;

fragment HEX_LITERAL 
    : ('16#' | '0' ('x'|'X')) HEX_DIGIT+
    ;

fragment HEX_DIGIT
    : (DIGIT|'a'..'f'|'A'..'F')
    ;

fragment DECIMAL_LITERAL 
    : ('0' | '1'..'9' DIGIT*)
    ;

fragment OCTAL_LITERAL 
    : '8#' ('0'..'7')+
    ;

fragment DIGIT
    : '0'..'9'
    ;

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

我希望解析规则在假想的DecimalNode下重写DECIMAL_LITERAL,而在BinaryNode下重写BINARY_LITERAL(而不是NumberNode下的所有内容)。

我试图通过更改词法规则中的令牌类型来做到这一点,这样我就可以在解析规则中相应地重写。

我想我应该可以通过一个动作来做到这一点,但是我一直无法弄清楚如何找到返回的令牌以便改变它的类型。 http://www.antlr.org/wiki/display/ANTLR3/Special+symbols+in+actions似乎表明$ tokenref应该有效,但根本不会被翻译。

或者还有另一种方法可以实现这个目标吗?

提前致谢。

1 个答案:

答案 0 :(得分:2)

对我来说似乎有点奇怪:将所有这些文字分组到一个Integer标记下,然后在解析器规则中再次将它们分开。

为什么不删除Integer并执行:

integer
    : BINARY_LITERAL // when output=AST, this creates a CommonTree with type 'BINARY_LITERAL'
    | HEX_LITERAL    // ...
    | DECIMAL_LITERAL
    | OCTAL_LITERAL 
    ;

BINARY_LITERAL
    : '2#' ('0' | '1')+
    ;

HEX_LITERAL 
    : ('16#' | '0' ('x'|'X')) HEX_DIGIT+
    ;

DECIMAL_LITERAL 
    : ('0' | '1'..'9' DIGIT*)
    ;

OCTAL_LITERAL 
    : '8#' ('0'..'7')+
    ;

或者你可以保留Int(eger)规则但是通过这样做来设置各种int-literals的数值:

Int
@init{int skip = 0, base = 10;}
    : ( DECIMAL_LITERAL
      | BINARY_LITERAL  {base = 2;  skip = 2;} 
      | OCTAL_LITERAL   {base = 8;  skip = 2;} 
      | HEX_LITERAL     {base = 16; skip = $text.contains("#") ? 3 : 2;} 
      )
      {
        setText(String.valueOf(Integer.parseInt($text.substring(skip), base)));
      }
    ;

fragment BINARY_LITERAL
    : '2#' ('0' | '1')+
    ;

fragment HEX_LITERAL 
    : ('16#' | '0' ('x'|'X')) HEX_DIGIT+
    ;

fragment DECIMAL_LITERAL 
    : ('0' | '1'..'9' DIGIT*)
    ;

fragment OCTAL_LITERAL 
    : '8#' ('0'..'7')+
    ;

小心给规则一个名称,因为目标语言的某些对象/类/保留字可以有(Integer,如果是Java)。


修改

好。我会留下我的另一个答案,万一路人想知道为什么我在提议这个......:)

以下是(我认为)您所追求的内容:

grammar T;

options {
  output=AST;
}

tokens {
  BinaryNode;
  OctalNode;
  HexNode;
  DecimalNode;
}

parse
 : integer+
 ;

integer
 : i=Integer -> {$i.text.startsWith("2#")}?         ^(BinaryNode Integer)
             -> {$i.text.startsWith("8#")}?         ^(OctalNode Integer)
             -> {$i.text.matches("(16#|0[xX]).*")}? ^(HexNode Integer)
             ->                                     ^(DecimalNode Integer)
 ;

Integer
 : DECIMAL_LITERAL
 | BINARY_LITERAL
 | HEX_LITERAL
 | OCTAL_LITERAL
 ;

fragment BINARY_LITERAL
 : '2#' ('0' | '1')+
 ;

fragment HEX_LITERAL 
 : ('16#' | '0' ('x'|'X')) HEX_DIGIT+
 ;

fragment HEX_DIGIT
 : (DIGIT|'a'..'f'|'A'..'F')
 ;

fragment DECIMAL_LITERAL 
 : ('0' | '1'..'9' DIGIT*)
 ;

fragment OCTAL_LITERAL 
 : '8#' ('0'..'7')+
 ;

fragment DIGIT
 : '0'..'9'
 ;

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

解析输入"2#1111 8#77 0xff 16#ff 123"将导致以下AST:

enter image description here

由于您丢失了有关每个文字的Integer类型的信息,因此您必须在integer - 规则(重写后的-> {boolean-expression}? ...内容中进行此项检查规则)。