2012-01-01 52 views
3

我想构建一个antlr语法来解析模板语言。该语言可以嵌入任何文本中,并且边界标记为打开/关闭标记:{{/}}。因此,一个有效的模板看起来是这样的:在antlr3语法中切换词法分析器状态

foo {{ someVariable }} bar 

foobar应该被忽略,{{}}标签内的部分应被解析。我发现this question基本上有问题的答案,除了标签只有一个{}。我试图修改语法来匹配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'); 
+0

请注意'IDENT:(LETTER)*;'(might)会导致词法分析器进入一个无限循环。 Lexer规则_必须总是匹配至少1个字符。 – 2012-01-01 16:12:13

回答

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

+0

非常感谢您的解释。它现在完美运作。 – pulse00 2012-01-01 17:56:11

+0

不客气@RobertGruendler。 – 2012-01-01 18:01:37

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