基於Predictive Parsing的ABNF語法分析器(一)——ABNF語法介紹

最近一直在做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


然後我們來看ABNF對自身的形式定義:

        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語法解析器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章