2012-01-01 50 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;} 
;