Как я могу изменить тип токена в правиле лексера ANTLR3?

У меня есть лексическое правило (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();};

Я хочу, чтобы правило parse переписывало DECIMAL_LITERAL под воображаемым DecimalNode, но BINARY_LITERAL под BinaryNode (а не все под NumberNode).

Я пытаюсь сделать это, изменив тип токена внутри лексического правила, чтобы затем я мог соответствующим образом переписать внутри правила синтаксического анализа.

Я думаю, что смогу сделать это с помощью действия, но я не смог понять, как найти возвращенный токен, чтобы изменить его тип. http://www.antlr.org/wiki/display/ANTLR3/Special+symbols+in+actions, похоже, указывает на то, что $ tokenref должен работать, но он вообще не переводится.

Или есть другой способ добиться этого?

Заранее спасибо.


person Nic Wolfe    schedule 18.07.2012    source источник
comment
Итак, DECIMAL_LITERAL -> DecimalNode и BINARY_LITERAL -> BinaryNode. А как насчет HEX_LITERAL и OCTAL_LITERAL? Я также вижу воображаемые токены узлов для этих двух, но вы их не упоминаете.   -  person Bart Kiers    schedule 18.07.2012
comment
Да, я не перечислял их все в своем вопросе, но мне нужен воображаемый узел для каждого из этих фрагментов. Ради этого примера я подумал, могу ли я различать DECIMAL_LITERAL и BINARY_LITERAL, я смогу выяснить остальное: 0)   -  person Nic Wolfe    schedule 18.07.2012
comment
Чтобы получить более полное представление о моей проблеме, см. эту грамматику. Я хочу переписать числа в DecimalNode, BinaryNode, OctalNode, HexNode и FloatNode вместо того, чтобы иметь их все как NumberNode. Сначала я сделал это, сославшись на их отдельные лексические правила непосредственно из моего правила синтаксического анализатора, но, чтобы избежать двусмысленности 1..2, мне пришлось преобразовать их во фрагменты и добавить еще одно лексическое правило над ними.   -  person Nic Wolfe    schedule 18.07.2012


Ответы (1)


Мне это кажется немного странным: группировать все такие литералы под одним токеном 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-литералов, выполнив:

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:

введите описание изображения здесь

Поскольку вы потеряли информацию о типе Integer каждого литерала, вам придется выполнить эту проверку в integer-правиле (-> {boolean-expression}? ... вещи после правил перезаписи).

person Bart Kiers    schedule 18.07.2012
comment
Упростив свою грамматику, чтобы опубликовать здесь краткий пример, мне пришлось удалить причину вопроса, поэтому я расскажу подробнее о том, что я пытаюсь сделать. Если вы посмотрите на свою вторую грамматику здесь stackoverflow.com/questions/8639783/ вы различаете операторы с плавающей запятой и операторы диапазона 1-2, имея только одно правило лексера, которое использует семантический предикат для разрешения неоднозначности. Я использую этот подход и в своей грамматике, что означает, что все мои числа сгруппированы под одним правилом лексера, подобным этому. Но я хочу, чтобы числа разбирались на разные деревья, отсюда и мой вопрос. - person Nic Wolfe; 18.07.2012