diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,19 @@
 # Changelog for commonmark-extensions
 
+## 0.2.3.6
+
+  * Fix pipe table parser so that `|`s don't interfere with
+    other block structures (Michael Howell, #111, fixing #52 and
+    #95). This parser is structured as a system that parses the
+    *second* line first, then parses the first line. That is, if
+    it detects a delimiter row as the second line of a
+    paragraph, it converts the paragraph into a table. This
+    seems counterintuitive, but it works better than trying to
+    convert a table into a paragraph, since it might need to be
+    something else.
+
+  * Improve parsing of inline math (#110).
+
 ## 0.2.3.5
 
   - Resolve entities inside wikilinks (#105, Michał Kukieła).
diff --git a/commonmark-extensions.cabal b/commonmark-extensions.cabal
--- a/commonmark-extensions.cabal
+++ b/commonmark-extensions.cabal
@@ -1,5 +1,5 @@
 name:           commonmark-extensions
-version:        0.2.3.5
+version:        0.2.3.6
 synopsis:       Pure Haskell commonmark parser.
 description:
    This library provides some useful extensions to core commonmark
diff --git a/src/Commonmark/Extensions/Math.hs b/src/Commonmark/Extensions/Math.hs
--- a/src/Commonmark/Extensions/Math.hs
+++ b/src/Commonmark/Extensions/Math.hs
@@ -41,9 +41,10 @@
   symbol '$'
   display <- (True <$ symbol '$') <|> (False <$ notFollowedBy whitespace)
   contents <- try $ untokenize <$> pDollarsMath 0
+  let isWs c = c == ' ' || c == '\t' || c == '\r' || c == '\n'
   if display
      then displayMath contents <$ symbol '$'
-     else if T.all (==' ') (T.takeEnd 1 contents)
+     else if T.null contents || isWs (T.last contents)
              -- don't allow math to end with SPACE + $
              then mzero
              else return $ inlineMath contents
diff --git a/src/Commonmark/Extensions/PipeTable.hs b/src/Commonmark/Extensions/PipeTable.hs
--- a/src/Commonmark/Extensions/PipeTable.hs
+++ b/src/Commonmark/Extensions/PipeTable.hs
@@ -12,7 +12,8 @@
  )
 where
 
-import Control.Monad (guard)
+import Control.Monad (guard, void, mzero)
+import Control.Monad.Trans.Class (lift)
 import Commonmark.Syntax
 import Commonmark.Types
 import Commonmark.Tokens
@@ -137,30 +138,55 @@
   { syntaxBlockSpecs = [pipeTableBlockSpec]
   }
 
+-- This parser is structured as a system that parses the *second* line first,
+-- then parses the first line. That is, if it detects a delimiter row as the
+-- second line of a paragraph, it converts the paragraph into a table. This seems
+-- counterintuitive, but it works better than trying to convert a table into
+-- a paragraph, since it might need to be something else.
+--
+-- See GH-52 and GH-95
 pipeTableBlockSpec :: (Monad m, IsBlock il bl, IsInline il,
                        HasPipeTable il bl)
                    => BlockSpec m il bl
 pipeTableBlockSpec = BlockSpec
      { blockType           = "PipeTable" -- :: Text
      , blockStart          = try $ do -- :: BlockParser m il bl ()
-         interruptsParagraph >>= guard . not
-         nonindentSpaces
-         notFollowedBy whitespace
-         pos <- getPosition
-         (cells, toks) <- withRaw pCells
-         nl <- lookAhead lineEnd
-         let tabledata = PipeTableData
-              { pipeTableAlignments = []
-              , pipeTableHeaders    = cells
-              , pipeTableRows       = []
-              }
-         addNodeToStack $
-               Node (defBlockData pipeTableBlockSpec){
-                         blockStartPos = [pos]
-                       , blockData = toDyn tabledata
-                       , blockLines = [toks ++ [nl]]
-                       } []
-         return BlockStartMatch
+             (cur:rest) <- nodeStack <$> getState
+             guard $ blockParagraph (bspec cur)
+             nonindentSpaces
+             pos <- getPosition
+             aligns <- pDividers
+             skipWhile (hasType Spaces)
+             lookAhead (eof <|> void lineEnd)
+
+             st <- getState
+
+             let headerLine =
+                   case blockLines $ rootLabel cur of
+                      [onlyLine] -> onlyLine
+                      _ -> []
+
+             cellsR <- lift $ runParserT pCells st "" headerLine
+             case cellsR of
+                Right cells ->
+                   if length cells /= length aligns
+                      then mzero -- parse fail: not a table
+                      else do
+                         updateState $ \st' -> st'{ nodeStack = rest }
+                         let tabledata = PipeTableData
+                               { pipeTableAlignments = aligns
+                               , pipeTableHeaders    = cells
+                               , pipeTableRows       = []
+                               }
+                         addNodeToStack $
+                            Node (defBlockData pipeTableBlockSpec){
+                                    blockStartPos = blockStartPos (rootLabel cur) ++ [pos]
+                                  , blockData = toDyn tabledata
+                                  , blockAttributes = blockAttributes (rootLabel cur)
+                                  } []
+                _ ->
+                   mzero -- parse fail: not a table
+             return BlockStartMatch
      , blockCanContain     = \_ -> False -- :: BlockSpec m il bl -> Bool
      , blockContainsLines  = False -- :: Bool
      , blockParagraph      = False -- :: Bool
@@ -173,26 +199,11 @@
                              , pipeTableHeaders = []
                              , pipeTableRows = [] }
          pos <- getPosition
-         if null (blockLines ndata)
-           then do
-             cells <- pCells
-             let tabledata' = tabledata{ pipeTableRows =
-                                 cells : pipeTableRows tabledata }
-             return $! (pos, Node ndata{ blockData =
-                                   toDyn tabledata' } children)
-           else
-             -- last line was first; check for separators
-             -- and if not found, convert to paragraph:
-             try (do aligns <- pDividers
-                     guard $ length aligns ==
-                             length (pipeTableHeaders tabledata)
-                     let tabledata' = tabledata{ pipeTableAlignments = aligns }
-                     return $! (pos, Node ndata{
-                                              blockLines = []
-                                            , blockData = toDyn tabledata'
-                                            } children))
-             <|> (return $! (pos, Node ndata{
-                                   blockSpec = paraSpec } children))
+         cells <- pCells
+         let tabledata' = tabledata{ pipeTableRows =
+                             cells : pipeTableRows tabledata }
+         return $! (pos, Node ndata{ blockData =
+                               toDyn tabledata' } children)
      , blockConstructor    = \(Node ndata _) -> do
          let tabledata = fromDyn
                 (blockData ndata)
@@ -206,8 +217,5 @@
                     (reverse $ pipeTableRows tabledata)
          return $! (pipeTable aligns headers rows)
      , blockFinalize       = \(Node ndata children) parent ->
-         defaultFinalizer
-           (if null (blockLines ndata)
-               then Node ndata children
-               else Node ndata{ blockSpec = paraSpec } children) parent
+         defaultFinalizer (Node ndata children) parent
      }
diff --git a/test/math.md b/test/math.md
--- a/test/math.md
+++ b/test/math.md
@@ -18,9 +18,13 @@
 ```````````````````````````````` example
 This is not math: 2000$.
 And neither is this $ 4 $.
+Or this $4
+$.
 .
 <p>This is not math: 2000$.
-And neither is this $ 4 $.</p>
+And neither is this $ 4 $.
+Or this $4
+$.</p>
 ````````````````````````````````
 
 Display math delimiters can be surrounded by whitespace:
diff --git a/test/pipe_tables.md b/test/pipe_tables.md
--- a/test/pipe_tables.md
+++ b/test/pipe_tables.md
@@ -264,3 +264,51 @@
 </code></pre>
 ````````````````````````````````
 
+
+Pipe tables have exactly one header row, and do not interrupt paragraphs.
+
+```````````````````````````````` example
+| Too much table | to be considered table |
+| Too much table | to be considered table |
+|----------------|------------------------|
+| Too much table | to be considered table |
+.
+<p>| Too much table | to be considered table |
+| Too much table | to be considered table |
+|----------------|------------------------|
+| Too much table | to be considered table |</p>
+````````````````````````````````
+
+
+Other block structures, like headers, have higher priority than tables.
+Tables can be nested in other elements, but don't benefit from laziness.
+
+```````````````````````````````` example
+# abc | def
+------|-----
+
+
+> abc | def
+> ----|-----
+
+
+> abc | def
+----|-----
+.
+<h1>abc | def</h1>
+<p>------|-----</p>
+<blockquote>
+<table>
+<thead>
+<tr>
+<th>abc</th>
+<th>def</th>
+</tr>
+</thead>
+</table>
+</blockquote>
+<blockquote>
+<p>abc | def
+----|-----</p>
+</blockquote>
+````````````````````````````````
