module Rakka.Wiki.Parser ( wikiPage ) where import Rakka.Wiki import Text.ParserCombinators.Parsec wikiPage :: Parser WikiPage wikiPage = do xs <- many (try blockElement) skipMany ( comment <|> (newline >> return ()) ) eof return xs blockElement :: Parser BlockElement blockElement = skipMany ( comment <|> (newline >> return ()) ) >> ( heading <|> paragraph ) heading :: Parser BlockElement heading = foldr (<|>) pzero (map heading' [1..5]) "heading" where heading' :: Int -> Parser BlockElement heading' n = do try $ do count n (char '=') notFollowedBy (char '=') ws x <- notFollowedBy (char '=') >> anyChar xs <- manyTill anyChar (try $ ws >> ( count n (char '=') ("trailing " ++ take n (repeat '=')) ) ) ws eol return (Heading n (x:xs)) paragraph :: Parser BlockElement paragraph = paragraph' >>= return . Paragraph where paragraph' :: Parser [InlineElement] paragraph' = do x <- inlineElement xs <- try ( do newline eof return [] -- \n で文字列が終はってゐたら、ここ -- で終了。 ) <|> try ( do newline ((oneOf ('\n':blockSymbols) >> pzero) <|> return ()) ys <- (paragraph' <|> return []) return (Text "\n" : ys) -- \n があり、その次に \n または -- blockSymbols があれば、fail して -- 最初の newline を讀んだ所まで卷き -- 戻す。 ) <|> try paragraph' -- それ以外の場合は次の inlineElement から -- を讀んで見る。但し一つも無くても良い。 <|> return [] -- 全部失敗したらここで終了。 return (x:xs) inlineElement :: Parser InlineElement inlineElement = skipMany comment >> ( try text <|> try pageLink ) text :: Parser InlineElement text = many1 (noneOf ('\n':inlineSymbols)) >>= return . Text pageLink :: Parser InlineElement pageLink = do string "[[" page <- option Nothing (many1 (noneOf "#|]") >>= return . Just) fragment <- option Nothing (char '#' >> many1 (noneOf "|]") >>= return . Just) text <- option Nothing (char '|' >> many1 (noneOf "]") >>= return . Just) case (page, fragment) of (Nothing, Nothing) -> pzero (_, _) -> return () string "]]" return $ PageLink page fragment text "page link" comment :: Parser () comment = (try (string "") >> case level of 1 -> return () n -> skipTillEnd (n - 1)) <|> (anyChar >> skipTillEnd level) ) blockSymbols :: [Char] blockSymbols = "=" inlineSymbols :: [Char] inlineSymbols = "<[" -- white space ws :: Parser () ws = skipMany ( (oneOf " \t" >> return ()) <|> comment ) -- end of line eol :: Parser () eol = ( (newline >> return ()) <|> eof )