最近一直在做Session Initiation Protocol (SIP)協議方面的開發,SIP在電信VoIP領域應用非常廣泛,是一個基於文本語法的協議。SIP的語法規範是使用ABNF來定義的。對SIP語法有興趣的同學請移步其Augmented BNF for the SIP Protocol章節。Augmented BNF for Syntax Specifications: ABNF本身也是一種語法規範,ABNF形式上可以由其自身來定義,有興趣的童鞋請參考其第4章“ABNF Definition of ABNF”。
因此,如果想做一個SIP協議棧,首先要有一個SIP的語法解析器,這個語法解析器屬於ABNF語法解析器。網上搜索ABNF語法解析器的生成器(ABNF parser generator)能夠搜索到不少。當然,如果從學習編譯原理的角度來說,我們更傾向於自己去寫一個ABNF parser generator,因爲如果我們自己動手寫過,以後就算用開源的生成器,用起來肯定會有更深刻的體會。
ABNF的語法定義很短,主要分爲兩部分,Core Rules和ABNF的主體部分。Core Rules主要是一些最基礎的符號定義:
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
BIT = "0" / "1"
CHAR = %x01-7F
; any 7-bit US-ASCII character,
excluding NUL
CR = %x0D
; carriage return
CRLF = CR LF
; Internet standard newline
CTL = %x00-1F / %x7F
; controls
DIGIT = %x30-39
; 0-9
DQUOTE = %x22
; " (Double Quote)
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
HTAB = %x09
; horizontal tab
LF = %x0A
; linefeed
LWSP = *(WSP / CRLF WSP)
; linear white space (past newline)
OCTET = %x00-FF
; 8 bits of data
SP = %x20
; space
VCHAR = %x21-7E
; visible (printing) characters
WSP = SP / HTAB
; white space
rulelist = 1*( rule / (*c-wsp c-nl) )
rule = rulename defined-as elements c-nl
; continues if next line starts
; with white space
rulename = ALPHA *(ALPHA / DIGIT / "-")
defined-as = *c-wsp ("=" / "=/") *c-wsp
; basic rules definition and
; incremental alternatives
elements = alternation *c-wsp
c-wsp = WSP / (c-nl WSP)
c-nl = comment / CRLF
; comment or newline
comment = ";" *(WSP / VCHAR) CRLF
alternation = concatenation
*(*c-wsp "/" *c-wsp concatenation)
concatenation = repetition *(1*c-wsp repetition)
repetition = [repeat] element
repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)
element = rulename / group / option /
char-val / num-val / prose-val
group = "(" *c-wsp alternation *c-wsp ")"
option = "[" *c-wsp alternation *c-wsp "]"
char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE
; quoted string of SP and VCHAR
without DQUOTE
num-val = "%" (bin-val / dec-val / hex-val)
bin-val = "b" 1*BIT
[ 1*("." 1*BIT) / ("-" 1*BIT) ]
; series of concatenated bit values
; or single ONEOF range
dec-val = "d" 1*DIGIT
[ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]
hex-val = "x" 1*HEXDIG
[ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]
prose-val = "<" *(%x20-3D / %x3F-7E) ">"
; bracketed string of SP and VCHAR
without angles
; prose description, to be used as
last resort
既然SIP語法是由ABNF語法定義的,而ABNF語法可以由其自身定義,那麼我們只要寫一個通用的ABNF語法解析器的生成器,就可以解決這個問題了。這個有點像先有雞還是先有蛋的問題:我們需要一個ABNF語法解析器,而ABNF語法解析器的生成器本身又要能夠解析ABNF語法。我們的方案是手工寫一個較爲簡單的ABNF語法分析器,然後用這個語法分析器去生成一個適用於SIP語法的更復雜的語法分析器。
在Alfred V. Aho等大牛的《Compilers》龍書中,recursive-descent parsing是最容易手工編寫的算法了,它是一種預測解析方法(predictive parsing),即通過前向看若干個輸入字符來決定選擇哪一條語法規則(Production),然後將非終止符(nonterminal)定義成一個函數,不斷的遞歸調用即可。對於ABNF自身語法來說,通常是前向看1~2個字符就足夠了,但也有侷限,後面我們會有分析。龍書中的預測分析器是針對上下文無關文法(BNF)的,ABNF與BNF稍有不同,例如沒有空符號(epsilon),增加了一些諸如表示重複的操作符號等,但大體上還是很相像的。
例如,對於一條語法:
stmt -> for ( optexpr; optexpr; optexpr ) stmt
解析器可以這樣寫:
void stmt() {
match(for); match('(');
optexpr(); match(';'); optexpr(); match(';'); optexpr();
match(')'); stmt();
}
怎麼樣?很簡單吧?下一篇我們將開始動手寫ABNF語法解析器。