@@ -44,7 +44,7 @@ import Control.Applicative ( Applicative, pure
44 , (<$>), (<$), (<*>), (<*), (*>), (<**>) )
44 , (<$>), (<$), (<*>), (<*), (*>), (<**>) )
45import Control.Arrow ((***))
45import Control.Arrow ((***))
46import Control.Monad (foldM, guard, liftM, liftM2, when)
46import Control.Monad (foldM, guard, liftM, liftM2, when)
47−import Control.Monad.Reader (Reader, runReader)
47+import Control.Monad.Reader (Reader, runReader, ask, asks)
48import Data.Char (toLower)
48import Data.Char (toLower)
49import Data.Default
49import Data.Default
50import Data.List (isPrefixOf, isSuffixOf)
50import Data.List (isPrefixOf, isSuffixOf)
@@ -59,7 +59,7 @@ readOrg opts s = readWith parseOrg def{ orgStateOptions = opts } (s ++ "\n\n")
60type OrgParser = Parser [Char] OrgParserState
60type OrgParser = Parser [Char] OrgParserState
62−parseOrg:: OrgParser Pandoc
62+parseOrg :: OrgParser Pandoc
63parseOrg = do
63parseOrg = do
64 blocks' <- parseBlocks
64 blocks' <- parseBlocks
65 st <- getState
65 st <- getState
@@ -70,6 +70,9 @@ parseOrg = do
70-- Parser State for Org
70-- Parser State for Org
73+type OrgNoteRecord = (String, F Blocks)
74+type OrgNoteTable = [OrgNoteRecord]
73-- | Org-mode parser state
76-- | Org-mode parser state
74data OrgParserState = OrgParserState
77data OrgParserState = OrgParserState
75 { orgStateOptions :: ReaderOptions
78 { orgStateOptions :: ReaderOptions
@@ -80,6 +83,7 @@ data OrgParserState = OrgParserState
80 , orgStateLastStrPos :: Maybe SourcePos
83 , orgStateLastStrPos :: Maybe SourcePos
81 , orgStateMeta :: Meta
84 , orgStateMeta :: Meta
82 , orgStateMeta' :: F Meta
85 , orgStateMeta' :: F Meta
86+ , orgStateNotes' :: OrgNoteTable
85instance HasReaderOptions OrgParserState where
89instance HasReaderOptions OrgParserState where
@@ -104,6 +108,7 @@ defaultOrgParserState = OrgParserState
104 , orgStateLastStrPos = Nothing
108 , orgStateLastStrPos = Nothing
105 , orgStateMeta = nullMeta
109 , orgStateMeta = nullMeta
106 , orgStateMeta' = return nullMeta
110 , orgStateMeta' = return nullMeta
111+ , orgStateNotes' = []
109updateLastStrPos :: OrgParser ()
114updateLastStrPos :: OrgParser ()
@@ -146,6 +151,11 @@ resetEmphasisNewlines :: OrgParser ()
146resetEmphasisNewlines = updateState $ \s ->
151resetEmphasisNewlines = updateState $ \s ->
147 s{ orgStateEmphasisNewlines = Nothing }
152 s{ orgStateEmphasisNewlines = Nothing }
154+addToNotesTable :: OrgNoteRecord -> OrgParser ()
155+addToNotesTable note = do
156+ oldnotes <- orgStateNotes' <$> getState
157+ updateState $ \s -> s{ orgStateNotes' = note:oldnotes }
151-- Adaptions and specializations of parsing utilities
161-- Adaptions and specializations of parsing utilities
@@ -157,6 +167,12 @@ newtype F a = F { unF :: Reader OrgParserState a
157runF :: F a -> OrgParserState -> a
167runF :: F a -> OrgParserState -> a
158runF = runReader . unF
168runF = runReader . unF
170+askF :: F OrgParserState
173+asksF :: (OrgParserState -> a) -> F a
160instance Monoid a => Monoid (F a) where
176instance Monoid a => Monoid (F a) where
161 mempty = return mempty
177 mempty = return mempty
162 mappend = liftM2 mappend
178 mappend = liftM2 mappend
@@ -191,6 +207,7 @@ block = choice [ mempty <$ blanklines
191 , return <$> hline
207 , return <$> hline
194 , paraOrPlain
211 , paraOrPlain
195 ] <?> "block"
212 ] <?> "block"
@@ -500,6 +517,16 @@ setAligns :: [Alignment]
500 -> F OrgTable
517 -> F OrgTable
501setAligns aligns t = return $ t{ orgTableAlignments = aligns }
518setAligns aligns t = return $ t{ orgTableAlignments = aligns }
521+-- Footnote defintions
523+noteBlock :: OrgParser (F Blocks)
526+ content <- skipSpaces *> paraOrPlain
527+ addToNotesTable (ref, content)
503-- Paragraphs or Plain text
530-- Paragraphs or Plain text
504paraOrPlain :: OrgParser (F Blocks)
531paraOrPlain :: OrgParser (F Blocks)
505paraOrPlain = try $
532paraOrPlain = try $
@@ -587,6 +614,7 @@ inline :: OrgParser (F Inlines)
588 choice [ whitespace
615 choice [ whitespace
589 , linebreak
616 , linebreak
590 , linkOrImage
618 , linkOrImage
592 , endline
620 , endline
@@ -632,6 +660,7 @@ endline = try $ do
632 notFollowedBy blankline
660 notFollowedBy blankline
633 notFollowedBy' exampleLine
661 notFollowedBy' exampleLine
634 notFollowedBy' hline
662 notFollowedBy' hline
663+ notFollowedBy' noteMarker
635 notFollowedBy' tableStart
664 notFollowedBy' tableStart
636 notFollowedBy' drawerStart
665 notFollowedBy' drawerStart
637 notFollowedBy' headerStart
666 notFollowedBy' headerStart
@@ -644,6 +673,39 @@ endline = try $ do
644 updateLastPreCharPos
673 updateLastPreCharPos
645 return . return $ B.space
674 return . return $ B.space
676+footnote :: OrgParser (F Inlines)
677+footnote = try $ inlineNote <|> referencedNote
679+inlineNote :: OrgParser (F Inlines)
680+inlineNote = try $ do
682+ ref <- many alphaNum
684+ note <- fmap B.para . trimInlinesF . mconcat <$> many1Till inline (char ']')
685+ when (not $ null ref) $
686+ addToNotesTable ("fn:" ++ ref, note)
687+ return $ B.note <$> note
689+referencedNote :: OrgParser (F Inlines)
690+referencedNote = try $ do
693+ notes <- asksF orgStateNotes'
694+ case lookup ref notes of
695+ Nothing -> return $ B.str $ "[" ++ ref ++ "]"
698+ let contents' = runF contents st{ orgStateNotes' = [] }
699+ return $ B.note contents'
701+noteMarker :: OrgParser String
702+noteMarker = try $ do
704+ choice [ many1Till digit (char ']')
705+ , (++) <$> string "fn:"
706+ <*> many1Till (noneOf "\n\r\t ") (char ']')
647linkOrImage :: OrgParser (F Inlines)
709linkOrImage :: OrgParser (F Inlines)
648linkOrImage = explicitOrImageLink <|> selflinkOrImage <?> "link or image"
710linkOrImage = explicitOrImageLink <|> selflinkOrImage <?> "link or image"