2010-10-24 67 views
6

我正在嘗試爲PHP serialize()格式編寫ANTLR語法,並且除了字符串以外,一切似乎都正常工作。問題是,序列化字符串格式爲:ANTLR規則消耗固定數量的字符

s:6:"length"; 

在正則表達式上來講,像s:(\d+):".{\1}";規則會形容這種格式,如果只有反向引用是在伯爵「比賽的數」允許(但他們都沒有) 。

但是我無法找到一種方法來表達這個詞法分析器或語法分析器的語法:整個想法是使讀取的字符數量取決於描述要讀取的字符數的反向引用,如Fortran Hollerith常量(即6HLength),而不是字符串分隔符。

ANTLR grammar for Fortran這個例子似乎指向的方式,但我不知道如何。請注意,我的目標語言是Python,而大部分的文檔和示例都是針對Java:

// numeral literal 
ICON {int counter=0;} : 
    /* other alternatives */ 
    // hollerith 
    'h' ({counter>0}? NOTNL {counter--;})* {counter==0}? 
     { 
     $setType(HOLLERITH); 
     String str = $getText; 
     str = str.replaceFirst("([0-9])+h", ""); 
     $setText(str); 
     } 
    /* more alternatives */ 
    ; 

回答

4

這樣以來s:3:"a"b";輸入是有效的,你不能在你的詞法分析器定義String令牌,除非第一個和最後雙引號是總是你的字符串的開始和結束。但我想這不是這種情況。

所以,你需要一個詞法規則是這樣的:

SString 
    : 's:' Int ':"' (.)* '";' 
    ; 

換句話說:匹配s:,那麼integer值之後:"那麼一個或多個字符,可以是任何東西,結尾";。但是,如果未達到值Int,則需要告訴詞法分析器停止使用。你可以通過在你的語法中混合一些簡單的代碼來做到這一點。您可以通過將其包裝在{}內嵌入明碼。所以第一令牌Int持有的價值轉換成稱爲chars的整數變量:

SString 
    : 's:' Int {chars = int($Int.text)} ':"' (.)* '";' 
    ; 

現在嵌入(.)*循環內一些代碼來阻止它,儘快消耗爲chars倒計時至零:

SString 
    : 's:' Int {chars = int($Int.text)} ':"' ({if chars == 0: break} . {chars = chars-1})* '";' 
    ; 

就是這樣。

小演示語法:

grammar Test; 

options { 
    language=Python; 
} 

parse 
    : (SString {print 'parsed: [\%s]' \% $SString.text})+ EOF 
    ; 

SString 
    : 's:' Int {chars = int($Int.text)} ':"' ({if chars == 0: break} . {chars = chars-1})* '";' 
    ; 

Int 
    : '0'..'9'+ 
    ; 

(請注意,你需要躲避%你的語法裏面!)

和測試腳本:

import antlr3 
from TestLexer import TestLexer 
from TestParser import TestParser 

input = 's:6:"length";s:1:""";s:0:"";s:3:"end";' 
char_stream = antlr3.ANTLRStringStream(input) 
lexer = TestLexer(char_stream) 
tokens = antlr3.CommonTokenStream(lexer) 
parser = TestParser(tokens) 
parser.parse() 

產生以下輸出:

parsed: [s:6:"length";] 
parsed: [s:1:""";] 
parsed: [s:0:"";] 
parsed: [s:3:"end";]