@@ -96,8 +96,8 @@ module Text.Pandoc.Templates ( renderTemplate
96import Data.Char (isAlphaNum)
96import Data.Char (isAlphaNum)
97import Control.Monad (guard, when)
97import Control.Monad (guard, when)
98import Data.Aeson (ToJSON(..), Value(..))
98import Data.Aeson (ToJSON(..), Value(..))
99−import qualified Data.Attoparsec.Text as A
99+import qualified Text.Parsec as P
100−import Data.Attoparsec.Text (Parser)
100+import Text.Parsec.Text (Parser)
101import Control.Applicative
101import Control.Applicative
102import qualified Data.Text as T
102import qualified Data.Text as T
103import Data.Text (Text)
103import Data.Text (Text)
@@ -172,7 +172,10 @@ renderTemplate :: (ToJSON a, TemplateTarget b) => Template -> a -> b
172renderTemplate (Template f) context = toTarget $ f $ toJSON context
172renderTemplate (Template f) context = toTarget $ f $ toJSON context
174compileTemplate :: Text -> Either String Template
174compileTemplate :: 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)
177-- | Like 'renderTemplate', but compiles the template first,
180-- | Like 'renderTemplate', but compiles the template first,
178-- raising an error if compilation fails.
181-- raising an error if compilation fails.
@@ -230,7 +233,7 @@ replaceVar _ _ old = old
231pTemplate :: Parser Template
234pTemplate :: Parser Template
232pTemplate = do
235pTemplate = do
233− sp <- A.option mempty pInitialSpace
236+ sp <- P.option mempty pInitialSpace
234 rest <- mconcat <$> many (pConditional <|>
237 rest <- mconcat <$> many (pConditional <|>
236 pNewline <|>
239 pNewline <|>
@@ -239,40 +242,43 @@ pTemplate = do
239 pEscapedDollar)
242 pEscapedDollar)
240 return $ sp <> rest
243 return $ sp <> rest
245+takeWhile1 :: (Char -> Bool) -> Parser Text
246+takeWhile1 f = T.pack <$> P.many1 (P.satisfy f)
242pLit :: Parser Template
248pLit :: Parser Template
243−pLit = lit <$> A.takeWhile1 (\x -> x /='$' && x /= '\n')
249+pLit = lit <$> takeWhile1 (\x -> x /='$' && x /= '\n')
245pNewline :: Parser Template
251pNewline :: Parser Template
246pNewline = do
252pNewline = do
247− A.char '\n'
253+ P.char '\n'
248− sp <- A.option mempty pInitialSpace
254+ sp <- P.option mempty pInitialSpace
249 return $ lit "\n" <> sp
255 return $ lit "\n" <> sp
251pInitialSpace :: Parser Template
257pInitialSpace :: Parser Template
252pInitialSpace = do
258pInitialSpace = do
253− sps <- A.takeWhile1 (==' ')
259+ sps <- takeWhile1 (==' ')
254 let indentVar = if T.null sps
260 let indentVar = if T.null sps
256 else indent (T.length sps)
262 else indent (T.length sps)
257− v <- A.option mempty $ indentVar <$> pVar
263+ v <- P.option mempty $ indentVar <$> pVar
258 return $ lit sps <> v
264 return $ lit sps <> v
260pEscapedDollar :: Parser Template
266pEscapedDollar :: Parser Template
261−pEscapedDollar = lit "$" <$ A.string "$$"
267+pEscapedDollar = lit "$" <$ P.try (P.string "$$")
263pVar :: Parser Template
269pVar :: Parser Template
264−pVar = var <$> (A.char '$' *> pIdent <* A.char '$')
270+pVar = var <$> (P.try $ P.char '$' *> pIdent <* P.char '$')
266pIdent :: Parser [Text]
272pIdent :: Parser [Text]
267pIdent = do
273pIdent = do
268 first <- pIdentPart
274 first <- pIdentPart
269− rest <- many (A.char '.' *> pIdentPart)
275+ rest <- many (P.char '.' *> pIdentPart)
270 return (first:rest)
276 return (first:rest)
272pIdentPart :: Parser Text
278pIdentPart :: Parser Text
273−pIdentPart = do
279+pIdentPart = P.try $ do
274− first <- A.letter
280+ first <- P.letter
275− rest <- A.takeWhile (\c -> isAlphaNum c || c == '_' || c == '-')
281+ rest <- T.pack <$> P.many (P.satisfy (\c -> isAlphaNum c || c == '_' || c == '-'))
276 let id' = T.singleton first <> rest
282 let id' = T.singleton first <> rest
277 guard $ id' `notElem` reservedWords
283 guard $ id' `notElem` reservedWords
278 return id'
284 return id'
@@ -281,38 +287,38 @@ reservedWords :: [Text]
281reservedWords = ["else","endif","for","endfor","sep"]
287reservedWords = ["else","endif","for","endfor","sep"]
283skipEndline :: Parser ()
289skipEndline :: 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 ()
286pConditional :: Parser Template
292pConditional :: Parser Template
287pConditional = do
293pConditional = do
288− A.string "$if("
294+ P.try $ P.string "$if("
289 id' <- pIdent
295 id' <- pIdent
290− A.string ")$"
296+ P.string ")$"
291 -- if newline after the "if", then a newline after "endif" will be swallowed
297 -- 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)
293 ifContents <- pTemplate
299 ifContents <- pTemplate
294− elseContents <- A.option mempty $
300+ elseContents <- P.option mempty $ P.try $
295− do A.string "$else$"
301+ do P.string "$else$"
296− when multiline $ A.option () skipEndline
302+ when multiline $ P.option () skipEndline
297 pTemplate
303 pTemplate
298− A.string "$endif$"
304+ P.string "$endif$"
299− when multiline $ A.option () skipEndline
305+ when multiline $ P.option () skipEndline
300 return $ cond id' ifContents elseContents
306 return $ cond id' ifContents elseContents
302pFor :: Parser Template
308pFor :: Parser Template
304− A.string "$for("
310+ P.try $ P.string "$for("
305 id' <- pIdent
311 id' <- pIdent
306− A.string ")$"
312+ P.string ")$"
307 -- if newline after the "for", then a newline after "endfor" will be swallowed
313 -- 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
309 contents <- pTemplate
315 contents <- pTemplate
310− sep <- A.option mempty $
316+ sep <- P.option mempty $
311− do A.string "$sep$"
317+ do P.try $ P.string "$sep$"
312− when multiline $ A.option () skipEndline
318+ when multiline $ P.option () skipEndline
313 pTemplate
319 pTemplate
314− A.string "$endfor$"
320+ P.string "$endfor$"
315− when multiline $ A.option () skipEndline
321+ when multiline $ P.option () skipEndline
316 return $ iter id' contents sep
322 return $ iter id' contents sep
318indent :: Int -> Template -> Template
324indent :: Int -> Template -> Template