@@ -96,8 +96,8 @@ module Text.Pandoc.Templates ( renderTemplate
9696import Data.Char (isAlphaNum)
9797import Control.Monad (guard, when)
9898import Data.Aeson (ToJSON(..), Value(..))
99−import qualified Data.Attoparsec.Text as A
100−import Data.Attoparsec.Text (Parser)
99+import qualified Text.Parsec as P
100+import Text.Parsec.Text (Parser)
101101import Control.Applicative
102102import qualified Data.Text as T
103103import Data.Text (Text)
@@ -172,7 +172,10 @@ renderTemplate :: (ToJSON a, TemplateTarget b) => Template -> a -> b
172172renderTemplate (Template f) context = toTarget $ f $ toJSON context
173173
174174compileTemplate :: Text -> Either String Template
175−compileTemplate template = A.parseOnly pTemplate template
175+compileTemplate template =
176+ case P.parse (pTemplate <* P.eof) "template" template of
177+ Left e -> Left (show e)
178+ Right x -> Right x
176179
177180-- | Like 'renderTemplate', but compiles the template first,
178181-- raising an error if compilation fails.
@@ -230,7 +233,7 @@ replaceVar _ _ old = old
230233
231234pTemplate :: Parser Template
232235pTemplate = do
233− sp <- A.option mempty pInitialSpace
236+ sp <- P.option mempty pInitialSpace
234237 rest <- mconcat <$> many (pConditional <|>
235238 pFor <|>
236239 pNewline <|>
@@ -239,40 +242,43 @@ pTemplate = do
239242 pEscapedDollar)
240243 return $ sp <> rest
241244
245+takeWhile1 :: (Char -> Bool) -> Parser Text
246+takeWhile1 f = T.pack <$> P.many1 (P.satisfy f)
247+
242248pLit :: Parser Template
243−pLit = lit <$> A.takeWhile1 (\x -> x /='$' && x /= '\n')
249+pLit = lit <$> takeWhile1 (\x -> x /='$' && x /= '\n')
244250
245251pNewline :: Parser Template
246252pNewline = do
247− A.char '\n'
248− sp <- A.option mempty pInitialSpace
253+ P.char '\n'
254+ sp <- P.option mempty pInitialSpace
249255 return $ lit "\n" <> sp
250256
251257pInitialSpace :: Parser Template
252258pInitialSpace = do
253− sps <- A.takeWhile1 (==' ')
259+ sps <- takeWhile1 (==' ')
254260 let indentVar = if T.null sps
255261 then id
256262 else indent (T.length sps)
257− v <- A.option mempty $ indentVar <$> pVar
263+ v <- P.option mempty $ indentVar <$> pVar
258264 return $ lit sps <> v
259265
260266pEscapedDollar :: Parser Template
261−pEscapedDollar = lit "$" <$ A.string "$$"
267+pEscapedDollar = lit "$" <$ P.try (P.string "$$")
262268
263269pVar :: Parser Template
264−pVar = var <$> (A.char '$' *> pIdent <* A.char '$')
270+pVar = var <$> (P.try $ P.char '$' *> pIdent <* P.char '$')
265271
266272pIdent :: Parser [Text]
267273pIdent = do
268274 first <- pIdentPart
269− rest <- many (A.char '.' *> pIdentPart)
275+ rest <- many (P.char '.' *> pIdentPart)
270276 return (first:rest)
271277
272278pIdentPart :: Parser Text
273−pIdentPart = do
274− first <- A.letter
275− rest <- A.takeWhile (\c -> isAlphaNum c || c == '_' || c == '-')
279+pIdentPart = P.try $ do
280+ first <- P.letter
281+ rest <- T.pack <$> P.many (P.satisfy (\c -> isAlphaNum c || c == '_' || c == '-'))
276282 let id' = T.singleton first <> rest
277283 guard $ id' `notElem` reservedWords
278284 return id'
@@ -281,38 +287,38 @@ reservedWords :: [Text]
281287reservedWords = ["else","endif","for","endfor","sep"]
282288
283289skipEndline :: Parser ()
284−skipEndline = A.skipWhile (`elem` " \t") >> A.char '\n' >> return ()
290+skipEndline = P.try $ P.skipMany (P.satisfy (`elem` " \t")) >> P.char '\n' >> return ()
285291
286292pConditional :: Parser Template
287293pConditional = do
288− A.string "$if("
294+ P.try $ P.string "$if("
289295 id' <- pIdent
290− A.string ")$"
296+ P.string ")$"
291297 -- if newline after the "if", then a newline after "endif" will be swallowed
292− multiline <- A.option False (True <$ skipEndline)
298+ multiline <- P.option False (True <$ skipEndline)
293299 ifContents <- pTemplate
294− elseContents <- A.option mempty $
295− do A.string "$else$"
296− when multiline $ A.option () skipEndline
300+ elseContents <- P.option mempty $ P.try $
301+ do P.string "$else$"
302+ when multiline $ P.option () skipEndline
297303 pTemplate
298− A.string "$endif$"
299− when multiline $ A.option () skipEndline
304+ P.string "$endif$"
305+ when multiline $ P.option () skipEndline
300306 return $ cond id' ifContents elseContents
301307
302308pFor :: Parser Template
303309pFor = do
304− A.string "$for("
310+ P.try $ P.string "$for("
305311 id' <- pIdent
306− A.string ")$"
312+ P.string ")$"
307313 -- if newline after the "for", then a newline after "endfor" will be swallowed
308− multiline <- A.option False $ skipEndline >> return True
314+ multiline <- P.option False $ skipEndline >> return True
309315 contents <- pTemplate
310− sep <- A.option mempty $
311− do A.string "$sep$"
312− when multiline $ A.option () skipEndline
316+ sep <- P.option mempty $
317+ do P.try $ P.string "$sep$"
318+ when multiline $ P.option () skipEndline
313319 pTemplate
314− A.string "$endfor$"
315− when multiline $ A.option () skipEndline
320+ P.string "$endfor$"
321+ when multiline $ P.option () skipEndline
316322 return $ iter id' contents sep
317323
318324indent :: Int -> Template -> Template