Переключение состояния лексера в грамматике antlr3 - PullRequest
3 голосов
/ 01 января 2012

Я пытаюсь создать грамматику antlr для разбора языка шаблонов. этот язык может быть встроен в любой текст, а границы помечены открывающими / закрывающими тегами: {{ / }}. Итак, действительный шаблон выглядит так:

    foo {{ someVariable }} bar

Где foo и bar должны игнорироваться, а часть внутри тегов {{ и }} должна быть проанализирована. Я нашел этот вопрос , который в основном имеет ответ на проблему, за исключением того, что теги - это только один { и }. Я попытался изменить грамматику так, чтобы она соответствовала 2 открывающим / закрывающим символам, но как только я это сделаю, правило BUFFER потребляет ВСЕ символы, а также открывающие и закрывающие скобки. Правило LD никогда не вызывается.

Кто-нибудь знает, почему лексер antlr потребляет все токены в правиле Buffer, когда разделители имеют 2 символа, но не использует разделители, когда они имеют только один символ?

    grammar Test;

    options { 
      output=AST;
      ASTLabelType=CommonTree; 
    }

    @lexer::members {
      private boolean insideTag = false;
    }

    start   
      :  (tag | BUFFER )*
      ;

    tag
      : LD IDENT^ RD
      ;

    LD @after {
      // flip lexer the state
      insideTag=true;
      System.err.println("FLIPPING TAG");
    } : '{{';

    RD @after {
      // flip the state back
      insideTag=false;
    } : '}}';

    SPACE    : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
    IDENT    : (LETTER)*;
    BUFFER   : { !insideTag }?=> ~(LD | RD)+;

    fragment LETTER : ('a'..'z' | 'A'..'Z');

Ответы [ 2 ]

2 голосов
/ 01 января 2012

Вы можете сопоставить любой символ один или несколько раз, пока не увидите {{ вперед, включив предикат в круглые скобки ( ... )+ (см. Правило BUFFER в демоверсии).

Демо:

grammar Test;

options { 
  output=AST;
  ASTLabelType=CommonTree; 
}

@lexer::members {
  private boolean insideTag = false;
}

start   
  :  tag EOF
  ;

tag
  : LD IDENT^ RD
  ;

LD 
@after {insideTag=true;} 
 : '{{'
 ;

RD 
@after {insideTag=false;} 
 : '}}'
 ;

BUFFER
 : ({!insideTag && !(input.LA(1)=='{' && input.LA(2)=='{')}?=> .)+ {$channel=HIDDEN;}
 ;

SPACE 
 : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}
 ;

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

Обратите внимание, что лучше сохранить правило BUFFER в качестве первого правила лексера в вашей грамматике: таким образом, это будет первый пробный токен.

Если вы сейчасparse "foo {{ someVariable }} bar", создается следующее AST:

enter image description here

0 голосов
/ 08 января 2012

Разве такая грамматика не соответствует вашим потребностям?Я не понимаю, почему БУФЕР должен быть таким сложным.

grammar test;

options { 
  output=AST;
  ASTLabelType=CommonTree; 
}

@lexer::members { 
    private boolean inTag=false;
}

start   
  :  tag* EOF
  ;

tag
  : LD IDENT RD -> IDENT
  ;

LD 
@after { inTag=true; }
 : '{{'
 ;

RD 
@after { inTag=false; }
 : '}}'
 ;

IDENT   :   {inTag}?=> ('a'..'z'|'A'..'Z'|'_') 'a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;

BUFFER
 : . {$channel=HIDDEN;}
 ;
...