1 module Rakka.Wiki.Parser
8 import Text.ParserCombinators.Parsec
11 wikiPage :: Parser WikiPage
12 wikiPage = do xs <- many (try blockElement)
15 (newline >> return ())
21 blockElement :: Parser BlockElement
22 blockElement = skipMany ( comment
24 (newline >> return ())
43 heading :: Parser BlockElement
44 heading = foldr (<|>) pzero (map heading' [1..5])
48 heading' :: Int -> Parser BlockElement
49 heading' n = do try $ do count n (char '=')
50 notFollowedBy (char '=')
52 x <- notFollowedBy (char '=') >> anyChar
53 xs <- manyTill anyChar (try $ ws >> ( count n (char '=')
55 ("trailing " ++ take n (repeat '='))
60 return (Heading n (x:xs))
63 horizontalLine :: Parser BlockElement
64 horizontalLine = try ( do count 4 (char '-')
74 listElement :: Parser BlockElement
75 listElement = listElement' [] >>= return . List
77 listElement' :: [Char] -> Parser ListElement
79 = try $ do t <- oneOf "*#"
81 xs <- items (stack ++ [t])
82 return (ListElement (toType t) xs)
85 items :: [Char] -> Parser [ListItem]
86 items stack = do xs <- many1 inlineElement
87 nested <- option Nothing
90 listElement' stack >>= return . Just
92 return $ (map Right xs ++ map Left (catMaybes [nested])) : rest
102 toType :: Char -> ListType
104 toType '#' = Numbered
107 definitionList :: Parser BlockElement
108 definitionList = many1 definition >>= return . DefinitionList
110 definition :: Parser Definition
111 definition = do char ';'
113 tHead <- inlineElement
116 return (Definition (tHead:tRest) d)
120 term :: Parser [InlineElement]
121 term = (char ':' >> ws >> return [])
123 (newline >> char ':' >> ws >> return [])
125 do x <- inlineElement
131 description :: Parser [InlineElement]
132 description = do x <- inlineElement
140 return (Text "\n" : xs)
143 (newline >> return [])
147 "description of term"
150 pdata :: Parser BlockElement
151 pdata = do try (string "<![PDATA[")
154 return (Preformatted [Text x])
156 pdata' :: Parser String
157 pdata' = do try (many (oneOf " \t\n") >> string "]]>")
165 leadingSpaced :: Parser BlockElement
166 leadingSpaced = (char ' ' >> leadingSpaced' >>= return . Preformatted)
170 leadingSpaced' :: Parser [InlineElement]
171 leadingSpaced' = do x <- inlineElement
181 return . (Text "\n" :)
187 blockTag :: Parser BlockElement
188 blockTag = pzero -- not implemented
191 paragraph :: Parser BlockElement
192 paragraph = paragraph' >>= return . Paragraph
194 paragraph' :: Parser [InlineElement]
195 paragraph' = do x <- inlineElement
196 xs <- try ( do newline
199 -- \n で文字列が終はってゐたら、ここ
204 ((oneOf ('\n':blockSymbols) >> pzero) <|> return ())
205 ((blockTag >> pzero) <|> return ())
206 ys <- (paragraph' <|> return [])
207 return (Text "\n" : ys)
208 -- \n があり、その次に \n、ブロックタ
209 -- グまたは blockSymbols があれば、
210 -- fail して 最初の newline を讀んだ
215 -- それ以外の場合は次の inlineElement から
216 -- を讀んで見る。但し一つも無くても良い。
218 return [] -- 全部失敗したらここで終了。
222 inlineElement :: Parser InlineElement
223 inlineElement = skipMany comment
235 cdata :: Parser InlineElement
236 cdata = try (string "<![CDATA[") >> cdata' >>= return . Text
238 cdata' :: Parser String
239 cdata' = do try (string "]]>")
247 text :: Parser InlineElement
250 many (noneOf ('\n':inlineSymbols))
252 return . Text . (':' :)
253 -- 定義リストとの關係上、コロンは先頭にしか來れない。
256 ( many1 (noneOf ('\n':inlineSymbols))
264 apostrophes :: Parser InlineElement
265 apostrophes = foldr (<|>) pzero (map try [apos1, apos2, apos3, apos4, apos5])
267 apos1 = apos 1 >> return (Text "'")
270 xs <- many1 inlineElement
275 xs <- many1 inlineElement
279 apos4 = apos 4 >> return (Text "'")
282 xs <- many1 inlineElement
284 return (Italic [Bold xs])
286 apos :: Int -> Parser ()
287 apos n = count n (char '\'') >> notFollowedBy (char '\'')
290 pageLink :: Parser InlineElement
291 pageLink = do try (string "[[")
292 page <- option Nothing
293 (many1 (noneOf "#|]") >>= return . Just)
294 fragment <- option Nothing
295 (char '#' >> many1 (noneOf "|]") >>= return . Just)
296 text <- option Nothing
297 (char '|' >> many1 (noneOf "]") >>= return . Just)
299 case (page, fragment) of
300 (Nothing, Nothing) -> pzero
304 return $ PageLink page fragment text
310 comment = (try (string "<!--") >> skipTillEnd 1)
314 skipTillEnd :: Int -> Parser ()
315 skipTillEnd level = ( (try (string "<!--") >> skipTillEnd (level + 1))
317 (try (string "-->") >> case level of
319 n -> skipTillEnd (n - 1))
321 (anyChar >> skipTillEnd level)
325 blockSymbols :: [Char]
326 blockSymbols = " =-*#;"
329 inlineSymbols :: [Char]
330 inlineSymbols = "<[:'"
334 ws = skipMany ( (oneOf " \t" >> return ())
341 eol = ( (newline >> return ())