git.iko.soy/typeable/pandoc
on master help sign in
Org reader: extract parsing function to module

The Org-mode reader uses many functions defined in the `Text.Pandoc.Parsing` utility module. Some of the functions are overwritten with versions adapted to Org-mode idiosyncrasies. These special functions, as well as the normal Pandoc versions, are combined in a single module to increase the ease of use. This leads to decoupling of Org-mode and Pandoc and hence to slightly cleaner code. The downside is code-bloat due to repeated import/export statements.

commita340c7249f8e19d36ee4a68663b4c97e0893292b authorAlbert Krewinkel <albert@zeitkraut.de> dateSun 22 May 2016, 16:33 (10 years ago) parentcc937ee
4 files changed+198−78
pandoc.cabal+1 src/Text/Pandoc/Readers/Org.hs+10−76 src/Text/Pandoc/Readers/Org/ParserState.hs+5−2 src/Text/Pandoc/Readers/Org/Parsing.hs+182
pandoc.cabal+1
@@ -394,6 +394,7 @@ Library
394394 Text.Pandoc.Readers.Odt.Arrows.State,
395395 Text.Pandoc.Readers.Odt.Arrows.Utils,
396396 Text.Pandoc.Readers.Org.ParserState,
397+ Text.Pandoc.Readers.Org.Parsing,
397398 Text.Pandoc.Writers.Shared,
398399 Text.Pandoc.Asciify,
399400 Text.Pandoc.MIME,
src/Text/Pandoc/Readers/Org.hs+10−76
@@ -29,27 +29,23 @@ Conversion of org-mode formatted plain text to 'Pandoc' document.
2929-}
3030module Text.Pandoc.Readers.Org ( readOrg ) where
3131
32+import Text.Pandoc.Readers.Org.ParserState
33+import Text.Pandoc.Readers.Org.Parsing
34+
3235import qualified Text.Pandoc.Builder as B
3336import Text.Pandoc.Builder ( Inlines, Blocks )
3437import Text.Pandoc.Definition
3538import Text.Pandoc.Compat.Monoid ((<>))
3639import Text.Pandoc.Error
3740import Text.Pandoc.Options
38−import qualified Text.Pandoc.Parsing as P
39−import Text.Pandoc.Parsing hiding ( F, unF, askF, asksF, runF
40− , anyLine, blanklines, newline
41− , orderedListMarker
42− , parseFromString
43− )
4441import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline)
45−import Text.Pandoc.Readers.Org.ParserState
4642import Text.Pandoc.Shared (compactify', compactify'DL)
4743import Text.TeXMath (readTeX, writePandoc, DisplayType(..))
4844import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
4945
5046import Control.Arrow (first)
5147import Control.Monad (foldM, guard, mplus, mzero, when)
52−import Control.Monad.Reader ( Reader, runReader )
48+import Control.Monad.Reader ( runReader )
5349import Data.Char (isAlphaNum, isSpace, toLower, toUpper)
5450import Data.List ( foldl', intersperse, isPrefixOf, isSuffixOf )
5551import qualified Data.Map as M
@@ -63,9 +59,6 @@ readOrg :: ReaderOptions -- ^ Reader options
6359 -> Either PandocError Pandoc
6460readOrg opts s = flip runReader def $ readWithM parseOrg def{ orgStateOptions = opts } (s ++ "\n\n")
6561
66−-- | The parser used to read org files.
67−type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal)
68−
6962--
7063-- Functions acting on the parser state
7164--
@@ -73,14 +66,6 @@ recordAnchorId :: String -> OrgParser ()
7366recordAnchorId i = updateState $ \s ->
7467 s{ orgStateAnchorIds = i : (orgStateAnchorIds s) }
7568
76−updateLastForbiddenCharPos :: OrgParser ()
77−updateLastForbiddenCharPos = getPosition >>= \p ->
78− updateState $ \s -> s{ orgStateLastForbiddenCharPos = Just p}
79−
80−updateLastPreCharPos :: OrgParser ()
81−updateLastPreCharPos = getPosition >>= \p ->
82− updateState $ \s -> s{ orgStateLastPreCharPos = Just p}
83−
8469pushToInlineCharStack :: Char -> OrgParser ()
8570pushToInlineCharStack c = updateState $ \s ->
8671 s{ orgStateEmphasisCharStack = c:orgStateEmphasisCharStack s }
@@ -258,57 +243,6 @@ isHeaderLevelLowerEq n blk =
258243 _ -> False
259244
260245
261−--
262−-- Adaptions and specializations of parsing utilities
263−--
264−
265−-- The version Text.Pandoc.Parsing cannot be used, as we need additional parts
266−-- of the state saved and restored.
267−parseFromString :: OrgParser a -> String -> OrgParser a
268−parseFromString parser str' = do
269− oldLastPreCharPos <- orgStateLastPreCharPos <$> getState
270− updateState $ \s -> s{ orgStateLastPreCharPos = Nothing }
271− result <- P.parseFromString parser str'
272− updateState $ \s -> s{ orgStateLastPreCharPos = oldLastPreCharPos }
273− return result
274−
275−-- | Like @Text.Parsec.Char.newline@, but causes additional state changes.
276−newline :: OrgParser Char
277−newline =
278− P.newline
279− <* updateLastPreCharPos
280− <* updateLastForbiddenCharPos
281−
282−-- | Like @Text.Parsec.Char.blanklines@, but causes additional state changes.
283−blanklines :: OrgParser [Char]
284−blanklines =
285− P.blanklines
286− <* updateLastPreCharPos
287− <* updateLastForbiddenCharPos
288−
289−anyLine :: OrgParser String
290−anyLine =
291− P.anyLine
292− <* updateLastPreCharPos
293− <* updateLastForbiddenCharPos
294−
295−-- | Succeeds when we're in list context.
296−inList :: OrgParser ()
297−inList = do
298− ctx <- orgStateParserContext <$> getState
299− guard (ctx == ListItemState)
300−
301−-- | Parse in different context
302−withContext :: ParserContext -- ^ New parser context
303− -> OrgParser a -- ^ Parser to run in that context
304− -> OrgParser a
305−withContext context parser = do
306− oldContext <- orgStateParserContext <$> getState
307− updateState $ \s -> s{ orgStateParserContext = context }
308− result <- parser
309− updateState $ \s -> s{ orgStateParserContext = oldContext }
310− return result
311−
312246--
313247-- parsing blocks
314248--
@@ -398,7 +332,7 @@ keyValues = try $
398332 endOfValue :: OrgParser ()
399333 endOfValue =
400334 lookAhead $ (() <$ try (many1 spaceChar <* key))
401− <|> () <$ P.newline
335+ <|> () <$ newline
402336
403337
404338--
@@ -675,7 +609,7 @@ propertiesDrawer = try $ do
675609 key = try $ skipSpaces *> char ':' *> many1Till nonspaceChar (char ':')
676610
677611 value :: OrgParser String
678− value = try $ skipSpaces *> manyTill anyChar (try $ skipSpaces *> P.newline)
612+ value = try $ skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline)
679613
680614keyValuesToAttr :: [(String, String)] -> Attr
681615keyValuesToAttr kvs =
@@ -696,7 +630,7 @@ keyValuesToAttr kvs =
696630figure :: OrgParser (F Blocks)
697631figure = try $ do
698632 figAttrs <- blockAttributes
699− src <- skipSpaces *> selfTarget <* skipSpaces <* P.newline
633+ src <- skipSpaces *> selfTarget <* skipSpaces <* newline
700634 guard . not . isNothing . blockAttrCaption $ figAttrs
701635 guard (isImageFilename src)
702636 let figName = fromMaybe mempty $ blockAttrName figAttrs
@@ -785,7 +719,7 @@ parseFormat = try $ do
785719header :: OrgParser (F Blocks)
786720header = try $ do
787721 level <- headerStart
788− title <- manyTill inline (lookAhead $ optional headerTags <* P.newline)
722+ title <- manyTill inline (lookAhead $ optional headerTags <* newline)
789723 tags <- option [] headerTags
790724 newline
791725 propAttr <- option nullAttr (keyValuesToAttr <$> propertiesDrawer)
@@ -1083,7 +1017,7 @@ definitionListItem parseMarkerGetLength = try $ do
10831017 return $ (,) <$> term' <*> fmap (:[]) contents'
10841018 where
10851019 definitionMarker =
1086− spaceChar *> string "::" <* (spaceChar <|> lookAhead P.newline)
1020+ spaceChar *> string "::" <* (spaceChar <|> lookAhead newline)
10871021
10881022
10891023-- parse raw text for one list item, excluding start marker and continuations
@@ -1559,7 +1493,7 @@ many1TillNOrLessNewlines n p end = try $
15591493 nMoreLines k cs = try $ (final k cs <|> rest k cs)
15601494 >>= uncurry nMoreLines
15611495 final _ cs = (\x -> (Nothing, cs ++ x)) <$> try finalLine
1562− rest m cs = (\x -> (minus1 <$> m, cs ++ x ++ "\n")) <$> try (manyTill p P.newline)
1496+ rest m cs = (\x -> (minus1 <$> m, cs ++ x ++ "\n")) <$> try (manyTill p newline)
15631497 finalLine = try $ manyTill p end
15641498 minus1 k = k - 1
15651499 oneOrMore cs = guard (not $ null cs) *> return cs
src/Text/Pandoc/Readers/Org/ParserState.hs+5−2
@@ -29,9 +29,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2929Define the Org-mode parser state.
3030-}
3131module Text.Pandoc.Readers.Org.ParserState
32− ( OrgParserState(..)
33− , OrgParserLocal(..)
32+ ( OrgParserState (..)
33+ , OrgParserLocal (..)
3434 , OrgNoteRecord
35+ , HasReaderOptions (..)
36+ , HasQuoteContext (..)
3537 , F(..)
3638 , askF
3739 , asksF
@@ -184,6 +186,7 @@ modifyExportSettings :: ExportSettingSetter a -> a -> OrgParserState -> OrgParse
184186modifyExportSettings setter val state =
185187 state { orgStateExportSettings = setter val . orgStateExportSettings $ state }
186188
189+
187190--
188191-- Parser state reader
189192--
src/Text/Pandoc/Readers/Org/Parsing.hsnew file+182
@@ -0,0 +1,182 @@
1+{-
2+Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Readers.Org.Options
21+ Copyright : Copyright (C) 2014-2016 Albert Krewinkel
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
25+
26+Org-mode parsing utilities.
27+
28+Most functions are simply re-exports from @Text.Pandoc.Parsing@, some
29+functions are adapted to Org-mode specific functionality.
30+-}
31+module Text.Pandoc.Readers.Org.Parsing
32+ ( OrgParser
33+ , anyLine
34+ , blanklines
35+ , newline
36+ , parseFromString
37+ , inList
38+ , withContext
39+ , updateLastForbiddenCharPos
40+ , updateLastPreCharPos
41+ -- * Re-exports from Text.Pandoc.Parser
42+ , ParserContext (..)
43+ , many1Till
44+ , notFollowedBy'
45+ , spaceChar
46+ , nonspaceChar
47+ , skipSpaces
48+ , blankline
49+ , enclosed
50+ , stringAnyCase
51+ , charsInBalanced
52+ , uri
53+ , withRaw
54+ , readWithM
55+ , guardEnabled
56+ , updateLastStrPos
57+ , notAfterString
58+ , ParserState (..)
59+ , registerHeader
60+ , QuoteContext (..)
61+ , singleQuoteStart
62+ , singleQuoteEnd
63+ , doubleQuoteStart
64+ , doubleQuoteEnd
65+ , dash
66+ , ellipses
67+ , citeKey
68+ -- * Re-exports from Text.Pandoc.Parsec
69+ , runParser
70+ , getInput
71+ , char
72+ , letter
73+ , digit
74+ , alphaNum
75+ , skipMany1
76+ , spaces
77+ , anyChar
78+ , string
79+ , count
80+ , eof
81+ , noneOf
82+ , oneOf
83+ , lookAhead
84+ , notFollowedBy
85+ , many
86+ , many1
87+ , manyTill
88+ , (<|>)
89+ , (<?>)
90+ , choice
91+ , try
92+ , sepBy
93+ , sepBy1
94+ , option
95+ , optional
96+ , optionMaybe
97+ , getState
98+ , updateState
99+ , SourcePos
100+ , getPosition
101+ ) where
102+
103+import Text.Pandoc.Readers.Org.ParserState
104+
105+import qualified Text.Pandoc.Parsing as P
106+import Text.Pandoc.Parsing hiding ( anyLine, blanklines, newline
107+ , parseFromString )
108+
109+import Control.Monad ( guard )
110+import Control.Monad.Reader ( Reader )
111+
112+-- | The parser used to read org files.
113+type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal)
114+
115+--
116+-- Adaptions and specializations of parsing utilities
117+--
118+
119+-- | Parse any line of text
120+anyLine :: OrgParser String
121+anyLine =
122+ P.anyLine
123+ <* updateLastPreCharPos
124+ <* updateLastForbiddenCharPos
125+
126+-- The version Text.Pandoc.Parsing cannot be used, as we need additional parts
127+-- of the state saved and restored.
128+parseFromString :: OrgParser a -> String -> OrgParser a
129+parseFromString parser str' = do
130+ oldLastPreCharPos <- orgStateLastPreCharPos <$> getState
131+ updateState $ \s -> s{ orgStateLastPreCharPos = Nothing }
132+ result <- P.parseFromString parser str'
133+ updateState $ \s -> s{ orgStateLastPreCharPos = oldLastPreCharPos }
134+ return result
135+
136+-- | Like @Text.Parsec.Char.newline@, but causes additional state changes.
137+newline :: OrgParser Char
138+newline =
139+ P.newline
140+ <* updateLastPreCharPos
141+ <* updateLastForbiddenCharPos
142+
143+-- | Like @Text.Parsec.Char.blanklines@, but causes additional state changes.
144+blanklines :: OrgParser [Char]
145+blanklines =
146+ P.blanklines
147+ <* updateLastPreCharPos
148+ <* updateLastForbiddenCharPos
149+
150+-- | Succeeds when we're in list context.
151+inList :: OrgParser ()
152+inList = do
153+ ctx <- orgStateParserContext <$> getState
154+ guard (ctx == ListItemState)
155+
156+-- | Parse in different context
157+withContext :: ParserContext -- ^ New parser context
158+ -> OrgParser a -- ^ Parser to run in that context
159+ -> OrgParser a
160+withContext context parser = do
161+ oldContext <- orgStateParserContext <$> getState
162+ updateState $ \s -> s{ orgStateParserContext = context }
163+ result <- parser
164+ updateState $ \s -> s{ orgStateParserContext = oldContext }
165+ return result
166+
167+--
168+-- Parser state update functions
169+--
170+
171+-- | Set the current position as the last position at which a forbidden char
172+-- was found (i.e. a character which is not allowed at the inner border of
173+-- markup).
174+updateLastForbiddenCharPos :: OrgParser ()
175+updateLastForbiddenCharPos = getPosition >>= \p ->
176+ updateState $ \s -> s{ orgStateLastForbiddenCharPos = Just p}
177+
178+-- | Set the current parser position as the position at which a character was
179+-- seen which allows inline markup to follow.
180+updateLastPreCharPos :: OrgParser ()
181+updateLastPreCharPos = getPosition >>= \p ->
182+ updateState $ \s -> s{ orgStateLastPreCharPos = Just p}