wvogel日記

自分用の技術備忘録が多めです.

Trifectaを触ってみた

Haskellのパーサーライブラリといえば、
Peggyなどもありますが、やはりParsecというイメージが強い。

で、これまで書いたprojectなんかも殆どParsecを使って書いてきました。

そしたらつい先日、TrifectaとかいうEkmettさんのライブラリが存在することを知りました。

特徴についてはA Parsing Trifectaなんかにも書かれています。

ParsecのようなMonadicパーサーは使いやすく、それに対してMonoidsは並列でincrementalである、と。
この二つを、記述上ではParsecを踏襲して作成したライブラリだそうです。

というわけで、まだその機能の全容を把握できたわけではないのですが、とりあえずこれを使ってXMLパーサを書いてみた。

こんな感じで定義。
同時に、いい感じにShowクラスのインスタンスにしておきます。

data XML = XML{
    tag :: String,
    content :: String,
    attribute :: [(String,String)],
    child :: Maybe XML
    }
import Text.Trifecta
import Control.Applicative

runXML :: Parser [XML]
runXML = (:) <$> xml <*> (runXML <|> return [])

xml :: Parser XML
xml = do
    (tag,attrs) <- (,) <$> (symbol "<" *> many alphaNum) <*> attributes
    (mkXML tag attrs) <$> value <*> node tag

attributes :: Parser [(String,String)]
attributes = do
    spaces
    [] <$ symbol ">" <|> (:) <$> elem <*> attributes where
        elem = (,) <$> attr <*> attrValue
        attr = many alphaNum
        term = spaces *> symbol "=" <* spaces
        attrValue = term *> stringLiteral <* spaces

value :: Parser String
value = many (noneOf "<")

node :: String -> Parser (Maybe XML)
node tagName = Nothing <$ isEnd <|> Just <$> xml <* isEnd where
    isEnd = symbol $ "</" ++ tagName ++ ">"


mkXML :: String -> [(String,String)] -> String -> Maybe XML -> XML
mkXML tag attr content child =
    XML {tag = tag, content = content,
         attribute = attr, child = child}

main = do
    file <- getLine
    parseTest (runXML <* eof) =<< readFile file

という感じで、ほとんどParsecと使用感変わらず使うことが出来ます。