aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Text/Pandoc/Parsing.hs55
-rw-r--r--src/Text/Pandoc/Readers/Markdown.hs671
-rw-r--r--src/Text/Pandoc/Readers/Org.hs474
-rw-r--r--src/Text/Pandoc/Readers/RST.hs2
4 files changed, 627 insertions, 575 deletions
diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs
index 33120e55d..5c27d3e6d 100644
--- a/src/Text/Pandoc/Parsing.hs
+++ b/src/Text/Pandoc/Parsing.hs
@@ -65,8 +65,7 @@ module Text.Pandoc.Parsing ( anyLine,
widthsFromIndices,
gridTableWith,
readWith,
- returnWarnings,
- returnState,
+ readWithWarnings,
readWithM,
testStringWith,
guardEnabled,
@@ -105,8 +104,11 @@ module Text.Pandoc.Parsing ( anyLine,
applyMacros',
Parser,
ParserT,
+ F(..),
+ runF,
+ askF,
+ asksF,
token,
- generalize,
-- * Re-exports from Text.Pandoc.Parsec
Stream,
runParser,
@@ -187,7 +189,7 @@ import Data.Default
import qualified Data.Set as Set
import Control.Monad.Reader
import Control.Monad.Identity
-import Control.Applicative ((<$>), (<*>), (*>), (<*), (<$))
+import Control.Applicative ((<$>), (<*>), (*>), (<*), (<$), Applicative)
import Data.Monoid
import Data.Maybe (catMaybes)
@@ -197,6 +199,22 @@ type Parser t s = Parsec t s
type ParserT = ParsecT
+newtype F a = F { unF :: Reader ParserState a } deriving (Monad, Applicative, Functor)
+
+runF :: F a -> ParserState -> a
+runF = runReader . unF
+
+askF :: F ParserState
+askF = F ask
+
+asksF :: (ParserState -> a) -> F a
+asksF f = F $ asks f
+
+instance Monoid a => Monoid (F a) where
+ mempty = return mempty
+ mappend = liftM2 mappend
+ mconcat = liftM mconcat . sequence
+
-- | Parse any line of text
anyLine :: Stream [Char] m Char => ParserT [Char] st m [Char]
anyLine = do
@@ -860,18 +878,15 @@ readWith :: Parser [Char] st a
-> Either PandocError a
readWith p t inp = runIdentity $ readWithM p t inp
-returnWarnings :: (Stream s m c)
- => ParserT s ParserState m a
- -> ParserT s ParserState m (a, [String])
-returnWarnings p = do
+readWithWarnings :: Parser [Char] ParserState a
+ -> ParserState
+ -> String
+ -> Either PandocError (a, [String])
+readWithWarnings p = readWith $ do
doc <- p
warnings <- stateWarnings <$> getState
return (doc, warnings)
--- | Return the final internal state with the result of a parser
-returnState :: (Stream s m c) => ParsecT s st m a -> ParsecT s st m (a, st)
-returnState p = (,) <$> p <*> getState
-
-- | Parse a string with @parser@ (for testing).
testStringWith :: (Show a, Stream [Char] Identity Char)
=> ParserT [Char] ParserState Identity a
@@ -893,6 +908,7 @@ data ParserState = ParserState
stateNotes :: NoteTable, -- ^ List of notes (raw bodies)
stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies)
stateMeta :: Meta, -- ^ Document metadata
+ stateMeta' :: F Meta, -- ^ Document metadata
stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used
stateHeaders :: M.Map Inlines String, -- ^ List of headers and ids (used for implicit ref links)
stateIdentifiers :: [String], -- ^ List of header identifiers used
@@ -907,8 +923,7 @@ data ParserState = ParserState
stateCaption :: Maybe Inlines, -- ^ Caption in current environment
stateInHtmlBlock :: Maybe String, -- ^ Tag type of HTML block being parsed
stateMarkdownAttribute :: Bool, -- ^ True if in markdown=1 context
- stateWarnings :: [String], -- ^ Warnings generated by the parser
- stateInFootnote :: Bool -- ^ True if in a footnote block.
+ stateWarnings :: [String] -- ^ Warnings generated by the parser
}
instance Default ParserState where
@@ -990,6 +1005,7 @@ defaultParserState =
stateNotes = [],
stateNotes' = [],
stateMeta = nullMeta,
+ stateMeta' = return nullMeta,
stateHeaderTable = [],
stateHeaders = M.empty,
stateIdentifiers = [],
@@ -1002,8 +1018,7 @@ defaultParserState =
stateCaption = Nothing,
stateInHtmlBlock = Nothing,
stateMarkdownAttribute = False,
- stateWarnings = [],
- stateInFootnote = False }
+ stateWarnings = []}
-- | Succeed only if the extension is enabled.
guardEnabled :: (Stream s m a, HasReaderOptions st) => Extension -> ParserT s st m ()
@@ -1042,7 +1057,7 @@ data QuoteContext
type NoteTable = [(String, String)]
-type NoteTable' = [(String, Blocks)] -- used in markdown reader
+type NoteTable' = [(String, F Blocks)] -- used in markdown reader
newtype Key = Key String deriving (Show, Read, Eq, Ord)
@@ -1238,15 +1253,11 @@ applyMacros' target = do
else return target
-- | Append a warning to the log.
-addWarning :: (Stream s m c) => Maybe SourcePos -> String -> ParserT s ParserState m ()
+addWarning :: Maybe SourcePos -> String -> Parser [Char] ParserState ()
addWarning mbpos msg =
updateState $ \st -> st{
stateWarnings = (msg ++ maybe "" (\pos -> " " ++ show pos) mbpos) :
stateWarnings st }
-
-generalize :: (Monad m) => Parser s st a -> ParserT s st m a
-generalize m = mkPT (\ s -> (return $ (return . runIdentity) <$> runIdentity (runParsecT m s)))
-
infixr 5 <+?>
(<+?>) :: (Monoid a, Monad m) => ParserT s st m a -> ParserT s st m a -> ParserT s st m a
a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>)
diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs
index 17270b741..656e4ec66 100644
--- a/src/Text/Pandoc/Readers/Markdown.hs
+++ b/src/Text/Pandoc/Readers/Markdown.hs
@@ -32,7 +32,7 @@ Conversion of markdown-formatted plain text to 'Pandoc' document.
module Text.Pandoc.Readers.Markdown ( readMarkdown,
readMarkdownWithWarnings ) where
-import Data.List ( transpose, sortBy, intersperse, intercalate, elemIndex)
+import Data.List ( transpose, sortBy, findIndex, intersperse, intercalate )
import qualified Data.Map as M
import Data.Scientific (coefficient, base10Exponent)
import Data.Ord ( comparing )
@@ -58,7 +58,6 @@ import Text.Pandoc.Readers.HTML ( htmlTag, htmlInBalanced, isInlineTag, isBlockT
import Data.Monoid (mconcat, mempty)
import Control.Applicative ((<$>), (<*), (*>), (<$), (<*>))
import Control.Monad
-import Control.Monad.Reader
import System.FilePath (takeExtension, addExtension)
import Text.HTML.TagSoup
import Text.HTML.TagSoup.Match (tagOpen)
@@ -67,30 +66,25 @@ import Text.Printf (printf)
import Debug.Trace (trace)
import Text.Pandoc.Error
-type MarkdownParser a = ParserT [Char] ParserState (Reader ParserState) a
+type MarkdownParser = Parser [Char] ParserState
-- | Read markdown from an input string and return a Pandoc document.
readMarkdown :: ReaderOptions -- ^ Reader options
-> String -- ^ String to parse (assuming @'\n'@ line endings)
-> Either PandocError Pandoc
readMarkdown opts s =
- runMarkdown opts s parseMarkdown
+ (readWith parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n")
-- | Read markdown from an input string and return a pair of a Pandoc document
-- and a list of warnings.
readMarkdownWithWarnings :: ReaderOptions -- ^ Reader options
-> String -- ^ String to parse (assuming @'\n'@ line endings)
- -> Either PandocError (Pandoc, [String])
-readMarkdownWithWarnings opts s = runMarkdown opts s (returnWarnings parseMarkdown)
+ -> Either PandocError (Pandoc, [String])
+readMarkdownWithWarnings opts s =
+ (readWithWarnings parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n")
-runMarkdown :: forall a . ReaderOptions -> String -> MarkdownParser a -> Either PandocError a
-runMarkdown opts inp p = fst <$> res
- where
- imd = readWithM (returnState p) def{ stateOptions = opts } (inp ++ "\n\n")
- res :: Either PandocError (a, ParserState)
- res = runReader imd s
- s :: ParserState
- s = either def snd res
+trimInlinesF :: F Inlines -> F Inlines
+trimInlinesF = liftM trimInlines
--
-- Constants and data structure definitions
@@ -127,10 +121,10 @@ inList = do
ctx <- stateParserContext <$> getState
guard (ctx == ListItemState)
-isNull :: Inlines -> Bool
-isNull = B.isNull
+isNull :: F Inlines -> Bool
+isNull ils = B.isNull $ runF ils def
-spnl :: Monad m => ParserT [Char] st m ()
+spnl :: Parser [Char] st ()
spnl = try $ do
skipSpaces
optional newline
@@ -170,9 +164,9 @@ litChar = escapedChar'
-- | Parse a sequence of inline elements between square brackets,
-- including inlines between balanced pairs of square brackets.
-inlinesInBalancedBrackets :: MarkdownParser Inlines
+inlinesInBalancedBrackets :: MarkdownParser (F Inlines)
inlinesInBalancedBrackets = charsInBalancedBrackets >>=
- parseFromString (trimInlines . mconcat <$> many inline)
+ parseFromString (trimInlinesF . mconcat <$> many inline)
charsInBalancedBrackets :: MarkdownParser [Char]
charsInBalancedBrackets = do
@@ -189,16 +183,16 @@ charsInBalancedBrackets = do
-- document structure
--
-titleLine :: MarkdownParser Inlines
+titleLine :: MarkdownParser (F Inlines)
titleLine = try $ do
char '%'
skipSpaces
res <- many $ (notFollowedBy newline >> inline)
<|> try (endline >> whitespace)
newline
- return $ trimInlines $ mconcat res
+ return $ trimInlinesF $ mconcat res
-authorsLine :: MarkdownParser [Inlines]
+authorsLine :: MarkdownParser (F [Inlines])
authorsLine = try $ do
char '%'
skipSpaces
@@ -207,13 +201,13 @@ authorsLine = try $ do
(char ';' <|>
try (newline >> notFollowedBy blankline >> spaceChar))
newline
- return $ filter (not . isNull) $ map (trimInlines . mconcat) authors
+ return $ sequence $ filter (not . isNull) $ map (trimInlinesF . mconcat) authors
-dateLine :: MarkdownParser Inlines
+dateLine :: MarkdownParser (F Inlines)
dateLine = try $ do
char '%'
skipSpaces
- trimInlines . mconcat <$> manyTill inline newline
+ trimInlinesF . mconcat <$> manyTill inline newline
titleBlock :: MarkdownParser ()
titleBlock = pandocTitleBlock <|> mmdTitleBlock
@@ -223,16 +217,20 @@ pandocTitleBlock = try $ do
guardEnabled Ext_pandoc_title_block
lookAhead (char '%')
title <- option mempty titleLine
- author <- option [] authorsLine
+ author <- option (return []) authorsLine
date <- option mempty dateLine
optional blanklines
- let meta' = (if B.isNull title then id else B.setMeta "title" title)
- . (if null author then id else B.setMeta "author" author)
- . (if B.isNull date then id else B.setMeta "date" date)
- $ nullMeta
- updateState $ \st -> st{ stateMeta = stateMeta st <> meta' }
-
-yamlMetaBlock :: MarkdownParser Blocks
+ let meta' = do title' <- title
+ author' <- author
+ date' <- date
+ return $
+ (if B.isNull title' then id else B.setMeta "title" title')
+ . (if null author' then id else B.setMeta "author" author')
+ . (if B.isNull date' then id else B.setMeta "date" date')
+ $ nullMeta
+ updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' }
+
+yamlMetaBlock :: MarkdownParser (F Blocks)
yamlMetaBlock = try $ do
guardEnabled Ext_yaml_metadata_block
pos <- getPosition
@@ -245,7 +243,7 @@ yamlMetaBlock = try $ do
optional blanklines
opts <- stateOptions <$> getState
meta' <- case Yaml.decodeEither' $ UTF8.fromString rawYaml of
- Right (Yaml.Object hashmap) -> return $
+ Right (Yaml.Object hashmap) -> return $ return $
H.foldrWithKey (\k v m ->
if ignorable k
then m
@@ -253,10 +251,10 @@ yamlMetaBlock = try $ do
Left _ -> m
Right v' -> B.setMeta (T.unpack k) v' m)
nullMeta hashmap
- Right Yaml.Null -> return nullMeta
+ Right Yaml.Null -> return $ return nullMeta
Right _ -> do
addWarning (Just pos) "YAML header is not an object"
- return nullMeta
+ return $ return nullMeta
Left err' -> do
case err' of
InvalidYaml (Just YamlParseException{
@@ -275,13 +273,13 @@ yamlMetaBlock = try $ do
_ -> addWarning (Just pos)
$ "Could not parse YAML header: " ++
show err'
- return nullMeta
- updateState $ \st -> st{ stateMeta = stateMeta st <> meta' }
+ return $ return nullMeta
+ updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' }
return mempty
-- ignore fields ending with _
ignorable :: Text -> Bool
-ignorable t = T.pack "_" `T.isSuffixOf` t
+ignorable t = (T.pack "_") `T.isSuffixOf` t
toMetaValue :: ReaderOptions -> Text -> Either PandocError MetaValue
toMetaValue opts x = toMeta <$> readMarkdown opts' (T.unpack x)
@@ -328,8 +326,8 @@ mmdTitleBlock = try $ do
guardEnabled Ext_mmd_title_block
kvPairs <- many1 kvPair
blanklines
- updateState $ \st -> st{ stateMeta = stateMeta st <>
- (Meta $ M.fromList kvPairs) }
+ updateState $ \st -> st{ stateMeta' = stateMeta' st <>
+ return (Meta $ M.fromList kvPairs) }
kvPair :: MarkdownParser (String, MetaValue)
kvPair = try $ do
@@ -353,11 +351,11 @@ parseMarkdown = do
optional titleBlock
blocks <- parseBlocks
st <- getState
- let meta = stateMeta st
- let Pandoc _ bs = B.doc blocks
+ let meta = runF (stateMeta' st) st
+ let Pandoc _ bs = B.doc $ runF blocks st
return $ Pandoc meta bs
-referenceKey :: MarkdownParser Blocks
+referenceKey :: MarkdownParser (F Blocks)
referenceKey = try $ do
pos <- getPosition
skipNonindentSpaces
@@ -384,7 +382,7 @@ referenceKey = try $ do
Just _ -> addWarning (Just pos) $ "Duplicate link reference `" ++ raw ++ "'"
Nothing -> return ()
updateState $ \s -> s { stateKeys = M.insert key target oldkeys }
- return mempty
+ return $ return mempty
referenceTitle :: MarkdownParser String
referenceTitle = try $ do
@@ -404,7 +402,7 @@ quotedTitle c = try $ do
-- | PHP Markdown Extra style abbreviation key. Currently
-- we just skip them, since Pandoc doesn't have an element for
-- an abbreviation.
-abbrevKey :: MarkdownParser Blocks
+abbrevKey :: MarkdownParser (F Blocks)
abbrevKey = do
guardEnabled Ext_abbreviations
try $ do
@@ -413,7 +411,7 @@ abbrevKey = do
char ':'
skipMany (satisfy (/= '\n'))
blanklines
- return mempty
+ return $ return mempty
noteMarker :: MarkdownParser String
noteMarker = string "[^" >> many1Till (satisfy $ not . isBlank) (char ']')
@@ -431,7 +429,7 @@ rawLines = do
rest <- many rawLine
return $ unlines (first:rest)
-noteBlock :: MarkdownParser Blocks
+noteBlock :: MarkdownParser (F Blocks)
noteBlock = try $ do
pos <- getPosition
skipNonindentSpaces
@@ -443,7 +441,7 @@ noteBlock = try $ do
rest <- many $ try $ blanklines >> indentSpaces >> rawLines
let raw = unlines (first:rest) ++ "\n"
optional blanklines
- parsed <- parseFromString (inFootnote parseBlocks) raw
+ parsed <- parseFromString parseBlocks raw
let newnote = (ref, parsed)
oldnotes <- stateNotes' <$> getState
case lookup ref oldnotes of
@@ -452,29 +450,21 @@ noteBlock = try $ do
updateState $ \s -> s { stateNotes' = newnote : oldnotes }
return mempty
-inFootnote :: MarkdownParser a -> MarkdownParser a
-inFootnote p = do
- st <- stateInFootnote <$> getState
- updateState (\s -> s { stateInFootnote = True } )
- r <- p
- updateState (\s -> s { stateInFootnote = st } )
- return r
-
--
-- parsing blocks
--
-parseBlocks :: MarkdownParser Blocks
+parseBlocks :: MarkdownParser (F Blocks)
parseBlocks = mconcat <$> manyTill block eof
-block :: MarkdownParser Blocks
+block :: MarkdownParser (F Blocks)
block = do
tr <- getOption readerTrace
pos <- getPosition
res <- choice [ mempty <$ blanklines
, codeBlockFenced
, yamlMetaBlock
- , guardEnabled Ext_latex_macros *> macro
+ , guardEnabled Ext_latex_macros *> (macro >>= return . return)
-- note: bulletList needs to be before header because of
-- the possibility of empty list items: -
, bulletList
@@ -496,28 +486,29 @@ block = do
, para
, plain
] <?> "block"
- when tr $
+ when tr $ do
+ st <- getState
trace (printf "line %d: %s" (sourceLine pos)
- (take 60 . show . B.toList $ res)) (return ())
+ (take 60 $ show $ B.toList $ runF res st)) (return ())
return res
--
-- header blocks
--
-header :: MarkdownParser Blocks
+header :: MarkdownParser (F Blocks)
header = setextHeader <|> atxHeader <?> "header"
-atxHeader :: MarkdownParser Blocks
+atxHeader :: MarkdownParser (F Blocks)
atxHeader = try $ do
- level <- length <$> many1 (char '#')
+ level <- many1 (char '#') >>= return . length
notFollowedBy $ guardEnabled Ext_fancy_lists >>
(char '.' <|> char ')') -- this would be a list
skipSpaces
- text <- trimInlines . mconcat <$> many (notFollowedBy atxClosing >> inline)
+ text <- trimInlinesF . mconcat <$> many (notFollowedBy atxClosing >> inline)
attr <- atxClosing
- attr' <- registerHeader attr text
- return $ B.headerWith attr' level text
+ attr' <- registerHeader attr (runF text defaultParserState)
+ return $ B.headerWith attr' level <$> text
atxClosing :: MarkdownParser Attr
atxClosing = try $ do
@@ -544,25 +535,25 @@ mmdHeaderIdentifier = do
skipSpaces
return (ident,[],[])
-setextHeader :: MarkdownParser Blocks
+setextHeader :: MarkdownParser (F Blocks)
setextHeader = try $ do
-- This lookahead prevents us from wasting time parsing Inlines
-- unless necessary -- it gives a significant performance boost.
lookAhead $ anyLine >> many1 (oneOf setextHChars) >> blankline
- text <- trimInlines . mconcat <$> many1 (notFollowedBy setextHeaderEnd >> inline)
+ text <- trimInlinesF . mconcat <$> many1 (notFollowedBy setextHeaderEnd >> inline)
attr <- setextHeaderEnd
underlineChar <- oneOf setextHChars
many (char underlineChar)
blanklines
- let level = (fromMaybe 0 $ elemIndex underlineChar setextHChars) + 1
- attr' <- registerHeader attr text
- return $ B.headerWith attr' level text
+ let level = (fromMaybe 0 $ findIndex (== underlineChar) setextHChars) + 1
+ attr' <- registerHeader attr (runF text defaultParserState)
+ return $ B.headerWith attr' level <$> text
--
-- hrule block
--
-hrule :: Monad m => ParserT [Char] st m Blocks
+hrule :: Parser [Char] st (F Blocks)
hrule = try $ do
skipSpaces
start <- satisfy isHruleChar
@@ -570,24 +561,24 @@ hrule = try $ do
skipMany (spaceChar <|> char start)
newline
optional blanklines
- return B.horizontalRule
+ return $ return B.horizontalRule
--
-- code blocks
--
indentedLine :: MarkdownParser String
-indentedLine = indentSpaces >> ((++ "\n") <$> anyLine)
+indentedLine = indentSpaces >> anyLine >>= return . (++ "\n")
-blockDelimiter :: Monad m
- => (Char -> Bool)
+blockDelimiter :: (Char -> Bool)
-> Maybe Int
- -> ParserT [Char] st m Int
+ -> Parser [Char] st Int
blockDelimiter f len = try $ do
c <- lookAhead (satisfy f)
case len of
Just l -> count l (char c) >> many (char c) >> return l
- Nothing -> count 3 (char c) >> ((+ 3) . length <$> many (char c))
+ Nothing -> count 3 (char c) >> many (char c) >>=
+ return . (+ 3) . length
attributes :: MarkdownParser Attr
attributes = try $ do
@@ -632,7 +623,7 @@ specialAttr = do
char '-'
return $ \(id',cs,kvs) -> (id',cs ++ ["unnumbered"],kvs)
-codeBlockFenced :: MarkdownParser Blocks
+codeBlockFenced :: MarkdownParser (F Blocks)
codeBlockFenced = try $ do
c <- try (guardEnabled Ext_fenced_code_blocks >> lookAhead (char '~'))
<|> (guardEnabled Ext_backtick_code_blocks >> lookAhead (char '`'))
@@ -644,7 +635,7 @@ codeBlockFenced = try $ do
blankline
contents <- manyTill anyLine (blockDelimiter (== c) (Just size))
blanklines
- return $ B.codeBlockWith attr $ intercalate "\n" contents
+ return $ return $ B.codeBlockWith attr $ intercalate "\n" contents
-- correctly handle github language identifiers
toLanguageId :: String -> String
@@ -653,7 +644,7 @@ toLanguageId = map toLower . go
go "objective-c" = "objectivec"
go x = x
-codeBlockIndented :: MarkdownParser Blocks
+codeBlockIndented :: MarkdownParser (F Blocks)
codeBlockIndented = do
contents <- many1 (indentedLine <|>
try (do b <- blanklines
@@ -661,15 +652,15 @@ codeBlockIndented = do
return $ b ++ l))
optional blanklines
classes <- getOption readerIndentedCodeClasses
- return $ B.codeBlockWith ("", classes, []) $
+ return $ return $ B.codeBlockWith ("", classes, []) $
stripTrailingNewlines $ concat contents
-lhsCodeBlock :: MarkdownParser Blocks
+lhsCodeBlock :: MarkdownParser (F Blocks)
lhsCodeBlock = do
guardEnabled Ext_literate_haskell
- (B.codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
+ (return . B.codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
(lhsCodeBlockBird <|> lhsCodeBlockLaTeX))
- <|> (B.codeBlockWith ("",["sourceCode","haskell"],[]) <$>
+ <|> (return . B.codeBlockWith ("",["sourceCode","haskell"],[]) <$>
lhsCodeBlockInverseBird)
lhsCodeBlockLaTeX :: MarkdownParser String
@@ -698,7 +689,7 @@ lhsCodeBlockBirdWith c = try $ do
blanklines
return $ intercalate "\n" lns'
-birdTrackLine :: Monad m => Char -> ParserT [Char] st m String
+birdTrackLine :: Char -> Parser [Char] st String
birdTrackLine c = try $ do
char c
-- allow html tags on left margin:
@@ -726,12 +717,12 @@ emailBlockQuote = try $ do
optional blanklines
return raw
-blockQuote :: MarkdownParser Blocks
+blockQuote :: MarkdownParser (F Blocks)
blockQuote = do
raw <- emailBlockQuote
-- parse the extracted block, which may contain various block elements:
- contents <- parseFromString parseBlocks $ intercalate "\n" raw ++ "\n\n"
- return $ B.blockQuote contents
+ contents <- parseFromString parseBlocks $ (intercalate "\n" raw) ++ "\n\n"
+ return $ B.blockQuote <$> contents
--
-- list blocks
@@ -774,7 +765,7 @@ anyOrderedListStart = try $ do
return res
listStart :: MarkdownParser ()
-listStart = bulletListStart <|> void anyOrderedListStart
+listStart = bulletListStart <|> (anyOrderedListStart >> return ())
listLine :: MarkdownParser String
listLine = try $ do
@@ -829,7 +820,7 @@ listContinuationLine = try $ do
return $ result ++ "\n"
listItem :: MarkdownParser a
- -> MarkdownParser Blocks
+ -> MarkdownParser (F Blocks)
listItem start = try $ do
first <- rawListItem start
continuations <- many listContinuation
@@ -845,14 +836,14 @@ listItem start = try $ do
updateState (\st -> st {stateParserContext = oldContext})
return contents
-orderedList :: MarkdownParser Blocks
+orderedList :: MarkdownParser (F Blocks)
orderedList = try $ do
(start, style, delim) <- lookAhead anyOrderedListStart
unless (style `elem` [DefaultStyle, Decimal, Example] &&
delim `elem` [DefaultDelim, Period]) $
guardEnabled Ext_fancy_lists
when (style == Example) $ guardEnabled Ext_example_lists
- items <- many1 $ listItem
+ items <- fmap sequence $ many1 $ listItem
( try $ do
optional newline -- if preceded by Plain block in a list
startpos <- sourceColumn <$> getPosition
@@ -864,12 +855,12 @@ orderedList = try $ do
atMostSpaces (tabStop - (endpos - startpos))
return res )
start' <- option 1 $ guardEnabled Ext_startnum >> return start
- return $ B.orderedListWith (start', style, delim) (compactify' items)
+ return $ B.orderedListWith (start', style, delim) <$> fmap compactify' items
-bulletList :: MarkdownParser Blocks
+bulletList :: MarkdownParser (F Blocks)
bulletList = do
- items <- many1 $ listItem bulletListStart
- return $ B.bulletList (compactify' items)
+ items <- fmap sequence $ many1 $ listItem bulletListStart
+ return $ B.bulletList <$> fmap compactify' items
-- definition lists
@@ -884,14 +875,14 @@ defListMarker = do
else mzero
return ()
-definitionListItem :: Bool -> MarkdownParser (Inlines, [Blocks])
+definitionListItem :: Bool -> MarkdownParser (F (Inlines, [Blocks]))
definitionListItem compact = try $ do
rawLine' <- anyLine
raw <- many1 $ defRawBlock compact
- term <- parseFromString (trimInlines . mconcat <$> many inline) rawLine'
+ term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawLine'
contents <- mapM (parseFromString parseBlocks) raw
optional blanklines
- return (term, contents)
+ return $ liftM2 (,) term (sequence contents)
defRawBlock :: Bool -> MarkdownParser String
defRawBlock compact = try $ do
@@ -914,34 +905,35 @@ defRawBlock compact = try $ do
return $ trimr (firstline ++ "\n" ++ unlines rawlines ++ cont) ++
if hasBlank || not (null cont) then "\n\n" else ""
-definitionList :: MarkdownParser Blocks
+definitionList :: MarkdownParser (F Blocks)
definitionList = try $ do
- lookAhead (anyLine >> optional (blankline >> notFollowedBy table) >>
+ lookAhead (anyLine >>
+ optional (blankline >> notFollowedBy (table >> return ())) >>
-- don't capture table caption as def list!
defListMarker)
compactDefinitionList <|> normalDefinitionList
-compactDefinitionList :: MarkdownParser Blocks
+compactDefinitionList :: MarkdownParser (F Blocks)
compactDefinitionList = do
guardEnabled Ext_compact_definition_lists
- items <- many1 $ definitionListItem True
- return $ B.definitionList (compactify'DL items)
+ items <- fmap sequence $ many1 $ definitionListItem True
+ return $ B.definitionList <$> fmap compactify'DL items
-normalDefinitionList :: MarkdownParser Blocks
+normalDefinitionList :: MarkdownParser (F Blocks)
normalDefinitionList = do
guardEnabled Ext_definition_lists
- items <- many1 $ definitionListItem False
- return $ B.definitionList items
+ items <- fmap sequence $ many1 $ definitionListItem False
+ return $ B.definitionList <$> items
--
-- paragraph block
--
-para :: MarkdownParser Blocks
+para :: MarkdownParser (F Blocks)
para = try $ do
exts <- getOption readerExtensions
- result <- trimInlines . mconcat <$> many1 inline
- option (B.plain result)
+ result <- trimInlinesF . mconcat <$> many1 inline
+ option (B.plain <$> result)
$ try $ do
newline
(blanklines >> return mempty)
@@ -958,17 +950,18 @@ para = try $ do
Just "div" -> () <$
lookAhead (htmlTag (~== TagClose "div"))
_ -> mzero
- return $
- case B.toList result of
+ return $ do
+ result' <- result
+ case B.toList result' of
[Image alt (src,tit)]
| Ext_implicit_figures `Set.member` exts ->
-- the fig: at beginning of title indicates a figure
- B.para $ B.singleton
+ return $ B.para $ B.singleton
$ Image alt (src,'f':'i':'g':':':tit)
- _ -> B.para result
+ _ -> return $ B.para result'
-plain :: MarkdownParser Blocks
-plain = B.plain . trimInlines . mconcat <$> many1 inline
+plain :: MarkdownParser (F Blocks)
+plain = fmap B.plain . trimInlinesF . mconcat <$> many1 inline
--
-- raw html
@@ -979,13 +972,13 @@ htmlElement = rawVerbatimBlock
<|> strictHtmlBlock
<|> liftM snd (htmlTag isBlockTag)
-htmlBlock :: MarkdownParser Blocks
+htmlBlock :: MarkdownParser (F Blocks)
htmlBlock = do
guardEnabled Ext_raw_html
try (do
(TagOpen t attrs) <- lookAhead $ fst <$> htmlTag isBlockTag
(guard (t `elem` ["pre","style","script"]) >>
- B.rawBlock "html" <$> rawVerbatimBlock)
+ (return . B.rawBlock "html") <$> rawVerbatimBlock)
<|> (do guardEnabled Ext_markdown_attribute
oldMarkdownAttribute <- stateMarkdownAttribute <$> getState
markdownAttribute <-
@@ -1004,35 +997,35 @@ htmlBlock = do
<|> (guardEnabled Ext_markdown_in_html_blocks >> rawHtmlBlocks))
<|> htmlBlock'
-htmlBlock' :: MarkdownParser Blocks
+htmlBlock' :: MarkdownParser (F Blocks)
htmlBlock' = try $ do
first <- htmlElement
skipMany spaceChar
optional blanklines
- return $ B.rawBlock "html" first
+ return $ return $ B.rawBlock "html" first
strictHtmlBlock :: MarkdownParser String
strictHtmlBlock = htmlInBalanced (not . isInlineTag)
rawVerbatimBlock :: MarkdownParser String
rawVerbatimBlock = try $ do
- (TagOpen tag _, open) <-
- htmlTag (tagOpen (`elem` ["pre", "style", "script"])
- (const True))
+ (TagOpen tag _, open) <- htmlTag (tagOpen (flip elem
+ ["pre", "style", "script"])
+ (const True))
contents <- manyTill anyChar (htmlTag (~== TagClose tag))
return $ open ++ contents ++ renderTags' [TagClose tag]
-rawTeXBlock :: MarkdownParser Blocks
+rawTeXBlock :: MarkdownParser (F Blocks)
rawTeXBlock = do
guardEnabled Ext_raw_tex
result <- (B.rawBlock "latex" . concat <$>
- generalize rawLaTeXBlock `sepEndBy1` blankline)
+ rawLaTeXBlock `sepEndBy1` blankline)
<|> (B.rawBlock "context" . concat <$>
rawConTeXtEnvironment `sepEndBy1` blankline)
spaces
- return result
+ return $ return result
-rawHtmlBlocks :: MarkdownParser Blocks
+rawHtmlBlocks :: MarkdownParser (F Blocks)
rawHtmlBlocks = do
(TagOpen tagtype _, raw) <- htmlTag isBlockTag
-- try to find closing tag
@@ -1044,10 +1037,10 @@ rawHtmlBlocks = do
contents <- mconcat <$> many (notFollowedBy' closer >> block)
result <-
(closer >>= \(_, rawcloser) -> return (
- (B.rawBlock "html" $ stripMarkdownAttribute raw) <>
+ return (B.rawBlock "html" $ stripMarkdownAttribute raw) <>
contents <>
- (B.rawBlock "html" rawcloser)))
- <|> return (B.rawBlock "html" raw <> contents)
+ return (B.rawBlock "html" rawcloser)))
+ <|> return (return (B.rawBlock "html" raw) <> contents)
updateState $ \st -> st{ stateInHtmlBlock = oldInHtmlBlock }
return result
@@ -1062,12 +1055,12 @@ stripMarkdownAttribute s = renderTags' $ map filterAttrib $ parseTags s
-- line block
--
-lineBlock :: MarkdownParser Blocks
+lineBlock :: MarkdownParser (F Blocks)
lineBlock = try $ do
guardEnabled Ext_line_blocks
lines' <- lineBlockLines >>=
- mapM (parseFromString (trimInlines . mconcat <$> many inline))
- return $ B.para (mconcat $ intersperse B.linebreak lines')
+ mapM (parseFromString (trimInlinesF . mconcat <$> many inline))
+ return $ B.para <$> (mconcat $ intersperse (return B.linebreak) lines')
--
-- Tables
@@ -1075,8 +1068,8 @@ lineBlock = try $ do
-- Parse a dashed line with optional trailing spaces; return its length
-- and the length including trailing space.
-dashedLine :: Monad m => Char
- -> ParserT [Char] st m (Int, Int)
+dashedLine :: Char
+ -> Parser [Char] st (Int, Int)
dashedLine ch = do
dashes <- many1 (char ch)
sp <- many spaceChar
@@ -1087,7 +1080,7 @@ dashedLine ch = do
-- Parse a table header with dashed lines of '-' preceded by
-- one (or zero) line of text.
simpleTableHeader :: Bool -- ^ Headerless table
- -> MarkdownParser ([Blocks], [Alignment], [Int])
+ -> MarkdownParser (F [Blocks], [Alignment], [Int])
simpleTableHeader headless = try $ do
rawContent <- if headless
then return ""
@@ -1106,8 +1099,9 @@ simpleTableHeader headless = try $ do
let rawHeads' = if headless
then replicate (length dashes) ""
else rawHeads
- heads <-
- mapM (parseFromString (mconcat <$> many plain) . trim) rawHeads'
+ heads <- fmap sequence
+ $ mapM (parseFromString (mconcat <$> many plain))
+ $ map trim rawHeads'
return (heads, aligns, indices)
-- Returns an alignment type for a table, based on a list of strings
@@ -1148,30 +1142,30 @@ rawTableLine indices = do
-- Parse a table line and return a list of lists of blocks (columns).
tableLine :: [Int]
- -> MarkdownParser [Blocks]
+ -> MarkdownParser (F [Blocks])
tableLine indices = rawTableLine indices >>=
- mapM (parseFromString (mconcat <$> many plain))
+ fmap sequence . mapM (parseFromString (mconcat <$> many plain))
-- Parse a multiline table row and return a list of blocks (columns).
multilineRow :: [Int]
- -> MarkdownParser [Blocks]
+ -> MarkdownParser (F [Blocks])
multilineRow indices = do
colLines <- many1 (rawTableLine indices)
let cols = map unlines $ transpose colLines
- mapM (parseFromString (mconcat <$> many plain)) cols
+ fmap sequence $ mapM (parseFromString (mconcat <$> many plain)) cols
-- Parses a table caption: inlines beginning with 'Table:'
-- and followed by blank lines.
-tableCaption :: MarkdownParser Inlines
+tableCaption :: MarkdownParser (F Inlines)
tableCaption = try $ do
guardEnabled Ext_table_captions
skipNonindentSpaces
string ":" <|> string "Table:"
- trimInlines . mconcat <$> many1 inline <* blanklines
+ trimInlinesF . mconcat <$> many1 inline <* blanklines
-- Parse a simple table with '---' header and one line per row.
simpleTable :: Bool -- ^ Headerless table
- -> MarkdownParser ([Alignment], [Double], [Blocks], [[Blocks]])
+ -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
simpleTable headless = do
(aligns, _widths, heads', lines') <-
tableWith (simpleTableHeader headless) tableLine
@@ -1185,12 +1179,12 @@ simpleTable headless = do
-- which may be multiline, separated by blank lines, and
-- ending with a footer (dashed line followed by blank line).
multilineTable :: Bool -- ^ Headerless table
- -> MarkdownParser ([Alignment], [Double], [Blocks], [[Blocks]])
+ -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
multilineTable headless =
tableWith (multilineTableHeader headless) multilineRow blanklines tableFooter
multilineTableHeader :: Bool -- ^ Headerless table
- -> MarkdownParser ([Blocks], [Alignment], [Int])
+ -> MarkdownParser (F [Blocks], [Alignment], [Int])
multilineTableHeader headless = try $ do
unless headless $
tableSep >> notFollowedBy blankline
@@ -1212,7 +1206,7 @@ multilineTableHeader headless = try $ do
let rawHeads = if headless
then replicate (length dashes) ""
else map (unlines . map trim) rawHeadsList
- heads <-
+ heads <- fmap sequence $
mapM (parseFromString (mconcat <$> many plain)) $
map trim rawHeads
return (heads, aligns, indices)
@@ -1222,7 +1216,7 @@ multilineTableHeader headless = try $ do
-- which may be grid, separated by blank lines, and
-- ending with a footer (dashed line followed by blank line).
gridTable :: Bool -- ^ Headerless table
- -> MarkdownParser ([Alignment], [Double], [Blocks], [[Blocks]])
+ -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
gridTable headless =
tableWith (gridTableHeader headless) gridTableRow
(gridTableSep '-') gridTableFooter
@@ -1231,14 +1225,14 @@ gridTableSplitLine :: [Int] -> String -> [String]
gridTableSplitLine indices line = map removeFinalBar $ tail $
splitStringByIndices (init indices) $ trimr line
-gridPart :: Monad m => Char -> ParserT [Char] st m (Int, Int)
+gridPart :: Char -> Parser [Char] st (Int, Int)
gridPart ch = do
dashes <- many1 (char ch)
char '+'
let lengthDashes = length dashes
return (lengthDashes, lengthDashes + 1)
-gridDashedLines :: Monad m => Char -> ParserT [Char] st m [(Int,Int)]
+gridDashedLines :: Char -> Parser [Char] st [(Int,Int)]
gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline
removeFinalBar :: String -> String
@@ -1251,7 +1245,7 @@ gridTableSep ch = try $ gridDashedLines ch >> return '\n'
-- | Parse header for a grid table.
gridTableHeader :: Bool -- ^ Headerless table
- -> MarkdownParser ([Blocks], [Alignment], [Int])
+ -> MarkdownParser (F [Blocks], [Alignment], [Int])
gridTableHeader headless = try $ do
optional blanklines
dashes <- gridDashedLines '-'
@@ -1260,7 +1254,9 @@ gridTableHeader headless = try $ do
else many1
(notFollowedBy (gridTableSep '=') >> char '|' >>
many1Till anyChar newline)
- unless headless (void $ gridTableSep '=')
+ if headless
+ then return ()
+ else gridTableSep '=' >> return ()
let lines' = map snd dashes
let indices = scanl (+) 0 lines'
let aligns = replicate (length lines') AlignDefault
@@ -1269,7 +1265,7 @@ gridTableHeader headless = try $ do
then replicate (length dashes) ""
else map (unlines . map trim) $ transpose
$ map (gridTableSplitLine indices) rawContent
- heads <- mapM (parseFromString parseBlocks . trim) rawHeads
+ heads <- fmap sequence $ mapM (parseFromString parseBlocks . trim) rawHeads
return (heads, aligns, indices)
gridTableRawLine :: [Int] -> MarkdownParser [String]
@@ -1280,12 +1276,12 @@ gridTableRawLine indices = do
-- | Parse row of grid table.
gridTableRow :: [Int]
- -> MarkdownParser [Blocks]
+ -> MarkdownParser (F [Blocks])
gridTableRow indices = do
colLines <- many1 (gridTableRawLine indices)
let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $
transpose colLines
- compactify' <$> mapM (parseFromString parseBlocks) cols
+ fmap compactify' <$> fmap sequence (mapM (parseFromString parseBlocks) cols)
removeOneLeadingSpace :: [String] -> [String]
removeOneLeadingSpace xs =
@@ -1311,12 +1307,12 @@ pipeBreak = try $ do
blankline
return (first:rest)
-pipeTable :: MarkdownParser ([Alignment], [Double], [Blocks], [[Blocks]])
+pipeTable :: MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
pipeTable = try $ do
(heads,aligns) <- (,) <$> pipeTableRow <*> pipeBreak
- lines' <- many pipeTableRow
+ lines' <- sequence <$> many pipeTableRow
let widths = replicate (length aligns) 0.0
- return (aligns, widths, heads, lines')
+ return $ (aligns, widths, heads, lines')
sepPipe :: MarkdownParser ()
sepPipe = try $ do
@@ -1324,7 +1320,7 @@ sepPipe = try $ do
notFollowedBy blankline
-- parse a row, also returning probable alignments for org-table cells
-pipeTableRow :: MarkdownParser [Blocks]
+pipeTableRow :: MarkdownParser (F [Blocks])
pipeTableRow = do
nonindentSpaces
openPipe <- (True <$ char '|') <|> return False
@@ -1336,14 +1332,16 @@ pipeTableRow = do
guard $ not (null rest && not openPipe)
optional (char '|')
blankline
- let cells = first:rest
- return $
- map (\ils ->
+ let cells = sequence (first:rest)
+ return $ do
+ cells' <- cells
+ return $ map
+ (\ils ->
case trimInlines ils of
ils' | B.isNull ils' -> mempty
- | otherwise -> B.plain ils') cells
+ | otherwise -> B.plain $ ils') cells'
-pipeTableHeaderPart :: Monad m => ParserT [Char] st m Alignment
+pipeTableHeaderPart :: Parser [Char] st Alignment
pipeTableHeaderPart = try $ do
skipMany spaceChar
left <- optionMaybe (char ':')
@@ -1358,7 +1356,7 @@ pipeTableHeaderPart = try $ do
(Just _,Just _) -> AlignCenter
-- Succeed only if current line contains a pipe.
-scanForPipe :: Monad m => ParserT [Char] st m ()
+scanForPipe :: Parser [Char] st ()
scanForPipe = do
inp <- getInput
case break (\c -> c == '\n' || c == '|') inp of
@@ -1368,22 +1366,22 @@ scanForPipe = do
-- | Parse a table using 'headerParser', 'rowParser',
-- 'lineParser', and 'footerParser'. Variant of the version in
-- Text.Pandoc.Parsing.
-tableWith :: MarkdownParser ([Blocks], [Alignment], [Int])
- -> ([Int] -> MarkdownParser [Blocks])
+tableWith :: MarkdownParser (F [Blocks], [Alignment], [Int])
+ -> ([Int] -> MarkdownParser (F [Blocks]))
-> MarkdownParser sep
-> MarkdownParser end
- -> MarkdownParser ([Alignment], [Double], [Blocks], [[Blocks]])
+ -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
tableWith headerParser rowParser lineParser footerParser = try $ do
(heads, aligns, indices) <- headerParser
- lines' <- rowParser indices `sepEndBy1` lineParser
+ lines' <- fmap sequence $ rowParser indices `sepEndBy1` lineParser
footerParser
numColumns <- getOption readerColumns
- let widths = case indices of
- [] -> replicate (length aligns) 0.0
- _ -> widthsFromIndices numColumns indices
- return (aligns, widths, heads, lines')
+ let widths = if (indices == [])
+ then replicate (length aligns) 0.0
+ else widthsFromIndices numColumns indices
+ return $ (aligns, widths, heads, lines')
-table :: MarkdownParser Blocks
+table :: MarkdownParser (F Blocks)
table = try $ do
frontCaption <- option Nothing (Just <$> tableCaption)
(aligns, widths, heads, lns) <-
@@ -1398,15 +1396,19 @@ table = try $ do
(gridTable False <|> gridTable True)) <?> "table"
optional blanklines
caption <- case frontCaption of
- Nothing -> option mempty tableCaption
+ Nothing -> option (return mempty) tableCaption
Just c -> return c
- return $ B.table caption (zip aligns widths) heads lns
+ return $ do
+ caption' <- caption
+ heads' <- heads
+ lns' <- lns
+ return $ B.table caption' (zip aligns widths) heads' lns'
--
-- inline
--
-inline :: MarkdownParser Inlines
+inline :: MarkdownParser (F Inlines)
inline = choice [ whitespace
, bareURL
, str
@@ -1429,7 +1431,7 @@ inline = choice [ whitespace
, rawLaTeXInline'
, exampleRef
, smart
- , B.singleton <$> charRef
+ , return . B.singleton <$> charRef
, symbol
, ltSign
] <?> "inline"
@@ -1440,42 +1442,43 @@ escapedChar' = try $ do
(guardEnabled Ext_all_symbols_escapable >> satisfy (not . isAlphaNum))
<|> oneOf "\\`*_{}[]()>#+-.!~\""
-escapedChar :: MarkdownParser Inlines
+escapedChar :: MarkdownParser (F Inlines)
escapedChar = do
result <- escapedChar'
case result of
- ' ' -> return $ B.str "\160" -- "\ " is a nonbreaking space
+ ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space
'\n' -> guardEnabled Ext_escaped_line_breaks >>
- return B.linebreak -- "\[newline]" is a linebreak
- _ -> return $ B.str [result]
+ return (return B.linebreak) -- "\[newline]" is a linebreak
+ _ -> return $ return $ B.str [result]
-ltSign :: MarkdownParser Inlines
+ltSign :: MarkdownParser (F Inlines)
ltSign = do
guardDisabled Ext_raw_html
<|> (notFollowedByHtmlCloser >> notFollowedBy' (htmlTag isBlockTag))
char '<'
- return $ B.str "<"
+ return $ return $ B.str "<"
-exampleRef :: MarkdownParser Inlines
+exampleRef :: MarkdownParser (F Inlines)
exampleRef = try $ do
guardEnabled Ext_example_lists
char '@'
lab <- many1 (alphaNum <|> oneOf "-_")
- st <- ask
- return $ case M.lookup lab (stateExamples st) of
- Just n -> B.str (show n)
- Nothing -> B.str ('@':lab)
+ return $ do
+ st <- askF
+ return $ case M.lookup lab (stateExamples st) of
+ Just n -> B.str (show n)
+ Nothing -> B.str ('@':lab)
-symbol :: MarkdownParser Inlines
+symbol :: MarkdownParser (F Inlines)
symbol = do
result <- noneOf "<\\\n\t "
<|> try (do lookAhead $ char '\\'
notFollowedBy' (() <$ rawTeXBlock)
char '\\')
- return $ B.str [result]
+ return $ return $ B.str [result]
-- parses inline code, between n `s and n `s
-code :: MarkdownParser Inlines
+code :: MarkdownParser (F Inlines)
code = try $ do
starts <- many1 (char '`')
skipSpaces
@@ -1485,17 +1488,18 @@ code = try $ do
notFollowedBy (char '`')))
attr <- option ([],[],[]) (try $ guardEnabled Ext_inline_code_attributes >>
optional whitespace >> attributes)
- return $ B.codeWith attr $ trim $ concat result
+ return $ return $ B.codeWith attr $ trim $ concat result
-math :: MarkdownParser Inlines
-math = (B.displayMath <$> (mathDisplay >>= applyMacros'))
- <|> ((B.math <$> (mathInline >>= applyMacros')) <+?>
- ((getOption readerSmart >>= guard) *> apostrophe <* notFollowedBy space))
+math :: MarkdownParser (F Inlines)
+math = (return . B.displayMath <$> (mathDisplay >>= applyMacros'))
+ <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?>
+ ((getOption readerSmart >>= guard) *> (return <$> apostrophe)
+ <* notFollowedBy space)
-- Parses material enclosed in *s, **s, _s, or __s.
-- Designed to avoid backtracking.
enclosure :: Char
- -> MarkdownParser Inlines
+ -> MarkdownParser (F Inlines)
enclosure c = do
-- we can't start an enclosure with _ if after a string and
-- the intraword_underscores extension is enabled:
@@ -1503,13 +1507,13 @@ enclosure c = do
<|> guard (c == '*')
<|> (guard =<< notAfterString)
cs <- many1 (char c)
- (B.str cs <>) <$> whitespace
- <|>
+ (return (B.str cs) <>) <$> whitespace
+ <|> do
case length cs of
3 -> three c
2 -> two c mempty
1 -> one c mempty
- _ -> return $ B.str cs
+ _ -> return (return $ B.str cs)
ender :: Char -> Int -> MarkdownParser ()
ender c n = try $ do
@@ -1522,74 +1526,74 @@ ender c n = try $ do
-- If one c, emit emph and then parse two.
-- If two cs, emit strong and then parse one.
-- Otherwise, emit ccc then the results.
-three :: Char -> MarkdownParser Inlines
+three :: Char -> MarkdownParser (F Inlines)
three c = do
contents <- mconcat <$> many (notFollowedBy (ender c 1) >> inline)
- (ender c 3 >> return ((B.strong . B.emph) contents))
- <|> (ender c 2 >> one c (B.strong contents))
- <|> (ender c 1 >> two c (B.emph contents))
- <|> return (B.str [c,c,c] <> contents)
+ (ender c 3 >> return ((B.strong . B.emph) <$> contents))
+ <|> (ender c 2 >> one c (B.strong <$> contents))
+ <|> (ender c 1 >> two c (B.emph <$> contents))
+ <|> return (return (B.str [c,c,c]) <> contents)
-- Parse inlines til you hit two c's, and emit strong.
-- If you never do hit two cs, emit ** plus inlines parsed.
-two :: Char -> Inlines -> MarkdownParser Inlines
+two :: Char -> F Inlines -> MarkdownParser (F Inlines)
two c prefix' = do
contents <- mconcat <$> many (try $ notFollowedBy (ender c 2) >> inline)
- (ender c 2 >> return (B.strong (prefix' <> contents)))
- <|> return (B.str [c,c] <> (prefix' <> contents))
+ (ender c 2 >> return (B.strong <$> (prefix' <> contents)))
+ <|> return (return (B.str [c,c]) <> (prefix' <> contents))
-- Parse inlines til you hit a c, and emit emph.
-- If you never hit a c, emit * plus inlines parsed.
-one :: Char -> Inlines -> MarkdownParser Inlines
+one :: Char -> F Inlines -> MarkdownParser (F Inlines)
one c prefix' = do
contents <- mconcat <$> many ( (notFollowedBy (ender c 1) >> inline)
<|> try (string [c,c] >>
notFollowedBy (ender c 1) >>
two c mempty) )
- (ender c 1 >> return (B.emph (prefix' <> contents)))
- <|> return (B.str [c] <> (prefix' <> contents))
+ (ender c 1 >> return (B.emph <$> (prefix' <> contents)))
+ <|> return (return (B.str [c]) <> (prefix' <> contents))
-strongOrEmph :: MarkdownParser Inlines
+strongOrEmph :: MarkdownParser (F Inlines)
strongOrEmph = enclosure '*' <|> enclosure '_'
--- | Parses a list oInlines between start and end delimiters.
+-- | Parses a list of inlines between start and end delimiters.
inlinesBetween :: (Show b)
=> MarkdownParser a
-> MarkdownParser b
- -> MarkdownParser Inlines
+ -> MarkdownParser (F Inlines)
inlinesBetween start end =
- (trimInlines . mconcat) <$> try (start >> many1Till inner end)
+ (trimInlinesF . mconcat) <$> try (start >> many1Till inner end)
where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline)
innerSpace = try $ whitespace <* notFollowedBy' end
-strikeout :: MarkdownParser Inlines
-strikeout = B.strikeout <$>
+strikeout :: MarkdownParser (F Inlines)
+strikeout = fmap B.strikeout <$>
(guardEnabled Ext_strikeout >> inlinesBetween strikeStart strikeEnd)
where strikeStart = string "~~" >> lookAhead nonspaceChar
>> notFollowedBy (char '~')
strikeEnd = try $ string "~~"
-superscript :: MarkdownParser Inlines
-superscript = B.superscript <$> try (do
+superscript :: MarkdownParser (F Inlines)
+superscript = fmap B.superscript <$> try (do
guardEnabled Ext_superscript
char '^'
mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '^'))
-subscript :: MarkdownParser Inlines
-subscript = B.subscript <$> try (do
+subscript :: MarkdownParser (F Inlines)
+subscript = fmap B.subscript <$> try (do
guardEnabled Ext_subscript
char '~'
mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '~'))
-whitespace :: MarkdownParser Inlines
-whitespace = spaceChar >> (lb <|> regsp) <?> "whitespace"
+whitespace :: MarkdownParser (F Inlines)
+whitespace = spaceChar >> return <$> (lb <|> regsp) <?> "whitespace"
where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak)
regsp = skipMany spaceChar >> return B.space
-nonEndline :: Monad m => ParserT [Char] st m Char
+nonEndline :: Parser [Char] st Char
nonEndline = satisfy (/='\n')
-str :: MarkdownParser Inlines
+str :: MarkdownParser (F Inlines)
str = do
result <- many1 alphaNum
updateLastStrPos
@@ -1597,14 +1601,14 @@ str = do
isSmart <- getOption readerSmart
if isSmart
then case likelyAbbrev result of
- [] -> return $ B.str result
+ [] -> return $ return $ B.str result
xs -> choice (map (\x ->
try (string x >> oneOf " \n" >>
lookAhead alphaNum >>
- return (B.str $
- result ++ spacesToNbr x ++ "\160"))) xs)
- <|> (return $ B.str result)
- else return $ B.str result
+ return (return $ B.str
+ $ result ++ spacesToNbr x ++ "\160"))) xs)
+ <|> (return $ return $ B.str result)
+ else return $ return $ B.str result
-- | if the string matches the beginning of an abbreviation (before
-- the first period, return strings that would finish the abbreviation.
@@ -1619,7 +1623,7 @@ likelyAbbrev x =
in map snd $ filter (\(y,_) -> y == x) abbrPairs
-- an endline character that can be treated as a space, not a structural break
-endline :: MarkdownParser Inlines
+endline :: MarkdownParser (F Inlines)
endline = try $ do
newline
notFollowedBy blankline
@@ -1632,18 +1636,18 @@ endline = try $ do
notFollowedBy (() <$ (lookAhead (char '`') >> codeBlockFenced))
notFollowedByHtmlCloser
(eof >> return mempty)
- <|> (guardEnabled Ext_hard_line_breaks >> return B.linebreak)
+ <|> (guardEnabled Ext_hard_line_breaks >> return (return B.linebreak))
<|> (guardEnabled Ext_ignore_line_breaks >> return mempty)
- <|> return B.space
+ <|> (return $ return B.space)
--
-- links
--
-- a reference label for a link
-reference :: MarkdownParser (Inlines, String)
+reference :: MarkdownParser (F Inlines, String)
reference = do notFollowedBy' (string "[^") -- footnote reference
- withRaw $ trimInlines <$> inlinesInBalancedBrackets
+ withRaw $ trimInlinesF <$> inlinesInBalancedBrackets
parenthesizedChars :: MarkdownParser [Char]
parenthesizedChars = do
@@ -1671,7 +1675,7 @@ source = do
linkTitle :: MarkdownParser String
linkTitle = quotedTitle '"' <|> quotedTitle '\''
-link :: MarkdownParser Inlines
+link :: MarkdownParser (F Inlines)
link = try $ do
st <- getState
guard $ stateAllowLinks st
@@ -1681,14 +1685,14 @@ link = try $ do
regLink B.link lab <|> referenceLink B.link (lab,raw)
regLink :: (String -> String -> Inlines -> Inlines)
- -> Inlines -> MarkdownParser Inlines
+ -> F Inlines -> MarkdownParser (F Inlines)
regLink constructor lab = try $ do
(src, tit) <- source
- return $ constructor src tit lab
+ return $ constructor src tit <$> lab
-- a link like [this][ref] or [this][] or [this]
referenceLink :: (String -> String -> Inlines -> Inlines)
- -> (Inlines, String) -> MarkdownParser Inlines
+ -> (F Inlines, String) -> MarkdownParser (F Inlines)
referenceLink constructor (lab, raw) = do
sp <- (True <$ lookAhead (char ' ')) <|> return False
(ref,raw') <- option (mempty, "") $
@@ -1702,22 +1706,24 @@ referenceLink constructor (lab, raw) = do
fallback <- parseFromString (mconcat <$> many inline) $ dropBrackets raw
implicitHeaderRefs <- option False $
True <$ guardEnabled Ext_implicit_header_references
- let makeFallback =
- B.str "[" <> fallback <> B.str "]" <>
+ let makeFallback = do
+ parsedRaw' <- parsedRaw
+ fallback' <- fallback
+ return $ B.str "[" <> fallback' <> B.str "]" <>
(if sp && not (null raw) then B.space else mempty) <>
- parsedRaw
- keys <- asks stateKeys
- headers <- asks stateHeaders
- return $
- case M.lookup key keys of
- Nothing ->
- let ref' = if labIsRef then lab else ref in
- if implicitHeaderRefs
- then case M.lookup ref' headers of
- Just ident -> constructor ('#':ident) "" lab
- Nothing -> makeFallback
- else makeFallback
- Just (src,tit) -> constructor src tit lab
+ parsedRaw'
+ return $ do
+ keys <- asksF stateKeys
+ case M.lookup key keys of
+ Nothing -> do
+ headers <- asksF stateHeaders
+ ref' <- if labIsRef then lab else ref
+ if implicitHeaderRefs
+ then case M.lookup ref' headers of
+ Just ident -> constructor ('#':ident) "" <$> lab
+ Nothing -> makeFallback
+ else makeFallback
+ Just (src,tit) -> constructor src tit <$> lab
dropBrackets :: String -> String
dropBrackets = reverse . dropRB . reverse . dropLB
@@ -1726,14 +1732,14 @@ dropBrackets = reverse . dropRB . reverse . dropLB
dropLB ('[':xs) = xs
dropLB xs = xs
-bareURL :: MarkdownParser Inlines
+bareURL :: MarkdownParser (F Inlines)
bareURL = try $ do
guardEnabled Ext_autolink_bare_uris
(orig, src) <- uri <|> emailAddress
notFollowedBy $ try $ spaces >> htmlTag (~== TagClose "a")
- return $ B.link src "" (B.str orig)
+ return $ return $ B.link src "" (B.str orig)
-autoLink :: MarkdownParser Inlines
+autoLink :: MarkdownParser (F Inlines)
autoLink = try $ do
char '<'
(orig, src) <- uri <|> emailAddress
@@ -1742,9 +1748,9 @@ autoLink = try $ do
-- final punctuation. for example: in `<http://hi---there>`,
-- the URI parser will stop before the dashes.
extra <- fromEntities <$> manyTill nonspaceChar (char '>')
- return $ B.link (src ++ escapeURI extra) "" (B.str $ orig ++ extra)
+ return $ return $ B.link (src ++ escapeURI extra) "" (B.str $ orig ++ extra)
-image :: MarkdownParser Inlines
+image :: MarkdownParser (F Inlines)
image = try $ do
char '!'
(lab,raw) <- reference
@@ -1754,33 +1760,38 @@ image = try $ do
_ -> B.image src
regLink constructor lab <|> referenceLink constructor (lab,raw)
-note :: MarkdownParser Inlines
+note :: MarkdownParser (F Inlines)
note = try $ do
guardEnabled Ext_footnotes
- (stateInFootnote <$> getState) >>= guard . not
ref <- noteMarker
- notes <- asks stateNotes'
- return $
+ return $ do
+ notes <- asksF stateNotes'
case lookup ref notes of
- Nothing -> B.str $ "[^" ++ ref ++ "]"
- Just contents -> B.note contents
-
-inlineNote :: MarkdownParser Inlines
+ Nothing -> return $ B.str $ "[^" ++ ref ++ "]"
+ Just contents -> do
+ st <- askF
+ -- process the note in a context that doesn't resolve
+ -- notes, to avoid infinite looping with notes inside
+ -- notes:
+ let contents' = runF contents st{ stateNotes' = [] }
+ return $ B.note contents'
+
+inlineNote :: MarkdownParser (F Inlines)
inlineNote = try $ do
guardEnabled Ext_inline_notes
char '^'
contents <- inlinesInBalancedBrackets
- return . B.note . B.para $ contents
+ return $ B.note . B.para <$> contents
-rawLaTeXInline' :: MarkdownParser Inlines
+rawLaTeXInline' :: MarkdownParser (F Inlines)
rawLaTeXInline' = try $ do
guardEnabled Ext_raw_tex
lookAhead $ char '\\' >> notFollowedBy' (string "start") -- context env
- RawInline _ s <- generalize rawLaTeXInline
- return $ B.rawInline "tex" s
+ RawInline _ s <- rawLaTeXInline
+ return $ return $ B.rawInline "tex" s
-- "tex" because it might be context or latex
-rawConTeXtEnvironment :: Monad m => ParserT [Char] st m String
+rawConTeXtEnvironment :: Parser [Char] st String
rawConTeXtEnvironment = try $ do
string "\\start"
completion <- inBrackets (letter <|> digit <|> spaceChar)
@@ -1789,14 +1800,14 @@ rawConTeXtEnvironment = try $ do
(try $ string "\\stop" >> string completion)
return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion
-inBrackets :: Monad m => (ParserT [Char] st m Char) -> ParserT [Char] st m String
+inBrackets :: (Parser [Char] st Char) -> Parser [Char] st String
inBrackets parser = do
char '['
contents <- many parser
char ']'
return $ "[" ++ contents ++ "]"
-spanHtml :: MarkdownParser Inlines
+spanHtml :: MarkdownParser (F Inlines)
spanHtml = try $ do
guardEnabled Ext_native_spans
(TagOpen _ attrs, _) <- htmlTag (~== TagOpen "span" [])
@@ -1808,10 +1819,10 @@ spanHtml = try $ do
Just s | null ident && null classes &&
map toLower (filter (`notElem` " \t;") s) ==
"font-variant:small-caps"
- -> return $ B.smallcaps contents
- _ -> return $ B.spanWith (ident, classes, keyvals) contents
+ -> return $ B.smallcaps <$> contents
+ _ -> return $ B.spanWith (ident, classes, keyvals) <$> contents
-divHtml :: MarkdownParser Blocks
+divHtml :: MarkdownParser (F Blocks)
divHtml = try $ do
guardEnabled Ext_native_divs
(TagOpen _ attrs, rawtag) <- htmlTag (~== TagOpen "div" [])
@@ -1829,11 +1840,11 @@ divHtml = try $ do
let ident = fromMaybe "" $ lookup "id" attrs
let classes = maybe [] words $ lookup "class" attrs
let keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"]
- return $ B.divWith (ident, classes, keyvals) contents
+ return $ B.divWith (ident, classes, keyvals) <$> contents
else -- avoid backtracing
- return $ B.rawBlock "html" (rawtag <> bls) <> contents
+ return $ return (B.rawBlock "html" (rawtag <> bls)) <> contents
-rawHtmlInline :: MarkdownParser Inlines
+rawHtmlInline :: MarkdownParser (F Inlines)
rawHtmlInline = do
guardEnabled Ext_raw_html
inHtmlBlock <- stateInHtmlBlock <$> getState
@@ -1848,17 +1859,19 @@ rawHtmlInline = do
then (\x -> isInlineTag x &&
not (isCloseBlockTag x))
else not . isTextTag
- return $ B.rawInline "html" result
+ return $ return $ B.rawInline "html" result
-- Citations
-cite :: MarkdownParser Inlines
+cite :: MarkdownParser (F Inlines)
cite = do
guardEnabled Ext_citations
- textualCite <|> do (cs, raw) <- withRaw normalCite
- return $ B.cite cs (B.text raw)
+ citations <- textualCite
+ <|> do (cs, raw) <- withRaw normalCite
+ return $ (flip B.cite (B.text raw)) <$> cs
+ return citations
-textualCite :: MarkdownParser Inlines
+textualCite :: MarkdownParser (F Inlines)
textualCite = try $ do
(_, key) <- citeKey
let first = Citation{ citationId = key
@@ -1872,26 +1885,29 @@ textualCite = try $ do
case mbrest of
Just (rest, raw) ->
return $ (flip B.cite (B.text $ '@':key ++ " " ++ raw) . (first:))
- rest
+ <$> rest
Nothing ->
(do (cs, raw) <- withRaw $ bareloc first
- return $ B.cite cs (B.text $ '@':key ++ " " ++ raw))
- <|> do st <- ask
- return $ case M.lookup key (stateExamples st) of
- Just n -> B.str (show n)
- _ -> B.cite [first] $ B.str $ '@':key
+ return $ (flip B.cite (B.text $ '@':key ++ " " ++ raw)) <$> cs)
+ <|> return (do st <- askF
+ return $ case M.lookup key (stateExamples st) of
+ Just n -> B.str (show n)
+ _ -> B.cite [first] $ B.str $ '@':key)
-bareloc :: Citation -> MarkdownParser [Citation]
+bareloc :: Citation -> MarkdownParser (F [Citation])
bareloc c = try $ do
spnl
char '['
suff <- suffix
- rest <- option [] $ try $ char ';' >> citeList
+ rest <- option (return []) $ try $ char ';' >> citeList
spnl
char ']'
- return $ c{ citationSuffix = B.toList suff } : rest
+ return $ do
+ suff' <- suff
+ rest' <- rest
+ return $ c{ citationSuffix = B.toList suff' } : rest'
-normalCite :: MarkdownParser [Citation]
+normalCite :: MarkdownParser (F [Citation])
normalCite = try $ do
char '['
spnl
@@ -1900,57 +1916,60 @@ normalCite = try $ do
char ']'
return citations
-suffix :: MarkdownParser Inlines
+suffix :: MarkdownParser (F Inlines)
suffix = try $ do
hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
spnl
- rest <- trimInlines . mconcat <$> many (notFollowedBy (oneOf ";]") >> inline)
+ rest <- trimInlinesF . mconcat <$> many (notFollowedBy (oneOf ";]") >> inline)
return $ if hasSpace
- then B.space <> rest
+ then (B.space <>) <$> rest
else rest
-prefix :: MarkdownParser Inlines
-prefix = trimInlines . mconcat <$>
+prefix :: MarkdownParser (F Inlines)
+prefix = trimInlinesF . mconcat <$>
manyTill inline (char ']' <|> liftM (const ']') (lookAhead citeKey))
-citeList :: MarkdownParser [Citation]
-citeList = sepBy1 citation (try $ char ';' >> spnl)
+citeList :: MarkdownParser (F [Citation])
+citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl)
-citation :: MarkdownParser Citation
+citation :: MarkdownParser (F Citation)
citation = try $ do
pref <- prefix
(suppress_author, key) <- citeKey
suff <- suffix
- return Citation{ citationId = key
- , citationPrefix = B.toList pref
- , citationSuffix = B.toList suff
- , citationMode = if suppress_author
- then SuppressAuthor
- else NormalCitation
- , citationNoteNum = 0
- , citationHash = 0
- }
-
-smart :: MarkdownParser Inlines
+ return $ do
+ x <- pref
+ y <- suff
+ return $ Citation{ citationId = key
+ , citationPrefix = B.toList x
+ , citationSuffix = B.toList y
+ , citationMode = if suppress_author
+ then SuppressAuthor
+ else NormalCitation
+ , citationNoteNum = 0
+ , citationHash = 0
+ }
+
+smart :: MarkdownParser (F Inlines)
smart = do
getOption readerSmart >>= guard
doubleQuoted <|> singleQuoted <|>
- choice [apostrophe, dash, ellipses]
+ choice (map (return <$>) [apostrophe, dash, ellipses])
-singleQuoted :: MarkdownParser Inlines
+singleQuoted :: MarkdownParser (F Inlines)
singleQuoted = try $ do
singleQuoteStart
withQuoteContext InSingleQuote $
- B.singleQuoted . trimInlines . mconcat <$>
+ fmap B.singleQuoted . trimInlinesF . mconcat <$>
many1Till inline singleQuoteEnd
-- doubleQuoted will handle regular double-quoted sections, as well
-- as dialogues with an open double-quote without a close double-quote
-- in the same paragraph.
-doubleQuoted :: MarkdownParser Inlines
+doubleQuoted :: MarkdownParser (F Inlines)
doubleQuoted = try $ do
doubleQuoteStart
contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
- (withQuoteContext InDoubleQuote doubleQuoteEnd >> return
- (B.doubleQuoted . trimInlines $ contents))
- <|> return (B.str "\8220" <> contents)
+ (withQuoteContext InDoubleQuote $ doubleQuoteEnd >> return
+ (fmap B.doubleQuoted . trimInlinesF $ contents))
+ <|> (return $ return (B.str "\8220") <> contents)
diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs
index fc63cc11e..ad9dc3ee8 100644
--- a/src/Text/Pandoc/Readers/Org.hs
+++ b/src/Text/Pandoc/Readers/Org.hs
@@ -1,9 +1,6 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
{-
Copyright (C) 2014 Albert Krewinkel <tarleb@moltkeplatz.de>
@@ -39,7 +36,8 @@ import Text.Pandoc.Builder ( Inlines, Blocks, HasMeta(..), (<>)
import Text.Pandoc.Definition
import Text.Pandoc.Options
import qualified Text.Pandoc.Parsing as P
-import Text.Pandoc.Parsing hiding ( newline, orderedListMarker
+import Text.Pandoc.Parsing hiding ( F, unF, askF, asksF, runF
+ , newline, orderedListMarker
, parseFromString, blanklines
)
import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline)
@@ -47,17 +45,17 @@ import Text.Pandoc.Shared (compactify', compactify'DL)
import Text.TeXMath (readTeX, writePandoc, DisplayType(..))
import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
-import Control.Applicative ( pure
+import Control.Applicative ( Applicative, pure
, (<$>), (<$), (<*>), (<*), (*>) )
import Control.Arrow (first)
-import Control.Monad (guard, mplus, mzero, when)
-import Control.Monad.Reader (Reader, runReader, asks, local)
+import Control.Monad (foldM, guard, liftM, liftM2, mplus, mzero, when)
+import Control.Monad.Reader (Reader, runReader, ask, asks, local)
import Data.Char (isAlphaNum, toLower)
import Data.Default
-import Data.List (intersperse, isPrefixOf, isSuffixOf, foldl')
+import Data.List (intersperse, isPrefixOf, isSuffixOf)
import qualified Data.Map as M
import Data.Maybe (fromMaybe, isJust)
-import Data.Monoid (mconcat, mempty, mappend)
+import Data.Monoid (Monoid, mconcat, mempty, mappend)
import Network.HTTP (urlEncode)
import Text.Pandoc.Error
@@ -66,28 +64,19 @@ import Text.Pandoc.Error
readOrg :: ReaderOptions -- ^ Reader options
-> String -- ^ String to parse (assuming @'\n'@ line endings)
-> Either PandocError Pandoc
-readOrg opts s = runOrg opts s parseOrg
+readOrg opts s = flip runReader def $ readWithM parseOrg def{ orgStateOptions = opts } (s ++ "\n\n")
-data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext
- , finalState :: OrgParserState }
+data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal)
-runOrg :: ReaderOptions -> String -> OrgParser a -> Either PandocError a
-runOrg opts inp p = fst <$> res
- where
- imd = readWithM (returnState p) def{ orgStateOptions = opts } (inp ++ "\n\n")
- res = runReader imd def { finalState = s }
- s :: OrgParserState
- s = either def snd res
-
parseOrg :: OrgParser Pandoc
parseOrg = do
blocks' <- parseBlocks
st <- getState
- let meta = orgStateMeta st
+ let meta = runF (orgStateMeta' st) st
let removeUnwantedBlocks = dropCommentTrees . filter (/= Null)
- return $ Pandoc meta $ removeUnwantedBlocks (B.toList $ blocks')
+ return $ Pandoc meta $ removeUnwantedBlocks (B.toList $ runF blocks' st)
-- | Drop COMMENT headers and the document tree below those headers.
dropCommentTrees :: [Block] -> [Block]
@@ -117,7 +106,7 @@ isHeaderLevelLowerEq n blk =
-- Parser State for Org
--
-type OrgNoteRecord = (String, Blocks)
+type OrgNoteRecord = (String, F Blocks)
type OrgNoteTable = [OrgNoteRecord]
type OrgBlockAttributes = M.Map String String
@@ -136,11 +125,12 @@ data OrgParserState = OrgParserState
, orgStateLastStrPos :: Maybe SourcePos
, orgStateLinkFormatters :: OrgLinkFormatters
, orgStateMeta :: Meta
+ , orgStateMeta' :: F Meta
, orgStateNotes' :: OrgNoteTable
}
instance Default OrgParserLocal where
- def = OrgParserLocal NoQuote def
+ def = OrgParserLocal NoQuote
instance HasReaderOptions OrgParserState where
extractReaderOptions = orgStateOptions
@@ -174,13 +164,13 @@ defaultOrgParserState = OrgParserState
, orgStateLastStrPos = Nothing
, orgStateLinkFormatters = M.empty
, orgStateMeta = nullMeta
+ , orgStateMeta' = return nullMeta
, orgStateNotes' = []
}
recordAnchorId :: String -> OrgParser ()
recordAnchorId i = updateState $ \s ->
- let as = orgStateAnchorIds s in
- s{ orgStateAnchorIds = i : as }
+ s{ orgStateAnchorIds = i : (orgStateAnchorIds s) }
addBlockAttribute :: String -> String -> OrgParser ()
addBlockAttribute key val = updateState $ \s ->
@@ -259,6 +249,30 @@ parseFromString parser str' = do
-- Adaptions and specializations of parsing utilities
--
+newtype F a = F { unF :: Reader OrgParserState a
+ } deriving (Monad, Applicative, Functor)
+
+runF :: F a -> OrgParserState -> a
+runF = runReader . unF
+
+askF :: F OrgParserState
+askF = F ask
+
+asksF :: (OrgParserState -> a) -> F a
+asksF f = F $ asks f
+
+instance Monoid a => Monoid (F a) where
+ mempty = return mempty
+ mappend = liftM2 mappend
+ mconcat = fmap mconcat . sequence
+
+trimInlinesF :: F Inlines -> F Inlines
+trimInlinesF = liftM trimInlines
+
+returnF :: a -> OrgParser (F a)
+returnF = return . return
+
+
-- | Like @Text.Parsec.Char.newline@, but causes additional state changes.
newline :: OrgParser Char
newline =
@@ -277,10 +291,10 @@ blanklines =
-- parsing blocks
--
-parseBlocks :: OrgParser Blocks
+parseBlocks :: OrgParser (F Blocks)
parseBlocks = mconcat <$> manyTill block eof
-block :: OrgParser Blocks
+block :: OrgParser (F Blocks)
block = choice [ mempty <$ blanklines
, optionalAttributes $ choice
[ orgBlock
@@ -291,14 +305,14 @@ block = choice [ mempty <$ blanklines
, drawer
, specialLine
, header
- , hline
+ , return <$> hline
, list
, latexFragment
, noteBlock
, paraOrPlain
] <?> "block"
-optionalAttributes :: OrgParser Blocks -> OrgParser Blocks
+optionalAttributes :: OrgParser (F Blocks) -> OrgParser (F Blocks)
optionalAttributes parser = try $
resetBlockAttributes *> parseBlockAttributes *> parser
@@ -318,7 +332,7 @@ parseAndAddAttribute key value = do
let key' = map toLower key
() <$ addBlockAttribute key' value
-lookupInlinesAttr :: String -> OrgParser (Maybe Inlines)
+lookupInlinesAttr :: String -> OrgParser (Maybe (F Inlines))
lookupInlinesAttr attr = try $ do
val <- lookupBlockAttribute attr
maybe (return Nothing)
@@ -332,20 +346,20 @@ lookupInlinesAttr attr = try $ do
type BlockProperties = (Int, String) -- (Indentation, Block-Type)
-orgBlock :: OrgParser Blocks
+orgBlock :: OrgParser (F Blocks)
orgBlock = try $ do
blockProp@(_, blkType) <- blockHeaderStart
($ blockProp) $
case blkType of
"comment" -> withRaw' (const mempty)
- "html" -> withRaw' (B.rawBlock blkType)
- "latex" -> withRaw' (B.rawBlock blkType)
- "ascii" -> withRaw' (B.rawBlock blkType)
- "example" -> withRaw' exampleCode
- "quote" -> withParsed B.blockQuote
+ "html" -> withRaw' (return . (B.rawBlock blkType))
+ "latex" -> withRaw' (return . (B.rawBlock blkType))
+ "ascii" -> withRaw' (return . (B.rawBlock blkType))
+ "example" -> withRaw' (return . exampleCode)
+ "quote" -> withParsed (fmap B.blockQuote)
"verse" -> verseBlock
"src" -> codeBlock
- _ -> withParsed (divWithClass blkType)
+ _ -> withParsed (fmap $ divWithClass blkType)
blockHeaderStart :: OrgParser (Int, String)
blockHeaderStart = try $ (,) <$> indent <*> blockType
@@ -353,10 +367,10 @@ blockHeaderStart = try $ (,) <$> indent <*> blockType
indent = length <$> many spaceChar
blockType = map toLower <$> (stringAnyCase "#+begin_" *> orgArgWord)
-withRaw' :: (String -> Blocks) -> BlockProperties -> OrgParser Blocks
+withRaw' :: (String -> F Blocks) -> BlockProperties -> OrgParser (F Blocks)
withRaw' f blockProp = (ignHeaders *> (f <$> rawBlockContent blockProp))
-withParsed :: (Blocks -> Blocks) -> BlockProperties -> OrgParser Blocks
+withParsed :: (F Blocks -> F Blocks) -> BlockProperties -> OrgParser (F Blocks)
withParsed f blockProp = (ignHeaders *> (f <$> parsedBlockContent blockProp))
ignHeaders :: OrgParser ()
@@ -365,11 +379,11 @@ ignHeaders = (() <$ newline) <|> (() <$ anyLine)
divWithClass :: String -> Blocks -> Blocks
divWithClass cls = B.divWith ("", [cls], [])
-verseBlock :: BlockProperties -> OrgParser Blocks
+verseBlock :: BlockProperties -> OrgParser (F Blocks)
verseBlock blkProp = try $ do
ignHeaders
content <- rawBlockContent blkProp
- B.para . mconcat . intersperse B.linebreak
+ fmap B.para . mconcat . intersperse (pure B.linebreak)
<$> mapM (parseFromString parseInlines) (lines content)
exportsCode :: [(String, String)] -> Bool
@@ -386,7 +400,7 @@ followingResultsBlock =
*> blankline
*> (unlines <$> many1 exampleLine))
-codeBlock :: BlockProperties -> OrgParser Blocks
+codeBlock :: BlockProperties -> OrgParser (F Blocks)
codeBlock blkProp = do
skipSpaces
(classes, kv) <- codeHeaderArgs <|> (mempty <$ ignHeaders)
@@ -396,15 +410,17 @@ codeBlock blkProp = do
let includeCode = exportsCode kv
let includeResults = exportsResults kv
let codeBlck = B.codeBlockWith ( id', classes, kv ) content
- labelledBlck <- maybe codeBlck (labelDiv codeBlck)
+ labelledBlck <- maybe (pure codeBlck)
+ (labelDiv codeBlck)
<$> lookupInlinesAttr "caption"
- let resultBlck = maybe mempty exampleCode resultsContent
+ let resultBlck = pure $ maybe mempty (exampleCode) resultsContent
return $ (if includeCode then labelledBlck else mempty)
<> (if includeResults then resultBlck else mempty)
where
labelDiv blk value =
- B.divWith nullAttr (labelledBlock value <> blk)
- labelledBlock = B.plain . B.spanWith ("", ["label"], [])
+ B.divWith nullAttr <$> (mappend <$> labelledBlock value
+ <*> pure blk)
+ labelledBlock = fmap (B.plain . B.spanWith ("", ["label"], []))
rawBlockContent :: BlockProperties -> OrgParser String
rawBlockContent (indent, blockType) = try $
@@ -413,7 +429,7 @@ rawBlockContent (indent, blockType) = try $
indentedLine = try $ ("" <$ blankline) <|> (indentWith indent *> anyLine)
blockEnder = try $ indentWith indent *> stringAnyCase ("#+end_" <> blockType)
-parsedBlockContent :: BlockProperties -> OrgParser Blocks
+parsedBlockContent :: BlockProperties -> OrgParser (F Blocks)
parsedBlockContent blkProps = try $ do
raw <- rawBlockContent blkProps
parseFromString parseBlocks (raw ++ "\n")
@@ -504,9 +520,9 @@ commaEscaped (',':cs@('*':_)) = cs
commaEscaped (',':cs@('#':'+':_)) = cs
commaEscaped cs = cs
-example :: OrgParser Blocks
+example :: OrgParser (F Blocks)
example = try $ do
- return . exampleCode =<< unlines <$> many1 exampleLine
+ return . return . exampleCode =<< unlines <$> many1 exampleLine
exampleCode :: String -> Blocks
exampleCode = B.codeBlockWith ("", ["example"], [])
@@ -515,7 +531,7 @@ exampleLine :: OrgParser String
exampleLine = try $ skipSpaces *> string ": " *> anyLine
-- Drawers for properties or a logbook
-drawer :: OrgParser Blocks
+drawer :: OrgParser (F Blocks)
drawer = try $ do
drawerStart
manyTill drawerLine (try drawerEnd)
@@ -541,12 +557,14 @@ drawerEnd = try $
--
-- Figures (Image on a line by itself, preceded by name and/or caption)
-figure :: OrgParser Blocks
+figure :: OrgParser (F Blocks)
figure = try $ do
(cap, nam) <- nameAndCaption
src <- skipSpaces *> selfTarget <* skipSpaces <* P.newline
guard (isImageFilename src)
- return $ B.para $ B.image src nam cap
+ return $ do
+ cap' <- cap
+ return $ B.para $ B.image src nam cap'
where
nameAndCaption =
do
@@ -562,8 +580,8 @@ figure = try $ do
--
-- Comments, Options and Metadata
-specialLine :: OrgParser Blocks
-specialLine = try $ metaLine <|> commentLine
+specialLine :: OrgParser (F Blocks)
+specialLine = fmap return . try $ metaLine <|> commentLine
metaLine :: OrgParser Blocks
metaLine = try $ mempty
@@ -583,14 +601,14 @@ commentLineStart = try $ mappend <$> many spaceChar <*> string "# "
declarationLine :: OrgParser ()
declarationLine = try $ do
key <- metaKey
- inlines <- metaInlines
+ inlinesF <- metaInlines
updateState $ \st ->
- let meta' = B.setMeta key inlines nullMeta
- in st { orgStateMeta = orgStateMeta st <> meta' }
+ let meta' = B.setMeta <$> pure key <*> inlinesF <*> pure nullMeta
+ in st { orgStateMeta' = orgStateMeta' st <> meta' }
return ()
-metaInlines :: OrgParser MetaValue
-metaInlines = (MetaInlines . B.toList) <$> inlinesTillNewline
+metaInlines :: OrgParser (F MetaValue)
+metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline
metaKey :: OrgParser String
metaKey = map toLower <$> many1 (noneOf ": \n\r")
@@ -631,11 +649,11 @@ parseFormat = try $ do
--
-- | Headers
-header :: OrgParser Blocks
+header :: OrgParser (F Blocks)
header = try $ do
level <- headerStart
title <- inlinesTillNewline
- return $ B.header level title
+ return $ B.header level <$> title
headerStart :: OrgParser Int
headerStart = try $
@@ -659,7 +677,7 @@ hline = try $ do
-- Tables
--
-data OrgTableRow = OrgContentRow [Blocks]
+data OrgTableRow = OrgContentRow (F [Blocks])
| OrgAlignRow [Alignment]
| OrgHlineRow
@@ -670,13 +688,13 @@ data OrgTable = OrgTable
, orgTableRows :: [[Blocks]]
}
-table :: OrgParser Blocks
+table :: OrgParser (F Blocks)
table = try $ do
lookAhead tableStart
do
rows <- tableRows
- (cptn :: Inlines) <- fromMaybe "" <$> lookupInlinesAttr "caption"
- return $ ($ cptn) . orgToPandocTable . normalizeTable . rowsToTable $ rows
+ cptn <- fromMaybe (pure "") <$> lookupInlinesAttr "caption"
+ return $ (<$> cptn) . orgToPandocTable . normalizeTable =<< rowsToTable rows
orgToPandocTable :: OrgTable
-> Inlines
@@ -692,11 +710,11 @@ tableRows = try $ many (tableAlignRow <|> tableHline <|> tableContentRow)
tableContentRow :: OrgParser OrgTableRow
tableContentRow = try $
- OrgContentRow <$> (tableStart *> manyTill tableContentCell newline)
+ OrgContentRow . sequence <$> (tableStart *> manyTill tableContentCell newline)
-tableContentCell :: OrgParser Blocks
+tableContentCell :: OrgParser (F Blocks)
tableContentCell = try $
- B.plain . trimInlines . mconcat <$> many1Till inline endOfCell
+ fmap B.plain . trimInlinesF . mconcat <$> many1Till inline endOfCell
endOfCell :: OrgParser Char
endOfCell = try $ char '|' <|> lookAhead newline
@@ -728,8 +746,8 @@ tableHline = try $
OrgHlineRow <$ (tableStart *> char '-' *> anyLine)
rowsToTable :: [OrgTableRow]
- -> OrgTable
-rowsToTable = foldl' (flip rowToContent) zeroTable
+ -> F OrgTable
+rowsToTable = foldM (flip rowToContent) zeroTable
where zeroTable = OrgTable 0 mempty mempty mempty
normalizeTable :: OrgTable
@@ -748,43 +766,45 @@ normalizeTable (OrgTable cols aligns heads lns) =
-- line as a header. All other horizontal lines are discarded.
rowToContent :: OrgTableRow
-> OrgTable
- -> OrgTable
+ -> F OrgTable
rowToContent OrgHlineRow t = maybeBodyToHeader t
-rowToContent (OrgAlignRow as) t = setLongestRow as . setAligns as $ t
-rowToContent (OrgContentRow rf) t = setLongestRow rf . appendToBody rf $ t
+rowToContent (OrgAlignRow as) t = setLongestRow as =<< setAligns as t
+rowToContent (OrgContentRow rf) t = do
+ rs <- rf
+ setLongestRow rs =<< appendToBody rs t
setLongestRow :: [a]
-> OrgTable
- -> OrgTable
+ -> F OrgTable
setLongestRow rs t =
- t{ orgTableColumns = max (length rs) (orgTableColumns t) }
+ return t{ orgTableColumns = max (length rs) (orgTableColumns t) }
maybeBodyToHeader :: OrgTable
- -> OrgTable
+ -> F OrgTable
maybeBodyToHeader t = case t of
OrgTable{ orgTableHeader = [], orgTableRows = b:[] } ->
- t{ orgTableHeader = b , orgTableRows = [] }
- _ -> t
+ return t{ orgTableHeader = b , orgTableRows = [] }
+ _ -> return t
appendToBody :: [Blocks]
-> OrgTable
- -> OrgTable
-appendToBody r t = t{ orgTableRows = orgTableRows t ++ [r] }
+ -> F OrgTable
+appendToBody r t = return t{ orgTableRows = orgTableRows t ++ [r] }
setAligns :: [Alignment]
-> OrgTable
- -> OrgTable
-setAligns aligns t = t{ orgTableAlignments = aligns }
+ -> F OrgTable
+setAligns aligns t = return $ t{ orgTableAlignments = aligns }
--
-- LaTeX fragments
--
-latexFragment :: OrgParser Blocks
+latexFragment :: OrgParser (F Blocks)
latexFragment = try $ do
envName <- latexEnvStart
content <- mconcat <$> manyTill anyLineNewline (latexEnd envName)
- return $ B.rawBlock "latex" (content `inLatexEnv` envName)
+ return . return $ B.rawBlock "latex" (content `inLatexEnv` envName)
where
c `inLatexEnv` e = mconcat [ "\\begin{", e, "}\n"
, c
@@ -814,7 +834,7 @@ latexEnvName = try $ do
--
-- Footnote defintions
--
-noteBlock :: OrgParser Blocks
+noteBlock :: OrgParser (F Blocks)
noteBlock = try $ do
ref <- noteMarker <* skipSpaces
content <- mconcat <$> blocksTillHeaderOrNote
@@ -826,37 +846,37 @@ noteBlock = try $ do
<|> () <$ lookAhead headerStart)
-- Paragraphs or Plain text
-paraOrPlain :: OrgParser Blocks
+paraOrPlain :: OrgParser (F Blocks)
paraOrPlain = try $ do
ils <- parseInlines
nl <- option False (newline >> return True)
try (guard nl >> notFollowedBy (orderedListStart <|> bulletListStart) >>
- (return $ B.para ils))
- <|> (return $ B.plain ils)
+ return (B.para <$> ils))
+ <|> (return (B.plain <$> ils))
-inlinesTillNewline :: OrgParser Inlines
-inlinesTillNewline = trimInlines . mconcat <$> manyTill inline newline
+inlinesTillNewline :: OrgParser (F Inlines)
+inlinesTillNewline = trimInlinesF . mconcat <$> manyTill inline newline
--
-- list blocks
--
-list :: OrgParser Blocks
+list :: OrgParser (F Blocks)
list = choice [ definitionList, bulletList, orderedList ] <?> "list"
-definitionList :: OrgParser Blocks
+definitionList :: OrgParser (F Blocks)
definitionList = try $ do n <- lookAhead (bulletListStart' Nothing)
- B.definitionList . compactify'DL
+ fmap B.definitionList . fmap compactify'DL . sequence
<$> many1 (definitionListItem $ bulletListStart' (Just n))
-bulletList :: OrgParser Blocks
+bulletList :: OrgParser (F Blocks)
bulletList = try $ do n <- lookAhead (bulletListStart' Nothing)
- B.bulletList . compactify'
+ fmap B.bulletList . fmap compactify' . sequence
<$> many1 (listItem (bulletListStart' $ Just n))
-orderedList :: OrgParser Blocks
-orderedList = B.orderedList . compactify'
+orderedList :: OrgParser (F Blocks)
+orderedList = fmap B.orderedList . fmap compactify' . sequence
<$> many1 (listItem orderedListStart)
genericListStart :: OrgParser String
@@ -893,7 +913,7 @@ orderedListStart = genericListStart orderedListMarker
where orderedListMarker = mappend <$> many1 digit <*> (pure <$> oneOf ".)")
definitionListItem :: OrgParser Int
- -> OrgParser (Inlines, [Blocks])
+ -> OrgParser (F (Inlines, [Blocks]))
definitionListItem parseMarkerGetLength = try $ do
markerLength <- parseMarkerGetLength
term <- manyTill (noneOf "\n\r") (try $ string "::")
@@ -902,12 +922,12 @@ definitionListItem parseMarkerGetLength = try $ do
cont <- concat <$> many (listContinuation markerLength)
term' <- parseFromString parseInlines term
contents' <- parseFromString parseBlocks $ line1 ++ blank ++ cont
- return (term', [contents'])
+ return $ (,) <$> term' <*> fmap (:[]) contents'
-- parse raw text for one list item, excluding start marker and continuations
listItem :: OrgParser Int
- -> OrgParser Blocks
+ -> OrgParser (F Blocks)
listItem start = try $ do
markerLength <- try start
firstLine <- anyLineNewline
@@ -933,7 +953,7 @@ anyLineNewline = (++ "\n") <$> anyLine
-- inline
--
-inline :: OrgParser Inlines
+inline :: OrgParser (F Inlines)
inline =
choice [ whitespace
, linebreak
@@ -960,31 +980,31 @@ inline =
] <* (guard =<< newlinesCountWithinLimits)
<?> "inline"
-parseInlines :: OrgParser Inlines
-parseInlines = trimInlines . mconcat <$> many1 inline
+parseInlines :: OrgParser (F Inlines)
+parseInlines = trimInlinesF . mconcat <$> many1 inline
-- treat these as potentially non-text when parsing inline:
specialChars :: [Char]
specialChars = "\"$'()*+-,./:<=>[\\]^_{|}~"
-whitespace :: OrgParser Inlines
-whitespace = B.space <$ skipMany1 spaceChar
+whitespace :: OrgParser (F Inlines)
+whitespace = pure B.space <$ skipMany1 spaceChar
<* updateLastPreCharPos
<* updateLastForbiddenCharPos
<?> "whitespace"
-linebreak :: OrgParser Inlines
-linebreak = try $ B.linebreak <$ string "\\\\" <* skipSpaces <* newline
+linebreak :: OrgParser (F Inlines)
+linebreak = try $ pure B.linebreak <$ string "\\\\" <* skipSpaces <* newline
-str :: OrgParser Inlines
-str = B.str <$> many1 (noneOf $ specialChars ++ "\n\r ")
+str :: OrgParser (F Inlines)
+str = return . B.str <$> many1 (noneOf $ specialChars ++ "\n\r ")
<* updateLastStrPos
-- | An endline character that can be treated as a space, not a structural
-- break. This should reflect the values of the Emacs variable
-- @org-element-pagaraph-separate@.
-endline :: OrgParser Inlines
+endline :: OrgParser (F Inlines)
endline = try $ do
newline
notFollowedBy blankline
@@ -1002,72 +1022,77 @@ endline = try $ do
decEmphasisNewlinesCount
guard =<< newlinesCountWithinLimits
updateLastPreCharPos
- return $ B.space
+ return . return $ B.space
-cite :: OrgParser Inlines
+cite :: OrgParser (F Inlines)
cite = try $ do
guardEnabled Ext_citations
(cs, raw) <- withRaw normalCite
- return $ flip B.cite (B.text raw) cs
+ return $ (flip B.cite (B.text raw)) <$> cs
-normalCite :: OrgParser [Citation]
+normalCite :: OrgParser (F [Citation])
normalCite = try $ char '['
*> skipSpaces
*> citeList
<* skipSpaces
<* char ']'
-citeList :: OrgParser [Citation]
-citeList = sepBy1 citation (try $ char ';' *> skipSpaces)
+citeList :: OrgParser (F [Citation])
+citeList = sequence <$> sepBy1 citation (try $ char ';' *> skipSpaces)
-citation :: OrgParser Citation
+citation :: OrgParser (F Citation)
citation = try $ do
pref <- prefix
(suppress_author, key) <- citeKey
suff <- suffix
- return $ Citation{ citationId = key
- , citationPrefix = B.toList pref
- , citationSuffix = B.toList suff
- , citationMode = if suppress_author
- then SuppressAuthor
- else NormalCitation
- , citationNoteNum = 0
- , citationHash = 0
- }
+ return $ do
+ x <- pref
+ y <- suff
+ return $ Citation{ citationId = key
+ , citationPrefix = B.toList x
+ , citationSuffix = B.toList y
+ , citationMode = if suppress_author
+ then SuppressAuthor
+ else NormalCitation
+ , citationNoteNum = 0
+ , citationHash = 0
+ }
where
- prefix = trimInlines . mconcat <$>
+ prefix = trimInlinesF . mconcat <$>
manyTill inline (char ']' <|> (']' <$ lookAhead citeKey))
suffix = try $ do
hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
skipSpaces
- rest <- trimInlines . mconcat <$>
+ rest <- trimInlinesF . mconcat <$>
many (notFollowedBy (oneOf ";]") *> inline)
- return $
- if hasSpace
- then B.space <> rest
- else rest
+ return $ if hasSpace
+ then (B.space <>) <$> rest
+ else rest
-footnote :: OrgParser Inlines
+footnote :: OrgParser (F Inlines)
footnote = try $ inlineNote <|> referencedNote
-inlineNote :: OrgParser Inlines
+inlineNote :: OrgParser (F Inlines)
inlineNote = try $ do
string "[fn:"
ref <- many alphaNum
char ':'
- note <- B.para . trimInlines . mconcat <$> many1Till inline (char ']')
+ note <- fmap B.para . trimInlinesF . mconcat <$> many1Till inline (char ']')
when (not $ null ref) $
addToNotesTable ("fn:" ++ ref, note)
- return $ B.note note
+ return $ B.note <$> note
-referencedNote :: OrgParser Inlines
+referencedNote :: OrgParser (F Inlines)
referencedNote = try $ do
ref <- noteMarker
- notes <- asks (orgStateNotes' . finalState)
- return $
+ return $ do
+ notes <- asksF orgStateNotes'
case lookup ref notes of
- Just contents -> B.note contents
- Nothing -> B.str $ "[" ++ ref ++ "]"
+ Nothing -> return $ B.str $ "[" ++ ref ++ "]"
+ Just contents -> do
+ st <- askF
+ let contents' = runF contents st{ orgStateNotes' = [] }
+ return $ B.note contents'
noteMarker :: OrgParser String
noteMarker = try $ do
@@ -1077,37 +1102,37 @@ noteMarker = try $ do
<*> many1Till (noneOf "\n\r\t ") (char ']')
]
-linkOrImage :: OrgParser Inlines
+linkOrImage :: OrgParser (F Inlines)
linkOrImage = explicitOrImageLink
<|> selflinkOrImage
<|> angleLink
<|> plainLink
<?> "link or image"
-explicitOrImageLink :: OrgParser Inlines
+explicitOrImageLink :: OrgParser (F Inlines)
explicitOrImageLink = try $ do
char '['
- src <- applyCustomLinkFormat =<< possiblyEmptyLinkTarget
+ srcF <- applyCustomLinkFormat =<< possiblyEmptyLinkTarget
title <- enclosedRaw (char '[') (char ']')
title' <- parseFromString (mconcat <$> many inline) title
char ']'
- alt <- internalLink src title'
- return $
- (if isImageFilename title
- then B.link src "" $ B.image title mempty mempty
- else fromMaybe alt (linkToInlines src title'))
+ return $ do
+ src <- srcF
+ if isImageFilename title
+ then pure $ B.link src "" $ B.image title mempty mempty
+ else linkToInlinesF src =<< title'
-selflinkOrImage :: OrgParser Inlines
+selflinkOrImage :: OrgParser (F Inlines)
selflinkOrImage = try $ do
src <- char '[' *> linkTarget <* char ']'
- return $ fromMaybe "" (linkToInlines src (B.str src))
+ return $ linkToInlinesF src (B.str src)
-plainLink :: OrgParser Inlines
+plainLink :: OrgParser (F Inlines)
plainLink = try $ do
(orig, src) <- uri
- return $ B.link src "" (B.str orig)
+ returnF $ B.link src "" (B.str orig)
-angleLink :: OrgParser Inlines
+angleLink :: OrgParser (F Inlines)
angleLink = try $ do
char '<'
link <- plainLink
@@ -1123,31 +1148,26 @@ linkTarget = enclosedByPair '[' ']' (noneOf "\n\r[]")
possiblyEmptyLinkTarget :: OrgParser String
possiblyEmptyLinkTarget = try linkTarget <|> ("" <$ string "[]")
-applyCustomLinkFormat :: String -> OrgParser String
+applyCustomLinkFormat :: String -> OrgParser (F String)
applyCustomLinkFormat link = do
let (linkType, rest) = break (== ':') link
- fmts <- asks finalState
- return $
- case M.lookup linkType (orgStateLinkFormatters fmts) of
- Just v -> (v (drop 1 rest))
- Nothing -> link
+ return $ do
+ formatter <- M.lookup linkType <$> asksF orgStateLinkFormatters
+ return $ maybe link ($ drop 1 rest) formatter
-- TODO: might be a lot smarter/cleaner to use parsec and ADTs for this kind
-- of parsing.
-linkToInlines :: String -> Inlines -> Maybe Inlines
-linkToInlines = \s ->
+linkToInlinesF :: String -> Inlines -> F Inlines
+linkToInlinesF s =
case s of
- _ | null s -> Just . B.link "" ""
- _ | isAnchor s -> Just . B.link s ""
- _ | isImageFilename s -> const . Just $ B.image s "" ""
- _ | isFileLink s -> Just . B.link (dropLinkType s) ""
- _ | isUri s -> Just . B.link s ""
- _ | isAbsoluteFilePath s -> Just . B.link ("file://" ++ s) ""
- _ | isRelativeFilePath s -> Just . B.link s ""
- _ -> const Nothing
-
-isAnchor :: String -> Bool
-isAnchor s = "#" `isPrefixOf` s
+ "" -> pure . B.link "" ""
+ ('#':_) -> pure . B.link s ""
+ _ | isImageFilename s -> const . pure $ B.image s "" ""
+ _ | isFileLink s -> pure . B.link (dropLinkType s) ""
+ _ | isUri s -> pure . B.link s ""
+ _ | isAbsoluteFilePath s -> pure . B.link ("file://" ++ s) ""
+ _ | isRelativeFilePath s -> pure . B.link s ""
+ _ -> internalLink s
isFileLink :: String -> Bool
isFileLink s = ("file:" `isPrefixOf` s) && not ("file://" `isPrefixOf` s)
@@ -1176,13 +1196,12 @@ isImageFilename filename =
imageExtensions = [ "jpeg" , "jpg" , "png" , "gif" , "svg" ]
protocols = [ "file", "http", "https" ]
-internalLink :: String -> Inlines -> OrgParser Inlines
+internalLink :: String -> Inlines -> F Inlines
internalLink link title = do
- anchorB <- asks finalState
- return $
- if link `elem` (orgStateAnchorIds anchorB)
- then B.link ('#':link) "" title
- else B.emph title
+ anchorB <- (link `elem`) <$> asksF orgStateAnchorIds
+ if anchorB
+ then return $ B.link ('#':link) "" title
+ else return $ B.emph title
-- | Parse an anchor like @<<anchor-id>>@ and return an empty span with
-- @anchor-id@ set as id. Legal anchors in org-mode are defined through
@@ -1190,11 +1209,11 @@ internalLink link title = do
-- @anchor-id@ contains spaces, we are more restrictive in what is accepted as
-- an anchor.
-anchor :: OrgParser Inlines
+anchor :: OrgParser (F Inlines)
anchor = try $ do
anchorId <- parseAnchor
recordAnchorId anchorId
- return $ B.spanWith (solidify anchorId, [], []) mempty
+ returnF $ B.spanWith (solidify anchorId, [], []) mempty
where
parseAnchor = string "<<"
*> many1 (noneOf "\t\n\r<>\"' ")
@@ -1212,7 +1231,7 @@ solidify = map replaceSpecialChar
| otherwise = '-'
-- | Parses an inline code block and marks it as an babel block.
-inlineCodeBlock :: OrgParser Inlines
+inlineCodeBlock :: OrgParser (F Inlines)
inlineCodeBlock = try $ do
string "src_"
lang <- many1 orgArgWordChar
@@ -1220,7 +1239,7 @@ inlineCodeBlock = try $ do
inlineCode <- enclosedByPair '{' '}' (noneOf "\n\r")
let attrClasses = [translateLang lang, rundocBlockClass]
let attrKeyVal = map toRundocAttrib (("language", lang) : opts)
- return $ B.codeWith ("", attrClasses, attrKeyVal) inlineCode
+ returnF $ B.codeWith ("", attrClasses, attrKeyVal) inlineCode
enclosedByPair :: Char -- ^ opening char
-> Char -- ^ closing char
@@ -1228,51 +1247,54 @@ enclosedByPair :: Char -- ^ opening char
-> OrgParser [a]
enclosedByPair s e p = char s *> many1Till p (char e)
-emph :: OrgParser Inlines
-emph = B.emph <$> emphasisBetween '/'
+emph :: OrgParser (F Inlines)
+emph = fmap B.emph <$> emphasisBetween '/'
-strong :: OrgParser Inlines
-strong = B.strong <$> emphasisBetween '*'
+strong :: OrgParser (F Inlines)
+strong = fmap B.strong <$> emphasisBetween '*'
-strikeout :: OrgParser Inlines
-strikeout = B.strikeout <$> emphasisBetween '+'
+strikeout :: OrgParser (F Inlines)
+strikeout = fmap B.strikeout <$> emphasisBetween '+'
-- There is no underline, so we use strong instead.
-underline :: OrgParser Inlines
-underline = B.strong <$> emphasisBetween '_'
+underline :: OrgParser (F Inlines)
+underline = fmap B.strong <$> emphasisBetween '_'
-verbatim :: OrgParser Inlines
-verbatim = B.code <$> verbatimBetween '='
+verbatim :: OrgParser (F Inlines)
+verbatim = return . B.code <$> verbatimBetween '='
-code :: OrgParser Inlines
-code = B.code <$> verbatimBetween '~'
+code :: OrgParser (F Inlines)
+code = return . B.code <$> verbatimBetween '~'
-subscript :: OrgParser Inlines
-subscript = B.subscript <$> try (char '_' *> subOrSuperExpr)
+subscript :: OrgParser (F Inlines)
+subscript = fmap B.subscript <$> try (char '_' *> subOrSuperExpr)
-superscript :: OrgParser Inlines
-superscript = B.superscript <$> try (char '^' *> subOrSuperExpr)
+superscript :: OrgParser (F Inlines)
+superscript = fmap B.superscript <$> try (char '^' *> subOrSuperExpr)
-math :: OrgParser Inlines
-math = B.math <$> choice [ math1CharBetween '$'
+math :: OrgParser (F Inlines)
+math = return . B.math <$> choice [ math1CharBetween '$'
, mathStringBetween '$'
, rawMathBetween "\\(" "\\)"
]
-displayMath :: OrgParser Inlines
-displayMath = B.displayMath <$> choice [ rawMathBetween "\\[" "\\]"
- , rawMathBetween "$$" "$$"
- ]
+displayMath :: OrgParser (F Inlines)
+displayMath = return . B.displayMath <$> choice [ rawMathBetween "\\[" "\\]"
+ , rawMathBetween "$$" "$$"
+ ]
+
+updatePositions :: Char
+ -> OrgParser (Char)
+updatePositions c = do
+ when (c `elem` emphasisPreChars) updateLastPreCharPos
+ when (c `elem` emphasisForbiddenBorderChars) updateLastForbiddenCharPos
+ return c
-symbol :: OrgParser Inlines
-symbol = B.str . (: "") <$> (oneOf specialChars >>= updatePositions)
- where updatePositions c = do
- when (c `elem` emphasisPreChars) updateLastPreCharPos
- when (c `elem` emphasisForbiddenBorderChars) updateLastForbiddenCharPos
- return c
+symbol :: OrgParser (F Inlines)
+symbol = return . B.str . (: "") <$> (oneOf specialChars >>= updatePositions)
emphasisBetween :: Char
- -> OrgParser Inlines
+ -> OrgParser (F Inlines)
emphasisBetween c = try $ do
startEmphasisNewlinesCounting emphasisAllowedNewlines
res <- enclosedInlines (emphasisStart c) (emphasisEnd c)
@@ -1349,9 +1371,9 @@ mathEnd c = try $ do
enclosedInlines :: OrgParser a
-> OrgParser b
- -> OrgParser Inlines
+ -> OrgParser (F Inlines)
enclosedInlines start end = try $
- trimInlines . mconcat <$> enclosed start end inline
+ trimInlinesF . mconcat <$> enclosed start end inline
enclosedRaw :: OrgParser a
-> OrgParser b
@@ -1430,7 +1452,7 @@ notAfterForbiddenBorderChar = do
return $ lastFBCPos /= Just pos
-- | Read a sub- or superscript expression
-subOrSuperExpr :: OrgParser Inlines
+subOrSuperExpr :: OrgParser (F Inlines)
subOrSuperExpr = try $
choice [ id <$> charsInBalanced '{' '}' (noneOf "\n\r")
, enclosing ('(', ')') <$> charsInBalanced '(' ')' (noneOf "\n\r")
@@ -1445,10 +1467,10 @@ simpleSubOrSuperString = try $
<*> many1 alphaNum
]
-inlineLaTeX :: OrgParser Inlines
+inlineLaTeX :: OrgParser (F Inlines)
inlineLaTeX = try $ do
cmd <- inlineLaTeXCommand
- maybe mzero return $
+ maybe mzero returnF $
parseAsMath cmd `mplus` parseAsMathMLSym cmd `mplus` parseAsInlineLaTeX cmd
where
parseAsMath :: String -> Maybe Inlines
@@ -1481,30 +1503,30 @@ inlineLaTeXCommand = try $ do
return cs
_ -> mzero
-smart :: OrgParser Inlines
+smart :: OrgParser (F Inlines)
smart = do
getOption readerSmart >>= guard
doubleQuoted <|> singleQuoted <|>
- choice [orgApostrophe, dash, ellipses]
+ choice (map (return <$>) [orgApostrophe, dash, ellipses])
where orgApostrophe =
(char '\'' <|> char '\8217') <* updateLastPreCharPos
<* updateLastForbiddenCharPos
*> return (B.str "\x2019")
-singleQuoted :: OrgParser Inlines
+singleQuoted :: OrgParser (F Inlines)
singleQuoted = try $ do
singleQuoteStart
withQuoteContext InSingleQuote $
- B.singleQuoted . trimInlines . mconcat <$>
+ fmap B.singleQuoted . trimInlinesF . mconcat <$>
many1Till inline singleQuoteEnd
-- doubleQuoted will handle regular double-quoted sections, as well
-- as dialogues with an open double-quote without a close double-quote
-- in the same paragraph.
-doubleQuoted :: OrgParser Inlines
+doubleQuoted :: OrgParser (F Inlines)
doubleQuoted = try $ do
doubleQuoteStart
contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
(withQuoteContext InDoubleQuote $ (doubleQuoteEnd <* updateLastForbiddenCharPos) >> return
- (B.doubleQuoted . trimInlines $ contents))
- <|> (return $ (B.str "\8220") <> contents)
+ (fmap B.doubleQuoted . trimInlinesF $ contents))
+ <|> (return $ return (B.str "\8220") <> contents)
diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs
index a8112bc81..564267ee5 100644
--- a/src/Text/Pandoc/Readers/RST.hs
+++ b/src/Text/Pandoc/Readers/RST.hs
@@ -60,7 +60,7 @@ readRST :: ReaderOptions -- ^ Reader options
readRST opts s = (readWith parseRST) def{ stateOptions = opts } (s ++ "\n\n")
readRSTWithWarnings :: ReaderOptions -> String -> Either PandocError (Pandoc, [String])
-readRSTWithWarnings opts s = (readWith (returnWarnings parseRST)) def{ stateOptions = opts } (s ++ "\n\n")
+readRSTWithWarnings opts s = (readWithWarnings parseRST) def{ stateOptions = opts } (s ++ "\n\n")
type RSTParser = Parser [Char] ParserState