2016-07-29 72 views
5

我試圖使用解析器組合器來解決問題。我試過如下:函數式編程:瞭解解析器組合器

注意:下面的代碼使用combinator library

styleParserItalic : Bool -> Parser (List (List Char , Style)) 
styleParserItalic bolded = 
let 
style = if bolded then Italic else Unstyled 
in 
(end `andThen` always ( succeed ([]))) 
<|> (string "(!ITALIC!)" `andThen` \_ -> styleParserItalic (not bolded) ) 
<|> (anyChar `andThen` \c -> styleParserItalic bolded `andThen` \cs -> succeed ((c :: [],style) :: cs)) 

我在努力理解它是如何運行的解析器,因爲解析器成功之前styleParserItalic解析器被調用。

有人能解釋解析器如何給它一個字符串時的工作方式嗎?

如果有人對分析器的目的和完整代碼感興趣,here是我以前的問題。

這是我就此瞭解遠遠

解析器會首先檢查它是否是一個行的末尾,如果沒有它會嘗試解析器字符串(!ITALIC!)如果這樣,那麼它會調用解析器的參數爲True或False(如果爲false,則會使其爲真)..

如果解析器未找到字符串(!ITALIC!),它將嘗試解析任何字符,然後它會調用解析器再次。

讓我感到困惑的是解析器只要解析任何字符,就會繼續調用自己!

編輯:*注意以下不是問題的一部分只是爲了共享代碼,如果有人有興趣

感謝所有響應,我已經更新瞭解析器解析粗體斜體下劃線...,按下面的屏幕截圖enter image description here

type Style = Bold| Unstyled | Italic | Coded | Lined | Titled | Marked  | Underline 

styleParser : Bool ->Bool ->Bool ->Bool-> Bool-> Bool->Bool 
           -> Parser (List (List Char ,  (Style,Style,Style,Style,Style,Style,Style))) 
           --(bold,italic ,code,line ,Titled,mark) 
styleParser bolded italiced coded lined titled marked underlined= 
    let 
    style = (
    if bolded  then Bold  else Unstyled 
    ,if italiced then Italic else Unstyled 
    ,if coded  then Coded  else Unstyled 
    ,if lined  then Lined  else Unstyled 
    ,if titled  then Titled else Unstyled 
    ,if marked  then Marked else Unstyled 
    ,if underlined then Underline else Unstyled 
    ) 
    in 
    (end `andThen` always (succeed ([]))) 
    <|> (string "//" `andThen` \_ -> styleParser bolded  italiced   coded  lined  titled  marked  (not underlined)) 
    <|> (string "**" `andThen` \_ -> styleParser (not bolded) italiced  coded  lined  titled  marked  underlined) 
    <|> (string "*" `andThen` \_ -> styleParser bolded  (not italiced) coded  lined  titled  marked  underlined) 
    <|> (string "`" `andThen` \_ -> styleParser bolded  italiced  (not coded) lined  titled  marked  underlined) 
    <|> (string "/br" `andThen` \_ -> styleParser bolded  italiced  coded  (not lined) titled  marked  underlined) 
    <|> (string "/*" `andThen` \_ -> styleParser bolded  italiced  coded  lined  (not titled) marked  underlined) 
    <|> (string "{-" `andThen` \_ -> styleParser bolded  italiced  coded  lined  titled  (not marked) underlined) 
    <|> (anyChar  `andThen` \c -> styleParser bolded  italiced   coded  lined  titled  marked  underlined `andThen` \cs -> succeed ((c :: [],style) :: cs)) 


foldStyleHtml : List (List Char , ( Style,Style,Style,Style,Style,Style,Style)) -> List (Html Msg) 
foldStyleHtml lst = 
    List.map styleToHtml lst 


styleToHtml : (List Char, (Style ,Style,Style,Style,Style,Style,Style)) -> Html Msg 
styleToHtml (a,b) = 
    case b of 
    (Bold,Italic,_,_,_,_,Unstyled)  -> strong [] [em [][ text (String.fromList a)]] 
    (Bold,Italic,_,_,_,_,Underline)  -> u[][ strong [] [em [][ text (String.fromList a)]]] 
    (Bold,Unstyled,_,_,_,_,Underline) -> u[][ strong [] [text (String.fromList a)]] 
    (Unstyled,Italic,_,_,_,_,Underline) -> u[][ em  [] [text (String.fromList a)]] 
(Unstyled,Italic,_,_,_,_,_)   -> em[] [text (String.fromList a)] 
(Bold,Unstyled,_,_,_,_,_)   -> strong [][ text (String.fromList a)] 
(_,_,Coded,_,_,_,_)     -> code [codeStyle ][text  (String.fromList a)] 
(_,_,_,Lined,_,_,_)     -> br [][text " "] 
    -- (_,_,_,_,Titled,_,_)     -> div [][text (String.fromList a)] 
    (_,_,_,_,_,Marked,_)     -> mark [][text (String.fromList a)] 
    (_,_,_,_,_,_,Underline)    -> u [][text (String.fromList a)] 
    (_,_,_,_,_,_,_)      -> text (String.fromList a) 

htmlParser : Parser (List (Html Msg)) 
htmlParser = 
styleParser False False False False False False False `andThen` (succeed << foldStyleHtml) 

runParser : Parser (List (Html Msg)) -> String -> Html Msg 
runParser parser str         = 
    case parse parser str of 
    (Ok htmls,_)-> div [] htmls 
    (Err err, _) -> div [ style [("color", "red")] ] [ text <| toString <| err] 
+1

我會虛心提[印跡後我幾年前寫的(https://oldfashionedsoftware.com/2008/08/16/easy-parsing-in-scala /)作爲分析器組合器的介紹。 –

回答

3

解析器組合(一般)消耗輸入作爲他們成功了。在這個庫中,如果string "(!ITALIC!)"失敗,它不會消耗任何輸入。由於使用<|>組合器,因此它會嘗試使用以anyChar開頭的代碼的下一部分。

anyChar成功時,它將消耗該單個字符並在andThen之後將其捕獲到c中。然後,當對styleParserItalic bolded進行遞歸調用時,剩餘的字符串(除了由anyChar捕獲的字符以外的所有內容)將被「抓取」。第二個andThen將遞歸組合器的輸出捕獲到cs中,並將捕獲的字符預加載到遞歸調用中字符列表的其餘部分上。

我認爲需要記住的重要部分是組合器在成功時消耗輸入,並且(通常)在失敗時不消耗輸入。

+0

謝謝,所以在每次調用遞歸styleParserItalic時,它都會將結果傳遞給cs並運行成功解析器,或者等待所有遞歸調用完成(所有字符解析),然後將結果解析爲cs? –

+1

由於它是遞歸的,它會在返回鏈之前遞歸到字符串的最後一個字符。這就是爲什麼有'end'的第一個combinator,它檢查輸入的結尾,如果匹配返回空列表(如果沒有'end'它將永遠旋轉)。遞歸樹然後摺疊並通過在遞歸調用返回的'cs'前面加上每個'c'來建立列表。 –

+0

可以說我想解析字符串「ABC」,最終結果應該是成功的(('A',Unstyled)::(B,Unstyled)::(C,Unstyled):: always(succeed([ )) ) 對 ?所以總是(成功([]))被添加到元組列表中? –

2

首先簡單介紹一下...

(1)在簽名內部列表中的每個元素:

styleParserItalic : Bool -> Parser (List (List Char , Style)) 
              ^^^^^^^^^ 

僅僅是一個單獨的字符。通過從最後一行刪除:: []

<|> ... `andThen` \cs -> succeed ((c ,style) :: cs)) 
            ^^^ 
             removed `:: []` 

您可以讓它具有此簽名。

(2)請注意,bolded參數僅影響樣式 - 它不影響控制流。它應該被稱爲italic,因爲如果參數爲True,輸出中顯示的樣式爲Italic,否則爲Unstyled

另請注意,一旦將此參數設置爲True,它將在所有後續遞歸調用中保持爲真。

所以,現在的算法是:

  1. 如果在該行的結束,返回空列表。
  2. 如果在(!ITALIC!),請在分析的其餘部分使用Italic作爲樣式。
  3. 否則,解析一個字符,解析該行的其餘部分並連接結果。

一個近似的Python算法會是這樣的:

def parseLine(style, line): 
    result = [] 
    while line: 
    if line.startsWith('(!ITALIC!)'): 
     line = line[8:] 
     style = Italic 
     # loop around 
    else: 
     result.append((line[0], style)) 
     line = line[1:] 
    return result