history

青木日記 RSS

<前の日 | この月 | 次の日>

2006-02-28

Haskell で行指向パーサ

Wiki のために行指向パーサが欲しくなったので、Parsec を使って書いてみた。 手間取るかと思ったが Text.ParserCombinator.Parsec.Char のコードをそっくり真似て書いたらあっさり動いてしまった。

module LineParser (LineParser, indented, blank, firstChar, anyLine) where
 
import Text.ParserCombinators.Parsec.Prim
import Text.ParserCombinators.Parsec.Pos
import TextUtils
import Data.Char (isSpace)
 
type LineParser st a = GenParser String st a
 
indented = firstChar isSpace
 
blank = satisfy (null . strip)
 
firstChar f = satisfy (testFirstChar f)
 
testFirstChar f ""    = False
testFirstChar f (c:_) = f c
 
anyLine = satisfy (const True)
 
satisfy :: (String -> Bool) -> LineParser st String
satisfy f = tokenPrim (\s -> show s)
                      (\pos s ss -> incSourceLine pos 1)
                      (\s -> if f s then Just s else Nothing)

さっそくこれを使って Wiki パーサを書きなおしてみた。 (以下パースのコードのみ抜粋)

compile :: String -> HTML
compile str = case parse document "" (lines str) of
                Right ptext -> ptext
                Left err -> p [Text (escape $ show err)] -- must not happen
 
document = do ss <- many block
              return (concat ss)
 
block  = (blank >> return [])
      <|> heading
      <|> ulist
      <|> olist
      <|> dlist
      <|> preformatted
      <|> paragraph
 
heading = do line <- firstChar (== '=')
             let (mark, label) = span (== '=') line
             return $ h (length mark) [Text (escape $ strip label)]
 
ulist = nestedList '*' ul
olist = nestedList '#' ol
 
nestedList mark xl = do items <- many1 itemLine
                        return $ compileList items
  where
    itemLine = do s <- firstChar (== mark)
                  ss <- many indented
                  return $ join (s:ss)
 
dlist = do lines <- many1 (firstChar (== ':'))
           return $ dl (concatMap compileItem lines)
 
preformatted = do lines <- many1 indented
                  return $ pre [Text . escape . join . unindentBlock $ lines]
 
paragraph = do line <- anyLine
               lines <- many (firstChar isNoFunc)
               return $ p . compileText . join $ (line:lines)
  where
    isNoFunc = (`notElem` "=*#: \t\r\n\v\f")

すっきりした。

(23:27)

天ぷらとフライの違い

天ぷら: 小麦粉

フライ: パン粉

(03:49)

よつば

なにいーっ! youtube って you tube だったのか! なぜか最後の e が a に見えたらしく「ようつば」だとばっかり思ってた。

さらに言えば「よつばと!」と何か関係があるものだと思っていた。

(04:00)

名前
メールアドレス

<前の日 | この月 | 次の日>
2002|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|04|05|06|09|10|
2009|07|
2010|09|

Copyright (c) 2002-2007 青木峰郎 / Minero Aoki. All rights reserved. LIRS