Литерал с плавающей точкой и параметр диапазона в ANTLR - PullRequest
4 голосов
/ 27 декабря 2011

Я работаю над парсером для языка D и столкнулся с проблемами, когда попытался добавить правило оператора «слайс». Вы можете найти грамматику ANTLR для этого здесь . В основном проблема заключается в том, что если лексер встречает строку, подобную этой: «1..2», он теряется полностью, и в конечном итоге он оказывается как одно значение с плавающей точкой, и, следовательно, правило postfixExpression для строки, такой как «a [10 .. 11] "оказывается объектом ExpArrIndex с аргументом ExpLiteralReal. Может кто-нибудь объяснить, что именно не так с числовыми литералами? (насколько я понимаю, что-то не так вокруг этих токенов)

Ответы [ 2 ]

4 голосов
/ 27 декабря 2011

Это можно сделать, выпустив два токена (токен Int и Range), когда вы встретите ".." внутри правила float. Для этого вам нужно переопределить два метода в вашем лексере.

Демонстрация с небольшой частью вашей Dee грамматики:

grammar Dee;

@lexer::members {

  java.util.Queue<Token> tokens = new java.util.LinkedList<Token>();

  public void offer(int ttype, String ttext) {
    this.emit(new CommonToken(ttype, ttext));
  }

  @Override
  public void emit(Token t) {
    state.token = t;
    tokens.offer(t);
  }

  @Override
  public Token nextToken() {
    super.nextToken();
    return tokens.isEmpty() ? Token.EOF_TOKEN : tokens.poll();
  }
}
parse
 : (t=. {System.out.printf("\%-15s '\%s'\n", tokenNames[$t.type], $t.text);})* EOF
 ;

Range
 : '..'
 ;

IntegerLiteral 
 : Integer IntSuffix?
 ;

FloatLiteral 
 : Float ImaginarySuffix? 
 ;

// skipping
Space
 : ' ' {skip();}
 ;

// fragments
fragment Float
 : d=DecimalDigits ( options {greedy = true; } : FloatTypeSuffix 
                   | '..' {offer(IntegerLiteral, $d.text); offer(Range, "..");}
                   | '.' DecimalDigits DecimalExponent?
                   )
 | '.' DecimalDigits DecimalExponent?
 ;

fragment DecimalExponent : 'e' | 'E' | 'e+' | 'E+' | 'e-' | 'E-' DecimalDigits;
fragment DecimalDigits   : ('0'..'9'|'_')+ ;
fragment FloatTypeSuffix : 'f' | 'F' | 'L';
fragment ImaginarySuffix : 'i';
fragment IntSuffix       : 'L'|'u'|'U'|'Lu'|'LU'|'uL'|'UL' ;
fragment Integer         : Decimal| Binary| Octal| Hexadecimal ;
fragment Decimal         : '0' | '1'..'9' (DecimalDigit | '_')* ;
fragment Binary          : ('0b' | '0B') ('0' | '1' | '_')+ ;
fragment Octal           : '0' (OctalDigit | '_')+ ;
fragment Hexadecimal     : ('0x' | '0X') (HexDigit | '_')+;  
fragment DecimalDigit    : '0'..'9' ;
fragment OctalDigit      : '0'..'7' ;
fragment HexDigit        : ('0'..'9'|'a'..'f'|'A'..'F') ;

Проверка грамматики с классом:

import org.antlr.runtime.*;

public class Main {
  public static void main(String[] args) throws Exception {
    DeeLexer lexer = new DeeLexer(new ANTLRStringStream("1..2 .. 33.33 ..21.0"));
    DeeParser parser = new DeeParser(new CommonTokenStream(lexer));
    parser.parse();
  }
}

А когда вы запускаете Main, выдается следующий вывод:

IntegerLiteral  '1'
Range           '..'
IntegerLiteral  '2'
Range           '..'
FloatLiteral    '33.33'
Range           '..'
FloatLiteral    '21.0'

EDIT

Да, как вы указали в комментариях, правило лексера может испускать только 1 единственный токен. Но , как вы сами уже попробовали, семантические предикаты действительно могут быть использованы, чтобы заставить лексера заглянуть в поток символов и убедиться, что на самом деле ".." после IntegerLiteral токена перед попыткой соответствует FloatLiteral.

Следующая грамматика выдаст те же токены, что и первое демо.

grammar Dee;

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

Range
 : '..'
 ;

Number
 : (IntegerLiteral Range)=> IntegerLiteral {$type=IntegerLiteral;}
 | (FloatLiteral)=>         FloatLiteral   {$type=FloatLiteral;}
 |                          IntegerLiteral {$type=IntegerLiteral;}
 ;

// skipping
Space
 : ' ' {skip();}
 ;

// fragments
fragment DecimalExponent : 'e' | 'E' | 'e+' | 'E+' | 'e-' | 'E-' DecimalDigits;
fragment DecimalDigits   : ('0'..'9'|'_')+ ;
fragment FloatLiteral    : Float ImaginarySuffix?;
fragment IntegerLiteral  : Integer IntSuffix?;
fragment FloatTypeSuffix : 'f' | 'F' | 'L';
fragment ImaginarySuffix : 'i';
fragment IntSuffix       : 'L'|'u'|'U'|'Lu'|'LU'|'uL'|'UL' ;
fragment Integer         : Decimal| Binary| Octal| Hexadecimal ;
fragment Decimal         : '0' | '1'..'9' (DecimalDigit | '_')* ;
fragment Binary          : ('0b' | '0B') ('0' | '1' | '_')+ ;
fragment Octal           : '0' (OctalDigit | '_')+ ;
fragment Hexadecimal     : ('0x' | '0X') (HexDigit | '_')+;  
fragment DecimalDigit    : '0'..'9' ;
fragment OctalDigit      : '0'..'7' ;
fragment HexDigit        : ('0'..'9'|'a'..'f'|'A'..'F') ;
fragment Float
 : d=DecimalDigits ( options {greedy = true; } : FloatTypeSuffix 
                   | '.' DecimalDigits DecimalExponent?
                   )
 | '.' DecimalDigits DecimalExponent?
 ;
2 голосов
/ 27 декабря 2011

из лексера doc D

Исходный текст разбивается на токены с использованием техники максимального жевания, т. Е. Лексический анализатор пытается создать самый длинный токен, какой только может.Например, >> - токен сдвига вправо, а не два больше токенов.Исключением из этого правила является то, что .., встроенный в то, что выглядит как два литерала с плавающей точкой, , как в 1..2, интерпретируется, как если бы .. был отделен пробелом от первого целого числа.

может сделать предварительный анализ, который делает s/(\d)\.\.(\d)/$1 .. $2/

...