diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,71 @@
 # Revision history for asciidoc-hs
 
+## 0.1.1 -- 2026-08-27
+
+  * Fix counter type for lowercase alpha start values.
+    `{counter:name:a}` produced UpperAlphaCounter due to a copy-paste error.
+
+  * Fix Meta Semigroup to keep title attributes with the title.
+    Both branches of the docTitleAttributes case returned m2's attributes,
+    so concatenating documents paired the first document's title with the
+    second document's title attributes.
+
+  * Allow empty cells in CSV/TSV tables.
+
+  * Require ';' to terminate character entity references.
+
+  * Traverse both components of definition list items in generic traversals.
+
+  * Require a space or end of line after definition list markers.
+
+  * Fix infinite loop on unterminated fenced and literal delimited blocks
+    at end of document.
+
+  * Consume extra backticks of a longer closing fence.
+    A closing fence longer than the opening one (e.g. closing a ``` block
+    with ````) was only matched up to the opening length, leaking the
+    remaining backticks into the following block.
+
+  * Guard against include cycles. A file that (transitively) included
+    itself caused infinite recursion in `handleIncludes`.
+
+  * Resolve attribute references at the point of use.
+    Attribute references were only substituted in a post-processing pass
+    using the end-of-document attribute values, so a reference before a
+    redefinition incorrectly picked up the later value.
+
+  * Infer implicit table headers from layout like Asciidoctor.
+    Tables were given a header row by default unless the noheader option
+    was set.  Asciidoctor instead only implies a header when the first row
+    sits on a single line directly after the opening border and is
+    followed by a blank line.
+
+  * Add a benchmark suite.
+
+  * Consume whole letter runs in the inline parser.
+
+  * Try macro, autolink and email starts once per letter run.
+
+  * Avoid the full cell-separator lookahead at every PSV cell character.
+
+  * Speed up the inline, table and post-processing paths.
+
+  * Flatten the parser monad stack. Replace the derived
+    ReaderT/StateT-over-attoparsec stack with a hand-rolled, inlined
+    equivalent, so primitive operations no longer pay for two layers
+    of transformer binds. Backtracking semantics are unchanged: state
+    changes made by a failed branch of `<|>` are discarded.
+
+  * Reject non-definition-list lines with a substring check.
+
+  * Skip typographic replacement when no trigger is present.
+
+  * Scan plain inline text in chunks, not per word.
+
+  * Replace attoparsec with a hand-rolled CPS parser.
+
+  * Do typographic replacement in one pass, without a String round trip.
+
 ## 0.1.0.5 -- 2026-08-27
 
     Tables: skip whitespace before cell spec (#13).
diff --git a/asciidoc.cabal b/asciidoc.cabal
--- a/asciidoc.cabal
+++ b/asciidoc.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.4
 name:               asciidoc
-version:            0.1.0.5
+version:            0.1.1
 synopsis:           AsciiDoc parser.
 description:        A parser for AsciiDoc syntax.
 license:            BSD-3-Clause
@@ -33,7 +33,6 @@
     build-depends:    base >=4.14 && <5
                     , text
                     , mtl
-                    , attoparsec
                     , filepath
                     , containers
                     , tagsoup
@@ -76,3 +75,16 @@
         directory,
         filepath,
         process
+
+benchmark asciidoc-bench
+    import:           warnings
+    default-language: Haskell2010
+    type:             exitcode-stdio-1.0
+    hs-source-dirs:   bench
+    main-is:          Main.hs
+    ghc-options:      -rtsopts "-with-rtsopts=-A32m"
+    build-depends:
+        base >=4.14 && <5,
+        asciidoc,
+        tasty-bench,
+        text
diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
+import Test.Tasty.Bench
+import qualified Data.Text as T
+import Data.Text (Text)
+import Data.Functor.Identity (Identity (..))
+import Data.Monoid (Sum (..))
+import AsciiDoc
+
+main :: IO ()
+main = defaultMain
+  [ bgroup "prose"
+      [ bench (show n <> "KB") $ nf parseSize (prose n)
+      | n <- [32, 64, 128 :: Int]
+      ]
+  , bgroup "longword"
+      [ bench (show n) $ nf parseSize (longword n)
+      | n <- [1000, 2000, 4000 :: Int]
+      ]
+  , bgroup "formatting"
+      [ bench "32KB" $ nf parseSize (formatting 32) ]
+  , bgroup "typography"
+      [ bench "32KB" $ nf parseSize (typography 32) ]
+  , bgroup "table"
+      [ bench (show n <> "rows") $ nf parseSize (table n)
+      | n <- [250, 500, 1000 :: Int]
+      ]
+  ]
+
+-- Parse a document and force its interesting parts, returning a size.
+parseSize :: Text -> Int
+parseSize t = docSize $ runIdentity $
+  parseDocument (const (Identity "")) raiseError "bench.adoc" t
+ where
+  raiseError fp pos msg =
+    error $ fp <> "@" <> show pos <> ": " <> msg
+
+docSize :: Document -> Int
+docSize d = getSum (foldBlocks (const (Sum 1)) d) +
+            getSum (foldInlines inlineSize d)
+ where
+  inlineSize (Inline _ (Str s)) = Sum (T.length s)
+  inlineSize _ = Sum 1
+
+-- n KB of plain prose paragraphs.
+prose :: Int -> Text
+prose n = T.replicate (n * 16) paragraph
+ where
+  paragraph = T.replicate 7 sentence <> "\n\n"  -- ~64 bytes/sentence
+  sentence = "The quick brown fox jumps over one lazy dog every morning. "
+
+-- A single unbroken run of letters (worst case for per-letter lookahead).
+longword :: Int -> Text
+longword n = T.replicate n "a" <> "\n"
+
+-- n KB of text with plenty of inline formatting.
+formatting :: Int -> Text
+formatting n = T.replicate (n * 16) paragraph
+ where
+  paragraph = T.replicate 8 chunk <> "\n\n"  -- 8 * 8 bytes
+  chunk = "a *b* `c` _d_ "
+
+-- n KB of prose with plenty of typographic replacements.
+typography :: Int -> Text
+typography n = T.replicate (n * 16) paragraph
+ where
+  paragraph = T.replicate 7 sentence <> "\n\n"  -- ~64 bytes/sentence
+  sentence = "It's odd -- the dog's list... (C) 2024 -> next <= prev now. "
+
+-- A PSV table with n rows of five cells.
+table :: Int -> Text
+table n =
+  "|===\n" <> T.replicate n row <> "|===\n"
+ where
+  row = "| alpha | beta | gamma | delta | epsilon\n"
diff --git a/src/AsciiDoc/AST.hs b/src/AsciiDoc/AST.hs
--- a/src/AsciiDoc/AST.hs
+++ b/src/AsciiDoc/AST.hs
@@ -120,7 +120,7 @@
                   , docTitleAttributes =
                                case docTitle m1 of
                                  [] -> docTitleAttributes m2
-                                 _ -> docTitleAttributes m2
+                                 _ -> docTitleAttributes m1
                   , docAuthors = docAuthors m1 <> docAuthors m2
                   , docRevision = docRevision m1 `mplus` docRevision m2
                   , docAttributes = docAttributes m1 <> docAttributes m2
diff --git a/src/AsciiDoc/Generic.hs b/src/AsciiDoc/Generic.hs
--- a/src/AsciiDoc/Generic.hs
+++ b/src/AsciiDoc/Generic.hs
@@ -60,6 +60,15 @@
   foldInlines f = foldMap (foldInlines f)
   mapInlines f = mapM (mapInlines f)
 
+-- Note: this instance is needed because the (t a) instance above would
+-- otherwise match pairs via the Traversable instance for ((,) a), which
+-- traverses only the second component.  Definition list items are pairs
+-- of ([Inline], [Block]), and both components must be traversed.
+instance {-# OVERLAPPING #-} (HasInlines a, HasInlines b)
+         => HasInlines (a, b) where
+  foldInlines f (x, y) = foldInlines f x <> foldInlines f y
+  mapInlines f (x, y) = liftM2 (,) (mapInlines f x) (mapInlines f y)
+
 instance HasInlines Inline where
   foldInlines f i@(Inline _ ty) =
     f i <> foldInlines f ty
@@ -123,6 +132,11 @@
 instance (HasBlocks a, Traversable t, Foldable t) => HasBlocks (t a) where
   foldBlocks f = foldMap (foldBlocks f)
   mapBlocks f = mapM (mapBlocks f)
+
+instance {-# OVERLAPPING #-} (HasBlocks a, HasBlocks b)
+         => HasBlocks (a, b) where
+  foldBlocks f (x, y) = foldBlocks f x <> foldBlocks f y
+  mapBlocks f (x, y) = liftM2 (,) (mapBlocks f x) (mapBlocks f y)
 
 instance HasBlocks Block where
   foldBlocks f i@(Block _ _ ty) =
diff --git a/src/AsciiDoc/Parse.hs b/src/AsciiDoc/Parse.hs
--- a/src/AsciiDoc/Parse.hs
+++ b/src/AsciiDoc/Parse.hs
@@ -1,7 +1,9 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE QuantifiedConstraints #-}
+{-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE ScopedTypeVariables #-}
@@ -19,15 +21,16 @@
 import qualified Data.Text as T
 import qualified Data.Text.Read as TR
 import Data.Text (Text)
-import Data.List (foldl', intersperse, isPrefixOf)
-import qualified Data.Attoparsec.Text as A
+import Data.List (foldl', intersperse, isPrefixOf, sortOn)
+import qualified Data.Text.Internal as TI
+import qualified Data.Text.Unsafe as TU
 import System.FilePath
 import Control.Applicative
 import Control.Monad
 import Control.Monad.State
 import Control.Monad.Reader
 import Data.Char (isAlphaNum, isAscii, isSpace, isLetter, isPunctuation, chr, isDigit,
-                  isUpper, isLower, ord)
+                  isHexDigit, digitToInt, isUpper, isLower, ord)
 import AsciiDoc.AST
 import AsciiDoc.Generic
 -- import Debug.Trace
@@ -42,10 +45,31 @@
                   -- ^ Path of file containing the text
               -> Text -- ^ Text to convert
               -> m Document
-parseDocument getFileContents raiseError path t =
-   handleResult (parse pDocument path t) >>= handleIncludes
-     >>= resolveAttributeReferences . addIdentifiers
-     >>= resolveCrossReferences
+parseDocument getFileContents raiseError path t = do
+  -- The parser records which constructs occurred, so that the
+  -- post-processing passes (each a full traversal of the AST) can be
+  -- skipped when they would do nothing.  When includes are expanded,
+  -- their contents are not reflected in the flags, so all passes run.
+  (doc0, flags) <- case parse ((,) <$> pDocument <*> gets parseFlags) path t of
+                     Left err -> do
+                       -- raiseError may return a fallback document whose
+                       -- contents we know nothing about, so run all passes.
+                       d <- raiseError path (errorPosition err)
+                                            (errorMessage err)
+                       pure (d, ParseFlags True True True True)
+                     Right r -> pure r
+  doc1 <- if sawInclude flags
+             then handleIncludes doc0
+             else pure doc0
+  let doc2 = if sawInclude flags || sawSection flags
+                then addIdentifiers doc1
+                else doc1
+  doc3 <- if sawInclude flags || sawAttributeReference flags
+             then resolveAttributeReferences doc2
+             else pure doc2
+  if sawInclude flags || sawCrossReference flags
+     then resolveCrossReferences doc3
+     else pure doc3
  where
   handleResult (Left err) =
     raiseError path (errorPosition err) (errorMessage err)
@@ -80,20 +104,25 @@
        Just x -> return $ Inline attr (Str x)
   goAttref _ il = return il
 
-  handleIncludes = mapBlocks handleIncludeBlock
+  handleIncludes = mapBlocks (handleIncludeBlock [path])
 
-  handleIncludeBlock (Block attr mbtitle (Include fp Nothing)) =
-    (do contents <- getFileContents fp
-        Block attr mbtitle . Include fp . Just . docBlocks <$>
-          handleResult (parse pDocument fp contents))
-      >>= mapBlocks handleIncludeBlock
-  handleIncludeBlock (Block attr mbtitle
-                         (IncludeListing mblang fp Nothing)) =
+  -- The first argument is the chain of files being included; a file
+  -- that (transitively) includes itself is left unexpanded instead of
+  -- recursing forever.
+  handleIncludeBlock seen b@(Block attr mbtitle (Include fp Nothing))
+    | fp `elem` seen = pure b
+    | otherwise =
+        (do contents <- getFileContents fp
+            Block attr mbtitle . Include fp . Just . docBlocks <$>
+              handleResult (parse pDocument fp contents))
+          >>= mapBlocks (handleIncludeBlock (fp : seen))
+  handleIncludeBlock seen (Block attr mbtitle
+                             (IncludeListing mblang fp Nothing)) =
     (do contents <- getFileContents fp
         pure $ Block attr mbtitle $ IncludeListing mblang fp
              $ Just (map (`SourceLine` []) (T.lines contents)))
-      >>= mapBlocks handleIncludeBlock
-  handleIncludeBlock x = pure x
+      >>= mapBlocks (handleIncludeBlock (fp : seen))
+  handleIncludeBlock _ x = pure x
 
 -- | Make a relative path relative to a parent's directory.
 -- Leaves absolute paths alone.
@@ -105,16 +134,96 @@
 
 --- Wrapped parser type:
 
-newtype P a = P { unP :: ReaderT ParserConfig (StateT ParserState A.Parser) a }
-  deriving (Functor, Applicative, Alternative, Monad, MonadPlus,
-            MonadFail, MonadReader ParserConfig, MonadState ParserState)
+-- A parser in continuation-passing style over the whole input Text.
+-- The config, state and remaining input are threaded by hand and
+-- passed directly to a success continuation, so binds allocate no
+-- intermediate results.  Incremental (chunked) input is not
+-- supported -- the whole document is in memory anyway -- which makes
+-- backtracking cheap: (<|>) simply re-runs the second parser with the
+-- state and input it saved.  As with StateT over a backtracking
+-- parser, state changes made by a failed branch of (<|>) are
+-- discarded.  The failure continuation receives the message and the
+-- remaining input at the failure site (used to report a position).
+newtype P a = P { unP :: forall r. ParserConfig -> ParserState -> Text
+                      -> (String -> Text -> r)              -- failure
+                      -> (a -> ParserState -> Text -> r)    -- success
+                      -> r }
 
+instance Functor P where
+  fmap f (P m) = P $ \c s t kf ks -> m c s t kf (\a s' t' -> ks (f a) s' t')
+  {-# INLINE fmap #-}
+
+instance Applicative P where
+  pure a = P $ \_ s t _ ks -> ks a s t
+  {-# INLINE pure #-}
+  P mf <*> P ma = P $ \c s t kf ks ->
+    mf c s t kf (\f s' t' ->
+      ma c s' t' kf (\a s'' t'' -> ks (f a) s'' t''))
+  {-# INLINE (<*>) #-}
+  P ma *> P mb = P $ \c s t kf ks ->
+    ma c s t kf (\_ s' t' -> mb c s' t' kf ks)
+  {-# INLINE (*>) #-}
+  P ma <* P mb = P $ \c s t kf ks ->
+    ma c s t kf (\a s' t' ->
+      mb c s' t' kf (\_ s'' t'' -> ks a s'' t''))
+  {-# INLINE (<*) #-}
+
+instance Monad P where
+  P m >>= f = P $ \c s t kf ks ->
+    m c s t kf (\a s' t' -> unP (f a) c s' t' kf ks)
+  {-# INLINE (>>=) #-}
+
+instance MonadFail P where
+  fail msg = P $ \_ _ t kf _ -> kf ("Failed reading: " <> msg) t
+
+instance Alternative P where
+  empty = P $ \_ _ t kf _ -> kf "empty" t
+  {-# INLINE empty #-}
+  -- Note that the success continuation is passed through unchanged:
+  -- once a branch succeeds, a later failure calls the failure
+  -- continuation in scope at that point, not the saved one, so it
+  -- does not backtrack into the right branch.
+  P a <|> P b = P $ \c s t kf ks -> a c s t (\_ _ -> b c s t kf ks) ks
+  {-# INLINE (<|>) #-}
+
+instance MonadPlus P
+
+instance MonadReader ParserConfig P where
+  ask = P $ \c s t _ ks -> ks c s t
+  {-# INLINE ask #-}
+  local f (P m) = P $ \c -> m (f c)
+  {-# INLINE local #-}
+
+instance MonadState ParserState P where
+  get = P $ \_ s t _ ks -> ks s s t
+  {-# INLINE get #-}
+  put s = P $ \_ _ t _ ks -> ks () s t
+  {-# INLINE put #-}
+  state f = P $ \_ s t _ ks -> case f s of (a, s') -> ks a s' t
+  {-# INLINE state #-}
+
 data ParserState = ParserState
                      { counterMap :: M.Map Text (CounterType, Int)
                      , docAttrs :: M.Map Text Text
+                     , parseFlags :: !ParseFlags
                      }
         deriving (Show)
 
+-- | Which constructs occurred during the parse; used to skip
+-- post-processing passes that would have no effect.
+data ParseFlags = ParseFlags
+  { sawInclude :: !Bool
+  , sawSection :: !Bool
+  , sawAttributeReference :: !Bool
+  , sawCrossReference :: !Bool
+  } deriving (Show)
+
+noParseFlags :: ParseFlags
+noParseFlags = ParseFlags False False False False
+
+setFlag :: (ParseFlags -> ParseFlags) -> P ()
+setFlag f = modify $ \s -> s{ parseFlags = f (parseFlags s) }
+
 defaultDocAttrs :: M.Map Text Text
 defaultDocAttrs = M.insert "sectids" "" mempty
 
@@ -135,23 +244,24 @@
                                  })
                     (ParserState { counterMap = mempty
                                  , docAttrs = defaultDocAttrs
+                                 , parseFlags = noParseFlags
                                  })
                     p
 
 parse' :: ParserConfig -> ParserState
        -> P a -> T.Text -> Either ParseError a
 parse' cfg st p t =
-  go $ A.parse (evalStateT ( runReaderT (unP p) cfg ) st) t
+  unP p cfg st t failure success
  where
-  go (A.Fail i _ msg) = Left $ ParseError (T.length t - T.length i)
-                             $ if "endOfInput" `isPrefixOf` msg
-                                  then "Unexpected " <> show (T.take 20 i)
-                                  else msg
-  go (A.Partial continue) = go (continue "")
-  go (A.Done _i r) = Right r
+  failure msg i = Left $ ParseError (T.length t - T.length i)
+                       $ if "endOfInput" `isPrefixOf` msg
+                            then "Unexpected " <> show (T.take 20 i)
+                            else msg
+  success a _ _ = Right a
 
 localP :: (ParserConfig -> ParserConfig) -> P a -> P a
-localP f (P p) = P (local f p)
+localP f (P p) = P $ \c -> p (f c)
+{-# INLINE localP #-}
 
 withBlockContext :: BlockContext -> P a -> P a
 withBlockContext bc =
@@ -160,80 +270,157 @@
 withHardBreaks :: P a -> P a
 withHardBreaks = localP (\conf -> conf{ hardBreaks = True })
 
-liftP :: A.Parser a -> P a
-liftP = P . lift . lift
+failP :: String -> P a
+failP msg = P $ \_ _ t kf _ -> kf msg t
 
 vchar :: Char -> P ()
-vchar = liftP . void . A.char
+vchar c = P $ \_ s t kf ks ->
+  case T.uncons t of
+    Just (c', t') | c' == c -> ks () s t'
+    _ -> kf "satisfy" t
+{-# INLINE vchar #-}
 
 char :: Char -> P Char
-char = liftP . A.char
+char c = P $ \_ s t kf ks ->
+  case T.uncons t of
+    Just (c', t') | c' == c -> ks c s t'
+    _ -> kf "satisfy" t
+{-# INLINE char #-}
 
 peekChar :: P (Maybe Char)
-peekChar = liftP A.peekChar
+peekChar = P $ \_ s t _ ks ->
+  case T.uncons t of
+    Just (c, _) -> ks (Just c) s t
+    Nothing -> ks Nothing s t
+{-# INLINE peekChar #-}
 
 peekChar' :: P Char
-peekChar' = liftP A.peekChar'
+peekChar' = P $ \_ s t kf ks ->
+  case T.uncons t of
+    Just (c, _) -> ks c s t
+    Nothing -> kf "not enough input" t
+{-# INLINE peekChar' #-}
 
 anyChar :: P Char
-anyChar = liftP A.anyChar
+anyChar = P $ \_ s t kf ks ->
+  case T.uncons t of
+    Just (c, t') -> ks c s t'
+    Nothing -> kf "not enough input" t
+{-# INLINE anyChar #-}
 
 satisfy :: (Char -> Bool) -> P Char
-satisfy = liftP . A.satisfy
+satisfy f = P $ \_ s t kf ks ->
+  case T.uncons t of
+    Just (c, t') | f c -> ks c s t'
+    _ -> kf "satisfy" t
+{-# INLINE satisfy #-}
 
 space :: P Char
-space = liftP A.space
+space = satisfy isSpace
 
 isEndOfLine :: Char -> Bool
-isEndOfLine = A.isEndOfLine
+isEndOfLine c = c == '\n' || c == '\r'
 
+-- The parser only ever advances by taking suffixes of the input, all
+-- slices of one underlying array, so the text consumed between two
+-- points is the prefix of the earlier remainder whose length is the
+-- difference of the remainders' lengths.
+consumed :: Text -> Text -> Text
+consumed (TI.Text arr off len) (TI.Text _ _ len') =
+  TI.text arr off (len - len')
+{-# INLINE consumed #-}
+
 match :: P a -> P (T.Text, a)
-match p = P $ do
-  parseInfo <- ask
-  parserState <- get
-  lift . lift $ A.match (evalStateT (runReaderT (unP p) parseInfo) parserState)
+match p = P $ \c s t kf ks ->
+  unP p c s t kf (\x _ t' -> ks (consumed t t', x) s t')
 
+-- Like match, but keeps the parser state changes made by the inner
+-- parser instead of discarding them.
+matchKeepingState :: P a -> P (T.Text, a)
+matchKeepingState p = P $ \c s t kf ks ->
+  unP p c s t kf (\x s' t' -> ks (consumed t t', x) s' t')
+
+-- Run a parser, then restore the input (and state) as they were.
+lookAhead :: P a -> P a
+lookAhead (P m) = P $ \c s t kf ks -> m c s t kf (\a _ _ -> ks a s t)
+
 string :: T.Text -> P T.Text
-string = liftP . A.string
+string pat = P $ \_ s t kf ks ->
+  case T.stripPrefix pat t of
+    Just t' -> ks pat s t'
+    Nothing -> kf "string" t
+{-# INLINE string #-}
 
 decimal :: Integral a => P a
-decimal = liftP A.decimal
+decimal = P $ \_ s t kf ks ->
+  case T.span isDigit t of
+    (ds, t') | T.null ds -> kf "decimal" t
+             | otherwise -> ks (T.foldl' step 0 ds) s t'
+ where
+  step n d = n * 10 + fromIntegral (ord d - 48)
 
+hexadecimal :: Integral a => P a
+hexadecimal = P $ \_ s t kf ks ->
+  case T.span isHexDigit t of
+    (ds, t') | T.null ds -> kf "hexadecimal" t
+             | otherwise -> ks (T.foldl' step 0 ds) s t'
+ where
+  step n d = n * 16 + fromIntegral (digitToInt d)
+
 endOfInput :: P ()
-endOfInput = liftP A.endOfInput
+endOfInput = P $ \_ s t kf ks ->
+  if T.null t then ks () s t else kf "endOfInput" t
 
 endOfLine :: P ()
-endOfLine = liftP A.endOfLine
+endOfLine = P $ \_ s t kf ks ->
+  case T.uncons t of
+    Just ('\n', t') -> ks () s t'
+    Just ('\r', t') | Just ('\n', t'') <- T.uncons t' -> ks () s t''
+    _ -> kf "endOfLine" t
+{-# INLINE endOfLine #-}
 
 takeWhile :: (Char -> Bool) -> P T.Text
-takeWhile f = liftP (A.takeWhile f)
+takeWhile f = P $ \_ s t _ ks ->
+  case T.span f t of (a, t') -> ks a s t'
+{-# INLINE takeWhile #-}
 
 takeWhile1 :: (Char -> Bool) -> P T.Text
-takeWhile1 f = liftP (A.takeWhile1 f)
+takeWhile1 f = P $ \_ s t kf ks ->
+  case T.span f t of
+    (a, t') | T.null a -> kf "takeWhile1" t
+            | otherwise -> ks a s t'
+{-# INLINE takeWhile1 #-}
 
 skipWhile :: (Char -> Bool) -> P ()
-skipWhile f = liftP (A.skipWhile f)
+skipWhile f = P $ \_ s t _ ks -> ks () s (T.dropWhile f t)
+{-# INLINE skipWhile #-}
 
 skipMany :: P a -> P ()
-skipMany = A.skipMany
+skipMany p = go
+ where
+  go = (p *> go) <|> pure ()
 
 option :: Alternative f => a -> f a -> f a
-option = A.option
+option x p = p <|> pure x
 
 choice :: [P a] -> P a
-choice = A.choice
+choice = foldr (<|>) (failP "choice")
 
 count :: Int -> P a -> P [a]
-count = A.count
+count = replicateM
 
 manyTill :: P a -> P b  -> P [a]
-manyTill = A.manyTill
+manyTill p end = go
+ where
+  go = ([] <$ end) <|> liftA2 (:) p go
 
 sepBy :: P a -> P b -> P [a]
-sepBy = A.sepBy
+sepBy p s = sepBy1 p s <|> pure []
 
 sepBy1 :: P a -> P b -> P [a]
-sepBy1 = A.sepBy1
+sepBy1 p s = go
+ where
+  go = liftA2 (:) p ((s *> go) <|> pure [])
 
 --- Block parsing:
 
@@ -544,6 +731,7 @@
         attr' <- pAttributes
         fp <- asks filePath
         let path = resolvePath fp (T.unpack target)
+        setFlag $ \f -> f{ sawInclude = True }
         pure $ Block (attr' <> attr) mbtitle $ Include path Nothing)
   ]
 
@@ -562,6 +750,7 @@
       -- ==== bar
       -- ==== baz
       -- bar is a level-3 section and will contain baz!
+      setFlag $ \f -> f{ sawSection = True }
       pure $ Section (Level (sectionLevel + 1)) title contents
     _ -> mzero
 
@@ -615,10 +804,20 @@
 pDefinitionListItem :: P ([Inline],[Block])
 pDefinitionListItem = do
   contexts <- asks blockContexts
+  -- The term/definition separator must occur before the end of the
+  -- line, so ordinary paragraph text can be rejected with a single
+  -- substring check instead of the chunked term scan below.
+  restOfLine <- lookAhead (takeWhile (not . isEndOfLine))
+  guard $ "::" `T.isInfixOf` restOfLine
   let marker = (do t <- takeWhile1 (== ':')
                    case contexts of
                        ListContext ':' n : _ -> guard (T.length t == n + 2)
-                       _ -> guard (T.length t == 2))
+                       _ -> guard (T.length t == 2)
+                   -- The marker must be followed by a space or the end of
+                   -- the line, so that e.g. std::vector is not mistaken
+                   -- for a term/definition separator.
+                   mbc <- peekChar
+                   guard $ maybe True (\c -> c == ' ' || isEndOfLine c) mbc)
   skipWhile (== ' ')
   term <- manyTill (takeWhile1 (\c -> not (isEndOfLine c || c == ':'))
                               <|> takeWhile1 (==':')) marker
@@ -707,7 +906,11 @@
 pDelimitedLiteralBlock c minimumNumber = do
   len <- length <$> some (vchar c) <* pBlankLine
   guard $ len >= minimumNumber
-  let endFence = count len (vchar c) *> (pBlankLine <|> endOfInput)
+  -- The bare endOfInput alternative makes an unterminated block extend
+  -- to the end of input; without it, manyTill would loop forever at end
+  -- of input because pLine succeeds there without consuming anything.
+  let endFence = (count len (vchar c) *> (pBlankLine <|> endOfInput))
+                 <|> endOfInput
   manyTill pLine endFence
 
 pDelimitedBlock :: Char -> Int -> P [Block]
@@ -755,7 +958,13 @@
   let mblang = case T.strip lang' of
                  "" -> Nothing
                  l -> Just (Language l)
-  lns <- toSourceLines <$> manyTill pLine (string ticks)
+  -- An unterminated block extends to the end of input; without the
+  -- endOfInput alternative, manyTill would loop forever at end of input
+  -- because pLine succeeds there without consuming anything.  A closing
+  -- fence may be longer than the opening one; consume the extra
+  -- backticks so they don't leak into the following block.
+  lns <- toSourceLines <$>
+    manyTill pLine ((string ticks *> skipWhile (== '`')) <|> endOfInput)
   pure $ Block attr mbtitle $ Listing mblang lns
 
 pListing :: Maybe BlockTitle -> Attr -> P Block
@@ -767,12 +976,14 @@
           _ -> (Nothing, attr)
   lns <- toSourceLines <$> pDelimitedLiteralBlock '-' 4
   fp <- asks filePath
-  pure $ Block attr' mbtitle $
-    case lns of
+  bt <- case lns of
       [SourceLine x []] | "include::" `T.isPrefixOf` x
           , Right ("include", target) <- parse pBlockMacro' fp x
-          -> IncludeListing mbLang (resolvePath fp (T.unpack target)) Nothing
-      _ -> Listing mbLang lns)
+          -> do setFlag $ \f -> f{ sawInclude = True }
+                pure $ IncludeListing mbLang
+                         (resolvePath fp (T.unpack target)) Nothing
+      _ -> pure $ Listing mbLang lns
+  pure $ Block attr' mbtitle bt)
  <|>
   (case attr of
     Attr ("listing":ps) kvs -> do
@@ -928,7 +1139,15 @@
   case contexts of
     ListContext{} : _ -> do
       guard $ t' /= "+"
-      guard $ not $ "::" `T.isInfixOf` t'
+      -- A definition list marker is a run of 2 to 4 colons followed by a
+      -- space or the end of the line.  A mere "::" infix (e.g. in
+      -- std::vector) does not start a definition list.
+      let isDlistMarker (_, post) =
+            let colons = T.takeWhile (== ':') post
+                rest = T.drop (T.length colons) post
+            in T.length colons >= 2 && T.length colons <= 4 &&
+               (T.null rest || T.head rest == ' ')
+      guard $ not $ any isDlistMarker (T.breakOnAll "::" t')
       guard $ case parse pAnyListItemStart fp (T.strip t) of
                 Left _ -> True
                 _ -> False
@@ -944,12 +1163,14 @@
   void $ string "==="
   skipWhile (=='=')
   pBlankLine
-  skipMany pBlankLine
   pure syntax
 
 pTable :: Maybe BlockTitle -> Attr -> P Block
 pTable mbtitle (Attr ps kvs) = do
   syntax' <- pTableBorder
+  -- Record whether a blank line separates the opening border from the
+  -- first row; if so, no header row is implied.
+  leadingBlank <- not . null <$> many pBlankLine
   mbcolspecs <- maybe (pure Nothing) (fmap Just . parseColspecs)
                   (M.lookup "cols" kvs)
   let options = maybe [] T.words $ M.lookup "options" kvs
@@ -967,8 +1188,7 @@
                  _ -> Nothing
   let tableOpts = TableOpts { tableSyntax = syntax
                             , tableSeparator = mbsep
-                            , tableHeader = "header" `elem` options ||
-                                "noheader" `notElem` options
+                            , tableHeader = "header" `elem` options
                             , tableFooter = "footer" `elem` options
                             }
   let getRows mbspecs rowspans = (([],[]) <$ pTableBorder) <|>
@@ -996,13 +1216,24 @@
                                                  [] -> specs
                                                  _ -> colspecs'))
                                      <$> getRows (Just specs) rowspans'
-  (rows, colspecs') <- getRows mbcolspecs (repeat (0 :: Int))
+  (rawRows, (rows, colspecs')) <-
+    matchKeepingState (getRows mbcolspecs (repeat (0 :: Int)))
   let attr' = Attr ps $ M.delete "format" .
                         M.delete "separator" .
                         M.delete "cols" .
                         M.delete "options" $ kvs
+  -- Like Asciidoctor, imply a header row when the first row sits on a
+  -- single line directly after the opening border and is followed by a
+  -- blank line.
+  let isBlankLine = T.all (\c -> c == ' ' || c == '\t')
+  let headerImplied = not leadingBlank && not (null rows) &&
+        case T.lines rawRows of
+          _ : l2 : _ -> isBlankLine l2
+          _ -> False
+  let hasHeader = tableHeader tableOpts ||
+        ("noheader" `notElem` options && headerImplied)
   let (mbHead, rest)
-        | tableHeader tableOpts = (Just (take 1 rows), drop 1 rows)
+        | hasHeader = (Just (take 1 rows), drop 1 rows)
         | otherwise = (Nothing, rows)
   let (mbFoot, bodyRows)
         | tableFooter tableOpts
@@ -1033,7 +1264,7 @@
 pColspec = ColumnSpec <$> optional pHorizAlign
                       <*> optional pVertAlign
                       <*> (pWidth <|> pure Nothing)
-                      <*> (toCellStyle <$> satisfy (A.inClass "adehlms")
+                      <*> (toCellStyle <$> satisfy isCellStyleChar
                              <|> pure Nothing)
 
 pHorizAlign :: P HorizAlign
@@ -1107,7 +1338,7 @@
           (T.pack <$>
             manyTill (satisfy (/='"') <|> ('"' <$ string "\"\"")) (vchar '"'))
     _ -> T.strip . T.replace "\"\"" "\"" <$>
-           takeWhile1 (\c -> c /= delim && not (isEndOfLine c))
+           takeWhile (\c -> c /= delim && not (isEndOfLine c))
 
 -- no "; escape delim with backslash
 pDSVTableRow:: Char -> Maybe [ColumnSpec] -> P [TableCell]
@@ -1139,14 +1370,42 @@
 pTableCellPSV mbsep allowNewlines colspecs = do
   let sep = fromMaybe '|' mbsep
   cellData <- pCellSep sep
-  t <- T.pack <$>
+  -- A cell separator match can only begin at the separator itself or
+  -- at one of the characters that may precede it in a cell spec
+  -- (whitespace, duplicate/span numbers, alignments, styles); a table
+  -- border only at its delimiter.  Runs of other characters can be
+  -- consumed at once, and the expensive lookahead for a separator or
+  -- border is only needed at characters that could begin one.
+  let couldStartCellSep c = c == sep || c == ' ' || c == '\t' ||
+        isDigit c || isCellSpecChar c
+  let couldStartBorder c = c == '|' || c == ':' || c == ','
+  let isPlainCellChar c = not (couldStartCellSep c) &&
+        not (couldStartBorder c) && c /= '\\' && not (isEndOfLine c)
+  t <- mconcat <$>
          many
-          (notFollowedBy (void (pCellSep sep) <|> void pTableBorder) *>
-           ((vchar '\\' *> char sep)
-             <|> satisfy (not . isEndOfLine)
-             <|> if allowNewlines
-                    then satisfy isEndOfLine
-                    else satisfy isEndOfLine <* notFollowedBy (pCellSep sep)))
+          (takeWhile1 isPlainCellChar
+           <|>
+           (do mbc <- peekChar
+               case mbc of
+                 Nothing -> mzero
+                 Just c -> do
+                   when (couldStartCellSep c) $
+                     notFollowedBy (void (pCellSep sep))
+                   when (couldStartBorder c) $
+                     notFollowedBy (void pTableBorder)
+                   -- pCellSep skips leading whitespace itself, so if it
+                   -- failed at the first space of a run it fails at
+                   -- every position within it; the whole run can be
+                   -- consumed after a single lookahead.
+                   if c == ' ' || c == '\t'
+                      then takeWhile1 (\d -> d == ' ' || d == '\t')
+                      else T.singleton <$>
+                        ((vchar '\\' *> char sep)
+                          <|> satisfy (not . isEndOfLine)
+                          <|> if allowNewlines
+                                 then satisfy isEndOfLine
+                                 else satisfy isEndOfLine
+                                        <* notFollowedBy (pCellSep sep))))
   let cell' = TableCell
                { cellContent = []
                , cellHorizAlign = cHorizAlign cellData
@@ -1194,6 +1453,29 @@
   , cStyle :: Maybe CellStyle }
   deriving (Show)
 
+-- The letters that may denote a cell style ("adehlms").
+isCellStyleChar :: Char -> Bool
+isCellStyleChar c =
+  case c of
+    'a' -> True
+    'd' -> True
+    'e' -> True
+    'h' -> True
+    'l' -> True
+    'm' -> True
+    's' -> True
+    _   -> False
+
+-- The characters that may occur in a cell spec (".<^>adehlms").
+isCellSpecChar :: Char -> Bool
+isCellSpecChar c =
+  case c of
+    '.' -> True
+    '<' -> True
+    '^' -> True
+    '>' -> True
+    _   -> isCellStyleChar c
+
 toCellStyle :: Char -> Maybe CellStyle
 toCellStyle 'a' = Just AsciiDocStyle
 toCellStyle 'd' = Just DefaultStyle
@@ -1212,6 +1494,12 @@
 pCellSep :: Char -> P CellData
 pCellSep sep = do
   skipWhile (\c -> c == ' ' || c == '\t')
+  -- Fail fast unless the next character can actually begin a cell
+  -- separator, so that speculative lookaheads stay cheap.
+  mbc <- peekChar
+  case mbc of
+    Just c | c == sep || isDigit c || isCellSpecChar c -> pure ()
+    _ -> mzero
   mult <- option 1 pMultiplier
   (colspan, rowspan) <- option (Nothing, Nothing) $ do
     a <- optional decimal
@@ -1221,7 +1509,7 @@
     pure (a, b)
   halign <- optional pHorizAlign
   valign <- optional pVertAlign
-  sty <- (toCellStyle <$> satisfy (A.inClass "adehlms")) <|> pure Nothing
+  sty <- (toCellStyle <$> satisfy isCellStyleChar) <|> pure Nothing
   notFollowedBy pTableBorder <* vchar sep
   pure $ CellData
     { cDuplicate = mult
@@ -1236,7 +1524,7 @@
 --- Inline parsing:
 
 pInlines :: P [Inline]
-pInlines = pInlines' []
+pInlines = pInlines' False []
 
 pComma :: P ()
 pComma = vchar ',' <* skipWhile isSpace
@@ -1303,41 +1591,249 @@
    vchar '"'
    pure $ T.pack result
 
-pInlines' :: [Char] -> P [Inline]
-pInlines' cs = do
-  (pLineComment *> pInlines' cs)
-    <|> (do il' <- pInline cs
-            let il = case il' of
-                       Inline (Attr ps kvs) (Span ils)
-                         | Nothing <- M.lookup "role" kvs
-                         -> Inline (Attr ps kvs) (Highlight ils)
-                       _ -> il'
-            addStr . (il:) <$> pInlines' [])
-    <|> (do c <- anyChar
-            pInlines' (c:cs))
-    <|> (addStr [] <$ endOfInput)
+-- The [Text] argument accumulates the plain text seen so far, in
+-- reverse chunk order; prependStr turns it into a Str inline.  The
+-- Bool records whether the accumulated text can contain the start of
+-- a typographic replacement, so that prependStr can skip
+-- replaceCharsText without rescanning the text.
+pInlines' :: Bool -> [Text] -> P [Inline]
+pInlines' !trig cs = P $ \cfg st t@(TI.Text arr off len) kf ks ->
+  -- Consume a whole run of plain characters in a single scan.  Only a
+  -- few characters can require anything other than plain text: the
+  -- characters that can begin an inline element or line comment, the
+  -- ':' that ends a macro or autolink name, and the '@' of an email
+  -- autolink.  Everything in between (letters, spaces, ordinary
+  -- punctuation) is consumed here without trying any parsers.  The
+  -- scan also notes replacement triggers ('-', '=', or a ".." pair).
+  let chunk i = if i == 0 then cs else TI.text arr off i : cs
+      go !i !tr !prevDot
+        | i >= len = ks (prependStr tr (chunk i) []) st T.empty
+        | otherwise =
+            case TU.iter t i of
+              TU.Iter c d
+                | isPlainInlineChar c ->
+                    go (i + d)
+                       (tr || c == '-' || c == '=' || (prevDot && c == '.'))
+                       (c == '.')
+                | otherwise ->
+                    unP (pInlineBoundary c tr (chunk i)) cfg st
+                        (TI.text arr (off + i) (len - i)) kf ks
+  in go 0 trig False
+
+-- Handle a stop character of the plain-text scan (not yet consumed).
+pInlineBoundary :: Char -> Bool -> [Text] -> P [Inline]
+pInlineBoundary c !trig cs
+  | c == ':' = pMacroAtColon trig cs plainChar
+  | c == '@' = pEmailAtBoundary trig cs plainChar
+  | c == '/' = (pLineComment *> pInlines' trig cs) <|> plainChar
+  | otherwise =
+      -- An inline start character.  '+' and '_' can also occur inside
+      -- the local part of an email autolink, whose attempt must come
+      -- first (it can only succeed when a '@' with a valid domain
+      -- follows, in which case the formatting parse would misfire).
+      (if isEmailLocalChar c then pEmailAtBoundary trig cs else id) $
+      (do il' <- pInline cs
+          let il = case il' of
+                     Inline (Attr ps kvs) (Span ils)
+                       | Nothing <- M.lookup "role" kvs
+                       -> Inline (Attr ps kvs) (Highlight ils)
+                     _ -> il'
+          prependStr trig cs . (il:) <$> pInlines' False [])
+      <|> plainChar
  where
-  addStr = case cs of
-              [] -> id
-              _  -> (Inline mempty (Str (T.pack (replaceChars $ reverse cs))):)
+  -- Consume the stop character (which failed to begin anything
+  -- special) as a chunk of its own; the next pInlines' scan picks up
+  -- the plain run that follows.
+  plainChar = do
+    _ <- anyChar
+    pInlines' (trig || isTriggerStop c) (T.singleton c : cs)
 
-replaceChars :: [Char] -> [Char]
-replaceChars [] = []
-replaceChars ('(':'C':')':cs) = '\169':replaceChars cs
-replaceChars ('(':'R':')':cs) = '\174':replaceChars cs
-replaceChars ('(':'T':'M':')':cs) = '\8482':replaceChars cs
-replaceChars (x:'-':'-':y:cs)
-  | x == ' ', y == ' ' = '\8201':'\8212':'\8201':replaceChars cs
-  | isAlphaNum x, isAlphaNum y = x:'\8212':'\8203':replaceChars (y:cs)
-  | otherwise = x:'-':'-':replaceChars (y:cs)
-replaceChars ('.':'.':'.':cs) = '\8230':replaceChars cs
-replaceChars ('-':'>':cs) = '\8594':replaceChars cs
-replaceChars ('=':'>':cs) = '\8658':replaceChars cs
-replaceChars ('<':'-':cs) = '\8592':replaceChars cs
-replaceChars ('<':'=':cs) = '\8656':replaceChars cs
-replaceChars ('\'':cs) = '\8217':replaceChars cs
-replaceChars (c:cs) = c:replaceChars cs
+-- Stop characters of the plain scan that are also replacement
+-- triggers.  ('-', '=' and '.' are not stop characters, so they are
+-- detected by the pInlines' scan instead; a ".." pair cannot
+-- straddle two chunks, since the character between them is a stop
+-- character and hence not a '.'.)
+isTriggerStop :: Char -> Bool
+isTriggerStop c = c == '\'' || c == '(' || c == '<'
 
+-- Characters that cannot begin an inline element or line comment, end
+-- a macro or autolink name, or start the domain of an email autolink.
+-- A run of them can be consumed at once without trying any parsers.
+isPlainInlineChar :: Char -> Bool
+isPlainInlineChar c =
+  case c of
+    '*'  -> False
+    '_'  -> False
+    '`'  -> False
+    '#'  -> False
+    '~'  -> False
+    '^'  -> False
+    '+'  -> False
+    '"'  -> False
+    '\'' -> False
+    '('  -> False
+    '{'  -> False
+    '\\' -> False
+    '<'  -> False
+    '&'  -> False
+    '['  -> False
+    '/'  -> False
+    ':'  -> False
+    '@'  -> False
+    _    -> True
+
+-- The Bool says whether the text can contain the start of a
+-- typographic replacement; it is tracked during scanning so that no
+-- extra pass over the text is needed here.
+prependStr :: Bool -> [Text] -> [Inline] -> [Inline]
+prependStr _ [] = id
+prependStr trig cs =
+  (Inline mempty (Str (replaced (T.concat (reverse cs)))):)
+ where
+  replaced = if trig then replaceCharsText else id
+
+-- A macro or autolink name ends at a ':'.  A name contains no
+-- plain-scan stop characters, so it must be a suffix of the plain
+-- text accumulated since the last boundary; only the few candidates
+-- whose last character matches the character before the ':' need to
+-- be checked, leftmost (i.e. longest) first.  This also covers names
+-- with a non-letter tail like indexterm2, whose '2' was consumed by
+-- the plain scan.
+pMacroAtColon :: Bool -> [Text] -> P [Inline] -> P [Inline]
+pMacroAtColon trig (piece : rest) alt
+  | not (T.null piece)
+  , Just candidates <- M.lookup (T.last piece) nestedInlineStartsByLastChar
+  = foldr tryCandidate alt candidates
+ where
+  tryCandidate (name, p) alt'
+    | name `T.isSuffixOf` piece =
+        (do vchar ':'
+            il <- p
+            let pre = T.dropEnd (T.length name) piece
+            let cs' = if T.null pre then rest else pre : rest
+            prependStr trig cs' . (il:) <$> pInlines' False []) <|> alt'
+    | otherwise = alt'
+pMacroAtColon _ _ alt = alt
+
+-- Try email autolinks at a '@' (or at a '+' or '_', which can occur
+-- inside a local part).  A local part starts at the beginning of a
+-- run of letters; every candidate start lies in the trailing run of
+-- email-local characters of the accumulated plain text.  Try each,
+-- leftmost first.  Starts in earlier chunks need not be considered:
+-- any that could reach this position was already tried, with the same
+-- local part and input position, at the boundary ending its chunk.
+pEmailAtBoundary :: Bool -> [Text] -> P [Inline] -> P [Inline]
+pEmailAtBoundary trig (piece : rest) alt
+  | not (T.null localSpan) = foldr tryStart alt (emailStarts localSpan)
+ where
+  localSpan = T.takeWhileEnd isEmailLocalChar piece
+  tryStart sfx alt' =
+    (do more <- takeWhile isEmailLocalChar
+        il <- pEmailAutolinkRest (sfx <> more)
+        let pre = T.dropEnd (T.length sfx) piece
+        let cs' = if T.null pre then rest else pre : rest
+        prependStr trig cs' . (il:) <$> pInlines' False []) <|> alt'
+pEmailAtBoundary _ _ alt = alt
+
+-- Suffixes of the given text beginning at the start of a run of
+-- letters, leftmost first: the candidate starts of an email
+-- autolink's local part.
+emailStarts :: Text -> [Text]
+emailStarts t
+  | T.null t' = []
+  | otherwise = t' : emailStarts (T.dropWhile isLetter t')
+ where
+  t' = T.dropWhile (not . isLetter) t
+
+-- Possible macro and autolink names, as (name, parser for what
+-- follows the name and ':').  Sorted by decreasing name length, so
+-- that the leftmost match within a run of plain text wins; macros
+-- come before autolink schemes of the same name.
+nestedInlineStarts :: [(Text, P Inline)]
+nestedInlineStarts =
+  sortOn (\(name, _) -> negate (T.length name)) $
+    [ (name, pInlineMacroTarget name) | name <- M.keys inlineMacros ] ++
+    [ (scheme, pAutolinkTarget (scheme <> ":")) | scheme <- autolinkSchemes ]
+
+-- The same candidates indexed by the last character of the name, so
+-- that a ':' boundary only has to check the few candidates that could
+-- end just before it, preserving the order of nestedInlineStarts
+-- within a bucket.
+nestedInlineStartsByLastChar :: M.Map Char [(Text, P Inline)]
+nestedInlineStartsByLastChar =
+  M.fromListWith (flip (++))
+    [ (T.last name, [x]) | x@(name, _) <- nestedInlineStarts ]
+
+-- Apply typographic replacements in a single pass, splicing
+-- replacements between unchanged slices of the input.  Equivalent to
+-- matching, at each position, the first of these patterns (x and y
+-- are arbitrary characters):
+--
+--   (C) (R) (TM)             -> copyright, registered, trademark sign
+--   " -- "                   -> thin space, em dash, thin space
+--   x--y  (x, y alphanumeric)-> x, em dash, zero-width space, y...
+--   x--y  (otherwise)        -> unchanged (consuming x "--")
+--   ...                      -> ellipsis
+--   ->  =>  <-  <=           -> arrows
+--   '                        -> right single quotation mark
+replaceCharsText :: Text -> Text
+replaceCharsText t@(TI.Text arr toff len) = go [] 0 0 '\0'
+ where
+  slice s e = TI.text arr (toff + s) (e - s)
+  charAt j = case TU.iter t j of TU.Iter c _ -> c
+  -- acc: finished output pieces in reverse order; s: start of the
+  -- current unchanged run; i: current position (byte offsets); prev:
+  -- the character ending at i (only meaningful when i > s).
+  go acc !s !i !prev
+    | i >= len =
+        case acc of
+          [] -> t                              -- nothing was replaced
+          _ -> T.concat (reverse (slice s i : acc))
+    | otherwise =
+        case c0 of
+          '-' | i1 < len, charAt i1 == '-' ->  -- a "--" pair at (i, i1)
+                  if i > s && i2 < len
+                    then                       -- x--y with x = prev
+                      let y = charAt i2
+                      in if prev == ' ' && y == ' '
+                           then emit (i - 1) "\8201\8212\8201" (i + 3)
+                         else if isAlphaNum prev && isAlphaNum y
+                           then emit i "\8212\8203" i2
+                         else go acc s i2 '-'  -- x "--" kept as-is
+                    else if i == s && i2 < len && charAt i2 == '-'
+                            && i3 < len
+                           then go acc s i3 '-' -- x--y, x a dash itself
+                           else plain
+              | i1 < len, charAt i1 == '>' -> emit i "\8594" i2
+          '(' | i2 < len, charAt i2 == ')', charAt i1 == 'C' ->
+                  emit i "\169" i3
+              | i2 < len, charAt i2 == ')', charAt i1 == 'R' ->
+                  emit i "\174" i3
+              | i3 < len, charAt i1 == 'T', charAt i2 == 'M',
+                charAt i3 == ')' -> emit i "\8482" (i + 4)
+          '.' | i2 < len, charAt i1 == '.', charAt i2 == '.' ->
+                  emit i "\8230" i3
+          '=' | i1 < len, charAt i1 == '>' -> emit i "\8658" i2
+          '<' | i1 < len, charAt i1 == '-' ->
+                  if i2 < len && charAt i2 == '-' && i3 < len
+                    then go acc s i3 '-'       -- x--y with x = '<'
+                    else emit i "\8592" i2
+              | i1 < len, charAt i1 == '=' -> emit i "\8656" i2
+          '\'' | i2 < len, charAt i1 == '-', charAt i2 == '-',
+                 i3 < len -> go acc s i3 '-'   -- x--y with x = '\''
+               | otherwise -> emit i "\8217" i1
+          _ -> plain
+    where
+      TU.Iter c0 d0 = TU.iter t i
+      i1 = i + 1
+      i2 = i + 2
+      i3 = i + 3
+      plain = go acc s (i + d0) c0
+      emit e piece j =
+        let acc' | e > s = piece : slice s e : acc
+                 | otherwise = piece : acc
+        in go acc' j j '\0'
+
 pShorthandAttributes :: P Attr
 pShorthandAttributes = do
   attr <- mconcat <$>
@@ -1358,10 +1854,12 @@
            _ -> mzero
   pure (key, val)
 
-pInline :: [Char] -> P Inline
+pInline :: [Text] -> P Inline
 pInline prevChars = do
+  -- The chunks in prevChars are non-empty by construction.
   let maybeUnconstrained = case prevChars of
-                              (d:_) -> isSpace d || isPunctuation d || d == '+'
+                              (t:_) -> let d = T.last t
+                                       in isSpace d || isPunctuation d || d == '+'
                               [] -> True
   let inMatched = pInMatched maybeUnconstrained
   (do attr <- pFormattedTextAttributes <|> pure mempty
@@ -1387,8 +1885,9 @@
                '<' -> pBracedAutolink <|> pCrossReference
                '&' -> pCharacterReference
                '[' -> pBibAnchor <|> pInlineAnchor
-               _ | isLetter c -> pInlineMacro <|> pAutolink <|> pEmailAutolink
-                 | otherwise -> mzero)
+               -- macros, autolinks and email autolinks are handled
+               -- at the ':' and '@' boundaries in pInlineBoundary
+               _ -> mzero)
 
 pIndexEntry :: Attr -> P Inline
 pIndexEntry attr = do
@@ -1415,7 +1914,9 @@
   let ts = T.split (==',') t
   case ts of
     [] -> mzero
-    [x] -> pure $ Inline mempty $ CrossReference x Nothing
+    [x] -> do
+      setFlag $ \f -> f{ sawCrossReference = True }
+      pure $ Inline mempty $ CrossReference x Nothing
     (x:xs) -> Inline mempty . CrossReference x . Just
                        <$> parseInlines (T.intercalate "," xs)
 
@@ -1436,18 +1937,32 @@
   isDoubled <- option False (True <$ vchar delim)
   followedBySpace <- maybe True isSpace <$> peekChar
   guard $ isDoubled || (maybeUnconstrained && not followedBySpace)
-  cs <- manyTill ( (vchar '\\' *> char delim) <|> anyChar )
-                   (if isDoubled
-                       then vchar delim *> vchar delim
-                       else vchar delim)
-  guard $ not $ null cs
+  t <- pMatchedContent isDoubled delim
+  guard $ not $ T.null t
   when (not isDoubled && maybeUnconstrained) $ do
     mbc <- peekChar
     case mbc of
       Nothing -> pure ()
       Just c -> guard $ isSpace c || isPunctuation c || c == '+'
-  Inline attr <$> toInlineType (T.pack cs)
+  Inline attr <$> toInlineType t
 
+-- Scan the content of a delimited span up to and including the closing
+-- delimiter (doubled or single), consuming runs of plain characters in
+-- chunks rather than character by character.  A backslash escapes the
+-- delimiter; in doubled mode a lone delimiter is content.
+pMatchedContent :: Bool -> Char -> P Text
+pMatchedContent isDoubled delim = mconcat <$> go
+ where
+  closing = if isDoubled
+               then vchar delim *> vchar delim
+               else vchar delim
+  go = ([] <$ closing) <|> ((:) <$> piece <*> go)
+  piece = takeWhile1 (\c -> c /= delim && c /= '\\')
+      <|> (vchar '\\' *> ((T.singleton <$> char delim) <|> pure "\\"))
+      <|> (T.singleton <$> char delim)
+          -- only reachable in doubled mode, when the delimiter is not
+          -- part of a closing pair
+
 pInlineAnchor :: P Inline
 pInlineAnchor = do
   void $ string "[["
@@ -1478,13 +1993,13 @@
   vchar '#' *> (((vchar 'x' <|> vchar 'X') *> pHexReference) <|> pDecimalReference)
  where
   pHexReference =
-    Inline mempty . Str . T.singleton . chr <$> (liftP A.hexadecimal <* vchar ';')
+    Inline mempty . Str . T.singleton . chr <$> (hexadecimal <* vchar ';')
   pDecimalReference =
     Inline mempty . Str . T.singleton . chr <$> (decimal <* vchar ';')
 
 pCharacterEntityReference :: P Inline
 pCharacterEntityReference = do
-  xs <- manyTill (satisfy isAlphaNum) (char ';' <|> space)
+  xs <- manyTill (satisfy isAlphaNum) (char ';')
   case lookupNamedEntity xs of
     Just s -> pure $ Inline mempty (Str (T.pack s))
     Nothing -> mzero
@@ -1500,9 +2015,9 @@
 pApostrophe '`' = Inline mempty (Str "’") <$ string "`'"
 pApostrophe _ = mzero
 
-pInlineMacro :: P Inline
-pInlineMacro = do
-  name <- choice (map (\n -> string n <* vchar ':') (M.keys inlineMacros))
+-- Parse the part of an inline macro after the name and ':'.
+pInlineMacroTarget :: Text -> P Inline
+pInlineMacroTarget name = do
   let targetChars = mconcat <$> some
        ( (string "pass:" *> vchar '[' *> takeWhile1 (/=']') <* vchar ']')
          <|>
@@ -1583,7 +2098,9 @@
       Inline (Attr mempty kvs) . Footnote fnid <$> parseInlines contents)
   , ("xref", \target -> do
         ils <- pBracketedText >>= parseInlines
-        let mbtext = if null ils then Nothing else Just ils
+        mbtext <- if null ils
+                     then Nothing <$ setFlag (\f -> f{ sawCrossReference = True })
+                     else pure (Just ils)
         pure $ Inline mempty $ CrossReference target mbtext)
   , ("image", \target -> do
         (Attr ps kvs) <- pAttributes
@@ -1628,9 +2145,12 @@
   in (description, Attr (drop 1 ps) kvs)
 
 
-pEmailAutolink :: P Inline
-pEmailAutolink = do
-  a <- takeWhile1 (\c -> isAlphaNum c || c == '_' || c == '.' || c == '+')
+isEmailLocalChar :: Char -> Bool
+isEmailLocalChar c = isAlphaNum c || c == '_' || c == '.' || c == '+'
+
+-- Parse the part of an email autolink after the local part.
+pEmailAutolinkRest :: Text -> P Inline
+pEmailAutolinkRest a = do
   vchar '@'
   b <- takeWhile1 isLetter
   vchar '.'
@@ -1643,10 +2163,18 @@
                   then pure [Inline mempty (Str email)]
                   else parseInlines description
 
+autolinkSchemes :: [Text]
+autolinkSchemes = ["http", "https", "irc", "ftp", "mailto"]
+
 pAutolink :: P Inline
 pAutolink = do
-  scheme <- choice (map string
-               ["http:", "https:", "irc:", "ftp:", "mailto:"])
+  scheme <- choice (map (\s -> string (s <> ":")) autolinkSchemes)
+  pAutolinkTarget scheme
+
+-- Parse the part of an autolink after the scheme (which includes the
+-- trailing ':').
+pAutolinkTarget :: Text -> P Inline
+pAutolinkTarget scheme = do
   let isSpecialPunct ',' = True
       isSpecialPunct '.' = True
       isSpecialPunct '?' = True
@@ -1728,7 +2256,7 @@
      pure (UpperAlphaCounter, 1 + (ord c - ord 'A'))
    pLowerValue = do
      c <- satisfy (\c -> isAscii c && isLower c)
-     pure (UpperAlphaCounter, 1 + (ord c - ord 'a'))
+     pure (LowerAlphaCounter, 1 + (ord c - ord 'a'))
    pDecimalValue = do
      n <- decimal
      pure (DecimalCounter, n)
@@ -1740,7 +2268,17 @@
   vchar '}'
   case M.lookup name replacements of
     Just r -> pure $ Inline mempty (Str r)
-    Nothing -> pure $ Inline mempty $ AttributeReference (AttributeName name)
+    Nothing -> do
+      -- Resolve document attributes at the point of use, so that a
+      -- reference sees the value in effect where it occurs.  References
+      -- to attributes defined later are left unresolved here and get
+      -- the end-of-document value in a post-processing pass.
+      attrs <- gets docAttrs
+      case M.lookup name attrs of
+        Just v -> pure $ Inline mempty (Str v)
+        Nothing -> do
+          setFlag $ \f -> f{ sawAttributeReference = True }
+          pure $ Inline mempty $ AttributeReference (AttributeName name)
 
 replacements :: M.Map Text Text
 replacements = M.fromList
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -21,7 +21,9 @@
   asciidoctorTests <- goldenTests "asciidoctor"
   featureTests <- goldenTests "feature"
   regressionTests <- goldenTests "regression"
-  defaultMain $ testGroup "Tests"
+  -- A global per-test timeout so that parser non-termination bugs make
+  -- tests fail instead of hanging the suite.
+  defaultMain $ localOption (mkTimeout 10000000) $ testGroup "Tests"
     [ testGroup "Asciidoctor" asciidoctorTests
     , testGroup "Feature" featureTests
     , testGroup "Regression" regressionTests
@@ -30,7 +32,11 @@
        , foldBlockTest
        , mapInlineTest
        , mapBlockTest
+       , definitionListTermsTest
        ]
+    , testGroup "AST"
+       [ metaSemigroupTest
+       ]
     ]
 
 goldenTests :: FilePath -> IO [TestTree]
@@ -89,6 +95,51 @@
   Just (_, '\n') -> xs
   _              -> xs <> "\n"
 
+
+definitionListTermsTest :: TestTree
+definitionListTermsTest = testCase "definitionListTerms" $ do
+  let dlistDoc = Document
+        { docMeta = mempty
+        , docBlocks =
+            [ Block mempty Nothing
+                (DefinitionList
+                   [ ( [Inline mempty (Str "term")]
+                     , [ Block mempty Nothing
+                           (Paragraph [Inline mempty (Str "def")]) ] )
+                   ])
+            ]
+        }
+  -- foldInlines and mapInlines must reach the term as well as the definition
+  foldInlines (\case
+                  Inline _ (Str s) -> s
+                  _ -> "") dlistDoc
+    @?= "termdef"
+  d <- mapInlines (\case
+                      Inline _ (Str _) -> pure $ Inline mempty (Str "X")
+                      x -> pure x) dlistDoc
+  foldInlines (\case
+                  Inline _ (Str s) -> s
+                  _ -> "") d
+    @?= "XX"
+  -- foldBlocks must reach the blocks in the definition
+  foldBlocks (\case
+                 Block _ _ (Paragraph _) -> [()]
+                 _ -> []) dlistDoc
+    @?= [()]
+
+metaSemigroupTest :: TestTree
+metaSemigroupTest = testCase "metaSemigroup" $ do
+  let attr1 = Attr ["one"] mempty
+  let attr2 = Attr ["two"] mempty
+  let meta1 = mempty{ docTitle = [Inline mempty (Str "First")]
+                    , docTitleAttributes = Just attr1 }
+  let meta2 = mempty{ docTitle = [Inline mempty (Str "Second")]
+                    , docTitleAttributes = Just attr2 }
+  -- title and its attributes are taken from the same document
+  docTitle (meta1 <> meta2) @?= [Inline mempty (Str "First")]
+  docTitleAttributes (meta1 <> meta2) @?= Just attr1
+  docTitle (mempty <> meta2) @?= [Inline mempty (Str "Second")]
+  docTitleAttributes (mempty <> meta2) @?= Just attr2
 
 testDoc :: Document
 testDoc = Document
diff --git a/test/asciidoctor/table/aligns-per-cell.test b/test/asciidoctor/table/aligns-per-cell.test
--- a/test/asciidoctor/table/aligns-per-cell.test
+++ b/test/asciidoctor/table/aligns-per-cell.test
@@ -33,29 +33,28 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline mempty (Str "Prefix the ")
-                                   , Inline mempty (Str "{vbar}")
-                                   , Inline mempty (Str " with ")
-                                   , Inline mempty (Str "{caret}")
-                                   , Inline mempty (Str " to center content horizontally")
-                                   ])
-                            ]
-                        , cellHorizAlign = Just AlignCenter
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline mempty (Str "Prefix the ")
+                                , Inline mempty (Str "{vbar}")
+                                , Inline mempty (Str " with ")
+                                , Inline mempty (Str "{caret}")
+                                , Inline mempty (Str " to center content horizontally")
+                                ])
+                         ]
+                     , cellHorizAlign = Just AlignCenter
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/basic.test b/test/asciidoctor/table/basic.test
--- a/test/asciidoctor/table/basic.test
+++ b/test/asciidoctor/table/basic.test
@@ -30,35 +30,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/cell-with-paragraphs.test b/test/asciidoctor/table/cell-with-paragraphs.test
--- a/test/asciidoctor/table/cell-with-paragraphs.test
+++ b/test/asciidoctor/table/cell-with-paragraphs.test
@@ -28,23 +28,22 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Single paragraph on row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Single paragraph on row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/colspan.test b/test/asciidoctor/table/colspan.test
--- a/test/asciidoctor/table/colspan.test
+++ b/test/asciidoctor/table/colspan.test
@@ -39,47 +39,46 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/insane-cells-formatting.test b/test/asciidoctor/table/insane-cells-formatting.test
--- a/test/asciidoctor/table/insane-cells-formatting.test
+++ b/test/asciidoctor/table/insane-cells-formatting.test
@@ -44,87 +44,84 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty
-                                       (Monospace
-                                          [ Inline
-                                              mempty
-                                              (Str "This content is duplicated across two columns.")
-                                          ])
-                                   ])
-                            , Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty
-                                       (Monospace
-                                          [ Inline mempty (Str "It is aligned right horizontally.")
-                                          ])
-                                   ])
-                            , Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty
-                                       (Monospace [ Inline mempty (Str "And it is monospaced.") ])
-                                   ])
-                            ]
-                        , cellHorizAlign = Just AlignRight
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty
-                                       (Monospace
-                                          [ Inline
-                                              mempty
-                                              (Str "This content is duplicated across two columns.")
-                                          ])
-                                   ])
-                            , Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty
-                                       (Monospace
-                                          [ Inline mempty (Str "It is aligned right horizontally.")
-                                          ])
-                                   ])
-                            , Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty
-                                       (Monospace [ Inline mempty (Str "And it is monospaced.") ])
-                                   ])
-                            ]
-                        , cellHorizAlign = Just AlignRight
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty
+                                    (Monospace
+                                       [ Inline
+                                           mempty
+                                           (Str "This content is duplicated across two columns.")
+                                       ])
+                                ])
+                         , Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty
+                                    (Monospace
+                                       [ Inline mempty (Str "It is aligned right horizontally.") ])
+                                ])
+                         , Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty
+                                    (Monospace [ Inline mempty (Str "And it is monospaced.") ])
+                                ])
+                         ]
+                     , cellHorizAlign = Just AlignRight
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty
+                                    (Monospace
+                                       [ Inline
+                                           mempty
+                                           (Str "This content is duplicated across two columns.")
+                                       ])
+                                ])
+                         , Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty
+                                    (Monospace
+                                       [ Inline mempty (Str "It is aligned right horizontally.") ])
+                                ])
+                         , Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty
+                                    (Monospace [ Inline mempty (Str "And it is monospaced.") ])
+                                ])
+                         ]
+                     , cellHorizAlign = Just AlignRight
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/rowspan.test b/test/asciidoctor/table/rowspan.test
--- a/test/asciidoctor/table/rowspan.test
+++ b/test/asciidoctor/table/rowspan.test
@@ -42,47 +42,46 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/with-autowidth-and-width.test b/test/asciidoctor/table/with-autowidth-and-width.test
--- a/test/asciidoctor/table/with-autowidth-and-width.test
+++ b/test/asciidoctor/table/with-autowidth-and-width.test
@@ -31,35 +31,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-autowidth.test b/test/asciidoctor/table/with-autowidth.test
--- a/test/asciidoctor/table/with-autowidth.test
+++ b/test/asciidoctor/table/with-autowidth.test
@@ -30,35 +30,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-cols-halign.test b/test/asciidoctor/table/with-cols-halign.test
--- a/test/asciidoctor/table/with-cols-halign.test
+++ b/test/asciidoctor/table/with-cols-halign.test
@@ -38,47 +38,46 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-cols-styles.test b/test/asciidoctor/table/with-cols-styles.test
--- a/test/asciidoctor/table/with-cols-styles.test
+++ b/test/asciidoctor/table/with-cols-styles.test
@@ -59,90 +59,88 @@
                  , colStyle = Just StrongStyle
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (BlockImage
-                                   (Target "sunset.jpg")
-                                   (Just (AltText "AsciiDoc content"))
-                                   Nothing
-                                   Nothing)
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty (Italic [ Inline mempty (Str "Emphasized text") ])
-                                   ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Styled like a header") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block mempty Nothing (LiteralBlock "Literal block\n") ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph
-                                   [ Inline
-                                       mempty (Monospace [ Inline mempty (Str "Monospaced text") ])
-                                   ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (BlockImage
+                                (Target "sunset.jpg")
+                                (Just (AltText "AsciiDoc content"))
                                 Nothing
-                                (Paragraph
-                                   [ Inline mempty (Bold [ Inline mempty (Str "Strong text") ]) ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+                                Nothing)
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline mempty (Italic [ Inline mempty (Str "Emphasized text") ])
+                                ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Styled like a header") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (LiteralBlock "Literal block\n") ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline
+                                    mempty (Monospace [ Inline mempty (Str "Monospaced text") ])
+                                ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph
+                                [ Inline mempty (Bold [ Inline mempty (Str "Strong text") ]) ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-cols-valign.test b/test/asciidoctor/table/with-cols-valign.test
--- a/test/asciidoctor/table/with-cols-valign.test
+++ b/test/asciidoctor/table/with-cols-valign.test
@@ -38,47 +38,46 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-cols-width.test b/test/asciidoctor/table/with-cols-width.test
--- a/test/asciidoctor/table/with-cols-width.test
+++ b/test/asciidoctor/table/with-cols-width.test
@@ -38,47 +38,46 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-float.test b/test/asciidoctor/table/with-float.test
--- a/test/asciidoctor/table/with-float.test
+++ b/test/asciidoctor/table/with-float.test
@@ -31,35 +31,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-footer.test b/test/asciidoctor/table/with-footer.test
--- a/test/asciidoctor/table/with-footer.test
+++ b/test/asciidoctor/table/with-footer.test
@@ -32,35 +32,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block
diff --git a/test/asciidoctor/table/with-frame-sides.test b/test/asciidoctor/table/with-frame-sides.test
--- a/test/asciidoctor/table/with-frame-sides.test
+++ b/test/asciidoctor/table/with-frame-sides.test
@@ -31,35 +31,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-grid-cols.test b/test/asciidoctor/table/with-grid-cols.test
--- a/test/asciidoctor/table/with-grid-cols.test
+++ b/test/asciidoctor/table/with-grid-cols.test
@@ -31,35 +31,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-id-and-role.test b/test/asciidoctor/table/with-id-and-role.test
--- a/test/asciidoctor/table/with-id-and-role.test
+++ b/test/asciidoctor/table/with-id-and-role.test
@@ -31,35 +31,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-title.test b/test/asciidoctor/table/with-title.test
--- a/test/asciidoctor/table/with-title.test
+++ b/test/asciidoctor/table/with-title.test
@@ -30,35 +30,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/asciidoctor/table/with-width.test b/test/asciidoctor/table/with-width.test
--- a/test/asciidoctor/table/with-width.test
+++ b/test/asciidoctor/table/with-width.test
@@ -31,35 +31,34 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block
-                                mempty
-                                Nothing
-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
-                            ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
-             []
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block
+                             mempty
+                             Nothing
+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])
+                         ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
              Nothing)
       ]
   }
diff --git a/test/feature/table/longborder.test b/test/feature/table/longborder.test
--- a/test/feature/table/longborder.test
+++ b/test/feature/table/longborder.test
@@ -30,27 +30,26 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a") ]) ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b") ]) ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block mempty Nothing (Paragraph [ Inline mempty (Str "c") ]) ]
diff --git a/test/regression/attribute_point_of_use.test b/test/regression/attribute_point_of_use.test
new file mode 100644
--- /dev/null
+++ b/test/regression/attribute_point_of_use.test
@@ -0,0 +1,33 @@
+:x: first
+
+{x}
+
+:x: second
+
+{x} and {y}
+
+:y: late
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes =
+            fromList
+              [ ( "sectids" , "" ) , ( "x" , "second" ) , ( "y" , "late" ) ]
+        }
+  , docBlocks =
+      [ Block mempty Nothing (Paragraph [ Inline mempty (Str "first") ])
+      , Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline mempty (Str "second")
+             , Inline mempty (Str " and ")
+             , Inline mempty (Str "late")
+             ])
+      ]
+  }
diff --git a/test/regression/counter_lowercase.test b/test/regression/counter_lowercase.test
new file mode 100644
--- /dev/null
+++ b/test/regression/counter_lowercase.test
@@ -0,0 +1,25 @@
+one {counter:foo:a} two {counter:foo} upper {counter:bar:C}
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline mempty (Str "one ")
+             , Inline mempty (Counter "foo" LowerAlphaCounter 1)
+             , Inline mempty (Str " two ")
+             , Inline mempty (Counter "foo" LowerAlphaCounter 2)
+             , Inline mempty (Str " upper ")
+             , Inline mempty (Counter "bar" UpperAlphaCounter 3)
+             ])
+      ]
+  }
diff --git a/test/regression/csv_empty_cell.test b/test/regression/csv_empty_cell.test
new file mode 100644
--- /dev/null
+++ b/test/regression/csv_empty_cell.test
@@ -0,0 +1,93 @@
+[format=csv,options="noheader"]
+|===
+a,,b
+,d,
+|===
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Table
+             [ ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             ]
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent = []
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
+                 [ TableCell
+                     { cellContent = []
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "d") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent = []
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
+             Nothing)
+      ]
+  }
diff --git a/test/regression/dlist_marker_needs_space.test b/test/regression/dlist_marker_needs_space.test
new file mode 100644
--- /dev/null
+++ b/test/regression/dlist_marker_needs_space.test
@@ -0,0 +1,54 @@
+* use std::vector here
+* second item
+
+A paragraph mentioning std::vector too.
+
+term:: a real definition
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (List
+             (BulletList (Level 1))
+             [ ListItem
+                 Nothing
+                 [ Block
+                     mempty
+                     Nothing
+                     (Paragraph [ Inline mempty (Str "use std::vector here") ])
+                 ]
+             , ListItem
+                 Nothing
+                 [ Block
+                     mempty Nothing (Paragraph [ Inline mempty (Str "second item") ])
+                 ]
+             ])
+      , Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline mempty (Str "A paragraph mentioning std::vector too.") ])
+      , Block
+          mempty
+          Nothing
+          (DefinitionList
+             [ ( [ Inline mempty (Str "term") ]
+               , [ Block
+                     mempty
+                     Nothing
+                     (Paragraph [ Inline mempty (Str "a real definition") ])
+                 ]
+               )
+             ])
+      ]
+  }
diff --git a/test/regression/dlist_term_traversal.test b/test/regression/dlist_term_traversal.test
new file mode 100644
--- /dev/null
+++ b/test/regression/dlist_term_traversal.test
@@ -0,0 +1,48 @@
+[#target]
+== Section Title
+
+<<target>> in term:: definition with <<target>>
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          Attr
+          ( [] , fromList [ ( "id" , "target" ) ] )
+          Nothing
+          (Section
+             (Level 1)
+             [ Inline mempty (Str "Section Title") ]
+             [ Block
+                 mempty
+                 Nothing
+                 (DefinitionList
+                    [ ( [ Inline
+                            mempty
+                            (CrossReference
+                               "target" (Just [ Inline mempty (Str "Section Title") ]))
+                        , Inline mempty (Str " in term")
+                        ]
+                      , [ Block
+                            mempty
+                            Nothing
+                            (Paragraph
+                               [ Inline mempty (Str "definition with ")
+                               , Inline
+                                   mempty
+                                   (CrossReference
+                                      "target" (Just [ Inline mempty (Str "Section Title") ]))
+                               ])
+                        ]
+                      )
+                    ])
+             ])
+      ]
+  }
diff --git a/test/regression/entity_needs_semicolon.test b/test/regression/entity_needs_semicolon.test
new file mode 100644
--- /dev/null
+++ b/test/regression/entity_needs_semicolon.test
@@ -0,0 +1,22 @@
+AT&amp;T but AT&amp T keeps its space.
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline mempty (Str "AT")
+             , Inline mempty (Str "&")
+             , Inline mempty (Str "T but AT&amp T keeps its space.")
+             ])
+      ]
+  }
diff --git a/test/regression/fenced_longer_close.test b/test/regression/fenced_longer_close.test
new file mode 100644
--- /dev/null
+++ b/test/regression/fenced_longer_close.test
@@ -0,0 +1,24 @@
+```
+code line
+````
+
+A paragraph after the block.
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty Nothing (Listing Nothing [ SourceLine "code line" [] ])
+      , Block
+          mempty
+          Nothing
+          (Paragraph [ Inline mempty (Str "A paragraph after the block.") ])
+      ]
+  }
diff --git a/test/regression/fenced_unterminated.test b/test/regression/fenced_unterminated.test
new file mode 100644
--- /dev/null
+++ b/test/regression/fenced_unterminated.test
@@ -0,0 +1,20 @@
+```
+foo
+bar
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Listing Nothing [ SourceLine "foo" [] , SourceLine "bar" [] ])
+      ]
+  }
diff --git a/test/regression/include_cycle.test b/test/regression/include_cycle.test
new file mode 100644
--- /dev/null
+++ b/test/regression/include_cycle.test
@@ -0,0 +1,33 @@
+include::include_cycle_helper.adoc[]
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Include
+             "test/regression/include_cycle_helper.adoc"
+             (Just
+                [ Block
+                    mempty
+                    Nothing
+                    (Paragraph [ Inline mempty (Str "Before the self-include.") ])
+                , Block
+                    mempty
+                    Nothing
+                    (Include "test/regression/include_cycle_helper.adoc" Nothing)
+                , Block
+                    mempty
+                    Nothing
+                    (Paragraph [ Inline mempty (Str "After the self-include.") ])
+                ]))
+      ]
+  }
diff --git a/test/regression/issue_5.test b/test/regression/issue_5.test
--- a/test/regression/issue_5.test
+++ b/test/regression/issue_5.test
@@ -33,27 +33,26 @@
                  , colStyle = Nothing
                  }
              ]
-             (Just
-                [ TableRow
-                    [ TableCell
-                        { cellContent =
-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col1") ]) ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    , TableCell
-                        { cellContent =
-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col2") ]) ]
-                        , cellHorizAlign = Nothing
-                        , cellVertAlign = Nothing
-                        , cellColspan = 1
-                        , cellRowspan = 1
-                        }
-                    ]
-                ])
+             Nothing
              [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
                  [ TableCell
                      { cellContent =
                          [ Block mempty Nothing (Paragraph [ Inline mempty (Str "11") ]) ]
diff --git a/test/regression/listing_unterminated.test b/test/regression/listing_unterminated.test
new file mode 100644
--- /dev/null
+++ b/test/regression/listing_unterminated.test
@@ -0,0 +1,20 @@
+----
+foo
+bar
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Listing Nothing [ SourceLine "foo" [] , SourceLine "bar" [] ])
+      ]
+  }
diff --git a/test/regression/table_implicit_header.test b/test/regression/table_implicit_header.test
new file mode 100644
--- /dev/null
+++ b/test/regression/table_implicit_header.test
@@ -0,0 +1,389 @@
+Implied header (first row on one line, then blank line):
+
+|===
+|A |B
+
+|a1 |b1
+|a2 |b2
+|===
+
+No blank line after first row, so no header:
+
+|===
+|a1 |b1
+|a2 |b2
+|===
+
+Explicit header option without header layout:
+
+[options="header"]
+|===
+|a1 |b1
+|a2 |b2
+|===
+
+Header layout suppressed by noheader option:
+
+[options="noheader"]
+|===
+|A |B
+
+|a1 |b1
+|===
+
+Blank line directly after the border, so no header:
+
+|===
+
+|a1 |b1
+|a2 |b2
+|===
+>>>
+Document
+  { docMeta =
+      Meta
+        { docTitle = []
+        , docTitleAttributes = Nothing
+        , docAuthors = []
+        , docRevision = Nothing
+        , docAttributes = fromList [ ( "sectids" , "" ) ]
+        }
+  , docBlocks =
+      [ Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline
+                 mempty
+                 (Str "Implied header (first row on one line, then blank line):")
+             ])
+      , Block
+          mempty
+          Nothing
+          (Table
+             [ ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             ]
+             (Just
+                [ TableRow
+                    [ TableCell
+                        { cellContent =
+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "A") ]) ]
+                        , cellHorizAlign = Nothing
+                        , cellVertAlign = Nothing
+                        , cellColspan = 1
+                        , cellRowspan = 1
+                        }
+                    , TableCell
+                        { cellContent =
+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "B") ]) ]
+                        , cellHorizAlign = Nothing
+                        , cellVertAlign = Nothing
+                        , cellColspan = 1
+                        , cellRowspan = 1
+                        }
+                    ]
+                ])
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
+             Nothing)
+      , Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline
+                 mempty (Str "No blank line after first row, so no header:")
+             ])
+      , Block
+          mempty
+          Nothing
+          (Table
+             [ ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             ]
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
+             Nothing)
+      , Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline
+                 mempty (Str "Explicit header option without header layout:")
+             ])
+      , Block
+          mempty
+          Nothing
+          (Table
+             [ ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             ]
+             (Just
+                [ TableRow
+                    [ TableCell
+                        { cellContent =
+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]
+                        , cellHorizAlign = Nothing
+                        , cellVertAlign = Nothing
+                        , cellColspan = 1
+                        , cellRowspan = 1
+                        }
+                    , TableCell
+                        { cellContent =
+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]
+                        , cellHorizAlign = Nothing
+                        , cellVertAlign = Nothing
+                        , cellColspan = 1
+                        , cellRowspan = 1
+                        }
+                    ]
+                ])
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
+             Nothing)
+      , Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline
+                 mempty (Str "Header layout suppressed by noheader option:")
+             ])
+      , Block
+          mempty
+          Nothing
+          (Table
+             [ ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             ]
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "A") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "B") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
+             Nothing)
+      , Block
+          mempty
+          Nothing
+          (Paragraph
+             [ Inline
+                 mempty (Str "Blank line directly after the border, so no header:")
+             ])
+      , Block
+          mempty
+          Nothing
+          (Table
+             [ ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             , ColumnSpec
+                 { colHorizAlign = Nothing
+                 , colVertAlign = Nothing
+                 , colWidth = Nothing
+                 , colStyle = Nothing
+                 }
+             ]
+             Nothing
+             [ TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             , TableRow
+                 [ TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 , TableCell
+                     { cellContent =
+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]
+                     , cellHorizAlign = Nothing
+                     , cellVertAlign = Nothing
+                     , cellColspan = 1
+                     , cellRowspan = 1
+                     }
+                 ]
+             ]
+             Nothing)
+      ]
+  }
