1 module Rakka.Wiki.Parser
9 import Text.ParserCombinators.Parsec
12 type CommandTypeOf = String -> Maybe CommandType
15 wikiPage :: CommandTypeOf -> Parser WikiPage
17 = do xs <- many (blockElement cmdTypeOf)
20 (newline >> return ())
26 blockElement :: CommandTypeOf -> Parser BlockElement
27 blockElement cmdTypeOf
28 = try $ do skipMany ( comment
30 (newline >> return ())
32 foldr (<|>) pzero [ heading
34 , listElement cmdTypeOf
35 , definitionList cmdTypeOf
37 , leadingSpaced cmdTypeOf
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 :: CommandTypeOf -> Parser BlockElement
75 listElement cmdTypeOf = listElement' [] >>= return . List
77 listElement' :: [Char] -> Parser ListElement
81 xs <- items (stack ++ [t])
82 return (ListElement (toType t) xs)
85 items :: [Char] -> Parser [ListItem]
86 items stack = do xs <- many1 $ inlineElement cmdTypeOf
87 nested <- option Nothing
88 $ try $ do skipMany comment
91 listElement' stack >>= return . Just
93 return $ (map Right xs ++ map Left (catMaybes [nested])) : rest
95 (try $ do skipMany comment
104 toType :: Char -> ListType
106 toType '#' = Numbered
109 definitionList :: CommandTypeOf -> Parser BlockElement
110 definitionList cmdTypeOf = many1 definition >>= return . DefinitionList
112 definition :: Parser Definition
113 definition = do char ';'
115 tHead <- inlineElement cmdTypeOf
118 return (Definition (tHead:tRest) d)
122 term :: Parser [InlineElement]
123 term = (char ':' >> ws >> return [])
125 (newline >> char ':' >> ws >> return [])
127 do x <- inlineElement cmdTypeOf
133 description :: Parser [InlineElement]
134 description = do x <- inlineElement cmdTypeOf
142 return (Text "\n" : xs)
145 (newline >> return [])
149 "description of term"
152 pdata :: Parser BlockElement
153 pdata = do try (string "<![PDATA[")
156 return (Preformatted [Text x])
158 pdata' :: Parser String
159 pdata' = do try (many (oneOf " \t\n") >> string "]]>")
167 leadingSpaced :: CommandTypeOf -> Parser BlockElement
168 leadingSpaced cmdTypeOf = (char ' ' >> leadingSpaced' >>= return . Preformatted)
172 leadingSpaced' :: Parser [InlineElement]
173 leadingSpaced' = do x <- inlineElement cmdTypeOf
183 return . (Text "\n" :)
189 blockCommand :: Parser BlockElement
190 blockCommand = pzero -- not implemented
193 paragraph :: CommandTypeOf -> Parser BlockElement
194 paragraph cmdTypeOf = paragraph' >>= return . Paragraph
196 paragraph' :: Parser [InlineElement]
197 paragraph' = do x <- inlineElement cmdTypeOf
198 xs <- try ( do newline
201 -- \n で文字列が終はってゐたら、ここ
206 ((oneOf ('\n':blockSymbols) >> pzero) <|> return ())
207 ys <- (paragraph' <|> return [])
208 return (Text "\n" : ys)
209 -- \n があり、その次に \n または
210 -- blockSymbols があれば、fail して
211 -- 最初の newline を讀んだ所まで卷き
216 -- それ以外の場合は次の inlineElement から
217 -- を讀んで見る。但し一つも無くても良い。
219 return [] -- 全部失敗したらここで終了。
223 blockCmd :: CommandTypeOf -> Parser BlockElement
225 = (try $ do (tagName, tagAttrs) <- openTag
226 case cmdTypeOf tagName of
227 Just BlockCommandType
230 return $ BlockCmd $ BlockCommand {
232 , bCmdAttributes = tagAttrs
238 (try $ do (tagName, tagAttrs) <- emptyTag
239 case cmdTypeOf tagName of
240 Just BlockCommandType
241 -> return $ BlockCmd $ BlockCommand {
243 , bCmdAttributes = tagAttrs
251 contents :: Parser [BlockElement]
252 contents = do x <- blockElement cmdTypeOf
256 (newline >> contents)
258 (comment >> contents)
263 inlineElement :: CommandTypeOf -> Parser InlineElement
264 inlineElement cmdTypeOf
265 = try $ do skipMany comment
266 foldr (<|>) pzero [ cdata
267 , apostrophes cmdTypeOf
270 , inlineCmd cmdTypeOf
274 cdata :: Parser InlineElement
275 cdata = try (string "<![CDATA[") >> cdata' >>= return . Text
277 cdata' :: Parser String
278 cdata' = do try (string "]]>")
286 text :: Parser InlineElement
289 many (noneOf ('\n':inlineSymbols))
291 return . Text . (':' :)
292 -- 定義リストとの關係上、コロンは先頭にしか來れない。
295 ( many1 (noneOf ('\n':inlineSymbols))
303 apostrophes :: CommandTypeOf -> Parser InlineElement
304 apostrophes cmdTypeOf = foldr (<|>) pzero (map try [apos1, apos2, apos3, apos4, apos5])
306 apos1 = apos 1 >> return (Text "'")
309 xs <- many1 $ inlineElement cmdTypeOf
314 xs <- many1 $ inlineElement cmdTypeOf
318 apos4 = apos 4 >> return (Text "'")
321 xs <- many1 $ inlineElement cmdTypeOf
323 return (Italic [Bold xs])
325 apos :: Int -> Parser ()
326 apos n = count n (char '\'') >> notFollowedBy (char '\'')
329 pageLink :: Parser InlineElement
330 pageLink = do try (string "[[")
331 page <- option Nothing
332 (many1 (noneOf "#|]") >>= return . Just)
333 fragment <- option Nothing
334 (char '#' >> many1 (noneOf "|]") >>= return . Just)
335 text <- option Nothing
336 (char '|' >> many1 (satisfy (/= ']')) >>= return . Just)
338 case (page, fragment) of
339 (Nothing, Nothing) -> pzero
343 return $ PageLink page fragment text
348 inlineCmd :: CommandTypeOf -> Parser InlineElement
350 = (try $ do (tagName, tagAttrs) <- openTag
351 case cmdTypeOf tagName of
352 Just InlineCommandType
355 return $ InlineCmd $ InlineCommand {
357 , iCmdAttributes = tagAttrs
363 (try $ do (tagName, tagAttrs) <- emptyTag
364 case cmdTypeOf tagName of
365 Just InlineCommandType
366 -> return $ InlineCmd $ InlineCommand {
368 , iCmdAttributes = tagAttrs
376 contents :: Parser [InlineElement]
377 contents = do x <- inlineElement cmdTypeOf
381 (comment >> contents)
383 (newline >> contents >>= return . (Text "\n" :))
388 openTag :: Parser (String, [Attribute])
389 openTag = try $ do char '<'
393 attrs <- many $ do attr <- tagAttr
400 emptyTag :: Parser (String, [Attribute])
401 emptyTag = try $ do char '<'
405 attrs <- many $ do attr <- tagAttr
414 closeTag :: String -> Parser ()
415 closeTag name = try $ do char '<'
425 tagAttr :: Parser (String, String)
426 tagAttr = do name <- many1 letter
429 value <- many (satisfy (/= '"'))
435 comment = (try (string "<!--") >> skipTillEnd 1)
439 skipTillEnd :: Int -> Parser ()
440 skipTillEnd level = ( (try (string "<!--") >> skipTillEnd (level + 1))
442 (try (string "-->") >> case level of
444 n -> skipTillEnd (n - 1))
446 (anyChar >> skipTillEnd level)
450 blockSymbols :: [Char]
451 blockSymbols = " =-*#;<"
454 inlineSymbols :: [Char]
455 inlineSymbols = "<[:'"
459 ws = skipMany ( (oneOf " \t" >> return ())
466 eol = ( (newline >> return ())