aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Krotov <ilabdsf@gmail.com>2018-01-30 19:09:07 +0300
committerAlexander Krotov <ilabdsf@gmail.com>2018-01-31 01:38:42 +0300
commit00b7ab8d0064c71bf33ce558ffa1e4c07e00f9b6 (patch)
treec496891584c0f3da41dbdc12b2b59ebeb4fec28a
parent309595aff33994d8325af518424eb6831d779de8 (diff)
downloadpandoc-00b7ab8d0064c71bf33ce558ffa1e4c07e00f9b6.tar.gz
Muse reader: replace ParserState with MuseState
-rw-r--r--src/Text/Pandoc/Readers/Muse.hs133
1 files changed, 104 insertions, 29 deletions
diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs
index c4175c4b2..4e1bb95ec 100644
--- a/src/Text/Pandoc/Readers/Muse.hs
+++ b/src/Text/Pandoc/Readers/Muse.hs
@@ -42,9 +42,11 @@ module Text.Pandoc.Readers.Muse (readMuse) where
import Control.Monad
import Control.Monad.Except (throwError)
import Data.Char (isLetter)
+import Data.Default
import Data.List (stripPrefix, intercalate)
import Data.List.Split (splitOn)
import qualified Data.Map as M
+import qualified Data.Set as Set
import Data.Maybe (fromMaybe)
import Data.Text (Text, unpack)
import System.FilePath (takeExtension)
@@ -55,7 +57,7 @@ import Text.Pandoc.Class (PandocMonad (..))
import Text.Pandoc.Definition
import Text.Pandoc.Logging
import Text.Pandoc.Options
-import Text.Pandoc.Parsing
+import Text.Pandoc.Parsing hiding (F)
import Text.Pandoc.Readers.HTML (htmlTag)
import Text.Pandoc.Shared (crFilter, underlineSpan)
@@ -65,12 +67,61 @@ readMuse :: PandocMonad m
-> Text
-> m Pandoc
readMuse opts s = do
- res <- readWithM parseMuse def{ stateOptions = opts } (unpack (crFilter s))
+ res <- readWithM parseMuse def{ museOptions = opts } (unpack (crFilter s))
case res of
Left e -> throwError e
Right d -> return d
-type MuseParser = ParserT String ParserState
+type F = Future MuseState
+
+data MuseState = MuseState { museMeta :: F Meta -- ^ Document metadata
+ , museOptions :: ReaderOptions
+ , museHeaders :: M.Map Inlines String -- ^ List of headers and ids (used for implicit ref links)
+ , museIdentifierList :: Set.Set String
+ , museLastStrPos :: Maybe SourcePos -- ^ Position after last str parsed
+ , museLogMessages :: [LogMessage]
+ , museNotes :: M.Map String (SourcePos, F Blocks)
+ , museInQuote :: Bool
+ , museInList :: Bool
+ , museInLink :: Bool
+ }
+
+instance Default MuseState where
+ def = defaultMuseState
+
+defaultMuseState :: MuseState
+defaultMuseState = MuseState { museMeta = return nullMeta
+ , museOptions = def
+ , museHeaders = M.empty
+ , museIdentifierList = Set.empty
+ , museLastStrPos = Nothing
+ , museLogMessages = []
+ , museNotes = M.empty
+ , museInQuote = False
+ , museInList = False
+ , museInLink = False
+ }
+
+type MuseParser = ParserT String MuseState
+
+instance HasReaderOptions MuseState where
+ extractReaderOptions = museOptions
+
+instance HasHeaderMap MuseState where
+ extractHeaderMap = museHeaders
+ updateHeaderMap f st = st{ museHeaders = f $ museHeaders st }
+
+instance HasIdentifierList MuseState where
+ extractIdentifierList = museIdentifierList
+ updateIdentifierList f st = st{ museIdentifierList = f $ museIdentifierList st }
+
+instance HasLastStrPosition MuseState where
+ setLastStrPos pos st = st{ museLastStrPos = Just pos }
+ getLastStrPos st = museLastStrPos st
+
+instance HasLogMessages MuseState where
+ addLogMessage m s = s{ museLogMessages = m : museLogMessages s }
+ getLogMessages = reverse . museLogMessages
--
-- main parser
@@ -83,7 +134,7 @@ parseMuse = do
eof
st <- getState
let doc = runF (do Pandoc _ bs <- B.doc <$> blocks
- meta <- stateMeta' st
+ meta <- museMeta st
return $ Pandoc meta bs) st
reportLogMessages
return doc
@@ -131,7 +182,7 @@ atStart :: PandocMonad m => MuseParser m a -> MuseParser m a
atStart p = do
pos <- getPosition
st <- getState
- guard $ stateLastStrPos st /= Just pos
+ guard $ museLastStrPos st /= Just pos
p
--
@@ -167,7 +218,7 @@ directive :: PandocMonad m => MuseParser m ()
directive = do
ext <- getOption readerExtensions
(key, value) <- if extensionEnabled Ext_amuse ext then parseAmuseDirective else parseEmacsDirective
- updateState $ \st -> st { stateMeta' = B.setMeta (translateKey key) <$> value <*> stateMeta' st }
+ updateState $ \st -> st { museMeta = B.setMeta (translateKey key) <$> value <*> museMeta st }
where translateKey "cover" = "cover-image"
translateKey x = x
@@ -179,7 +230,7 @@ parseBlock :: PandocMonad m => MuseParser m (F Blocks)
parseBlock = do
res <- blockElements <|> para
optionMaybe blankline
- trace (take 60 $ show $ B.toList $ runF res defaultParserState)
+ trace (take 60 $ show $ B.toList $ runF res def)
return res
blockElements :: PandocMonad m => MuseParser m (F Blocks)
@@ -222,15 +273,15 @@ separator = try $ do
header :: PandocMonad m => MuseParser m (F Blocks)
header = try $ do
- st <- stateParserContext <$> getState
- q <- stateQuoteContext <$> getState
- getPosition >>= \pos -> guard (st == NullState && q == NoQuote && sourceColumn pos == 1)
+ st <- museInList <$> getState
+ q <- museInQuote <$> getState
+ getPosition >>= \pos -> guard (not st && not q && sourceColumn pos == 1)
level <- fmap length $ many1 $ char '*'
guard $ level <= 5
spaceChar
content <- trimInlinesF . mconcat <$> manyTill inline eol
anchorId <- option "" parseAnchor
- attr <- registerHeader (anchorId, [], []) (runF content defaultParserState)
+ attr <- registerHeader (anchorId, [], []) (runF content def)
return $ B.headerWith attr level <$> content
example :: PandocMonad m => MuseParser m (F Blocks)
@@ -284,7 +335,11 @@ rightTag = snd <$> parseHtmlContent "right"
quoteTag :: PandocMonad m => MuseParser m (F Blocks)
quoteTag = do
- res <- snd <$> withQuoteContext InDoubleQuote (parseHtmlContent "quote")
+ st <- getState
+ let oldInQuote = museInQuote st
+ setState $ st{ museInQuote = True }
+ res <- snd <$> (parseHtmlContent "quote")
+ setState $ st{ museInQuote = oldInQuote }
return $ B.blockQuote <$> res
-- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025
@@ -316,8 +371,8 @@ commentTag = htmlElement "comment" >> return mempty
para :: PandocMonad m => MuseParser m (F Blocks)
para = do
indent <- length <$> many spaceChar
- st <- stateParserContext <$> getState
- let f = if st /= ListItemState && indent >= 2 && indent < 6 then B.blockQuote else id
+ st <- museInList <$> getState
+ let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id
fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement
where
endOfParaElement = lookAhead $ try (eof <|> newBlockElement)
@@ -338,11 +393,11 @@ amuseNoteBlock = try $ do
pos <- getPosition
ref <- noteMarker <* spaceChar
content <- listItemContents
- oldnotes <- stateNotes' <$> getState
+ oldnotes <- museNotes <$> getState
case M.lookup ref oldnotes of
Just _ -> logMessage $ DuplicateNoteReference ref pos
Nothing -> return ()
- updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes }
+ updateState $ \s -> s{ museNotes = M.insert ref (pos, content) oldnotes }
return mempty
-- Emacs version of note
@@ -353,11 +408,11 @@ emacsNoteBlock = try $ do
pos <- getPosition
ref <- noteMarker <* skipSpaces
content <- mconcat <$> blocksTillNote
- oldnotes <- stateNotes' <$> getState
+ oldnotes <- museNotes <$> getState
case M.lookup ref oldnotes of
Just _ -> logMessage $ DuplicateNoteReference ref pos
Nothing -> return ()
- updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes }
+ updateState $ \s -> s{ museNotes = M.insert ref (pos, content) oldnotes }
return mempty
where
blocksTillNote =
@@ -392,10 +447,10 @@ lineBlock = try $ do
withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a
withListContext p = do
state <- getState
- let oldContext = stateParserContext state
- setState $ state { stateParserContext = ListItemState }
+ let oldInList = museInList state
+ setState $ state { museInList = True }
parsed <- p
- updateState (\st -> st {stateParserContext = oldContext})
+ updateState (\st -> st { museInList = oldInList })
return parsed
listItemContents' :: PandocMonad m => Int -> MuseParser m (F Blocks)
@@ -430,18 +485,38 @@ bulletList = try $ do
rest <- many $ listItem (col - 1) (char '-')
return $ B.bulletList <$> sequence (first : rest)
+-- | Parses an ordered list marker and returns list attributes.
+anyMuseOrderedListMarker :: PandocMonad m => MuseParser m ListAttributes
+anyMuseOrderedListMarker = do
+ (style, start) <- decimal <|> lowerAlpha <|> lowerRoman <|> upperAlpha <|> upperRoman
+ char '.'
+ return (start, style, Period)
+
+museOrderedListMarker :: PandocMonad m
+ => ListNumberStyle
+ -> MuseParser m Int
+museOrderedListMarker style = do
+ (_, start) <- case style of
+ Decimal -> decimal
+ UpperRoman -> upperRoman
+ LowerRoman -> lowerRoman
+ UpperAlpha -> upperAlpha
+ LowerAlpha -> lowerAlpha
+ _ -> fail "Unhandled case"
+ char '.'
+ return start
+
orderedList :: PandocMonad m => MuseParser m (F Blocks)
orderedList = try $ do
many spaceChar
pos <- getPosition
let col = sourceColumn pos
guard $ col /= 1
- p@(_, style, delim) <- anyOrderedListMarker
+ p@(_, style, _) <- anyMuseOrderedListMarker
guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman]
- guard $ delim == Period
void spaceChar <|> lookAhead eol
first <- listItemContents
- rest <- many $ listItem (col - 1) (orderedListMarker style delim)
+ rest <- many $ listItem (col - 1) (museOrderedListMarker style)
return $ B.orderedListWith p <$> sequence (first : rest)
definitionListItem :: PandocMonad m => Int -> MuseParser m (F (Inlines, [Blocks]))
@@ -606,12 +681,12 @@ footnote :: PandocMonad m => MuseParser m (F Inlines)
footnote = try $ do
ref <- noteMarker
return $ do
- notes <- asksF stateNotes'
+ notes <- asksF museNotes
case M.lookup ref notes of
Nothing -> return $ B.str $ "[" ++ ref ++ "]"
Just (_pos, contents) -> do
st <- askF
- let contents' = runF contents st { stateNotes' = M.empty }
+ let contents' = runF contents st { museNotes = M.empty }
return $ B.note contents'
whitespace :: PandocMonad m => MuseParser m (F Inlines)
@@ -713,10 +788,10 @@ symbol = return . B.str <$> count 1 nonspaceChar
link :: PandocMonad m => MuseParser m (F Inlines)
link = try $ do
st <- getState
- guard $ stateAllowLinks st
- setState $ st{ stateAllowLinks = False }
+ guard $ not $ museInLink st
+ setState $ st{ museInLink = True }
(url, title, content) <- linkText
- setState $ st{ stateAllowLinks = True }
+ setState $ st{ museInLink = False }
return $ case stripPrefix "URL:" url of
Nothing -> if isImageUrl url
then B.image url title <$> fromMaybe (return mempty) content