From 66fd3247eac9a564aca179ca9fe4fd007ee471e7 Mon Sep 17 00:00:00 2001
From: Sascha Wilde <wilde@sha-bang.de>
Date: Fri, 27 Oct 2017 01:19:28 +0200
Subject: [PATCH] Creole reader (#3994)

This is feature complete but not very thoroughly tested yet.
---
 pandoc.cabal                      |   2 +
 src/Text/Pandoc/Readers.hs        |   3 +
 src/Text/Pandoc/Readers/Creole.hs | 316 ++++++++++++++++++++++++++++++
 test/Tests/Readers/Creole.hs      | 273 ++++++++++++++++++++++++++
 test/test-pandoc.hs               |   2 +
 5 files changed, 596 insertions(+)
 create mode 100644 src/Text/Pandoc/Readers/Creole.hs
 create mode 100644 test/Tests/Readers/Creole.hs

diff --git a/pandoc.cabal b/pandoc.cabal
index a317ea31c..4d19bc41b 100644
--- a/pandoc.cabal
+++ b/pandoc.cabal
@@ -385,6 +385,7 @@ library
                    Text.Pandoc.Readers.LaTeX.Types,
                    Text.Pandoc.Readers.Markdown,
                    Text.Pandoc.Readers.CommonMark,
+                   Text.Pandoc.Readers.Creole,
                    Text.Pandoc.Readers.MediaWiki,
                    Text.Pandoc.Readers.Vimwiki,
                    Text.Pandoc.Readers.RST,
@@ -583,6 +584,7 @@ test-suite test-pandoc
                   Tests.Readers.Txt2Tags
                   Tests.Readers.EPUB
                   Tests.Readers.Muse
+                  Tests.Readers.Creole
                   Tests.Writers.Native
                   Tests.Writers.ConTeXt
                   Tests.Writers.Docbook
diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs
index 92a185e0d..9fc9c3d18 100644
--- a/src/Text/Pandoc/Readers.hs
+++ b/src/Text/Pandoc/Readers.hs
@@ -45,6 +45,7 @@ module Text.Pandoc.Readers
   , readOdt
   , readMarkdown
   , readCommonMark
+  , readCreole
   , readMediaWiki
   , readVimwiki
   , readRST
@@ -76,6 +77,7 @@ import Text.Pandoc.Error
 import Text.Pandoc.Extensions
 import Text.Pandoc.Options
 import Text.Pandoc.Readers.CommonMark
+import Text.Pandoc.Readers.Creole
 import Text.Pandoc.Readers.DocBook
 import Text.Pandoc.Readers.Docx
 import Text.Pandoc.Readers.EPUB
@@ -117,6 +119,7 @@ readers = [ ("native"       , TextReader readNative)
            ,("markdown_github" , TextReader readMarkdown)
            ,("markdown_mmd",  TextReader readMarkdown)
            ,("commonmark"   , TextReader readCommonMark)
+           ,("creole"       , TextReader readCreole)
            ,("gfm"          , TextReader readCommonMark)
            ,("rst"          , TextReader readRST)
            ,("mediawiki"    , TextReader readMediaWiki)
diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs
new file mode 100644
index 000000000..ab90772ef
--- /dev/null
+++ b/src/Text/Pandoc/Readers/Creole.hs
@@ -0,0 +1,316 @@
+{-
+  Copyright (C) 2017 Sascha Wilde <wilde@sha-bang.de>
+
+  partly based on all the other readers, especialy the work by
+  John MacFarlane <jgm@berkeley.edu> and
+  Alexander Sulfrian <alexander.sulfrian@fu-berlin.de>
+  all bugs are solely created by me.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+-}
+
+{- |
+   Module      : Text.Pandoc.Readers.Creole
+   Copyright   : Copyright (C) 2017 Sascha Wilde
+   License     : GNU GPL, version 2 or above
+
+   Maintainer  : Sascha Wilde <wilde@sha-bang.de>
+   Stability   : WIP
+   Portability : portable
+
+Conversion of creole text to 'Pandoc' document.
+-}
+module Text.Pandoc.Readers.Creole ( readCreole
+                                  ) where
+
+import Control.Monad.Except (liftM2, throwError, guard)
+import qualified Data.Foldable as F
+import qualified Text.Pandoc.Builder as B
+import Text.Pandoc.Class (PandocMonad(..))
+import Text.Pandoc.Definition
+import Text.Pandoc.Options
+import Text.Pandoc.Parsing hiding (enclosed)
+import Text.Pandoc.Shared (crFilter)
+import Data.Maybe (fromMaybe)
+import Data.Monoid
+import Data.Text (Text)
+import qualified Data.Text as T
+
+
+-- | Read creole from an input string and return a Pandoc document.
+readCreole :: PandocMonad m
+          => ReaderOptions
+          -> Text
+          -> m Pandoc
+readCreole opts s = do
+  res <- readWithM parseCreole def{ stateOptions = opts }
+             (T.unpack (crFilter s) ++ "\n\n")
+  case res of
+       Left e  -> throwError e
+       Right d -> return d
+
+type CRLParser = ParserT [Char] ParserState
+
+--
+-- Utility funcitons
+--
+
+(<+>) :: (Monad m, Monoid a) => m a -> m a -> m a
+(<+>) = liftM2 (<>)
+
+-- we have to redefine `enclosed' from Text.Pandoc.Parsing, because it
+-- assumes, that there can't be a space after the start parser, but
+-- with creole this is possible.
+enclosed :: (Show end, PandocMonad m) => CRLParser m start   -- ^ start parser
+         -> CRLParser m end  -- ^ end parser
+         -> CRLParser m a    -- ^ content parser (to be used repeatedly)
+         -> CRLParser m [a]
+enclosed start end parser = try $ start >> many1Till parser end
+
+--
+-- main parser
+--
+
+specialChars :: [Char]
+specialChars = "*/~{}\\|[]()<>\"'"
+
+parseCreole :: PandocMonad m => CRLParser m Pandoc
+parseCreole = do
+  bs <- mconcat <$> many block
+  spaces
+  eof
+  return $ B.doc bs
+
+
+--
+-- block parsers
+--
+
+block :: PandocMonad m => CRLParser m B.Blocks
+block = do
+  res <- mempty <$ skipMany1 blankline
+         <|> nowiki
+         <|> header
+         <|> horizontalRule
+         <|> anyList 1
+         <|> table
+         <|> para
+  skipMany blankline
+  return res
+
+nowiki :: PandocMonad m => CRLParser m B.Blocks
+nowiki = try $ nowikiStart >> manyTill content nowikiEnd >>= return . B.codeBlock . mconcat
+  where
+    content = brackets <|> line
+    brackets = try $ option "" ((:[]) <$> newline)
+               <+> (char ' ' >> (many (char ' ') <+> string "}}}") <* eol)
+    line = option "" ((:[]) <$> newline) <+> manyTill anyChar eol
+    eol = lookAhead $ try $ nowikiEnd <|> newline
+    nowikiStart = optional newline >> string "{{{" >> skipMany spaceChar >> newline
+    nowikiEnd = try $ linebreak >> string "}}}" >> skipMany spaceChar >> newline
+
+header :: PandocMonad m => CRLParser m B.Blocks
+header = try $ do
+  skipSpaces
+  level <- many1 (char '=') >>= return . length
+  guard $ level <= 6
+  skipSpaces
+  content <- B.str <$> manyTill (noneOf "\n") headerEnd
+  return $ B.header level content
+  where
+    headerEnd = try $ skipSpaces >> many (char '=') >> skipSpaces >> newline
+
+unorderedList :: PandocMonad m => Int -> CRLParser m B.Blocks
+unorderedList = list '*' B.bulletList
+
+orderedList :: PandocMonad m => Int -> CRLParser m B.Blocks
+orderedList = list '#' B.orderedList
+
+anyList :: PandocMonad m => Int -> CRLParser m B.Blocks
+anyList n = unorderedList n <|> orderedList n
+
+anyListItem :: PandocMonad m => Int -> CRLParser m B.Blocks
+anyListItem n = listItem '*' n <|> listItem '#' n
+
+list :: PandocMonad m => Char -> ([B.Blocks] -> B.Blocks) -> Int -> CRLParser m B.Blocks
+list c f n = many1 (itemPlusSublist <|> listItem c n)
+             >>= return . f
+  where itemPlusSublist = try $ listItem c n <+> anyList (n+1)
+
+listItem :: PandocMonad m => Char -> Int -> CRLParser m B.Blocks
+listItem c n = (listStart >> many1Till inline itemEnd)
+               >>= return . B.plain . B.trimInlines .mconcat
+  where
+    listStart = try $ optional newline >> skipSpaces >> count n (char c)
+                >> (lookAhead $ noneOf [c]) >> skipSpaces
+    itemEnd = endOfParaElement <|> nextItem n
+              <|> if n < 3 then nextItem (n+1)
+                  else nextItem (n+1) <|> nextItem (n-1)
+    nextItem x = lookAhead $ try $ blankline >> anyListItem x >> return mempty
+
+table :: PandocMonad m => CRLParser m B.Blocks
+table = try $ do
+  headers <- optionMaybe headerRow
+  rows <- many1 row
+  return $ B.simpleTable (fromMaybe [mempty] headers) rows
+  where
+    headerRow = try $ skipSpaces >> many1Till headerCell rowEnd
+    headerCell = B.plain . B.trimInlines . mconcat
+                 <$> (string "|=" >> many1Till inline cellEnd)
+    row = try $ skipSpaces >> many1Till cell rowEnd
+    cell = B.plain . B.trimInlines . mconcat
+           <$> (char '|' >> many1Till inline cellEnd)
+    rowEnd = try $ optional (char '|') >> skipSpaces >> newline
+    cellEnd = lookAhead $ try $ char '|' <|> rowEnd
+
+para :: PandocMonad m => CRLParser m B.Blocks
+para = many1Till inline endOfParaElement >>= return . result . mconcat
+ where
+   result content   = if F.all (==Space) content
+                      then mempty
+                      else B.para $ B.trimInlines content
+
+endOfParaElement :: PandocMonad m => CRLParser m ()
+endOfParaElement = lookAhead $ endOfInput <|> endOfPara
+                   <|> startOfList <|> startOfTable
+                   <|> startOfHeader <|> hr <|> startOfNowiki
+  where
+   endOfInput    = try $ skipMany blankline >> skipSpaces >> eof
+   endOfPara     = try $ blankline >> skipMany1 blankline
+   startOf      :: PandocMonad m => CRLParser m a -> CRLParser m ()
+   startOf p     = try $ blankline >> p >> return mempty
+   startOfList   = startOf $ anyList 1
+   startOfTable  = startOf $ table
+   startOfHeader = startOf header
+   startOfNowiki = startOf nowiki
+   hr            = startOf horizontalRule
+
+horizontalRule :: PandocMonad m => CRLParser m B.Blocks
+horizontalRule = try $ skipSpaces >> string "----" >> skipSpaces >> newline
+                 >> return B.horizontalRule
+
+--
+-- inline parsers
+--
+
+inline :: PandocMonad m => CRLParser m B.Inlines
+inline = choice [ whitespace
+                , escapedLink
+                , escapedChar
+                , link
+                , inlineNowiki
+                , placeholder
+                , image
+                , forcedLinebreak
+                , bold
+                , finalBold
+                , italics
+                , finalItalics
+                , str
+                , symbol
+                ] <?> "inline"
+
+escapedChar :: PandocMonad m => CRLParser m B.Inlines
+escapedChar = (try $ char '~' >> noneOf "\t\n ") >>= return . B.str . (:[])
+
+escapedLink :: PandocMonad m => CRLParser m B.Inlines
+escapedLink = try $ do
+  char '~'
+  (orig, _) <- uri
+  return $ B.str orig
+
+image :: PandocMonad m => CRLParser m B.Inlines
+image = try $ do
+  (orig, src) <- wikiImg
+  return $ B.image src "" (B.str $ orig)
+  where
+    linkSrc = many $ noneOf "|}\n\r\t"
+    linkDsc = char '|' >> many (noneOf "}\n\r\t")
+    wikiImg = try $ do
+      string "{{"
+      src <- linkSrc
+      dsc <- option "" linkDsc
+      string "}}"
+      return (dsc, src)
+
+link :: PandocMonad m => CRLParser m B.Inlines
+link = try $ do
+  (orig, src) <- uriLink <|> wikiLink
+  return $ B.link src "" orig
+  where
+    linkSrc = many $ noneOf "|]\n\r\t"
+    linkDsc :: PandocMonad m => String -> CRLParser m B.Inlines
+    linkDsc otxt = B.str
+                   <$> (try $ option otxt
+                         (char '|' >> many (noneOf "]\n\r\t")))
+    linkImg = try $ char '|' >> image
+    wikiLink = try $ do
+      string "[["
+      src <- linkSrc
+      dsc <- linkImg <|> linkDsc src
+      string "]]"
+      return (dsc, src)
+    uriLink = try $ do
+      (orig, src) <- uri
+      return (B.str orig, src)
+
+inlineNowiki :: PandocMonad m => CRLParser m B.Inlines
+inlineNowiki = B.code <$> (start >> manyTill (noneOf "\n\r") end)
+  where
+    start = try $ string "{{{"
+    end = try $ string "}}}" >> (lookAhead $ noneOf "}")
+
+placeholder :: PandocMonad m => CRLParser m B.Inlines
+-- The semantics of the placeholder is basicallly implementation
+-- dependent, so there is no way to DTRT for all cases.
+-- So for now we just drop them.
+placeholder = B.text <$> (try $ string "<<<" >> manyTill anyChar (string ">>>")
+              >> return "")
+
+whitespace :: PandocMonad m => CRLParser m B.Inlines
+whitespace = (lb <|> regsp) >>= return
+  where lb = try $ skipMany spaceChar >> linebreak >> return B.space
+        regsp = try $ skipMany1 spaceChar >> return B.space
+
+linebreak :: PandocMonad m => CRLParser m B.Inlines
+linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline)
+  where lastNewline  = eof >> return mempty
+        innerNewline = return B.space
+
+symbol :: PandocMonad m => CRLParser m B.Inlines
+symbol = oneOf specialChars >>= return . B.str . (:[])
+
+str :: PandocMonad m => CRLParser m B.Inlines
+str = let strChar = noneOf ("\t\n " ++ specialChars) in
+        many1 strChar >>= return . B.str
+
+bold :: PandocMonad m => CRLParser m B.Inlines
+bold = B.strong . mconcat <$>
+       enclosed (string "**") (try $ string "**") inline
+
+italics :: PandocMonad m => CRLParser m B.Inlines
+italics = B.emph . mconcat <$>
+          enclosed (string "//") (try $ string "//") inline
+
+finalBold :: PandocMonad m => CRLParser m B.Inlines
+finalBold = B.strong . mconcat <$>
+            try (string "**" >> many1Till inline endOfParaElement)
+
+finalItalics :: PandocMonad m => CRLParser m B.Inlines
+finalItalics = B.emph . mconcat <$>
+               try (string "//" >> many1Till inline endOfParaElement)
+
+forcedLinebreak :: PandocMonad m => CRLParser m B.Inlines
+forcedLinebreak = try $ string "\\\\" >> return B.linebreak
diff --git a/test/Tests/Readers/Creole.hs b/test/Tests/Readers/Creole.hs
new file mode 100644
index 000000000..96517c25c
--- /dev/null
+++ b/test/Tests/Readers/Creole.hs
@@ -0,0 +1,273 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Tests.Readers.Creole (tests) where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+import Test.Tasty
+import Tests.Helpers
+import Text.Pandoc
+import Text.Pandoc.Arbitrary ()
+import Text.Pandoc.Builder
+
+creole :: Text -> Pandoc
+creole = purely $ readCreole def{ readerStandalone = True }
+
+infix 4 =:
+(=:) :: ToString c
+     => String -> (Text, c) -> TestTree
+(=:) = test creole
+
+tests :: [TestTree]
+tests = [
+  testGroup "Basic Text Formatting" [
+          "bold, single line, fully delimited" =:
+          "only **bold** is bold"
+          =?> para ("only " <> strong "bold" <> " is bold")
+        , "italics, single line, fully delimited" =:
+          "only //this// is in italics"
+          =?> para ("only " <> emph "this" <> " is in italics")
+        , "bold in italics, fully delimited" =:
+          "//**this**// is in bold italics"
+          =?> para (emph (strong "this") <> " is in bold italics")
+        , "italics in bold, fully delimited" =:
+          "**//this//** is in bold italics"
+          =?> para (strong (emph "this") <> " is in bold italics")
+
+        , "escape bold marker" =:
+          "~**not bold" =?> para "**not bold"
+        , "escape italics marker" =:
+          "~//not in italics" =?> para "//not in italics"
+
+        , "inline nowiki, simple" =:
+          "this is {{{**not** ~interpreted}}} at all"
+          =?> para ("this is " <> code "**not** ~interpreted" <> " at all")
+        , "inline nowiki, curly braces inside" =:
+          "this is {{{{{{//including// some `}' chars}}}}}}"
+          =?> para ("this is " <> code "{{{//including// some `}' chars}}}")
+
+        , "placeholder" =:
+          "foo <<<place holder>>> bar"
+          =?> para "foo bar"
+        , "placeholder escaped" =:
+          "foo ~<<<no place holder>>> bar"
+          =?> para "foo <<<no place holder>>> bar"
+        ]
+  , testGroup "Headers" [
+          "header level 1, no space, no trailing =" =:
+          "= Top-Level Header"
+          =?> header 1 (str "Top-Level Header")
+        , "header level 1, leading space, trailing =" =:
+          " = Top-Level Header = "
+          =?> header 1 (str "Top-Level Header")
+        , "header level 2, no space, no trailing =" =:
+          "== Second Level"
+          =?> header 2 (str "Second Level")
+        , "header level 2, leading space, no trailing =" =:
+          "   == Second Level"
+          =?> header 2 (str "Second Level")
+        , "header level 3, no space, no trailing =" =:
+          "=== Third"
+          =?> header 3 (str "Third")
+        , "header level 3, no space, > 3 trailing =" =:
+          "=== Third ======="
+          =?> header 3 (str "Third")
+        , "header level 4, no space, no trailing =" =:
+          "==== Fourth Level Heading"
+          =?> header 4 (str "Fourth Level Heading")
+        , "header level 4, no space, < 4 trailing =" =:
+          "==== Fourth Level Heading =="
+          =?> header 4 (str "Fourth Level Heading")
+        , "header level 5, no space, no trailing =" =:
+          "===== Fifth"
+          =?> header 5 (str "Fifth")
+        , "header level 6, no space, no trailing =" =:
+          "====== Sixth"
+          =?> header 6 (str "Sixth")
+        ]
+  , testGroup "Paragraphs" [
+          "paragraphs: multiple, one line" =:
+          "first line\n\nanother line\n"
+          =?> para "first line" <> para "another line"
+          ]
+  , testGroup "Lists" [
+          "unordered list, two entries, one separating space" =:
+          "* foo\n* bar"
+          =?> bulletList [ plain "foo", plain "bar" ]
+        , "unordered list, three entries, one separating space" =:
+          "* foo\n* bar\n* baz"
+          =?> bulletList [ plain "foo", plain "bar", plain "baz" ]
+        , "para followed by, unordered list, two entries, one separating space" =:
+          "blubber\n* foo\n* bar"
+          =?> para "blubber" <> bulletList [ plain "foo", plain "bar" ]
+        , "nested unordered list, one separating space" =:
+          "* foo\n** bar\n** baz\n* blubb"
+          =?> bulletList [ plain "foo"
+                         <> bulletList [ plain "bar", plain "baz" ]
+                         , plain "blubb" ]
+        , "nested many unordered lists, one separating space" =:
+          ("* foo\n** bar\n*** third\n*** third two\n** baz\n*** third again\n"
+           <> "**** fourth\n***** fith\n* blubb")
+          =?> bulletList [ plain "foo"
+                           <> bulletList [ plain "bar"
+                                           <> bulletList [ plain "third"
+                                                         , plain "third two"]
+                                         , plain "baz"
+                                           <> bulletList [ plain "third again"
+                                                         <> bulletList [
+                                                             plain "fourth"
+                                                             <> bulletList [
+                                                                 plain "fith"
+                                                                 ]
+                                                             ]
+                                                         ]
+                                         ]
+                         , plain "blubb" ]
+        , "nested unordered list, mixed separating space" =:
+          "*foo\n   **   bar\n    **baz\n *      blubb"
+          =?> bulletList [ plain "foo"
+                         <> bulletList [ plain "bar", plain "baz" ]
+                         , plain "blubb" ]
+        , "ordered list, two entries, one separating space" =:
+          "# foo\n# bar"
+          =?> orderedList [ plain "foo", plain "bar" ]
+        , "ordered list, three entries, one separating space" =:
+          "# foo\n# bar\n# baz"
+          =?> orderedList [ plain "foo", plain "bar", plain "baz" ]
+        , "para followed by, ordered list, two entries, one separating space" =:
+          "blubber\n# foo\n# bar"
+          =?> para "blubber" <> orderedList [ plain "foo", plain "bar" ]
+        , "nested ordered list, one separating space" =:
+          "# foo\n## bar\n## baz\n# blubb"
+          =?> orderedList [ plain "foo"
+                         <> orderedList [ plain "bar", plain "baz" ]
+                         , plain "blubb" ]
+        , "nested many ordered lists, one separating space" =:
+          ("# foo\n## bar\n### third\n### third two\n## baz\n### third again\n"
+           <> "#### fourth\n##### fith\n# blubb")
+          =?> orderedList [ plain "foo"
+                           <> orderedList [ plain "bar"
+                                           <> orderedList [ plain "third"
+                                                         , plain "third two"]
+                                         , plain "baz"
+                                           <> orderedList [ plain "third again"
+                                                         <> orderedList [
+                                                             plain "fourth"
+                                                             <> orderedList [
+                                                                 plain "fith"
+                                                                 ]
+                                                             ]
+                                                         ]
+                                         ]
+                         , plain "blubb" ]
+        , "nested ordered list, mixed separating space" =:
+          "#foo\n   ##   bar\n    ##baz\n #      blubb"
+          =?> orderedList [ plain "foo"
+                         <> orderedList [ plain "bar", plain "baz" ]
+                         , plain "blubb" ]
+        , "mixed nested ordered and unordered lists, one separating space" =:
+          ("# foo\n** bar\n### third\n### third two\n** baz\n### third again\n"
+           <> "#### fourth\n***** fith\n# blubb")
+          =?> orderedList [ plain "foo"
+                           <> bulletList [ plain "bar"
+                                           <> orderedList [ plain "third"
+                                                         , plain "third two"]
+                                         , plain "baz"
+                                           <> orderedList [ plain "third again"
+                                                         <> orderedList [
+                                                             plain "fourth"
+                                                             <> bulletList [
+                                                                 plain "fith"
+                                                                 ]
+                                                             ]
+                                                         ]
+                                         ]
+                         , plain "blubb" ]
+        ]
+  , testGroup "NoWiki" [
+          "quoted block, simple" =:
+          "{{{\nfoo bar\n  //baz//\n}}}"
+          =?> codeBlock "foo bar\n  //baz//"
+        , "quoted block, curly bracket exception" =:
+          "{{{\nfoo bar\n  }}}\nbaz\n }}}\n}}}"
+          =?> codeBlock "foo bar\n }}}\nbaz\n}}}"
+        , "forced line breaks" =:
+          "{{{no break!\\\\here}}} but a break\\\\here!"
+          =?> para (code "no break!\\\\here" <> " but a break"
+                    <> linebreak <> "here!")
+        ]
+  , testGroup "Images and Links" [
+          "image simple" =:
+          "{{foo.png}}" =?> para (image "foo.png" "" (str ""))
+        , "image with alt text" =:
+          "Image of a bar: {{/path/to/bar.png|A Bar}} look at it!"
+          =?> para ("Image of a bar: "
+                    <> image "/path/to/bar.png" "" (str "A Bar") <> " look at it!")
+
+        , "auto link" =:
+          "foo http://foo.example.com/bar/baz.html bar"
+          =?> para ("foo "
+                    <> link "http://foo.example.com/bar/baz.html" ""
+                     (str "http://foo.example.com/bar/baz.html")
+                    <> " bar")
+        , "escaped auto link" =:
+          "foo ~http://foo.example.com/bar/baz.html bar"
+          =?> para ("foo http://foo.example.com/bar/baz.html bar")
+        , "wiki link simple" =:
+          "foo [[http://foo.example.com/foo.png]] bar"
+          =?> para ("foo "
+                    <> link "http://foo.example.com/foo.png" ""
+                     (str "http://foo.example.com/foo.png")
+                    <> " bar")
+        , "wiki link with name" =:
+          "foo [[http://foo.example.com/foo.png|my link]] bar"
+          =?> para ("foo "
+                    <> link "http://foo.example.com/foo.png" ""
+                     (str "my link")
+                    <> " bar")
+        , "image link" =:
+          "[[http://foo.example.com/|{{foo.png}}]]"
+          =?> para (link "http://foo.example.com/" "" (image "foo.png" "" (str "")))
+        ]
+  , testGroup "Table" [
+          "Table with Header" =:
+          T.unlines [ "|= Foo |= Bar |= Baz  |"
+                    , "| One  | Two  | Three |"
+                    , "| 1    | 2    | 3     |"
+                    , "| A    | B    | C     |"
+                    ]
+          =?> simpleTable
+            [plain "Foo", plain "Bar" , plain "Baz"]
+            [[plain "One", plain "Two" , plain "Three"]
+            ,[plain "1", plain "2" , plain "3"]
+            ,[plain "A", plain "B" , plain "C"]]
+        , "Table without Header" =:
+          T.unlines [ "| One  | Two  | Three |"
+                    , "| 1    | 2    | 3     |"
+                    , "| A    | B    | C     |"
+                    ]
+          =?> simpleTable [mempty]
+            [[plain "One", plain "Two" , plain "Three"]
+            ,[plain "1", plain "2" , plain "3"]
+            ,[plain "A", plain "B" , plain "C"]]
+        , "Table without Header, no markers at line ends" =:
+          T.unlines [ "| One  | Two  | Three"
+                    , "| 1    | 2    | 3"
+                    , "| A    | B    | C  "
+                    ]
+          =?> simpleTable [mempty]
+            [[plain "One", plain "Two" , plain "Three"]
+            ,[plain "1", plain "2" , plain "3"]
+            ,[plain "A", plain "B" , plain "C"]]
+        , "Table with Header, with formatting" =:
+          T.unlines [ "|= **Foo**        |= **Bar** |= **Baz**  |"
+                    , "|//one// element  |//second// elt|Three  |"
+                    , "| {{{1}}}    | {{{{}}}}    | [[link]]    |"
+                    ]
+          =?> simpleTable
+            [plain $ strong "Foo", plain $ strong "Bar" , plain $ strong "Baz"]
+            [[plain (emph "one" <> " element"), plain (emph "second" <> " elt")
+             ,plain "Three"]
+            ,[plain $ code "1", plain $ code "{}"
+             ,plain $ link "link" "" (str "link")]]
+        ]
+  ]
diff --git a/test/test-pandoc.hs b/test/test-pandoc.hs
index caa2b7c65..acfc4c5cd 100644
--- a/test/test-pandoc.hs
+++ b/test/test-pandoc.hs
@@ -17,6 +17,7 @@ import qualified Tests.Readers.Org
 import qualified Tests.Readers.RST
 import qualified Tests.Readers.Txt2Tags
 import qualified Tests.Readers.Muse
+import qualified Tests.Readers.Creole
 import qualified Tests.Shared
 import qualified Tests.Writers.AsciiDoc
 import qualified Tests.Writers.ConTeXt
@@ -63,6 +64,7 @@ tests = testGroup "pandoc tests" [ Tests.Command.tests
           , testGroup "Txt2Tags" Tests.Readers.Txt2Tags.tests
           , testGroup "EPUB" Tests.Readers.EPUB.tests
           , testGroup "Muse" Tests.Readers.Muse.tests
+          , testGroup "Creole" Tests.Readers.Creole.tests
           ]
         , testGroup "Lua filters" Tests.Lua.tests
         ]