diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -2,7 +2,14 @@
 
 ## Unreleased changes
 
-## 0.20200201 released 2020-02-01
+## 8.8.4.0 released 2020-02-01
+- New modules:
+  - `Language.Haskell.GhclibparserEx.GHC.Hs.Expr`
+- Moved modules:
+  - `Language.Haskell.GhclibparserEx.HsExtendInstances` ->
+    `Language.Haskell.GhclibparserEx.GHC.Hs.ExtendInstances`;
+
+## 0.20200201.1.0 released 2020-02-01
 - New modules:
   - `Language.Haskell.GhclibparserEx.HsExtendInstances`.
 
diff --git a/ghc-lib-parser-ex.cabal b/ghc-lib-parser-ex.cabal
--- a/ghc-lib-parser-ex.cabal
+++ b/ghc-lib-parser-ex.cabal
@@ -1,6 +1,6 @@
 cabal-version: >= 1.18
 name:           ghc-lib-parser-ex
-version:        0.20200201.1.0
+version:        0.20200205.0.0
 description:    Please see the README on GitHub at <https://github.com/shayne-fletcher/ghc-lib-parser-ex#readme>
 homepage:       https://github.com/shayne-fletcher/ghc-lib-parser-ex#readme
 bug-reports:    https://github.com/shayne-fletcher/ghc-lib-parser-ex/issues
@@ -30,12 +30,13 @@
 library
   exposed-modules:
       Language.Haskell.GhclibParserEx.Config
+      Language.Haskell.GhclibParserEx.Dump
       Language.Haskell.GhclibParserEx.DynFlags
-      Language.Haskell.GhclibParserEx.HsExtendInstances
+      Language.Haskell.GhclibParserEx.Fixity
+      Language.Haskell.GhclibParserEx.GHC.Hs.Expr
+      Language.Haskell.GhclibParserEx.GHC.Hs.ExtendInstances
       Language.Haskell.GhclibParserEx.Parse
       Language.Haskell.GhclibParserEx.Outputable
-      Language.Haskell.GhclibParserEx.Dump
-      Language.Haskell.GhclibParserEx.Fixity
   other-modules:
       Paths_ghc_lib_parser_ex
   hs-source-dirs:
diff --git a/src/Language/Haskell/GhclibParserEx/Dump.hs b/src/Language/Haskell/GhclibParserEx/Dump.hs
--- a/src/Language/Haskell/GhclibParserEx/Dump.hs
+++ b/src/Language/Haskell/GhclibParserEx/Dump.hs
@@ -1,19 +1,6 @@
 -- Copyright (c) 2020, Shayne Fletcher. All rights reserved.
 -- SPDX-License-Identifier: BSD-3-Clause.
 {-
-Currently 'showAstData' is only available in ghc-lib. I intend to
-move it to ghc-lib-parser. Then, we'll be able to get at it with
-something like,
-
-  #if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
-    import GHC.Hs.Dump
-  #else
-   import HsDumpAst
-   #endif
-
-The implementation is reproduced here until that time.
--}
-{-
 (c) The University of Glasgow 2006
 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
 -}
@@ -28,6 +15,21 @@
   , BlankSrcSpan(..),
 ) where
 
+#if !defined(MIN_VERSION_ghc_lib_parser)
+-- Using native ghc.
+#  if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
+import GHC.Hs.Dump
+#  else
+import HsDumpAst
+#  endif
+#else
+-- Using ghc-lib-parser. Recent versions will include
+-- GHC.Hs.Dump (it got moved in from ghc-lib on 2020-02-05).
+# if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
+import GHC.Hs.Dump
+#  else
+-- For simplicity, just assume it's missing from 8.8 ghc-lib-parser
+-- builds and reproduce the implementation.
 import Prelude as X hiding ((<>))
 
 import Data.Data hiding (Fixity)
@@ -233,3 +235,5 @@
      -> (forall d1 d2. (Data d1, Data d2) => c (t d1 d2))
      -> c a
 ext2 def ext = maybe def id (dataCast2 ext)
+#  endif
+#endif
diff --git a/src/Language/Haskell/GhclibParserEx/GHC/Hs/Expr.hs b/src/Language/Haskell/GhclibParserEx/GHC/Hs/Expr.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Haskell/GhclibParserEx/GHC/Hs/Expr.hs
@@ -0,0 +1,139 @@
+-- Copyright (c) 2020, Shayne Fletcher. All rights reserved.
+-- SPDX-License-Identifier: BSD-3-Clause.
+
+{-# OPTIONS_GHC -Wno-missing-fields #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE LambdaCase #-}
+#include "ghclib_api.h"
+
+module Language.Haskell.GhclibParserEx.GHC.Hs.Expr(
+  isTag, isDol, isDot, isReturn, isSection, isRecConstr, isRecUpdate,
+  isVar, isPar, isApp, isOpApp, isAnyApp, isLexeme, isLambda, isQuasiQuote,
+  isDotApp, isTypeApp, isWHNF, isLCase,
+  isFieldPun, isRecStmt, isParComp, isMDo, isTupleSection, isString, isPrimLiteral,
+  isSpliceDecl, isFieldWildcard, isUnboxed,
+  hasFieldsDotDot,
+  varToStr, strToVar,
+  fromChar
+  ) where
+
+#if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
+import GHC.Hs
+#else
+import HsSyn
+#endif
+import SrcLoc
+import RdrName
+import OccName
+import Name
+import BasicTypes
+import TysWiredIn
+
+#if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
+noExt :: NoExtField
+noExt = noExtField
+#endif
+
+-- 'True' if the provided expression is a variable with name 'tag'.
+isTag :: String -> LHsExpr GhcPs -> Bool
+isTag tag = \case (L _ (HsVar _ (L _ s))) -> occNameString (rdrNameOcc s) == tag; _ -> False
+
+isDot, isDol, isReturn, isSection, isRecConstr, isRecUpdate,
+  isVar, isPar, isApp, isOpApp, isAnyApp, isLexeme, isQuasiQuote,
+  isLambda, isDotApp, isTypeApp, isWHNF, isLCase :: LHsExpr GhcPs -> Bool
+isDol = isTag "$"
+isDot = isTag "."
+isReturn x = isTag "return" x || isTag "pure" x -- Allow both 'pure' and 'return' as they have the same semantics.
+isSection = \case (L _ SectionL{}) -> True ; (L _ SectionR{}) -> True; _ -> False
+isRecConstr = \case (L _ RecordCon{}) -> True; _ -> False
+isRecUpdate = \case (L _ RecordUpd{}) -> True; _ -> False
+isVar = \case (L _ HsVar{}) -> True; _ -> False
+isPar = \case (L _ HsPar{}) -> True; _ -> False
+isApp = \case (L _ HsApp{}) -> True; _ -> False
+isOpApp = \case (L _ OpApp{}) -> True; _ -> False
+isAnyApp x = isApp x || isOpApp x
+isLexeme = \case (L _ HsVar{}) -> True; (L _ HsOverLit{}) -> True; (L _ HsLit{}) -> True; _ -> False
+isLambda = \case (L _ HsLam{}) -> True; _ -> False
+isQuasiQuote = \case (L _ (HsSpliceE _ HsQuasiQuote{})) -> True; _ -> False
+isDotApp = \case (L _ (OpApp _ _ op _)) -> isDot op; _ -> False
+isTypeApp = \case (L _ HsAppType{}) -> True; _ -> False
+isWHNF = \case
+  (L _ (HsVar _ (L _ x))) -> isRdrDataCon x
+  (L _ (HsLit _ x)) -> case x of HsString{} -> False; HsInt{} -> False; HsRat{} -> False; _ -> True
+  (L _ HsLam{}) -> True
+  (L _ ExplicitTuple{}) -> True
+  (L _ ExplicitList{}) -> True
+  (L _ (HsPar _ x)) -> isWHNF x
+  (L _ (ExprWithTySig _ x _)) -> isWHNF x
+  -- Other (unknown) constructors may have bang patterns in them, so
+  -- approximate.
+  (L _ (HsApp _ (L _ (HsVar _ (L _ x))) _))
+    | occNameString (rdrNameOcc x) `elem` ["Just", "Left", "Right"] -> True
+  _ -> False
+isLCase = \case (L _ HsLamCase{}) -> True; _ -> False
+
+-- Field is punned e.g. '{foo}'.
+isFieldPun :: LHsRecField GhcPs (LHsExpr GhcPs) -> Bool
+isFieldPun = \case (L _ HsRecField {hsRecPun=True}) -> True; _ -> False
+
+-- Contains a '..' as in 'Foo{..}'
+hasFieldsDotDot :: HsRecFields GhcPs (LHsExpr GhcPs) -> Bool
+hasFieldsDotDot = \case HsRecFields {rec_dotdot=Just _} -> True; _ -> False
+
+isRecStmt :: StmtLR GhcPs GhcPs (LHsExpr GhcPs) -> Bool
+isRecStmt = \case RecStmt{} -> True; _ -> False
+
+isParComp :: StmtLR GhcPs GhcPs (LHsExpr GhcPs) -> Bool
+isParComp = \case ParStmt{} -> True; _ -> False
+
+isMDo :: HsStmtContext Name -> Bool
+isMDo = \case MDoExpr -> True; _ -> False
+
+isTupleSection :: HsTupArg GhcPs -> Bool
+isTupleSection = \case Missing{} -> True; _ -> False
+
+isString :: HsLit GhcPs -> Bool
+isString = \case HsString{} -> True; _ -> False
+
+isPrimLiteral :: HsLit GhcPs -> Bool
+isPrimLiteral = \case
+  HsCharPrim{} -> True
+  HsStringPrim{} -> True
+  HsIntPrim{} -> True
+  HsWordPrim{} -> True
+  HsInt64Prim{} -> True
+  HsWord64Prim{} -> True
+  HsFloatPrim{} -> True
+  HsDoublePrim{} -> True
+  _ -> False
+
+isSpliceDecl :: HsExpr GhcPs -> Bool
+isSpliceDecl = \case HsSpliceE{} -> True; _ -> False
+
+-- Field has a '_' as in '{foo=_} or is punned e.g. '{foo}'.
+isFieldWildcard :: LHsRecField GhcPs (LHsExpr GhcPs) -> Bool
+isFieldWildcard = \case
+#if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
+  (L _ HsRecField {hsRecFieldArg=(L _ (HsUnboundVar _ s))}) -> occNameString s == "_"
+#else
+  (L _ HsRecField {hsRecFieldArg=(L _ (EWildPat _))}) -> True
+#endif
+  (L _ HsRecField {hsRecPun=True}) -> True
+  (L _ HsRecField {}) -> False
+
+isUnboxed :: Boxity -> Bool
+isUnboxed = \case Unboxed -> True; _ -> False
+
+varToStr :: LHsExpr GhcPs -> String
+varToStr (L _ (HsVar _ (L _ n)))
+  | n == consDataCon_RDR = ":"
+  | n == nameRdrName nilDataConName = "[]"
+  | n == nameRdrName (getName (tupleDataCon Boxed 0)) = "()"
+  | otherwise = occNameString (rdrNameOcc n)
+varToStr _ = ""
+
+strToVar :: String -> LHsExpr GhcPs
+strToVar x = noLoc $ HsVar noExt (noLoc $ mkRdrUnqual (mkVarOcc x))
+
+fromChar :: LHsExpr GhcPs -> Maybe Char
+fromChar = \case (L _ (HsLit _ (HsChar _ x))) -> Just x; _ -> Nothing
diff --git a/src/Language/Haskell/GhclibParserEx/GHC/Hs/ExtendInstances.hs b/src/Language/Haskell/GhclibParserEx/GHC/Hs/ExtendInstances.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Haskell/GhclibParserEx/GHC/Hs/ExtendInstances.hs
@@ -0,0 +1,50 @@
+-- Copyright (c) 2020, Shayne Fletcher. All rights reserved.
+-- SPDX-License-Identifier: BSD-3-Clause.
+
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module Language.Haskell.GhclibParserEx.GHC.Hs.ExtendInstances (
+    HsExtendInstances(..), extendInstances, astEq, astListEq)
+where
+
+-- At times, there are terms in Haskell syntax we work with that are
+-- not in `Eq`, `Show` or `Ord` and we need them to be.
+
+-- This work-around resorts to implementing Eq and Ord via
+-- lexicographic string comparisons. As long as two different terms
+-- never map to the same string, basing `Eq` and `Ord` on their string
+-- representations rather than the terms themselves, leads to
+-- identical results.
+
+import Outputable
+import Data.Data
+import Data.Function
+
+import Language.Haskell.GhclibParserEx.Dump
+
+newtype HsExtendInstances a =
+  HsExtendInstances { unextendInstances :: a }
+    deriving Outputable
+
+extendInstances :: a -> HsExtendInstances a
+extendInstances = HsExtendInstances
+
+-- Use 'showAstData'. This is preferable to 'ppr' in that trees that
+-- only differ in arrangement due to fixities will produce differing
+-- string representations.
+toStr :: Data a => HsExtendInstances a -> String
+toStr (HsExtendInstances e) =
+  Outputable.showSDocUnsafe $ showAstData BlankSrcSpan e
+
+instance Data a => Eq (HsExtendInstances a) where (==) a b = toStr a == toStr b
+instance Data a => Ord (HsExtendInstances a) where compare = compare `on` toStr
+
+astEq :: Data a => a -> a -> Bool
+astEq a b = extendInstances a == extendInstances b
+
+astListEq :: Data a => [a] -> [a] -> Bool
+astListEq as bs = length as == length bs && all (uncurry astEq) (zip as bs)
+
+-- Use 'ppr' for 'Show'.
+instance Outputable a => Show (HsExtendInstances a) where
+  show (HsExtendInstances e) =  Outputable.showSDocUnsafe $ Outputable.ppr e
diff --git a/src/Language/Haskell/GhclibParserEx/HsExtendInstances.hs b/src/Language/Haskell/GhclibParserEx/HsExtendInstances.hs
deleted file mode 100644
--- a/src/Language/Haskell/GhclibParserEx/HsExtendInstances.hs
+++ /dev/null
@@ -1,50 +0,0 @@
--- Copyright (c) 2020, Shayne Fletcher. All rights reserved.
--- SPDX-License-Identifier: BSD-3-Clause.
-
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-
-module Language.Haskell.GhclibParserEx.HsExtendInstances (
-    HsExtendInstances(..), extendInstances, astEq, astListEq)
-where
-
--- At times, there are terms in Haskell syntax we work with that are
--- not in `Eq`, `Show` or `Ord` and we need them to be.
-
--- This work-around resorts to implementing Eq and Ord via
--- lexicographic string comparisons. As long as two different terms
--- never map to the same string, basing `Eq` and `Ord` on their string
--- representations rather than the terms themselves, leads to
--- identical results.
-
-import Outputable
-import Data.Data
-import Data.Function
-
-import Language.Haskell.GhclibParserEx.Dump
-
-newtype HsExtendInstances a =
-  HsExtendInstances { unextendInstances :: a }
-    deriving Outputable
-
-extendInstances :: a -> HsExtendInstances a
-extendInstances = HsExtendInstances
-
--- Use 'showAstData'. This is preferable to 'ppr' in that trees that
--- only differ in arrangement due to fixities will produce differing
--- string representations.
-toStr :: Data a => HsExtendInstances a -> String
-toStr (HsExtendInstances e) =
-  Outputable.showSDocUnsafe $ showAstData BlankSrcSpan e
-
-instance Data a => Eq (HsExtendInstances a) where (==) a b = toStr a == toStr b
-instance Data a => Ord (HsExtendInstances a) where compare = compare `on` toStr
-
-astEq :: Data a => a -> a -> Bool
-astEq a b = extendInstances a == extendInstances b
-
-astListEq :: Data a => [a] -> [a] -> Bool
-astListEq as bs = length as == length bs && all (uncurry astEq) (zip as bs)
-
--- Use 'ppr' for 'Show'.
-instance Outputable a => Show (HsExtendInstances a) where
-  show (HsExtendInstances e) =  Outputable.showSDocUnsafe $ Outputable.ppr e
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -15,10 +15,10 @@
 
 import Language.Haskell.GhclibParserEx.Config
 import Language.Haskell.GhclibParserEx.DynFlags
-import Language.Haskell.GhclibParserEx.HsExtendInstances
 import Language.Haskell.GhclibParserEx.Parse
 import Language.Haskell.GhclibParserEx.Dump
 import Language.Haskell.GhclibParserEx.Fixity
+import Language.Haskell.GhclibParserEx.GHC.Hs.ExtendInstances
 
 #if defined (GHCLIB_API_811) || defined (GHCLIB_API_810)
 import GHC.Hs
