簡體   English   中英

解析Haskell自定義數據類型

[英]Parsing Haskell custom data types

我已經通過這里提供的Haskell Koans工作: https//github.com/roman/HaskellKoans

我被困在最后兩個Koans上,都涉及解析自定義代數數據類型。 這是第一個:

data Atom = AInt Int | ASym Text deriving (Eq, Show)

testAtomParser :: Test
testAtomParser = testCase "atom parser" $ do
    -- Change parser with the correct parser to use
    --
    let parser = <PARSER HERE> :: P.Parser Atom
    assertParse (ASym "ab") $ P.parseOnly parser "ab"
    assertParse (ASym "a/b") $ P.parseOnly parser "a/b"
    assertParse (ASym "a/b") $ P.parseOnly parser "a/b c"
    assertParse (AInt 54321) $ P.parseOnly parser "54321"

如何定義變量解析器,以便它可以解析代數數據類型Atom來傳遞斷言?

一世。

ADT的解析器傾向於反映ADT的形狀。 你的ADT是由兩個不相交的部分組成的,所以你的解析器也可能有兩個不相交的部分

atom = _ <|> _

II。

假設我們知道如何解析單個數字(讓我們稱之為基本解析器digit ),那么我們通過重復它來解析(非負)整數。

natural = let loop = digit >> loop in loop

這成功地解析了無限的數字流並將它們拋棄。 我們可以做得更好嗎? 不幸的是,不僅僅是一個monad實例,我們需要另一個基本的組合器, many ,它修改了一些其他解析器以消耗輸入0次或更多次,將結果累積到列表中。 我們實際上會稍微調整一下,因為空解析不是有效數字

many1 p = do x  <- p
             xs <- many p
             return (x:xs)

natural' = many1 digit

III。

原子怎么樣? 為了傳遞測試用例,似乎原子必須是1對多的字母數字字符反斜杠。 同樣,這個不相交的結構可以立即在我們的解析器中表達

sym = many1 (_ <|> _)

我們將再次使用一些內置的簡單解析器組合來構建我們想要的東西,比如satisfy :: (Char -> Bool) -> Parser Char匹配任何滿足某些謂詞的字符。 我們可以立即構建另一個有用的組合器, char c = satisfy (==c) :: Char -> Parser Char然后我們就完成了。

sym = many1 (char '/' <|> satisfy isAlpha)

其中isAlpha是一個謂詞,就像正則表達式[a-zA-Z]

IV。

所以現在我們有了解析器的核心

natural <|> sym :: Parser String

many1組合器將我們的字符解析器提升為字符列表的解析器( String s!)。 這個提升動作也是構建ADT解析器的基本思路。 我們想把我們的Parser String升級為Parser Atom 一種方法是使用函數toAtom :: String -> Atom然后我們可以將其fmapParser

atom' :: Parser Atom
atom' = fmap toAtom (natural <|> sym)

但是類型為String -> Atom的函數首先會破壞構建解析器的目的。

如I.中所述,重要的部分是ADT的形狀反映在我們的atom解析器的形狀中。 我們需要利用它來構建我們的最終解析器。

V.

我們需要利用atom解析器結構中的信息。 讓我們構建兩個函數

liftInt :: String -> Atom  -- creates `AInt`s
liftSym :: String -> Atom  -- creates `ASym`s

liftInt = AInt . read
liftSym = ASym

每一個都說明了將String轉換為Atom的方法,同時也聲明了我們正在處理的是什么類型Atom 值得注意的是,如果我們傳遞一個無法解析為Int的字符串, liftInt將拋出運行時錯誤。 幸運的是,這正是我們所知道的。

atomInt :: Parser Atom
atomInt = liftInt <$> natural

atomSym :: Parser Sym
atomSym = liftSym <$> sym

atom'' = atomInt <|> atomSym

現在我們的atom''解析器利用了natural只返回有效解析的字符串的保證 - 我們對read的調用不會失敗!---我們嘗試按順序構建AIntASym ,像ADT的結構一樣,在一個不相交的結構中一個接一個地嘗試。

VI。

整個社會就是這樣

atom''' =     AInt . read <$> many1 digit
          <|> ASym <$> many1 (    char '/' 
                              <|> satisfy isAlpha)

這展示了解析器組合器的樂趣。 整個事物是使用小巧,可組合的簡單部件從地面構建的。 每個人都做了一個非常小的工作,但他們一起跨越了大量的解析器。

您還可以使用ADT中的更多分支,更完整指定的符號類型解析器或使用<?>故障裝飾輕松擴充此語法,以便在失敗的分析中包含大量錯誤消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM