在antlr3语法中切换词法分析器状态

时间:2012-01-01 12:54:55

标签: antlr antlr3

我正在尝试构造一个用于解析模板语言的antlr语法。该语言可嵌入任何文本中,并且边界标记为打开/关闭标记:{{ / }}。所以有效的模板如下所示:

    foo {{ someVariable }} bar

应忽略foobar,并且应解析{{}}标记内的部分。我发现this question基本上有问题的答案,但标签只有一个{}。我试图修改语法以匹配2个打开/关闭字符,但是一旦我这样做,BUFFER规则就会消耗所有字符,也包括开始和结束括号。永远不会调用LD规则。

当分隔符有2个字符时,有没有人知道为什么antlr词法分析器在Buffer规则中使用所有标记,但是当它们只有一个字符时不会使用分隔符?

    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 个答案:

答案 0 :(得分:2)

您可以匹配任何字符一次或多次,直到您通过在括号{{中包含谓词来提前( ... )+(参见演示中的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规则作为语法中的第一个词法分析器规则:这样,它将成为第一个尝试的令牌。

如果您现在解析"foo {{ someVariable }} bar",则会创建以下AST:

enter image description here

答案 1 :(得分:0)

这样的语法不适合你的需求吗?我不明白为什么BUFFER需要那么复杂。

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;}
 ;