2013年11月27日 星期三

parsec 剖析器組合子語義

字元剖析器

a = char 'a'

a 剖析器會消化 a 字元,並傳回字元 a,若為其它字元則會剖析失敗。

序列剖析

序列剖析器可組合數個剖析器,這些剖析器以do區塊組合,並依序應用這些剖析器分析,若序列中某個剖析器失敗,則整個剖析器便失敗,如下例:

openClose = do{ char '('
              ; char ')'
              }

openClose 會先剖析 ( 字元,若剖析成功,則再剖析 ) 字元。
預測剖析器

預測剖析器依掃描之首字元來選擇子剖析器,子剖析器以 <|> 運算子組合,此掃描首字元會被消化掉。

任何一個子剖析器成功,則傳回該子剖析器之值。

aORb =   char 'a'
     <|> char 'b'
a
b

aORb 可剖析 a 字元或 b 字元。

try 剖析器

預測剖析器可利用 <|> 運算子來建構。

testOr  =   string "(a)"
        <|> string "(b)"

testOr 剖析器因只依掃描之首字元來選擇剖析器,而上例首字元均為 (,故無法明確選擇用那個剖析器,會出現下列錯誤。


Main> run testOr "(b)"      
parse error at (line 1, column 2):
unexpected 'b'
expecting 'a'

必須用左因子分解法修改剖析器才可,如下:

testOr1 = do{ char '('
            ; char 'a' <|> char 'b'
            ; char ')'
            }

若不想用左因子分解法,則可使用 try 剖析器,try 剖析器剖析失敗,則會把消化的輸入復原。


testOr2 =   try (string "(a)")
        <|> string "(b)"



testOr3 =   do{ try (string "(a"); char ')'; return "(a)" }
        <|> string "(b)"


空條件剖析指若沒有任何剖析器可選擇,則會在沒有消化任何輸入下,成功傳回值。

parens :: Parser ()
parens = do{ 
            char '(' ; 
            parens ; 
            char ')' ; 
            parens 
           } 
         <|> return ()
上述剖析器可剖析以下字串
()
(())

分隔字串處理(sepBy, endBy, sepEndBy)

parsec 提供2個剖析組合子,sepBy 及 endBy,用來處理實體由分隔字串區分之串列,最常見的格式應該是csv(comma seperated values)逗號分隔值組。

這些組合子均接受兩個參數,第一個是剖析實體之組合子,另一個是剖析分隔字串之組合子,最後在無法繼續剖析時,傳回實體值之串列。
簡單的語義如下例:

x endBy _  

x_x_x_x_x_  -> [x,x,x,x,x]

x sepBy _

x_x_x_x_x   -> [x,x,x,x,x]

x sepEndBy _

x_x_x_x_x_  -> [x,x,x,x,x]
x_x_x_x_x   -> [x,x,x,x,x]
xa_         -> [x]          a 並非 x 可剖析,故傳回一個 a。  


num endBy . 接受下列字串並傳回:
1.               -> [1]
1.1.            -> [1,1]
1.1.2.         -> [1,1,2]

letter sepEndBy ,
""   ->  []
a    ->  [a]
a,   ->  [a]
a,b  ->  [a,b]
a,b, ->  [a,b]
ab   ->  [a]

沒有留言:

張貼留言