diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,22 @@
 # Revision history for PyF
 
+- Due to the dependencies refactor, `PyF` no have no dependencies other than the one packaged with GHC. The direct result is that `PyF` build time is reduced to 6s versus 4 minutes and 20s before.
+- Remove the dependency to `megaparsec` and replaces it by `parsec`. This should have minor impact on the error messages.
+- *Huge Change*. The parsing of embeded expression does not depend anymore on `haskell-src-ext` and `haskell-src-meta` and instead depends on the built-in `ghc` lib.
+- Added instances for `(Lazy)ByteString` to `PyFClassify` and `PyFToString`. `ByteString` can now be integrated into format string, and will be decoded as ascii.
+- Relax the constraint for floating point formatting from `RealFrac` to `Real`. As a result, a few new type can be formatted as floating point number. One drawback is that some `Integral` are `Real` too and hence it is not an error anymore to format an integral as floating point, but you still need to explicitly select a floating point formatter.
+- Added instance for `(Nominal)DiffTime` to `PyFClassify`, so you can now format them without conversion.
+- Introducing of the new typeclass `PyfFormatIntegral` and `PyfFormatFractional` in order to customize the formatting for numbers. An instance is derived for respectively any `Integral` and `Real` types.
+- Support for `Char` formatting, as string (showing the `Char` value) or as integral, showing the `ord`.
+- `Data.Ratio`.
+
+- Introducing `fmtTrim` module. It offers the same behavior as `fmt`, but trims common indentation. Se `PyF.trimIndent` for documentation.
+- Introducing `raw` for convenience. It is a multiline string without any escaping, formatting neither leading whitespace handling.
+- Introducing `str` and `strTrim`. They are similar to `fmt` and `fmtTrim` but without formatting. You can see them as multiline haskell string, with special character escaping, but without formatting. For convenience, the `strTrim` version also removes indentation.
+
+
+- `fmtWithDelimiters` is gone and replaced by `mkFormatter` in `PyF` which is "more" generic. 
+
 ## 0.9.0.3 -- 2021-02-06
 
 - Test phase do not depend anymore on python (unless cabal flag `python_test` is
diff --git a/PyF.cabal b/PyF.cabal
--- a/PyF.cabal
+++ b/PyF.cabal
@@ -1,15 +1,15 @@
 cabal-version:       2.4
 name:                PyF
-version:             0.9.0.3
-synopsis: Quasiquotations for a python like interpolated string formater
-description: Quasiquotations for a python like interpolated string formater.
+version:             0.10.0.1
+synopsis:            Quasiquotations for a python like interpolated string formatter
+description:         Quasiquotations for a python like interpolated string formatter.
 license:             BSD-3-Clause
 license-file:        LICENSE
 author:              Guillaume Bouchard
 maintainer:          guillaum.bouchard@gmail.com
 category:            Text
 build-type:          Simple
-extra-source-files:  ChangeLog.md Readme.md test/failureCases/*.hs test/golden/*.golden
+extra-source-files:  ChangeLog.md Readme.md test/golden/*.golden
 
 Flag python_test
     Description: Enable extensive python testing
@@ -21,34 +21,33 @@
                   PyF
                   PyF.Class
                   PyF.Internal.PythonSyntax
+                  PyF.Internal.Meta
                   PyF.Internal.QQ
-                  PyF.Internal.Extensions
                   PyF.Formatters
+                  PyF.Internal.ParserEx
+                  PyF.Internal.Parser
 
-  build-depends:      base >= 4.9 && < 5.0
+  build-depends:       base >= 4.9 && < 5.0
+                     , bytestring
                      , template-haskell
                      , text
-                     , containers
-
-                     -- Parsec and some transitive deps
-                     , megaparsec >= 7.0 && < 10.0
+                     , time
+                     , parsec
                      , mtl
-
-                     -- haskell-src-meta < 0.8.2 does not correctly handle TypeApplication
-                     -- That's not critical for PyF, so you can ignore this bound if needed
-                     , haskell-src-meta >= 0.8.2
-                     , haskell-src-exts
+                     , ghc
+                     , ghc-boot
   hs-source-dirs: src
-  ghc-options: -Wall
+  ghc-options: -Wall -Wunused-packages -Wincomplete-uni-patterns
   default-language:    Haskell2010
+  default-extensions: QuasiQuotes
 
 test-suite pyf-test
   type:                exitcode-stdio-1.0
   hs-source-dirs:      test
   main-is:             Spec.hs
   other-modules: SpecUtils SpecCustomDelimiters
-  build-depends:       base, PyF, hspec, text, template-haskell
-  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:       base, PyF, hspec, template-haskell, text, bytestring, time
+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages
   default-language:    Haskell2010
   if flag(python_test)
     cpp-options: -DPYTHON_TEST
@@ -59,15 +58,15 @@
   hs-source-dirs:      test
   main-is:             SpecOverloaded.hs
   build-depends:       base, PyF, hspec, text, bytestring
-  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N
+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages
   default-language:    Haskell2010
 
 test-suite pyf-failure
   type:                exitcode-stdio-1.0
   hs-source-dirs:      test
   main-is:             SpecFail.hs
-  build-depends:       base, hspec, text, template-haskell, process, hspec, temporary, hashable, filepath, deepseq, directory, HUnit
-  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:       base, hspec, text, process, hspec, temporary, filepath, deepseq, HUnit
+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages
   default-language:    Haskell2010
 
 source-repository head
diff --git a/Readme.md b/Readme.md
--- a/Readme.md
+++ b/Readme.md
@@ -1,7 +1,5 @@
 # PyF
 
-[![CircleCI](https://circleci.com/gh/guibou/PyF.svg?style=svg)](https://circleci.com/gh/guibou/PyF)
-
 *PyF* is a Haskell library for string interpolation and formatting.
 
 *PyF* exposes a quasiquoter `f` which introduces string interpolation and formatting with a mini language inspired from printf and Python.
@@ -29,7 +27,7 @@
 
 You will need the extension `QuasiQuotes`, enable it with `{-# LANGUAGE QuasiQuotes #-}` in top of your source file or with `:set -XQuasiQuotes` in your `ghci` session. `ExtendedDefaultRules` and `OverloadedStrings` may be more convenient.
 
-Expression to be formatted are referenced by `{expression:formatingOptions}` where `formatingOptions` follows the [Python format mini-language](https://docs.python.org/3/library/string.html#formatspec). It is recommended to read the python documentation, but the [Test file](https://github.com/guibou/PyF/blob/master/test/Spec.hs) as well as this readme contain many examples.
+Expression to be formatted are referenced by `{expression:formattingOptions}` where `formattingOptions` follows the [Python format mini-language](https://docs.python.org/3/library/string.html#formatspec). It is recommended to read the python documentation, but the [Test file](https://github.com/guibou/PyF/blob/main/test/Spec.hs) as well as this readme contain many examples.
 
 # More Examples
 
@@ -263,7 +261,9 @@
 import PyF
 
 myCustomFormatter :: QuasiQuoter
-myCustomFormatter = fmtWithDelimiters ('@','!')
+myCustomFormatter = mkFormatter "fmtWithDelimiters" (fmtConfig {
+  delimiters = ('@','!')
+  })
 ```
 
 Later, in another module:
@@ -278,6 +278,8 @@
 
 Escaping still works by doubling the delimiters, `@@!!@@!!` will be formatted as `@!@!`.
 
+Have a look at `PyF.mkFormatter` for all the details about customization.
+
 ## Difference with the Python Syntax
 
 The implementation is unit-tested against the reference python implementation (python 3.6.4) and should match its result. However some formatters are not supported or some (minor) differences can be observed.
@@ -305,6 +307,17 @@
 cabal new-test
 ```
 
+There are a few available shells for you.
+
+- `nix-shell` is the default, current GHC version with language server available.
+- `nix-shell ./. -A pyf_xx.shell` is another GHC version (change `xx`) without language server.
+- `nix-shell ./. -A pyf_xx.shell_hls` is another GHC version (change `xx`) with language server.
+
+We also provide a few utility functions:
+
+- `nix-build ./ -A hlint` will check hlint.
+- `nix-shell ./ -A ormolu-fix` will format the codebase.
+
 # Library note
 
 `PyF.Formatters` exposes two functions to format numbers. They are type-safe (as much as possible) and comes with a combination of formatting options not seen in other formatting libraries:
@@ -316,9 +329,37 @@
 
 # GHC compatibility
 
-This library is tested with ghc 8.6, 8.8, 8.10 and 9.0.
+This library is tested in CI with ghc 8.6 to 9.2.
 
 # Conclusion
 
 Don't hesitate to make any suggestion, I'll be more than happy to work on it.
 
+# Hacking
+
+Everything works with nix. But you can also try with manual cabal / stack if you wish.
+
+- `nix-shell` will open a shell with everything you need to work on PyF, including haskell-language-server. It may be a bit too much, so you can instead:
+- `nix-shell ./ -A pyf_current.shell` which opens a shell without the haskell-language-server.
+- `nix-shell ./ -A pyf_current.shell_hls` which opens a shell with the haskell-language-server, same as the default.
+- `nix-shell ./ -A pyf_xx.shell[_hls]` opens a shell (with or without language server) for a difference GHC version. Uses `pyf_86`, `pyf_88`, `pyf_810`, `pyf_90` and `pyf_92`. Note that the language server may not be available for these version and it make take a bit to compile everything.
+
+Once in the shell, use `cabal build`, `cabal test`, `cabal repl`.
+
+There is a cachix available, used by CI:
+
+- `cachix use guibou`
+
+You can locally build and test everything using:
+
+- `nix-build -A pyf_all`.
+
+Don't hesitate to submit a PR not tested on all GHC versions.
+
+## Formatting
+
+The codebase is formatted with `ormolu` and this is checked in the CI. Please run:
+
+- `nix-shell ./ -A ormolu-fix`
+
+Before submitting.
diff --git a/src/PyF.hs b/src/PyF.hs
--- a/src/PyF.hs
+++ b/src/PyF.hs
@@ -1,37 +1,124 @@
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE ViewPatterns #-}
 
 -- | A lot of quasiquoters to format and interpolate string expression
 module PyF
   ( fmt,
-
-    -- * With custom delimiters
-    fmtWithDelimiters,
+    fmtTrim,
+    str,
+    strTrim,
+    raw,
     module PyF.Class,
+
+    -- * Whitespace utilities
+    trimIndent,
+
+    -- * Configuration
+    mkFormatter,
+    defaultConfig,
+    fmtConfig,
+    strConfig,
+    addTrim,
+    addFormatting,
   )
 where
 
+import Data.Char (isSpace)
+import Data.List (intercalate)
 import Language.Haskell.TH.Quote (QuasiQuoter (..))
 import PyF.Class
-import PyF.Internal.QQ (toExp)
-
-templateF :: (Char, Char) -> String -> QuasiQuoter
-templateF delimiters fName =
-  QuasiQuoter
-    { quoteExp = toExp delimiters,
-      quotePat = err "pattern",
-      quoteType = err "type",
-      quoteDec = err "declaration"
-    }
-  where
-    err name = error (fName ++ ": This QuasiQuoter can not be used as a " ++ name ++ "!")
+import PyF.Internal.QQ (Config (..), expQQ, toExp, wrapFromString)
 
 -- | Generic formatter, can format an expression to any @t@ as long as
 --   @t@ is an instance of 'IsString'.
 fmt :: QuasiQuoter
-fmt = templateF pythonDelimiters "fmt"
+fmt = mkFormatter "fmt" fmtConfig
 
-fmtWithDelimiters :: (Char, Char) -> QuasiQuoter
-fmtWithDelimiters delimiters = templateF delimiters "fmtWithDelimiters"
+-- | Format with whitespace trimming.
+fmtTrim :: QuasiQuoter
+fmtTrim = mkFormatter "fmtTrim" (addTrim fmtConfig)
 
-pythonDelimiters :: (Char, Char)
-pythonDelimiters = ('{', '}')
+-- | multiline string, no interpolation.
+str :: QuasiQuoter
+str = mkFormatter "str" strConfig
+
+-- | multiline string, no interpolation, but does indentation trimming.
+strTrim :: QuasiQuoter
+strTrim = mkFormatter "strTrim" (addTrim strConfig)
+
+-- | Raw string, no interpolation neither escaping is performed.
+raw :: QuasiQuoter
+raw = expQQ "raw" (\s -> [|s|])
+
+-- | Removes the trailing whitespace of a string.
+--
+-- - First line is ignored if it only contains whitespaces
+-- - All other line common indentation is removed, ignoring line with only whitespaces.
+--
+-- >>> trimIndent "\n   hello\n   - a\n   - b\n   "
+-- "hello\n- a\n- b\n"
+--
+-- See 'fmtTrim' for a quasiquoter with this behavior
+trimIndent :: String -> String
+trimIndent s =
+  case lines s of
+    [] -> ""
+    [_] -> s
+    (firstLine : others) ->
+      let -- Discard the first line if needed
+          usedLines
+            | all isSpace firstLine = others ++ trail
+            | otherwise = firstLine : others ++ trail
+
+          -- If the string ends with a newline, `lines` will discard it. We restore it.
+          trail
+            | last s == '\n' = [""]
+            | otherwise = []
+          -- Find the minimum indent common to all lines
+          biggestLines = map (length . takeWhile isSpace) (filter (not . all isSpace) usedLines)
+
+          stripLen = case biggestLines of
+            [] -> 0
+            _ -> minimum biggestLines
+
+          -- drop them
+          trimmedLines = map (drop stripLen) usedLines
+       in intercalate "\n" trimmedLines
+
+-- | This is an empty configuration. No formatting, no post processing
+defaultConfig :: Config
+defaultConfig =
+  Config
+    { delimiters = Nothing,
+      postProcess = id
+    }
+
+-- | Configuration for 'str' it just wrap the multiline string with 'fromString'.
+strConfig :: Config
+strConfig =
+  Config
+    { delimiters = Nothing,
+      postProcess = wrapFromString
+    }
+
+-- | The config for 'fmt'.
+fmtConfig :: Config
+fmtConfig = addFormatting ('{', '}') strConfig
+
+-- | Add indentation trimming to a configuration.
+addTrim :: Config -> Config
+addTrim config =
+  config
+    { postProcess = \q -> postProcess config [|PyF.trimIndent $(q)|]
+    }
+
+-- | Enable formatting
+addFormatting :: (Char, Char) -> Config -> Config
+addFormatting delims c = c {delimiters = Just delims}
+
+-- | Build a formatter. See the 'Config' type for details, as well as
+-- 'fmtConfig' and 'strConfig' for examples.
+mkFormatter :: String -> Config -> QuasiQuoter
+mkFormatter name config = expQQ name (toExp config)
diff --git a/src/PyF/Class.hs b/src/PyF/Class.hs
--- a/src/PyF/Class.hs
+++ b/src/PyF/Class.hs
@@ -1,17 +1,57 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DefaultSignatures #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
 
+-- | You want to add formatting support for your custom type. This is the right module.
+--
+-- In PyF, formatters are in three categories:
+--
+-- - Integral numbers, which are numbers without fractional part
+-- - Fractional numbers, which are numbers with a fractional part
+-- - String, which represents text.
+--
+-- The formatting can be either explicit or implicit. For example:
+--
+-- >>> let x = 10 in [fmt|{x}|]
+-- 10
+--
+-- Is an implicit formatting to number, but:
+--
+-- >>> let x = 10 in [fmt|{x:d}|]
+--
+-- Is an explicit formatting to Integral numbers, using @d@.
+--
+-- Implicit formatting will only format to either Integral, Fractional or text,
+-- and this choice is done by the (open) type family `PyFCategory'.
+--
+-- This modules also provides 3 type class for formatting.
+--
+-- - 'PyfFormatFractional' and 'PyfFormatIntegral' are responsible for
+-- formatting integral and fractional numbers. Default instances are provided
+-- respectively for 'Real' and 'Integral'. 'PyFToString' is in charge of
+-- formatting text.
 module PyF.Class where
 
+import qualified Data.ByteString
+import qualified Data.ByteString.Lazy
+import Data.Char (ord)
 import Data.Int
+import qualified Data.Ratio
 import qualified Data.Text as SText
+import qualified Data.Text.Encoding as E
 import qualified Data.Text.Lazy as LText
+import qualified Data.Time
 import Data.Word
 import Numeric.Natural
+import PyF.Formatters
 
+-- * Default formatting classification
+
 -- | The three categories of formatting in 'PyF'
 data PyFCategory
   = -- | Format as an integral, no fractional part, precise value
@@ -50,22 +90,38 @@
 
 type instance PyFClassify Word64 = 'PyFIntegral
 
+-- Float numbers
+
 type instance PyFClassify Float = 'PyFFractional
 
 type instance PyFClassify Double = 'PyFFractional
 
+type instance PyFClassify Data.Time.DiffTime = 'PyFFractional
+
+type instance PyFClassify Data.Time.NominalDiffTime = 'PyFFractional
+
+type instance PyFClassify (Data.Ratio.Ratio i) = 'PyFFractional
+
+-- String
+
 type instance PyFClassify String = 'PyFString
 
 type instance PyFClassify LText.Text = 'PyFString
 
 type instance PyFClassify SText.Text = 'PyFString
 
+type instance PyFClassify Data.ByteString.ByteString = 'PyFString
+
+type instance PyFClassify Data.ByteString.Lazy.ByteString = 'PyFString
+
+type instance PyFClassify Char = 'PyFString
+
+-- * String formatting
+
 -- | Convert a type to string
---   The default implementation uses `Show`
+-- This is used for the string formatting.
 class PyFToString t where
   pyfToString :: t -> String
-  default pyfToString :: Show t => t -> String
-  pyfToString = show
 
 instance PyFToString String where pyfToString = id
 
@@ -73,4 +129,76 @@
 
 instance PyFToString SText.Text where pyfToString = SText.unpack
 
+instance PyFToString Data.ByteString.ByteString where pyfToString = SText.unpack . E.decodeUtf8
+
+instance PyFToString Data.ByteString.Lazy.ByteString where pyfToString = pyfToString . Data.ByteString.Lazy.toStrict
+
+instance PyFToString Char where pyfToString c = [c]
+
+-- | Default instance. Convert any type with a 'Show instance.
 instance {-# OVERLAPPABLE #-} Show t => PyFToString t where pyfToString = show
+
+-- * Real formatting (with optional fractional part)
+
+-- | Apply a fractional formatting to any type.
+--
+-- A default instance for any 'Real' is provided which internally converts to
+-- 'Double', which may not be efficient or results in rounding errors.
+--
+-- You can provide your own instance and internally use 'formatFractional'
+-- which does have the same signatures as 'pyfFormatFractional' but with a
+-- 'RealFrac' constraint.
+class PyfFormatFractional a where
+  pyfFormatFractional ::
+    Format t t' 'Fractional ->
+    -- | Sign formatting
+    SignMode ->
+    -- | Padding
+    Maybe (Int, AlignMode k, Char) ->
+    -- | Grouping
+    Maybe (Int, Char) ->
+    -- | Precision
+    Maybe Int ->
+    a ->
+    String
+
+-- | Default instance working for any 'Real'. Internally it converts the type to 'Double'.
+instance {-# OVERLAPPABLE #-} Real t => PyfFormatFractional t where
+  pyfFormatFractional f s p g prec v = formatFractional f s p g prec (realToFrac @t @Double v)
+
+-- | This instance does not do any conversion.
+instance PyfFormatFractional Double where pyfFormatFractional = formatFractional
+
+-- | This instance does not do any conversion.
+instance PyfFormatFractional Float where pyfFormatFractional = formatFractional
+
+-- * Integral formatting
+
+-- | Apply an integral formatting to any type.
+--
+-- A default instance for any 'Integral' is provided.
+--
+-- You can provide your own instance and internally use 'formatIntegral'
+-- which does have the same signatures as 'pyfFormatIntegral' but with an
+-- 'Integral' constraint.
+class PyfFormatIntegral i where
+  pyfFormatIntegral ::
+    Format t t' 'Integral ->
+    -- | Sign formatting
+    SignMode ->
+    -- | Padding
+    Maybe (Int, AlignMode k, Char) ->
+    -- | Grouping
+    Maybe (Int, Char) ->
+    i ->
+    String
+
+-- | Default instance for any 'Integral'.
+instance {-# OVERLAPPABLE #-} Integral t => PyfFormatIntegral t where
+  pyfFormatIntegral f s p g v = formatIntegral f s p g v
+
+-- | Returns the numerical value of a 'Char'
+-- >>> [fmt|{'a':d}|]
+-- 97
+instance PyfFormatIntegral Char where
+  pyfFormatIntegral f s p g v = formatIntegral f s p g (ord v)
diff --git a/src/PyF/Formatters.hs b/src/PyF/Formatters.hs
--- a/src/PyF/Formatters.hs
+++ b/src/PyF/Formatters.hs
@@ -26,8 +26,9 @@
 -- For integrals:
 --
 --    * Binary / Hexa / Octal / Character representation
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 module PyF.Formatters
-  ( -- * Generic formating function
+  ( -- * Generic formatting function
     formatString,
     formatIntegral,
     formatFractional,
@@ -125,18 +126,28 @@
   -- Upper should come AFTER Alt, so this disallow any future alt
   Upper :: Format alt 'CanUpper f -> Format 'NoAlt 'NoUpper f
 
+
+newtype ShowIntegral i = ShowIntegral i
+  deriving (Real, Enum, Ord, Eq, Num, Integral)
+
+-- | Stupid instance in order to use 'Numeric.showIntAtBase' which needs a
+-- 'Show' constraint for error reporting when number are negative.
+-- However, in 'reprIntegral', there is no negative number, so the case is
+-- impossible, but it allows the removal of the 'Show' constraint.
+instance Show (ShowIntegral i) where
+  show _ = error "show should not be called on ShowIntegral"
+
 -- Internal Integral
--- Todo: remove the Show constraint ?
 -- Needed for debug in Numeric function, this is painful
-reprIntegral :: (Show i, Integral i) => Format t t' 'Integral -> i -> Repr
+reprIntegral :: (Integral i) => Format t t' 'Integral -> i -> Repr
 reprIntegral fmt i = IntegralRepr sign $ format fmt
   where
     format :: Format t t' 'Integral -> String
     format = \case
       Decimal -> Numeric.showInt iAbs ""
-      Octal -> Numeric.showOct iAbs ""
-      Binary -> Numeric.showIntAtBase 2 (\digit -> if digit == 0 then '0' else '1') iAbs ""
-      Hexa -> Numeric.showHex iAbs ""
+      Octal -> Numeric.showOct (ShowIntegral iAbs) ""
+      Binary -> Numeric.showIntAtBase 2 (\digit -> if digit == 0 then '0' else '1') (ShowIntegral iAbs) ""
+      Hexa -> Numeric.showHex (ShowIntegral iAbs) ""
       Upper fmt' -> map toUpper $ format fmt'
       Character -> [chr (fromIntegral i)]
       Alternate fmt' -> format fmt'
@@ -264,7 +275,7 @@
 
 -- | Format an integral number
 formatIntegral ::
-  (Show i, Integral i) =>
+  Integral i =>
   Format t t' 'Integral ->
   SignMode ->
   -- | Padding
diff --git a/src/PyF/Internal/Extensions.hs b/src/PyF/Internal/Extensions.hs
deleted file mode 100644
--- a/src/PyF/Internal/Extensions.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-module PyF.Internal.Extensions
-  ( thExtToMetaExt,
-  )
-where
-
-import qualified Language.Haskell.Exts.Extension as Exts
-import qualified Language.Haskell.TH as TH
-
--- | Associate a template haskell extension to an haskell-src-ext extension
-thExtToMetaExt :: TH.Extension -> Maybe Exts.Extension
-thExtToMetaExt e =
-  Exts.EnableExtension <$> case e of
-    -- For some reason, the casing is different between TH and haskell-src-exts
-    TH.Cpp -> Just Exts.CPP
-    -- Hope for the best, if haskell-src-ext knows the extension or not
-    _ -> case Exts.parseExtension (show e) of
-      Exts.EnableExtension ext -> Just ext
-      Exts.DisableExtension ext -> Just ext
-      Exts.UnknownExtension _ -> Nothing
diff --git a/src/PyF/Internal/Meta.hs b/src/PyF/Internal/Meta.hs
new file mode 100644
--- /dev/null
+++ b/src/PyF/Internal/Meta.hs
@@ -0,0 +1,341 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE TemplateHaskellQuotes #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module PyF.Internal.Meta (toExp, baseDynFlags, translateTHtoGHCExt) where
+
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Hs.Type (HsWildCardBndrs (..), HsType (..))
+#elif MIN_VERSION_ghc(8,10,0)
+import GHC.Hs.Types (HsWildCardBndrs (..), HsType (..))
+#else
+import HsTypes (HsWildCardBndrs (..), HsType (..))
+#endif
+
+#if MIN_VERSION_ghc(8,10,0)
+import GHC.Hs.Expr as Expr
+import GHC.Hs.Extension as Ext
+import GHC.Hs.Pat as Pat
+import GHC.Hs.Lit
+#else
+import HsExpr as Expr
+import HsExtension as Ext
+import HsPat as Pat
+import HsLit
+#endif
+
+import qualified Data.ByteString as B
+import qualified Language.Haskell.TH.Syntax as GhcTH
+import qualified Language.Haskell.TH.Syntax as TH
+import PyF.Internal.ParserEx (fakeLlvmConfig, fakeSettings)
+
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Types.SrcLoc
+import GHC.Types.Name
+import GHC.Types.Name.Reader
+import GHC.Data.FastString
+#if MIN_VERSION_ghc(9,2,0)
+import GHC.Utils.Outputable (ppr)
+import GHC.Types.Basic (Boxity(..))
+import GHC.Types.SourceText (il_value, rationalFromFractionalLit)
+import GHC.Driver.Ppr (showSDocDebug)
+#else
+import GHC.Utils.Outputable (ppr, showSDocDebug)
+import GHC.Types.Basic (il_value, fl_value, Boxity(..))
+#endif
+import GHC.Driver.Session (DynFlags, xopt_set, defaultDynFlags)
+import qualified GHC.Unit.Module as Module
+#else
+import SrcLoc
+import Name
+import RdrName
+import FastString
+import Outputable (ppr, showSDocDebug)
+import BasicTypes (il_value, fl_value, Boxity(..))
+import DynFlags (DynFlags, xopt_set, defaultDynFlags)
+import qualified Module
+#endif
+
+
+#if MIN_VERSION_ghc(9,2,0)
+-- TODO: why this disapears in GHC >= 9.2?
+fl_value = rationalFromFractionalLit
+#endif
+
+toLit :: HsLit GhcPs -> TH.Lit
+toLit (HsChar _ c) = TH.CharL c
+toLit (HsCharPrim _ c) = TH.CharPrimL c
+toLit (HsString _ s) = TH.StringL (unpackFS s)
+toLit (HsStringPrim _ s) = TH.StringPrimL (B.unpack s)
+toLit (HsInt _ i) = TH.IntegerL (il_value i)
+toLit (HsIntPrim _ i) = TH.IntPrimL i
+toLit (HsWordPrim _ i) = TH.WordPrimL i
+toLit (HsInt64Prim _ i) = TH.IntegerL i
+toLit (HsWord64Prim _ i) = TH.WordPrimL i
+toLit (HsInteger _ i _) = TH.IntegerL i
+toLit (HsRat _ f _) = TH.FloatPrimL (fl_value f)
+toLit (HsFloatPrim _ f) = TH.FloatPrimL (fl_value f)
+toLit (HsDoublePrim _ f) = TH.DoublePrimL (fl_value f)
+
+#if !MIN_VERSION_ghc(9,0,0)
+toLit (XLit _) = noTH "toLit" "XLit"
+#endif
+
+toLit' :: OverLitVal -> TH.Lit
+toLit' (HsIntegral i) = TH.IntegerL (il_value i)
+toLit' (HsFractional f) = TH.RationalL (fl_value f)
+toLit' (HsIsString _ fs) = TH.StringL (unpackFS fs)
+
+toType :: HsType GhcPs -> TH.Type
+toType (HsWildCardTy _) = TH.WildCardT
+toType (HsTyVar _ _ n) =
+  let n' = unLoc n
+   in if isRdrTyVar n'
+        then TH.VarT (toName n')
+        else TH.ConT (toName n')
+toType t = todo "toType" (showSDocDebug (baseDynFlags []) . ppr $ t)
+
+toName :: RdrName -> TH.Name
+toName n = case n of
+  (Unqual o) -> TH.mkName (occNameString o)
+  (Qual m o) -> TH.mkName (Module.moduleNameString m <> "." <> occNameString o)
+  (Orig m o) -> error "orig"
+  (Exact n1) -> error "exact"
+
+toFieldExp :: a
+toFieldExp = undefined
+
+toPat :: DynFlags -> Pat.Pat GhcPs -> TH.Pat
+toPat _dynFlags (Pat.VarPat _ (unLoc -> name)) = TH.VarP (toName name)
+toPat dynFlags p = todo "toPat" (showSDocDebug dynFlags . ppr $ p)
+
+toExp :: DynFlags -> Expr.HsExpr GhcPs -> TH.Exp
+toExp _ (Expr.HsVar _ n) =
+  let n' = unLoc n
+   in if isRdrDataCon n'
+        then TH.ConE (toName n')
+        else TH.VarE (toName n')
+#if MIN_VERSION_ghc(9,0,0)
+toExp _ (Expr.HsUnboundVar _ n)              = TH.UnboundVarE (TH.mkName . occNameString $ n)
+#else
+toExp _ (Expr.HsUnboundVar _ n)              = TH.UnboundVarE (TH.mkName . occNameString . Expr.unboundVarOcc $ n)
+#endif
+toExp _ Expr.HsIPVar {} = noTH "toExp" "HsIPVar"
+toExp _ (Expr.HsLit _ l) = TH.LitE (toLit l)
+toExp _ (Expr.HsOverLit _ OverLit {ol_val}) = TH.LitE (toLit' ol_val)
+toExp d (Expr.HsApp _ e1 e2) = TH.AppE (toExp d . unLoc $ e1) (toExp d . unLoc $ e2)
+#if MIN_VERSION_ghc(8,8,0)
+toExp d (Expr.HsAppType _ e HsWC {hswc_body}) = TH.AppTypeE (toExp d . unLoc $ e) (toType . unLoc $ hswc_body)
+#else
+toExp d (Expr.HsAppType HsWC {hswc_body} e) = TH.AppTypeE (toExp d . unLoc $ e) (toType . unLoc $ hswc_body)
+#endif
+toExp d (Expr.OpApp _ e1 o e2) = TH.UInfixE (toExp d . unLoc $ e1) (toExp d . unLoc $ o) (toExp d . unLoc $ e2)
+toExp d (Expr.NegApp _ e _) = TH.AppE (TH.VarE 'negate) (toExp d . unLoc $ e)
+-- NOTE: for lambda, there is only one match
+toExp d (Expr.HsLam _ (Expr.MG _ (unLoc -> (map unLoc -> [Expr.Match _ _ (map unLoc -> ps) (Expr.GRHSs _ [unLoc -> Expr.GRHS _ _ (unLoc -> e)] _)])) _)) = TH.LamE (fmap (toPat d) ps) (toExp d e)
+-- toExp (Expr.Let _ bs e)                       = TH.LetE (toDecs bs) (toExp e)
+-- toExp (Expr.If _ a b c)                       = TH.CondE (toExp a) (toExp b) (toExp c)
+-- toExp (Expr.MultiIf _ ifs)                    = TH.MultiIfE (map toGuard ifs)
+-- toExp (Expr.Case _ e alts)                    = TH.CaseE (toExp e) (map toMatch alts)
+-- toExp (Expr.Do _ ss)                          = TH.DoE (map toStmt ss)
+-- toExp e@Expr.MDo{}                            = noTH "toExp" e
+#if MIN_VERSION_ghc(9, 2, 0)
+toExp d (Expr.ExplicitTuple _ args boxity) = ctor tupArgs
+#else
+toExp d (Expr.ExplicitTuple _ (map unLoc -> args) boxity) = ctor tupArgs
+#endif
+  where
+    toTupArg (Expr.Present _ e) = Just $ unLoc e
+    toTupArg (Expr.Missing _) = Nothing
+    toTupArg _ = error "impossible case"
+
+    ctor = case boxity of
+      Boxed -> TH.TupE
+      Unboxed -> TH.UnboxedTupE
+
+#if MIN_VERSION_ghc(8,10,0)
+    tupArgs = fmap ((fmap (toExp d)) . toTupArg) args
+#else
+    tupArgs = case traverse toTupArg args of
+      Nothing -> error "Tuple section are not supported by template haskell < 8.10"
+      Just args -> fmap (toExp d) args
+#endif
+
+-- toExp (Expr.List _ xs)                        = TH.ListE (fmap toExp xs)
+toExp d (Expr.HsPar _ e) = TH.ParensE (toExp d . unLoc $ e)
+toExp d (Expr.SectionL _ (unLoc -> a) (unLoc -> b)) = TH.InfixE (Just . toExp d $ a) (toExp d b) Nothing
+toExp d (Expr.SectionR _ (unLoc -> a) (unLoc -> b)) = TH.InfixE Nothing (toExp d a) (Just . toExp d $ b)
+toExp _ (Expr.RecordCon _ name HsRecFields {rec_flds}) =
+  TH.RecConE (toName . unLoc $ name) (fmap toFieldExp rec_flds)
+-- toExp (Expr.RecUpdate _ e xs)                 = TH.RecUpdE (toExp e) (fmap toFieldExp xs)
+-- toExp (Expr.ListComp _ e ss)                  = TH.CompE $ map convert ss ++ [TH.NoBindS (toExp e)]
+--  where
+--   convert (Expr.QualStmt _ st)                = toStmt st
+--   convert s                                   = noTH "toExp ListComp" s
+-- toExp (Expr.ExpTypeSig _ e t)                 = TH.SigE (toExp e) (toType t)
+#if MIN_VERSION_ghc(9, 2, 0)
+toExp d (Expr.ExplicitList _ (map unLoc -> args)) = TH.ListE (map (toExp d) args)
+#else
+toExp d (Expr.ExplicitList _ _ (map unLoc -> args)) = TH.ListE (map (toExp d) args)
+#endif
+toExp d (Expr.ArithSeq _ _ e) = TH.ArithSeqE $ case e of
+  (From a) -> TH.FromR (toExp d $ unLoc a)
+  (FromThen a b) -> TH.FromThenR (toExp d $ unLoc a) (toExp d $ unLoc b)
+  (FromTo a b) -> TH.FromToR (toExp d $ unLoc a) (toExp d $ unLoc b)
+  (FromThenTo a b c) -> TH.FromThenToR (toExp d $ unLoc a) (toExp d $ unLoc b) (toExp d $ unLoc c)
+toExp dynFlags e = todo "toExp" (showSDocDebug dynFlags . ppr $ e)
+
+todo :: (Show e) => String -> e -> a
+todo fun thing = error . concat $ [moduleName, ".", fun, ": not implemented: ", show thing]
+
+noTH :: (Show e) => String -> e -> a
+noTH fun thing = error . concat $ [moduleName, ".", fun, ": no TemplateHaskell for: ", show thing]
+
+moduleName :: String
+moduleName = "PyF.Internal.Meta"
+
+baseDynFlags :: [GhcTH.Extension] -> DynFlags
+baseDynFlags exts =
+  let enable = GhcTH.TemplateHaskellQuotes : exts
+   in foldl xopt_set (defaultDynFlags fakeSettings fakeLlvmConfig) enable
+
+translateTHtoGHCExt :: TH.Extension -> GhcTH.Extension
+translateTHtoGHCExt TH.Cpp = GhcTH.Cpp
+translateTHtoGHCExt TH.OverlappingInstances = GhcTH.OverlappingInstances
+translateTHtoGHCExt TH.UndecidableInstances = GhcTH.UndecidableInstances
+translateTHtoGHCExt TH.IncoherentInstances = GhcTH.IncoherentInstances
+translateTHtoGHCExt TH.UndecidableSuperClasses = GhcTH.UndecidableSuperClasses
+translateTHtoGHCExt TH.MonomorphismRestriction = GhcTH.MonomorphismRestriction
+#if ! MIN_VERSION_ghc(9,2,0)
+translateTHtoGHCExt TH.MonoPatBinds = GhcTH.MonoPatBinds
+#endif
+translateTHtoGHCExt TH.MonoLocalBinds = GhcTH.MonoLocalBinds
+translateTHtoGHCExt TH.RelaxedPolyRec = GhcTH.RelaxedPolyRec
+translateTHtoGHCExt TH.ExtendedDefaultRules = GhcTH.ExtendedDefaultRules
+translateTHtoGHCExt TH.ForeignFunctionInterface = GhcTH.ForeignFunctionInterface
+translateTHtoGHCExt TH.UnliftedFFITypes = GhcTH.UnliftedFFITypes
+translateTHtoGHCExt TH.InterruptibleFFI = GhcTH.InterruptibleFFI
+translateTHtoGHCExt TH.CApiFFI = GhcTH.CApiFFI
+translateTHtoGHCExt TH.GHCForeignImportPrim = GhcTH.GHCForeignImportPrim
+translateTHtoGHCExt TH.JavaScriptFFI = GhcTH.JavaScriptFFI
+translateTHtoGHCExt TH.ParallelArrays = GhcTH.ParallelArrays
+translateTHtoGHCExt TH.Arrows = GhcTH.Arrows
+translateTHtoGHCExt TH.TemplateHaskell = GhcTH.TemplateHaskell
+translateTHtoGHCExt TH.TemplateHaskellQuotes = GhcTH.TemplateHaskellQuotes
+translateTHtoGHCExt TH.QuasiQuotes = GhcTH.QuasiQuotes
+translateTHtoGHCExt TH.ImplicitParams = GhcTH.ImplicitParams
+translateTHtoGHCExt TH.ImplicitPrelude = GhcTH.ImplicitPrelude
+translateTHtoGHCExt TH.ScopedTypeVariables = GhcTH.ScopedTypeVariables
+translateTHtoGHCExt TH.AllowAmbiguousTypes = GhcTH.AllowAmbiguousTypes
+translateTHtoGHCExt TH.UnboxedTuples = GhcTH.UnboxedTuples
+translateTHtoGHCExt TH.UnboxedSums = GhcTH.UnboxedSums
+translateTHtoGHCExt TH.BangPatterns = GhcTH.BangPatterns
+translateTHtoGHCExt TH.TypeFamilies = GhcTH.TypeFamilies
+translateTHtoGHCExt TH.TypeFamilyDependencies = GhcTH.TypeFamilyDependencies
+translateTHtoGHCExt TH.TypeInType = GhcTH.TypeInType
+translateTHtoGHCExt TH.OverloadedStrings = GhcTH.OverloadedStrings
+translateTHtoGHCExt TH.OverloadedLists = GhcTH.OverloadedLists
+translateTHtoGHCExt TH.NumDecimals = GhcTH.NumDecimals
+translateTHtoGHCExt TH.DisambiguateRecordFields = GhcTH.DisambiguateRecordFields
+translateTHtoGHCExt TH.RecordWildCards = GhcTH.RecordWildCards
+translateTHtoGHCExt TH.RecordPuns = GhcTH.RecordPuns
+translateTHtoGHCExt TH.ViewPatterns = GhcTH.ViewPatterns
+translateTHtoGHCExt TH.GADTs = GhcTH.GADTs
+translateTHtoGHCExt TH.GADTSyntax = GhcTH.GADTSyntax
+translateTHtoGHCExt TH.NPlusKPatterns = GhcTH.NPlusKPatterns
+translateTHtoGHCExt TH.DoAndIfThenElse = GhcTH.DoAndIfThenElse
+translateTHtoGHCExt TH.BlockArguments = GhcTH.BlockArguments
+translateTHtoGHCExt TH.RebindableSyntax = GhcTH.RebindableSyntax
+translateTHtoGHCExt TH.ConstraintKinds = GhcTH.ConstraintKinds
+translateTHtoGHCExt TH.PolyKinds = GhcTH.PolyKinds
+translateTHtoGHCExt TH.DataKinds = GhcTH.DataKinds
+translateTHtoGHCExt TH.InstanceSigs = GhcTH.InstanceSigs
+translateTHtoGHCExt TH.ApplicativeDo = GhcTH.ApplicativeDo
+translateTHtoGHCExt TH.StandaloneDeriving = GhcTH.StandaloneDeriving
+translateTHtoGHCExt TH.DeriveDataTypeable = GhcTH.DeriveDataTypeable
+translateTHtoGHCExt TH.AutoDeriveTypeable = GhcTH.AutoDeriveTypeable
+translateTHtoGHCExt TH.DeriveFunctor = GhcTH.DeriveFunctor
+translateTHtoGHCExt TH.DeriveTraversable = GhcTH.DeriveTraversable
+translateTHtoGHCExt TH.DeriveFoldable = GhcTH.DeriveFoldable
+translateTHtoGHCExt TH.DeriveGeneric = GhcTH.DeriveGeneric
+translateTHtoGHCExt TH.DefaultSignatures = GhcTH.DefaultSignatures
+translateTHtoGHCExt TH.DeriveAnyClass = GhcTH.DeriveAnyClass
+translateTHtoGHCExt TH.DeriveLift = GhcTH.DeriveLift
+translateTHtoGHCExt TH.DerivingStrategies = GhcTH.DerivingStrategies
+translateTHtoGHCExt TH.DerivingVia = GhcTH.DerivingVia
+translateTHtoGHCExt TH.TypeSynonymInstances = GhcTH.TypeSynonymInstances
+translateTHtoGHCExt TH.FlexibleContexts = GhcTH.FlexibleContexts
+translateTHtoGHCExt TH.FlexibleInstances = GhcTH.FlexibleInstances
+translateTHtoGHCExt TH.ConstrainedClassMethods = GhcTH.ConstrainedClassMethods
+translateTHtoGHCExt TH.MultiParamTypeClasses = GhcTH.MultiParamTypeClasses
+translateTHtoGHCExt TH.NullaryTypeClasses = GhcTH.NullaryTypeClasses
+translateTHtoGHCExt TH.FunctionalDependencies = GhcTH.FunctionalDependencies
+translateTHtoGHCExt TH.UnicodeSyntax = GhcTH.UnicodeSyntax
+translateTHtoGHCExt TH.ExistentialQuantification = GhcTH.ExistentialQuantification
+translateTHtoGHCExt TH.MagicHash = GhcTH.MagicHash
+translateTHtoGHCExt TH.EmptyDataDecls = GhcTH.EmptyDataDecls
+translateTHtoGHCExt TH.KindSignatures = GhcTH.KindSignatures
+translateTHtoGHCExt TH.RoleAnnotations = GhcTH.RoleAnnotations
+translateTHtoGHCExt TH.ParallelListComp = GhcTH.ParallelListComp
+translateTHtoGHCExt TH.TransformListComp = GhcTH.TransformListComp
+translateTHtoGHCExt TH.MonadComprehensions = GhcTH.MonadComprehensions
+translateTHtoGHCExt TH.GeneralizedNewtypeDeriving = GhcTH.GeneralizedNewtypeDeriving
+translateTHtoGHCExt TH.RecursiveDo = GhcTH.RecursiveDo
+translateTHtoGHCExt TH.PostfixOperators = GhcTH.PostfixOperators
+translateTHtoGHCExt TH.TupleSections = GhcTH.TupleSections
+translateTHtoGHCExt TH.PatternGuards = GhcTH.PatternGuards
+translateTHtoGHCExt TH.LiberalTypeSynonyms = GhcTH.LiberalTypeSynonyms
+translateTHtoGHCExt TH.RankNTypes = GhcTH.RankNTypes
+translateTHtoGHCExt TH.ImpredicativeTypes = GhcTH.ImpredicativeTypes
+translateTHtoGHCExt TH.TypeOperators = GhcTH.TypeOperators
+translateTHtoGHCExt TH.ExplicitNamespaces = GhcTH.ExplicitNamespaces
+translateTHtoGHCExt TH.PackageImports = GhcTH.PackageImports
+translateTHtoGHCExt TH.ExplicitForAll = GhcTH.ExplicitForAll
+translateTHtoGHCExt TH.AlternativeLayoutRule = GhcTH.AlternativeLayoutRule
+translateTHtoGHCExt TH.AlternativeLayoutRuleTransitional = GhcTH.AlternativeLayoutRuleTransitional
+translateTHtoGHCExt TH.DatatypeContexts = GhcTH.DatatypeContexts
+translateTHtoGHCExt TH.NondecreasingIndentation = GhcTH.NondecreasingIndentation
+translateTHtoGHCExt TH.RelaxedLayout = GhcTH.RelaxedLayout
+translateTHtoGHCExt TH.TraditionalRecordSyntax = GhcTH.TraditionalRecordSyntax
+translateTHtoGHCExt TH.LambdaCase = GhcTH.LambdaCase
+translateTHtoGHCExt TH.MultiWayIf = GhcTH.MultiWayIf
+translateTHtoGHCExt TH.BinaryLiterals = GhcTH.BinaryLiterals
+translateTHtoGHCExt TH.NegativeLiterals = GhcTH.NegativeLiterals
+translateTHtoGHCExt TH.HexFloatLiterals = GhcTH.HexFloatLiterals
+translateTHtoGHCExt TH.DuplicateRecordFields = GhcTH.DuplicateRecordFields
+translateTHtoGHCExt TH.OverloadedLabels = GhcTH.OverloadedLabels
+translateTHtoGHCExt TH.EmptyCase = GhcTH.EmptyCase
+translateTHtoGHCExt TH.PatternSynonyms = GhcTH.PatternSynonyms
+translateTHtoGHCExt TH.PartialTypeSignatures = GhcTH.PartialTypeSignatures
+translateTHtoGHCExt TH.NamedWildCards = GhcTH.NamedWildCards
+translateTHtoGHCExt TH.StaticPointers = GhcTH.StaticPointers
+translateTHtoGHCExt TH.TypeApplications = GhcTH.TypeApplications
+translateTHtoGHCExt TH.Strict = GhcTH.Strict
+translateTHtoGHCExt TH.StrictData = GhcTH.StrictData
+#if ! MIN_VERSION_ghc(9,2,0)
+translateTHtoGHCExt TH.MonadFailDesugaring = GhcTH.MonadFailDesugaring
+#endif
+translateTHtoGHCExt TH.EmptyDataDeriving = GhcTH.EmptyDataDeriving
+translateTHtoGHCExt TH.NumericUnderscores = GhcTH.NumericUnderscores
+translateTHtoGHCExt TH.QuantifiedConstraints = GhcTH.QuantifiedConstraints
+translateTHtoGHCExt TH.StarIsType = GhcTH.StarIsType
+
+#if MIN_VERSION_ghc(8,10,0)
+translateTHtoGHCExt TH.CUSKs = GhcTH.CUSKs
+translateTHtoGHCExt TH.ImportQualifiedPost = GhcTH.ImportQualifiedPost
+translateTHtoGHCExt TH.UnliftedNewtypes = GhcTH.UnliftedNewtypes
+translateTHtoGHCExt TH.StandaloneKindSignatures = GhcTH.StandaloneKindSignatures
+#endif
+#if MIN_VERSION_ghc(9,0,0)
+translateTHtoGHCExt TH.QualifiedDo = GhcTH.QualifiedDo
+translateTHtoGHCExt TH.LinearTypes = GhcTH.LinearTypes
+translateTHtoGHCExt TH.LexicalNegation = GhcTH.LexicalNegation
+#endif
+#if MIN_VERSION_ghc(9,2,0)
+translateTHtoGHCExt TH.UnliftedDatatypes = GhcTH.UnliftedDatatypes
+translateTHtoGHCExt TH.FieldSelectors = GhcTH.FieldSelectors
+translateTHtoGHCExt TH.OverloadedRecordDot = GhcTH.OverloadedRecordDot
+translateTHtoGHCExt TH.OverloadedRecordUpdate = GhcTH.OverloadedRecordUpdate
+#endif
diff --git a/src/PyF/Internal/Parser.hs b/src/PyF/Internal/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/PyF/Internal/Parser.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ViewPatterns #-}
+
+-- | This module is here to parse Haskell expression using the GHC Api
+module PyF.Internal.Parser (parseExpression) where
+
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Parser.Lexer (ParseResult (..), PState (..))
+#elif MIN_VERSION_ghc(8,10,0)
+import Lexer (ParseResult (..), PState (..))
+#else
+import Lexer (ParseResult (..))
+#endif
+
+#if MIN_VERSION_ghc(9,2,0)
+import qualified GHC.Parser.Errors.Ppr as ParserErrorPpr
+#endif
+
+#if MIN_VERSION_ghc(9,0,0)
+import qualified GHC.Types.SrcLoc as SrcLoc
+#else
+import qualified SrcLoc
+#endif
+
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Driver.Session (DynFlags)
+#else
+import DynFlags (DynFlags)
+#endif
+
+#if MIN_VERSION_ghc(8,10,0)
+import GHC.Hs.Expr as Expr
+import GHC.Hs.Extension as Ext
+#else
+import HsExpr as Expr
+import HsExtension as Ext
+import Outputable (showSDoc)
+#endif
+
+import qualified PyF.Internal.ParserEx as ParseExp
+
+parseExpression :: String -> DynFlags -> Either (Int, Int, String) (HsExpr GhcPs)
+parseExpression s dynFlags =
+  case ParseExp.parseExpression s dynFlags of
+    POk _ locatedExpr ->
+      let expr = SrcLoc.unLoc locatedExpr
+       in Right
+            expr
+
+#if MIN_VERSION_ghc(9,2,0)
+    -- TODO messages?
+    PFailed PState{loc=SrcLoc.psRealLoc -> srcLoc, errors=errorMessages} ->
+#elif MIN_VERSION_ghc(9,0,0)
+    PFailed PState{loc=SrcLoc.psRealLoc -> srcLoc, messages=msgs} ->
+#elif MIN_VERSION_ghc(8,10,0)
+    PFailed PState{loc=srcLoc, messages=msgs} ->
+#else
+    -- TODO: check for pattern failure
+    PFailed _ (SrcLoc.srcSpanEnd -> SrcLoc.RealSrcLoc srcLoc) doc ->
+#endif
+#if MIN_VERSION_ghc(9,2,0)
+            let
+                psErrToString e = show $ ParserErrorPpr.pprError e
+                err = concatMap psErrToString errorMessages
+                -- err = concatMap show errorMessages
+                line = SrcLoc.srcLocLine srcLoc
+                col = SrcLoc.srcLocCol srcLoc
+            in Left (line, col, err)
+#elif MIN_VERSION_ghc(8,10,0)
+            let -- TODO: do not ignore "warnMessages"
+                -- I have no idea what they can be
+                (_warnMessages, errorMessages) = msgs dynFlags
+                err = concatMap show errorMessages
+                line = SrcLoc.srcLocLine srcLoc
+                col = SrcLoc.srcLocCol srcLoc
+            in Left (line, col, err)
+#else
+            let err = showSDoc dynFlags doc
+                line = SrcLoc.srcLocLine srcLoc
+                col = SrcLoc.srcLocCol srcLoc
+            in Left (line, col, err)
+#endif
diff --git a/src/PyF/Internal/ParserEx.hs b/src/PyF/Internal/ParserEx.hs
new file mode 100644
--- /dev/null
+++ b/src/PyF/Internal/ParserEx.hs
@@ -0,0 +1,201 @@
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE TupleSections #-}
+{-# OPTIONS_GHC -Wno-missing-fields -Wno-name-shadowing -Wno-unused-imports #-}
+{-# LANGUAGE CPP #-}
+module PyF.Internal.ParserEx (fakeSettings, fakeLlvmConfig, parseExpression)
+where
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Settings.Config
+import GHC.Driver.Session
+import GHC.Utils.Fingerprint
+import GHC.Platform
+import GHC.Settings
+#elif MIN_VERSION_ghc(8, 10, 0)
+import Config
+import DynFlags
+import Fingerprint
+import GHC.Platform
+import ToolSettings
+#else
+import Config
+import DynFlags
+import Fingerprint
+import Platform
+#endif
+
+#if MIN_VERSION_ghc(8,10,0)
+import GHC.Hs
+#else
+import HsSyn
+#endif
+
+#if MIN_VERSION_ghc(9, 2, 0)
+import GHC.Driver.Config
+#endif
+
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Parser.PostProcess
+import GHC.Driver.Session
+import GHC.Data.StringBuffer
+import GHC.Parser.Lexer
+import qualified GHC.Parser.Lexer as Lexer
+import qualified GHC.Parser as Parser
+import GHC.Data.FastString
+import GHC.Types.SrcLoc
+import GHC.Driver.Backpack.Syntax
+import GHC.Unit.Info
+import GHC.Types.Name.Reader
+#else
+import StringBuffer
+import Lexer
+import qualified Parser
+import FastString
+import SrcLoc
+import RdrName
+#endif
+
+#if MIN_VERSION_ghc(9, 0, 0)
+#else
+import RdrHsSyn
+#endif
+
+import Data.Data hiding (Fixity)
+
+#if MIN_VERSION_ghc(9,0,0)
+import GHC.Hs
+
+#if MIN_VERSION_ghc(9, 2, 0)
+import GHC.Types.Fixity
+import GHC.Types.SourceText
+#else
+import GHC.Types.Basic
+#endif
+
+import GHC.Types.Name.Reader
+import GHC.Types.Name
+import GHC.Types.SrcLoc
+#elif MIN_VERSION_ghc(8, 10, 0)
+import BasicTypes
+import OccName
+#else
+import BasicTypes
+import OccName
+#endif
+
+import Data.Maybe
+
+fakeSettings :: Settings
+fakeSettings = Settings
+#if MIN_VERSION_ghc(9, 2, 0)
+  { sGhcNameVersion=ghcNameVersion
+  , sFileSettings=fileSettings
+  , sTargetPlatform=platform
+  , sPlatformMisc=platformMisc
+  , sToolSettings=toolSettings
+  }
+#elif MIN_VERSION_ghc(8, 10, 0)
+  { sGhcNameVersion=ghcNameVersion
+  , sFileSettings=fileSettings
+  , sTargetPlatform=platform
+  , sPlatformMisc=platformMisc
+  , sPlatformConstants=platformConstants
+  , sToolSettings=toolSettings
+  }
+#else
+  { sTargetPlatform=platform
+  , sPlatformConstants=platformConstants
+  , sProjectVersion=cProjectVersion
+  , sProgramName="ghc"
+  , sOpt_P_fingerprint=fingerprint0
+  }
+#endif
+  where
+#if MIN_VERSION_ghc(8, 10, 0)
+    toolSettings = ToolSettings {
+      toolSettings_opt_P_fingerprint=fingerprint0
+      }
+    fileSettings = FileSettings {}
+    platformMisc = PlatformMisc {}
+    ghcNameVersion =
+      GhcNameVersion{ghcNameVersion_programName="ghc"
+                    ,ghcNameVersion_projectVersion=cProjectVersion
+                    }
+#endif
+    platform =
+      Platform{
+#if MIN_VERSION_ghc(9, 0, 0)
+    -- It doesn't matter what values we write here as these fields are
+    -- not referenced for our purposes. However the fields are strict
+    -- so we must say something.
+        platformByteOrder=LittleEndian
+      , platformHasGnuNonexecStack=True
+      , platformHasIdentDirective=False
+      , platformHasSubsectionsViaSymbols=False
+      , platformIsCrossCompiling=False
+      , platformLeadingUnderscore=False
+      , platformTablesNextToCode=False
+#if MIN_VERSION_ghc(9, 2, 0)
+      , platform_constants=platformConstants
+#endif
+      ,
+#endif
+
+#if MIN_VERSION_ghc(9, 2, 0)
+        platformWordSize=PW8
+      , platformArchOS=ArchOS {archOS_arch=ArchUnknown, archOS_OS=OSUnknown}
+#elif MIN_VERSION_ghc(8, 10, 0)
+        platformWordSize=PW8
+      , platformMini=PlatformMini {platformMini_arch=ArchUnknown, platformMini_os=OSUnknown}
+#else
+        platformWordSize=8
+      , platformOS=OSUnknown
+#endif
+      , platformUnregisterised=True
+      }
+#if MIN_VERSION_ghc(9, 2, 0)
+    platformConstants = Nothing
+#else
+    platformConstants =
+      PlatformConstants{pc_DYNAMIC_BY_DEFAULT=False,pc_WORD_SIZE=8}
+#endif
+
+#if MIN_VERSION_ghc(8, 10, 0)
+fakeLlvmConfig :: LlvmConfig
+fakeLlvmConfig = LlvmConfig [] []
+#else
+fakeLlvmConfig :: (LlvmTargets, LlvmPasses)
+fakeLlvmConfig = ([], [])
+#endif
+
+-- From Language.Haskell.GhclibParserEx.GHC.Parser
+
+parse :: P a -> String -> DynFlags -> ParseResult a
+parse p str flags =
+  Lexer.unP p parseState
+  where
+    location = mkRealSrcLoc (mkFastString "<string>") 1 1
+    buffer = stringToStringBuffer str
+    parseState =
+#if MIN_VERSION_ghc(9, 2, 0)
+      initParserState (initParserOpts flags) buffer location
+#else
+      mkPState flags buffer location
+#endif
+
+
+#if MIN_VERSION_ghc(9, 2, 0)
+parseExpression :: String -> DynFlags -> ParseResult (LocatedA (HsExpr GhcPs))
+parseExpression s flags =
+  case parse Parser.parseExpression s flags of
+    POk s e -> unP (runPV (unECP e)) s
+    PFailed ps -> PFailed ps
+#elif MIN_VERSION_ghc(8, 10, 0)
+parseExpression :: String -> DynFlags -> ParseResult (Located (HsExpr GhcPs))
+parseExpression s flags =
+  case parse Parser.parseExpression s flags of
+    POk s e -> unP (runECP_P e) s
+    PFailed ps -> PFailed ps
+#else
+parseExpression = parse Parser.parseExpression
+#endif
diff --git a/src/PyF/Internal/PythonSyntax.hs b/src/PyF/Internal/PythonSyntax.hs
--- a/src/PyF/Internal/PythonSyntax.hs
+++ b/src/PyF/Internal/PythonSyntax.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE OverloadedStrings #-}
@@ -21,25 +22,21 @@
   )
 where
 
+import Control.Applicative (some)
 import Control.Monad.Reader
 import qualified Data.Char
 import Data.Maybe (fromMaybe)
-import qualified Data.Set as Set -- For fancyFailure
-import Data.Void (Void)
-import qualified Language.Haskell.Exts.Extension as ParseExtension
-import qualified Language.Haskell.Exts.Parser as ParseExp
-import qualified Language.Haskell.Exts.SrcLoc as SrcLoc
-import qualified Language.Haskell.Meta.Syntax.Translate as SyntaxTranslate
-import Language.Haskell.TH.Syntax
+import qualified Language.Haskell.TH.LanguageExtensions as ParseExtension
+import Language.Haskell.TH.Syntax (Exp)
 import PyF.Formatters
-import Text.Megaparsec
-import Text.Megaparsec.Char
-import qualified Text.Megaparsec.Char.Lexer as L
+import PyF.Internal.Meta
+import qualified PyF.Internal.Parser as ParseExp
+import Text.Parsec
 
-type Parser t = ParsecT Void String (Reader ParsingContext) t
+type Parser t = ParsecT String () (Reader ParsingContext) t
 
 data ParsingContext = ParsingContext
-  { delimiters :: (Char, Char),
+  { delimiters :: Maybe (Char, Char),
     enabledExtensions :: [ParseExtension.Extension]
   }
   deriving (Show)
@@ -86,26 +83,39 @@
 --                       (FixedF (Precision 2) NormalForm Minus)
 --                        Nothing))]
 parseGenericFormatString :: Parser [Item]
-parseGenericFormatString =
-  many (rawString <|> escapedParenthesis <|> replacementField) <* eof
+parseGenericFormatString = do
+  delimitersM <- asks delimiters
 
-rawString :: Parser Item
-rawString = do
-  (openingChar, closingChar) <- asks delimiters
-  chars <- some (noneOf [openingChar, closingChar])
+  case delimitersM of
+    Nothing -> many (rawString Nothing)
+    Just _ -> many (rawString delimitersM <|> escapedParenthesis <|> replacementField) <* eof
+
+rawString :: Maybe (Char, Char) -> Parser Item
+rawString delimsM = do
+  let delims = case delimsM of
+                 Nothing -> []
+                 Just (openingChar, closingChar) -> [openingChar, closingChar]
+
+  -- lookahead
+  let p = some (noneOf delims)
+  chars <- lookAhead p
+
   case escapeChars chars of
     Left remaining -> do
-      offset <- getOffset
-      setOffset (offset - length remaining)
-      fancyFailure (Set.singleton (ErrorFail "lexical error in literal section"))
-    Right escaped -> return (Raw escaped)
+      -- Consume up to the error location
+      void $ count (length chars - length remaining) anyChar
+      fail "Lexical error in literal section"
+    Right escaped -> do
+      -- Consumne everything
+      void p
+      return (Raw escaped)
 
 escapedParenthesis :: Parser Item
 escapedParenthesis = do
-  (openingChar, closingChar) <- asks delimiters
+  Just (openingChar, closingChar) <- asks delimiters
   Raw <$> (parseRaw openingChar <|> parseRaw closingChar)
   where
-    parseRaw c = [c] <$ string (replicate 2 c)
+    parseRaw c = [c] <$ try (string (replicate 2 c))
 
 -- | Replace escape chars with their value. Results in a Left with the
 -- remainder of the string on encountering a lexical error (such as a bad escape
@@ -125,16 +135,16 @@
 replacementField :: Parser Item
 replacementField = do
   exts <- asks enabledExtensions
-  (charOpening, charClosing) <- asks delimiters
+  Just (charOpening, charClosing) <- asks delimiters
   _ <- char charOpening
-  expr <- evalExpr exts (many (noneOf (charClosing : ":" :: String)))
-  fmt <- optional $ do
+  expr <- evalExpr exts (some (noneOf (charClosing : ":" :: String)) <?> "an haskell expression")
+  fmt <- optionMaybe $ do
     _ <- char ':'
     formatSpec
   _ <- char charClosing
   pure (Replacement expr fmt)
 
--- | Default formating mode, no padding, default precision, no grouping, no sign handling
+-- | Default formatting mode, no padding, default precision, no grouping, no sign handling
 pattern DefaultFormatMode :: FormatMode
 pattern DefaultFormatMode = FormatMode PaddingDefault (DefaultF PrecisionDefault Minus) Nothing
 
@@ -177,7 +187,7 @@
 data TypeFlag = Flagb | Flagc | Flagd | Flage | FlagE | Flagf | FlagF | Flagg | FlagG | Flagn | Flago | Flags | Flagx | FlagX | FlagPercent
   deriving (Show)
 
--- | All formating type
+-- | All formatting type
 data TypeFormat
   = -- | Default, depends on the infered type of the expression
     DefaultF Precision SignMode
@@ -215,27 +225,25 @@
 data AlternateForm = AlternateForm | NormalForm
   deriving (Show)
 
-lastCharFailed :: String -> Parser t
-lastCharFailed err = do
-  offset <- getOffset
-  setOffset (offset - 1)
-  fancyFailure (Set.singleton (ErrorFail err))
-
 evalExpr :: [ParseExtension.Extension] -> Parser String -> Parser Exp
 evalExpr exts exprParser = do
-  offset <- getOffset
-  s <- exprParser
-  -- Setup the parser using the provided list of extensions
-  -- Which are detected by template haskell at splice position
-  let parseMode = ParseExp.defaultParseMode {ParseExp.extensions = exts}
-  case SyntaxTranslate.toExp <$> ParseExp.parseExpWithMode parseMode s of
-    ParseExp.ParseOk expr -> pure expr
-    ParseExp.ParseFailed (SrcLoc.SrcLoc _name' line col) err -> do
-      let linesBefore = take (line - 1) (lines s)
-          currentOffset = length (unlines linesBefore) + col - 1
-      setOffset (offset + currentOffset)
-      fancyFailure (Set.singleton (ErrorFail err))
+  s <- lookAhead exprParser
+  -- Setup the dyn flags using the provided list of extensions
+  let exts' = fmap translateTHtoGHCExt exts
+  let dynFlags = baseDynFlags exts'
+  case ParseExp.parseExpression s dynFlags of
+    Right expr -> do
+      -- Consumne the expression
+      void exprParser
+      pure (toExp dynFlags expr)
+    Left (lineError, colError, err) -> do
+      -- Skip lines
+      replicateM_ (lineError - 1) (manyTill anyChar newline)
+      -- Skip columns
+      void $ count (colError - 2) anyChar
 
+      fail $ err <> " in haskell expression"
+
 overrideAlignmentIfZero :: Bool -> Maybe (Maybe Char, AnyAlign) -> Maybe (Maybe Char, AnyAlign)
 overrideAlignmentIfZero True Nothing = Just (Just '0', AnyAlign AlignInside)
 overrideAlignmentIfZero True (Just (Nothing, al)) = Just (Just '0', al)
@@ -243,35 +251,44 @@
 
 formatSpec :: Parser FormatMode
 formatSpec = do
-  al' <- optional alignment
-  s <- optional sign
+  al' <- optionMaybe alignment
+  s <- optionMaybe sign
   alternateForm <- option NormalForm (AlternateForm <$ char '#')
   hasZero <- option False (True <$ char '0')
   let al = overrideAlignmentIfZero hasZero al'
-  w <- optional width
-  grouping <- optional groupingOption
+  w <- optionMaybe width
+  grouping <- optionMaybe groupingOption
   prec <- option PrecisionDefault parsePrecision
-  t <- optional type_
+
+  t <- optionMaybe $ lookAhead type_
   let padding = case w of
         Just p -> Padding p al
         Nothing -> PaddingDefault
   case t of
     Nothing -> pure (FormatMode padding (DefaultF prec (fromMaybe Minus s)) grouping)
     Just flag -> case evalFlag flag padding grouping prec alternateForm s of
-      Right fmt -> pure (FormatMode padding fmt grouping)
+      Right fmt -> do
+        -- Consumne the parser
+        void type_
+        pure (FormatMode padding fmt grouping)
       Left typeError ->
-        lastCharFailed typeError
+        fail typeError
 
 parsePrecision :: Parser Precision
 parsePrecision = do
   exts <- asks enabledExtensions
-  (charOpening, charClosing) <- asks delimiters
+  Just (charOpening, charClosing) <- asks delimiters
   _ <- char '.'
   choice
     [ Precision . Value <$> precision,
-      char charOpening *> (Precision . HaskellExpr <$> evalExpr exts (manyTill anySingle (char charClosing)))
+      char charOpening *> (Precision . HaskellExpr <$> evalExpr exts (someTill (satisfy (/= charClosing)) (char charClosing) <?> "an haskell expression"))
     ]
 
+-- | Similar to 'manyTill' but always parse one element.
+-- Be careful, @someTill p e@ may parse @e@ as first element if @e@ is a subset of @p@.
+someTill :: Stream s m t => ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
+someTill p e = (:) <$> p <*> manyTill p e
+
 evalFlag :: TypeFlag -> Padding -> Maybe Char -> Precision -> AlternateForm -> Maybe SignMode -> Either String TypeFormat
 evalFlag Flagb _pad _grouping prec alt s = failIfPrec prec (BinaryF alt (defSign s))
 evalFlag Flagc _pad _grouping prec alt s = failIfS s =<< failIfPrec prec =<< failIfAlt alt CharacterF
@@ -338,7 +355,7 @@
     ]
 
 fill :: Parser Char
-fill = anySingle
+fill = anyChar
 
 align :: Parser AnyAlign
 align =
@@ -361,7 +378,7 @@
 width = integer
 
 integer :: Parser Integer
-integer = L.decimal -- incomplete: see: https://docs.python.org/3/reference/lexical_analysis.html#grammar-token-integer
+integer = read <$> some (oneOf ['0' .. '9']) -- incomplete: see: https://docs.python.org/3/reference/lexical_analysis.html#grammar-token-integer
 
 groupingOption :: Parser Char
 groupingOption = oneOf ("_," :: String)
diff --git a/src/PyF/Internal/QQ.hs b/src/PyF/Internal/QQ.hs
--- a/src/PyF/Internal/QQ.hs
+++ b/src/PyF/Internal/QQ.hs
@@ -1,8 +1,10 @@
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DisambiguateRecordFields #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell #-}
@@ -10,92 +12,158 @@
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE ViewPatterns #-}
 
 -- | This module uses the python mini language detailed in
 -- 'PyF.Internal.PythonSyntax' to build an template haskell expression
 -- representing a formatted string.
 module PyF.Internal.QQ
   ( toExp,
-    toExpPython,
+    Config (..),
+    wrapFromString,
+    expQQ,
   )
 where
 
 import Control.Monad.Reader
+import Data.Kind
 import Data.Maybe (fromMaybe)
-import qualified Data.Maybe
 import Data.Proxy
 import Data.String (fromString)
 import GHC.TypeLits
-import Language.Haskell.TH
+import Language.Haskell.TH hiding (Type)
+import Language.Haskell.TH.Quote
 import PyF.Class
 import PyF.Formatters (AnyAlign (..))
 import qualified PyF.Formatters as Formatters
-import PyF.Internal.Extensions
 import PyF.Internal.PythonSyntax
-import Text.Megaparsec
+import Text.Parsec
+import Text.Parsec.Error (errorMessages, messageString, setErrorPos, showErrorMessages)
+import Text.ParserCombinators.Parsec.Error (Message (..))
 
--- Be Careful: empty format string
+-- | Configuration for the quasiquoter
+data Config = Config
+  { -- | What are the delimiters for interpolation. 'Nothing' means no
+    -- interpolation / formatting.
+    delimiters :: Maybe (Char, Char),
+    -- | Post processing. The input 'Exp' represents a 'String'. Common use
+    -- case includes using 'wrapFromString' to add 'fromString' in the context
+    -- of 'OverloadedStrings'.
+    postProcess :: Q Exp -> Q Exp
+  }
 
+-- | Build a quasiquoter for expression
+expQQ :: String -> (String -> Q Exp) -> QuasiQuoter
+expQQ fName qExp =
+  QuasiQuoter
+    { quoteExp = qExp,
+      quotePat = err "pattern",
+      quoteType = err "type",
+      quoteDec = err "declaration"
+    }
+  where
+    err :: String -> t
+    err name = error (fName ++ ": This QuasiQuoter can not be used as a " ++ name ++ "!")
+
+-- | If 'OverloadedStrings' is enabled, from the input expression with
+-- 'fromString'.
+wrapFromString :: ExpQ -> Q Exp
+wrapFromString e = do
+  exts <- extsEnabled
+  if OverloadedStrings `elem` exts
+    then [|fromString $(e)|]
+    else e
+
 -- | Parse a string and return a formatter for it
-toExp :: (Char, Char) -> String -> Q Exp
-toExp expressionDelimiters s = do
+toExp :: Config -> String -> Q Exp
+toExp Config {delimiters = expressionDelimiters, postProcess} s = do
   filename <- loc_filename <$> location
-  thExts <- extsEnabled
-  let exts = Data.Maybe.mapMaybe thExtToMetaExt thExts
-  let wrapFromString e =
-        if OverloadedStrings `elem` thExts
-          then [|fromString $(e)|]
-          else e
+  exts <- extsEnabled
   let context = ParsingContext expressionDelimiters exts
-  case runReader (runParserT parseGenericFormatString filename s) context of
+  case runReader (runParserT parseGenericFormatString () filename s) context of
     Left err -> do
       err' <- overrideErrorForFile filename err
-      fail (errorBundlePretty err')
-    Right items -> wrapFromString (goFormat items)
+      fail =<< prettyError filename s err'
+    Right items -> postProcess (goFormat items)
 
--- Megaparsec displays error relative to what they parsed
+-- | Display a pretty version of an error, with caret and file context.
+prettyError ::
+  -- | Filename of the file which contains the error
+  FilePath ->
+  -- | Content of the file
+  String ->
+  -- | Parse error from parsec
+  ParseError ->
+  Q String
+prettyError filename s err = do
+  let sourceLoc = errorPos err
+      line = sourceLine sourceLoc
+      column = sourceColumn sourceLoc
+      name = sourceName sourceLoc
+      carretOffset = column - 1
+      carret = replicate carretOffset ' ' <> "^"
+      colIndicator = show line <> " | "
+      colPrefix = replicate (length (show line)) ' ' <> " |"
+
+  code <- case filename of
+    -- If that's an interectavi file, we don't know much, so just dump the string.
+    "<interactive>" -> pure s
+    _ -> do
+      content <- runIO (readFile filename)
+      pure $ lines content !! (line - 1)
+
+  pure $
+    unlines $
+      [ name <> ":" <> show line <> ":" <> show column <> ":",
+        colPrefix,
+        colIndicator <> code,
+        colPrefix <> " " <> carret
+      ]
+        ++ formatErrorMessages err
+
+-- | Format a bunch of error
+formatErrorMessages :: ParseError -> [String]
+formatErrorMessages err
+  -- If there is an explicit error message from parsec, use only that
+  | not $ null messages = map messageString messages
+  -- Otherwise, uses parsec formatting
+  | otherwise = [showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input" (errorMessages err)]
+  where
+    (_sysUnExpect, msgs1) = span (SysUnExpect "" ==) (errorMessages err)
+    (_unExpect, msgs2) = span (UnExpect "" ==) msgs1
+    (_expect, messages) = span (Expect "" ==) msgs2
+
+-- Parsec displays error relative to what they parsed
 -- However the formatting string is part of a more complex file and we
 -- want error reporting relative to that file
-overrideErrorForFile :: FilePath -> ParseErrorBundle String e -> Q (ParseErrorBundle String e)
+overrideErrorForFile :: FilePath -> ParseError -> Q ParseError
 -- We have no may to recover interactive content
--- So we won't do better than displaying the megaparsec
+-- So we won't do better than displaying the Parsec
 -- error relative to the quasi quote content
 overrideErrorForFile "<interactive>" err = pure err
 -- We know the content of the file here
-overrideErrorForFile filename err = do
+overrideErrorForFile _ err = do
   (line, col) <- loc_start <$> location
-  fileContent <- runIO (readFile filename)
-  let -- drop the first lines of the file up to the line containing the quasiquote
-      -- then, split in what is before the QQ and what is after.
-      -- e.g.  blablabla [fmt|hello|] will split to
-      -- "blablabla [fmt|" and "hello|]"
-      (prefix, postfix) = splitAt (col - 1) $ unlines $ drop (line - 1) (lines fileContent)
-  pure $
-    err
-      { bundlePosState =
-          (bundlePosState err)
-            { pstateInput = postfix,
-              pstateSourcePos = SourcePos filename (mkPos line) (mkPos col),
-              pstateOffset = 0,
-              pstateLinePrefix = prefix
-            }
-      }
-
-toExpPython :: String -> Q Exp
-toExpPython = toExp ('{', '}')
+  let sourcePos = errorPos err
+      sourcePos'
+        | sourceLine sourcePos == 1 = incSourceColumn (incSourceLine sourcePos (line - 1)) (col - 1)
+        | otherwise = setSourceColumn (incSourceLine sourcePos (line - 1)) (sourceColumn sourcePos)
+  pure $ setErrorPos sourcePos' err
 
 {-
 Note: Empty String Lifting
 
-Empty string are lifter as [] instead of "", so I'm using LitE (String L) instead
+Empty string are lifted as [] instead of "", so I'm using LitE (String L) instead
 -}
 
 goFormat :: [Item] -> Q Exp
+-- We special case on empty list in order to generate an empty string
 goFormat [] = pure $ LitE (StringL "") -- see [Empty String Lifting]
-goFormat items = foldl1 fofo <$> mapM toFormat items
+goFormat items = foldl1 sappendQ <$> mapM toFormat items
 
-fofo :: Exp -> Exp -> Exp
-fofo s0 s1 = InfixE (Just s0) (VarE '(<>)) (Just s1)
+-- | call `<>` between two 'Exp'
+sappendQ :: Exp -> Exp -> Exp
+sappendQ s0 s1 = InfixE (Just s0) (VarE '(<>)) (Just s1)
 
 -- Real formatting is here
 
@@ -175,13 +243,13 @@
   PaddingK i Nothing -> Padding i Nothing
   PaddingK i (Just (c, a)) -> Padding i (Just (c, AnyAlign a))
 
-formatAnyIntegral :: (Show i, Integral i) => Formatters.Format t t' 'Formatters.Integral -> Formatters.SignMode -> Maybe (Integer, AnyAlign, Char) -> Maybe (Int, Char) -> i -> String
-formatAnyIntegral f s Nothing grouping i = Formatters.formatIntegral f s Nothing grouping i
-formatAnyIntegral f s (Just (padSize, AnyAlign alignMode, c)) grouping i = Formatters.formatIntegral f s (Just (fromIntegral padSize, alignMode, c)) grouping i
+formatAnyIntegral :: PyfFormatIntegral i => Formatters.Format t t' 'Formatters.Integral -> Formatters.SignMode -> Maybe (Integer, AnyAlign, Char) -> Maybe (Int, Char) -> i -> String
+formatAnyIntegral f s Nothing grouping i = pyfFormatIntegral f s Nothing grouping i
+formatAnyIntegral f s (Just (padSize, AnyAlign alignMode, c)) grouping i = pyfFormatIntegral f s (Just (fromIntegral padSize, alignMode, c)) grouping i
 
-formatAnyFractional :: (RealFloat i) => Formatters.Format t t' 'Formatters.Fractional -> Formatters.SignMode -> Maybe (Integer, AnyAlign, Char) -> Maybe (Int, Char) -> Maybe Int -> i -> String
-formatAnyFractional f s Nothing grouping p i = Formatters.formatFractional f s Nothing grouping p i
-formatAnyFractional f s (Just (padSize, AnyAlign alignMode, c)) grouping p i = Formatters.formatFractional f s (Just (fromIntegral padSize, alignMode, c)) grouping p i
+formatAnyFractional :: (PyfFormatFractional i) => Formatters.Format t t' 'Formatters.Fractional -> Formatters.SignMode -> Maybe (Integer, AnyAlign, Char) -> Maybe (Int, Char) -> Maybe Int -> i -> String
+formatAnyFractional f s Nothing grouping p i = pyfFormatFractional f s Nothing grouping p i
+formatAnyFractional f s (Just (padSize, AnyAlign alignMode, c)) grouping p i = pyfFormatFractional f s (Just (fromIntegral padSize, alignMode, c)) grouping p i
 
 class FormatAny i k where
   formatAny :: Formatters.SignMode -> PaddingK k -> Maybe (Int, Char) -> Maybe Int -> i -> String
@@ -189,13 +257,13 @@
 instance (FormatAny2 (PyFClassify t) t k) => FormatAny t k where
   formatAny = formatAny2 (Proxy :: Proxy (PyFClassify t))
 
-class FormatAny2 (c :: PyFCategory) (i :: *) (k :: Formatters.AlignForString) where
+class FormatAny2 (c :: PyFCategory) (i :: Type) (k :: Formatters.AlignForString) where
   formatAny2 :: Proxy c -> Formatters.SignMode -> PaddingK k -> Maybe (Int, Char) -> Maybe Int -> i -> String
 
 instance (Show t, Integral t) => FormatAny2 'PyFIntegral t k where
   formatAny2 _ s a p _precision = formatAnyIntegral Formatters.Decimal s (newPaddingUnQ (paddingKToPadding a)) p
 
-instance (RealFloat t) => FormatAny2 'PyFFractional t k where
+instance (PyfFormatFractional t) => FormatAny2 'PyFFractional t k where
   formatAny2 _ s a = formatAnyFractional Formatters.Generic s (newPaddingUnQ (paddingKToPadding a))
 
 newPaddingKForString :: PaddingK 'Formatters.AlignAll -> Maybe (Int, Formatters.AlignMode 'Formatters.AlignAll, Char)
@@ -210,7 +278,3 @@
 
 instance TypeError ('Text "String type is incompatible with inside padding (=).") => FormatAny2 'PyFString t 'Formatters.AlignNumber where
   formatAny2 = error "Unreachable"
-
-type family ToFmt t where
-  ToFmt 'PyFIntegral = 'Formatters.Integral
-  ToFmt 'PyFFractional = 'Formatters.Fractional
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -4,12 +4,25 @@
 {-# LANGUAGE DerivingStrategies #-}
 {-# LANGUAGE ExtendedDefaultRules #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 
-import Data.Text
+-- This warning is disabled because any expression with literal leads to it.
+{-# OPTIONS -Wno-type-defaults #-}
+
+import qualified Data.ByteString
+import qualified Data.ByteString.Char8
+import qualified Data.ByteString.Lazy
+import qualified Data.ByteString.Lazy.Char8
+import qualified Data.List as List
+import qualified Data.Ratio
+import qualified Data.Text
+import qualified Data.Text.Lazy
+import qualified Data.Time
 import PyF
 import SpecCustomDelimiters
 import SpecUtils
@@ -25,8 +38,8 @@
 main :: IO ()
 main = hspec spec
 
-newtype FooFloating = FooFloating Float
-  deriving newtype (Show, RealFloat, RealFrac, Floating, Fractional, Real, Enum, Num, Ord, Eq)
+newtype FooFloating t = FooFloating t
+  deriving newtype (Show, RealFloat, RealFrac, Floating, Fractional, Real, Enum, Num, Ord, Eq, PyfFormatFractional)
 
 newtype FooIntegral = FooIntegral Integer
   deriving newtype (Show, Integral, Real, Enum, Num, Ord, Eq)
@@ -36,14 +49,12 @@
 data FooDefault = FooDefault
   deriving (Show)
 
-instance PyFToString FooDefault
-
 instance PyFToString Foo where
   pyfToString Foo = "I'm a Foo"
 
 type instance PyFClassify Foo = 'PyFString
 
-type instance PyFClassify FooFloating = 'PyFFractional
+type instance PyFClassify (FooFloating t) = 'PyFFractional
 
 type instance PyFClassify FooIntegral = 'PyFIntegral
 
@@ -143,23 +154,43 @@
         it "default" $(checkExample "{1.0:10}" "       1.0")
         it "default" $(checkExample "{\"h\":10}" "h         ")
     describe "NaN" $ do
-      let nan = 0.0 / 0
-      it "nan" $(checkExample "{nan}" "nan")
-      it "nan f" $(checkExample "{nan:f}" "nan")
-      it "nan e" $(checkExample "{nan:e}" "nan")
-      it "nan g" $(checkExample "{nan:g}" "nan")
-      it "nan F" $(checkExample "{nan:F}" "NAN")
-      it "nan G" $(checkExample "{nan:G}" "NAN")
-      it "nan E" $(checkExample "{nan:E}" "NAN")
+      describe "float" $ do
+        let nan = 0.0 / 0 :: Float
+        it "nan" $(checkExample "{nan}" "nan")
+        it "nan f" $(checkExample "{nan:f}" "nan")
+        it "nan e" $(checkExample "{nan:e}" "nan")
+        it "nan g" $(checkExample "{nan:g}" "nan")
+        it "nan F" $(checkExample "{nan:F}" "NAN")
+        it "nan G" $(checkExample "{nan:G}" "NAN")
+        it "nan E" $(checkExample "{nan:E}" "NAN")
+      describe "double" $ do
+        let nan = 0.0 / 0 :: Double
+        it "nan" $(checkExample "{nan}" "nan")
+        it "nan f" $(checkExample "{nan:f}" "nan")
+        it "nan e" $(checkExample "{nan:e}" "nan")
+        it "nan g" $(checkExample "{nan:g}" "nan")
+        it "nan F" $(checkExample "{nan:F}" "NAN")
+        it "nan G" $(checkExample "{nan:G}" "NAN")
+        it "nan E" $(checkExample "{nan:E}" "NAN")
     describe "Infinite" $ do
-      let inf = 1.0 / 0
-      it "infinite" $(checkExample "{inf}" "inf")
-      it "infinite f" $(checkExample "{inf:f}" "inf")
-      it "infinite e" $(checkExample "{inf:e}" "inf")
-      it "infinite g" $(checkExample "{inf:g}" "inf")
-      it "infinite F" $(checkExample "{inf:F}" "INF")
-      it "infinite G" $(checkExample "{inf:G}" "INF")
-      it "infinite E" $(checkExample "{inf:E}" "INF")
+      describe "float" $ do
+        let inf = 1.0 / 0 :: Float
+        it "infinite" $(checkExample "{inf}" "inf")
+        it "infinite f" $(checkExample "{inf:f}" "inf")
+        it "infinite e" $(checkExample "{inf:e}" "inf")
+        it "infinite g" $(checkExample "{inf:g}" "inf")
+        it "infinite F" $(checkExample "{inf:F}" "INF")
+        it "infinite G" $(checkExample "{inf:G}" "INF")
+        it "infinite E" $(checkExample "{inf:E}" "INF")
+      describe "double" $ do
+        let inf = 1.0 / 0 :: Double
+        it "infinite" $(checkExample "{inf}" "inf")
+        it "infinite f" $(checkExample "{inf:f}" "inf")
+        it "infinite e" $(checkExample "{inf:e}" "inf")
+        it "infinite g" $(checkExample "{inf:g}" "inf")
+        it "infinite F" $(checkExample "{inf:F}" "INF")
+        it "infinite G" $(checkExample "{inf:G}" "INF")
+        it "infinite E" $(checkExample "{inf:E}" "INF")
     describe "Grouping" $ do
       it "groups int" $(checkExample "{123456789:,d}" "123,456,789")
       it "groups int with _" $(checkExample "{123456789:_d}" "123_456_789")
@@ -262,6 +293,8 @@
   describe "language extensions" $ do
     it "parses @Int" $
       [fmt|hello {show @Int 10}|] `shouldBe` "hello 10"
+    it "parses @_" $
+      [fmt|hello {show @_ 10}|] `shouldBe` "hello 10"
     it "parses BinaryLiterals" $
       [fmt|hello {0b1111}|] `shouldBe` "hello 15"
   describe "custom types" $ do
@@ -275,5 +308,170 @@
     it "works with classify" $ do
       [fmt|{Foo}|] `shouldBe` "I'm a Foo"
       [fmt|{FooIntegral 100}|] `shouldBe` "100"
-      [fmt|{FooFloating 100.123}|] `shouldBe` "100.123"
+      let fooDouble = FooFloating (100.123 :: Double) in [fmt|{fooDouble}|] `shouldBe` "100.123"
+      let fooFloat = FooFloating (100.123 :: Float) in [fmt|{fooFloat}|] `shouldBe` "100.123"
       [fmt|{FooDefault}|] `shouldBe` "FooDefault"
+
+  describe "instances" $ do
+    describe "default" $ do
+      it "bytestring" $ do
+        let x = "hello" :: Data.ByteString.ByteString in [fmt|{x}|] `shouldBe` "hello"
+      it "bytestring lazy" $ do
+        let x = "hello" :: Data.ByteString.Lazy.ByteString in [fmt|{x}|] `shouldBe` "hello"
+      it "bytestring char 8 lazy" $ do
+        let x = "hello" :: Data.ByteString.Lazy.Char8.ByteString in [fmt|{x}|] `shouldBe` "hello"
+      it "bytestring char 8" $ do
+        let x = "hello" :: Data.ByteString.Char8.ByteString in [fmt|{x}|] `shouldBe` "hello"
+      it "text" $ do
+        let x = "hello" :: Data.Text.Text in [fmt|{x}|] `shouldBe` "hello"
+      it "lazy text" $ do
+        let x = "hello" :: Data.Text.Lazy.Text in [fmt|{x}|] `shouldBe` "hello"
+      it "DiffTime" $ do
+        let x = 3 :: Data.Time.DiffTime in [fmt|{x}|] `shouldBe` "3.0"
+      it "NominalDiffTime" $ do
+        let x = 3 :: Data.Time.NominalDiffTime in [fmt|{x}|] `shouldBe` "3.0"
+      it "Ratio" $ do
+        let x = 3 :: Data.Ratio.Ratio Int in [fmt|{x}|] `shouldBe` "3.0"
+      it "Int" $ do
+        let x = 3 :: Int in [fmt|{x}|] `shouldBe` "3"
+      it "Char" $ do
+        let x = 'a' in [fmt|{x}|] `shouldBe` "a"
+    describe "forced" $ do
+      it "bytestring" $ do
+        let x = "hello" :: Data.ByteString.ByteString in [fmt|{x:s}|] `shouldBe` "hello"
+      it "bytestring lazy" $ do
+        let x = "hello" :: Data.ByteString.Lazy.ByteString in [fmt|{x:s}|] `shouldBe` "hello"
+      it "bytestring char 8 lazy" $ do
+        let x = "hello" :: Data.ByteString.Lazy.Char8.ByteString in [fmt|{x:s}|] `shouldBe` "hello"
+      it "bytestring char 8" $ do
+        let x = "hello" :: Data.ByteString.Char8.ByteString in [fmt|{x:s}|] `shouldBe` "hello"
+      it "text" $ do
+        let x = "hello" :: Data.Text.Text in [fmt|{x:s}|] `shouldBe` "hello"
+      it "lazy text" $ do
+        let x = "hello" :: Data.Text.Text in [fmt|{x:s}|] `shouldBe` "hello"
+      it "DiffTime" $ do
+        let x = 3 :: Data.Time.DiffTime in [fmt|{x:.2f}|] `shouldBe` "3.00"
+      it "NominalDiffTime" $ do
+        let x = 3 :: Data.Time.NominalDiffTime in [fmt|{x:.2f}|] `shouldBe` "3.00"
+      it "Ratio" $ do
+        let x = 3 :: Data.Ratio.Ratio Int in [fmt|{x:.2f}|] `shouldBe` "3.00"
+      describe "int" $ do
+        it "decimal" $ do
+          let x = 3 :: Int in [fmt|{x:d}|] `shouldBe` "3"
+        it "fractional" $ do
+          let x = 3 :: Int in [fmt|{x:.2f}|] `shouldBe` "3.00"
+      describe "Char" $ do
+        it "string" $ do
+          let x = 'a' in [fmt|{x:s}|] `shouldBe` "a"
+        it "int" $ do
+          let x = 'a' in [fmt|{x:d}|] `shouldBe` "97"
+
+  describe "syntax" $ do
+    describe "name" $ do
+      it "qualified" $ do
+        [fmt|{List.sort [1,2,3]:s}|] `shouldBe` "[1,2,3]"
+    describe "literals" $ do
+      it "list" $ do
+        [fmt|{[1,2,3]:s}|] `shouldBe` "[1,2,3]"
+      describe "arith seq" $ do
+        it "from" $ [fmt|{take 3 [1..]:s}|] `shouldBe` "[1,2,3]"
+        it "fromthen" $ [fmt|{take 3 [1, 3..]:s}|] `shouldBe` "[1,3,5]"
+        it "fromto" $ [fmt|{[1..3]:s}|] `shouldBe` "[1,2,3]"
+        it "fromthento" $ [fmt|{[1,3..5]:s}|] `shouldBe` "[1,3,5]"
+
+    it "lambda" $ do
+      [fmt|{(\x -> 2 * x) 10}|] `shouldBe` "20"
+    it "section" $ do
+      [fmt|{(100/) 10:.0f}|] `shouldBe` "10"
+      [fmt|{(/2) 10:.0f}|] `shouldBe` "5"
+    it "tuples" $ do
+      [fmt|{fst (1, 2)}|] `shouldBe` "1"
+
+-- Disabled because it does not build with GHC < 8.10
+-- xit "tuples section" $ do
+-- [fmt|{fst ((,2) 1)}|] `shouldBe` "1"
+
+  describe "multiline trimming" $ do
+    it "works with overloading" $ do
+      [fmtTrim|hello|] `shouldBe` ("hello" :: Data.Text.Text)
+    it "overloading in overloading" $ do
+      let foo = [fmtTrim|hello {10}|]
+      -- foo `shouldBe` "hello 10"
+      [fmtTrim|biz {foo} goodbye|] `shouldBe` ("biz hello 10 goodbye" :: Data.Text.Text)
+    it "do not fail on trailing ignore line return" $ do
+      [fmtTrim|
+      hello\
+
+      |] `shouldBe` "hello\n"
+    it "do not take too much indent in account" $ do
+      [fmtTrim|
+      hello
+      - a
+        - b
+      - c
+      |] `shouldBe` "hello\n- a\n  - b\n- c\n"
+    it "works with empty lines" $ do
+      [fmtTrim|
+      hello
+
+
+      |] `shouldBe` "hello\n\n\n"
+    it "works with empty last lines" $ do
+      [fmtTrim|
+      hello
+
+
+|] `shouldBe` "hello\n\n\n"
+    it "works" $ do
+      [fmtTrim|
+                  hello
+                  - a
+                   - b
+
+                  - c
+      |]
+        `shouldBe` "hello\n- a\n - b\n\n- c\n"
+    it "works with replacement" $ do
+      [fmtTrim|
+                  hello
+                  - a
+                   - {pi:.2}|]
+        `shouldBe` "hello\n- a\n - 3.14"
+    it "Do not ignore not indented lines" $ do
+      [fmtTrim|  hello
+- a
+ - {pi:.2}|]
+        `shouldBe` "  hello\n- a\n - 3.14"
+
+    it "works with multiline" $ do
+      [fmtTrim|
+                  hello
+                  - a
+                   - {
+                2 + 2
+                   :d}|]
+        `shouldBe` "hello\n- a\n - 4"
+
+    it "behaves well with escaped first line" $ do
+      [fmtTrim|\
+                  - a
+                  - b
+                  |]
+        `shouldBe` "- a\n- b\n"
+    it "Do not touch single lines" $ do
+      [fmtTrim|  hello|] `shouldBe` "  hello"
+  describe "raw" $ do
+    it "does not escape anything" $ [raw|hello
+  - a \n {\
+  - b }
+  |] `shouldBe` "hello\n  - a \\n {\\\n  - b }\n  "
+  describe "str" $ do
+    it "basic escaping but no indentation neither formatting" $ [str|hello
+  - a \n {\
+  - b {pi}
+  |] `shouldBe` "hello\n  - a \n {  - b {pi}\n  "
+  describe "strTrim" $ do
+    it "basic escaping neither formatting" $ [strTrim|
+  - a \b {
+  - b {pi}
+  |] `shouldBe` "- a \b {\n- b {pi}\n"
diff --git a/test/SpecCustomDelimiters.hs b/test/SpecCustomDelimiters.hs
--- a/test/SpecCustomDelimiters.hs
+++ b/test/SpecCustomDelimiters.hs
@@ -2,6 +2,13 @@
 
 import Language.Haskell.TH.Quote
 import PyF
+import PyF.Internal.QQ
 
 myCustomFormatter :: QuasiQuoter
-myCustomFormatter = fmtWithDelimiters ('@', '!')
+myCustomFormatter =
+  mkFormatter
+    "fmt"
+    ( fmtConfig
+        { delimiters = Just ('@', '!')
+        }
+    )
diff --git a/test/SpecFail.hs b/test/SpecFail.hs
--- a/test/SpecFail.hs
+++ b/test/SpecFail.hs
@@ -4,7 +4,6 @@
 
 import Control.DeepSeq
 import Control.Exception
-import Data.Hashable
 import qualified Data.Text as Text
 import System.Exit
 import System.FilePath
@@ -50,12 +49,14 @@
         -- Clean package environment
         "-hide-all-packages",
         "-package base",
-        "-package megaparsec",
+        "-package bytestring",
+        "-package parsec",
         "-package text",
         "-package template-haskell",
-        "-package haskell-src-exts",
-        "-package haskell-src-meta",
+        "-package ghc-boot",
         "-package mtl",
+        "-package ghc",
+        "-package time",
         "-package containers"
       ]
       ""
@@ -70,10 +71,13 @@
 -- sanitize a compilation result by removing variables strings such as
 -- temporary files name
 sanitize :: FilePath -> String -> String
-sanitize path s =
-  -- strip the filename
-  let t = Text.pack s
-   in Text.unpack (Text.replace (Text.pack path) (Text.pack "INITIALPATH") t)
+sanitize path =
+  Text.unpack
+    -- Strip the filename
+    . Text.replace (Text.pack path) (Text.pack "INITIALPATH")
+    -- GHC 9.0 replaces [Char] by String everywhere
+    . Text.replace (Text.pack "[Char]") (Text.pack "String")
+    . Text.pack
 
 golden :: HasCallStack => String -> String -> IO ()
 golden name output = do
@@ -97,24 +101,25 @@
       assertFailure diffOutput
     else writeFile goldenFile output
 
--- if the compilation fails, runs a golden test on compilation output
--- else, fails the test
-fileFailCompile :: HasCallStack => FilePath -> Spec
-fileFailCompile path = do
-  fileContent <- runIO $ readFile path
-  -- I'm using the hash of the path, considering that the file content can evolve
-  failCompileContent (hash path) path fileContent
-
 failCompile :: HasCallStack => String -> Spec
-failCompile s = failCompileContent (hash s) s (makeTemplate s)
+failCompile s = failCompileContent s s (makeTemplate s)
 
-failCompileContent :: HasCallStack => Int -> String -> String -> Spec
+failCompileContent :: HasCallStack => String -> String -> String -> Spec
 failCompileContent h caption fileContent =
-  before (checkCompile fileContent) $
+  before (checkCompile fileContent) $ do
+    let goldenPath = concatMap cleanSpecialChars h
     it (show caption) $ \res -> case res of
-      CompileError output -> golden (show h) output
-      _ -> assertFailure (show $ ".golden/" <> show h <> "\n" <> show res)
+      CompileError output -> golden goldenPath output
+      _ -> assertFailure (show $ ".golden/" <> goldenPath <> "\n" <> show res)
 
+-- Remove chars which are not accepted in a path name
+cleanSpecialChars :: Char -> [Char]
+cleanSpecialChars '/' = "SLASH"
+cleanSpecialChars '\\' = "BACKSLASH"
+cleanSpecialChars ':' = "COLON"
+cleanSpecialChars '\n' = "NEWLINE"
+cleanSpecialChars e = pure e
+
 main :: IO ()
 main = hspec spec
 
@@ -144,13 +149,6 @@
         failCompile "{hello:+s}"
         failCompile "{hello: s}"
         failCompile "{hello:-s}"
-    describe "number" $ do
-      failCompile "{truncate' number:f}"
-      failCompile "{truncate' number:g}"
-      failCompile "{truncate' number:G}"
-      failCompile "{truncate' number:e}"
-      failCompile "{truncate' number:E}"
-      failCompile "{truncate' number:%}"
     describe "number with precision" $ do
       failCompile "{truncate number:.3d}"
       failCompile "{truncate number:.3o}"
@@ -176,20 +174,23 @@
     describe "on haskell expression parsing" $ do
       describe "single line" $
         failCompile "{1 + - / lalalal}"
+      describe "empty expression" $
+        failCompile "{}"
+      describe "sub expression" $ do
+        describe "simple failure" $
+          failCompile "{pi:.{/}}"
+        describe "empty failure" $
+          failCompile "{pi:.{}}"
       describe "multiples lines" $
         failCompile "hello\n    {\nlet a = 5\n    b = 10\nin 1 + - / lalalal}"
     describe "non-doubled delimiters" $ do
       failCompile "hello } world"
       failCompile "hello { world"
-    describe "fail is not enabled extension" $
-      failCompile "{0b0001}"
-    describe "lexical errors" $
-      failCompile "foo\\Pbar"
-    describe "fileFailures" $
-      mapM_
-        fileFailCompile
-        [ "test/failureCases/bug18.hs"
-        ]
+    describe "lexical errors" $ do
+      describe "single line" $
+        failCompile "foo\\Pbar"
+      describe "multiple line" $
+        failCompile "foo\nbli\\Pbar"
     describe "Wrong type" $ do
       failCompile "{True}"
       failCompile "{True:f}"
diff --git a/test/SpecUtils.hs b/test/SpecUtils.hs
--- a/test/SpecUtils.hs
+++ b/test/SpecUtils.hs
@@ -15,6 +15,7 @@
 import System.Process
 #endif
 import Test.Hspec
+import PyF (fmtConfig)
 
 -- * Utils
 
@@ -47,7 +48,7 @@
   case pythonRes of
     Nothing -> [|expectationFailure $ "Expression: `" ++ s ++ "` fails in python"|]
     Just res -> do
-      let qexp = [|$(toExpPython s) `shouldBe` res|]
+      let qexp = [|$(toExp fmtConfig s) `shouldBe` res|]
       case exampleStr of
         Nothing -> qexp
         Just e ->
@@ -72,4 +73,4 @@
 --     `formatString` is equal to result. It does not check the result
 --     against the python implementation
 checkExampleDiff :: String -> String -> Q Exp
-checkExampleDiff s res = [|$(toExpPython s) `shouldBe` res|]
+checkExampleDiff s res = [|$(toExp fmtConfig s) `shouldBe` res|]
diff --git a/test/failureCases/bug18.hs b/test/failureCases/bug18.hs
deleted file mode 100644
--- a/test/failureCases/bug18.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
-
-{-# OPTIONS -Wall -Werror #-}
-
-{-
-  This test actually fails with -Wall because fieldA is not
-  initialized. PyF is doing nothing wrong. But bug #18 shows that GHC
-  error message leaks a lot of the PyF generated code which is not
-  beautiful.
-
-  This tests helps tracking the conciseness of the PyF generated code.
--}
-
-import qualified Data.Text as T
-import PyF
-
-data Foo = Foo
-  { fieldA :: (),
-    fieldB :: T.Text
-  }
-  deriving (Show)
-
-yolo :: Foo
-yolo =
-  Foo
-    { fieldB = [fmt|hello what's up {x}|]
-    }
-  where
-    x :: T.Text
-    x = T.pack "hi"
-
-main = print yolo
diff --git a/test/golden/-4342647300489545826.golden b/test/golden/-4342647300489545826.golden
deleted file mode 100644
--- a/test/golden/-4342647300489545826.golden
+++ /dev/null
@@ -1,16 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:11:10:
-   |
-11 | in 1 + - / lalalal}|]
-   |          ^
-Parse error: /
-
-    • In the quasi-quotation: [fmt|hello
-    {
-let a = 5
-    b = 10
-in 1 + - / lalalal}|]
-  |
-7 | main = putStrLn [fmt|hello
-  |                      ^^^^^...
diff --git a/test/golden/-559153311506421916.golden b/test/golden/-559153311506421916.golden
deleted file mode 100644
--- a/test/golden/-559153311506421916.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:33:
-  |
-7 | main = putStrLn [fmt|{hello:=100s}|]
-  |                                 ^
-String type is incompatible with inside padding (=).
-
-    • In the quasi-quotation: [fmt|{hello:=100s}|]
-  |
-7 | main = putStrLn [fmt|{hello:=100s}|]
-  |                      ^^^^^^^^^^^^^^^
diff --git a/test/golden/-608734388740815869.golden b/test/golden/-608734388740815869.golden
deleted file mode 100644
--- a/test/golden/-608734388740815869.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:30:
-  |
-7 | main = putStrLn [fmt|{hello: s}|]
-  |                              ^
-Type incompatible with sign field ( ), use any of {'b', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'x', 'X', '%'} or remove the sign field.
-
-    • In the quasi-quotation: [fmt|{hello: s}|]
-  |
-7 | main = putStrLn [fmt|{hello: s}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-7029437438637288708.golden b/test/golden/-7029437438637288708.golden
deleted file mode 100644
--- a/test/golden/-7029437438637288708.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Bool) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) True)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) True)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) True)
-  |
-7 | main = putStrLn [fmt|{True:f}|]
-  |                      ^^^^^^^^^^
diff --git a/test/golden/-7030000414293697510.golden b/test/golden/-7030000414293697510.golden
deleted file mode 100644
--- a/test/golden/-7030000414293697510.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral Bool) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) True)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) True)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) True)
-  |
-7 | main = putStrLn [fmt|{True:d}|]
-  |                      ^^^^^^^^^^
diff --git a/test/golden/-7033096789363194467.golden b/test/golden/-7033096789363194467.golden
deleted file mode 100644
--- a/test/golden/-7033096789363194467.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (PyFToString Bool) arising from a use of ‘pyfToString’
-    • In the second argument of ‘(.)’, namely ‘pyfToString’
-      In the expression: (PyF.Formatters.formatString (PyF.Internal.QQ.newPaddingKForString PyF.Internal.QQ.PaddingDefaultK)) Nothing . pyfToString
-      In the first argument of ‘putStrLn’, namely ‘(((PyF.Formatters.formatString (PyF.Internal.QQ.newPaddingKForString PyF.Internal.QQ.PaddingDefaultK)) Nothing . pyfToString) True)’
-  |
-7 | main = putStrLn [fmt|{True:s}|]
-  |                      ^^^^^^^^^^
diff --git a/test/golden/-8862740133902269087.golden b/test/golden/-8862740133902269087.golden
deleted file mode 100644
--- a/test/golden/-8862740133902269087.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) number)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) number)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) number)
-  |
-7 | main = putStrLn [fmt|{number:b}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-8863303109558677885.golden b/test/golden/-8863303109558677885.golden
deleted file mode 100644
--- a/test/golden/-8863303109558677885.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) number)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) number)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) number)
-  |
-7 | main = putStrLn [fmt|{number:d}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-8865836508904655568.golden b/test/golden/-8865836508904655568.golden
deleted file mode 100644
--- a/test/golden/-8865836508904655568.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) number)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) number)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) number)
-  |
-7 | main = putStrLn [fmt|{number:o}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-8866962460351694124.golden b/test/golden/-8866962460351694124.golden
deleted file mode 100644
--- a/test/golden/-8866962460351694124.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (PyFToString Float) arising from a use of ‘pyfToString’
-    • In the second argument of ‘(.)’, namely ‘pyfToString’
-      In the expression: (PyF.Formatters.formatString (PyF.Internal.QQ.newPaddingKForString PyF.Internal.QQ.PaddingDefaultK)) Nothing . pyfToString
-      In the first argument of ‘putStrLn’, namely ‘(((PyF.Formatters.formatString (PyF.Internal.QQ.newPaddingKForString PyF.Internal.QQ.PaddingDefaultK)) Nothing . pyfToString) number)’
-  |
-7 | main = putStrLn [fmt|{number:s}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-8868932879679082057.golden b/test/golden/-8868932879679082057.golden
deleted file mode 100644
--- a/test/golden/-8868932879679082057.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) number)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) number)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) number)
-  |
-7 | main = putStrLn [fmt|{number:x}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-8877940512730742825.golden b/test/golden/-8877940512730742825.golden
deleted file mode 100644
--- a/test/golden/-8877940512730742825.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) number)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) number)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) number)
-  |
-7 | main = putStrLn [fmt|{number:X}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/-955223088025738585.golden b/test/golden/-955223088025738585.golden
deleted file mode 100644
--- a/test/golden/-955223088025738585.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Int) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-  |
-7 | main = putStrLn [fmt|{truncate' number:%}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/-972393886248710740.golden b/test/golden/-972393886248710740.golden
deleted file mode 100644
--- a/test/golden/-972393886248710740.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Int) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-  |
-7 | main = putStrLn [fmt|{truncate' number:f}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/-972675374043359879.golden b/test/golden/-972675374043359879.golden
deleted file mode 100644
--- a/test/golden/-972675374043359879.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Int) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-  |
-7 | main = putStrLn [fmt|{truncate' number:g}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/-973238354129060121.golden b/test/golden/-973238354129060121.golden
deleted file mode 100644
--- a/test/golden/-973238354129060121.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Int) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-  |
-7 | main = putStrLn [fmt|{truncate' number:e}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/-981683007095020647.golden b/test/golden/-981683007095020647.golden
deleted file mode 100644
--- a/test/golden/-981683007095020647.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Int) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-  |
-7 | main = putStrLn [fmt|{truncate' number:G}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/-982245987180720889.golden b/test/golden/-982245987180720889.golden
deleted file mode 100644
--- a/test/golden/-982245987180720889.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat Int) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) (truncate' number))
-  |
-7 | main = putStrLn [fmt|{truncate' number:E}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/1039339902865820990.golden b/test/golden/1039339902865820990.golden
deleted file mode 100644
--- a/test/golden/1039339902865820990.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:30:
-  |
-7 | main = putStrLn [fmt|{hello:+s}|]
-  |                              ^
-Type incompatible with sign field (+), use any of {'b', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'x', 'X', '%'} or remove the sign field.
-
-    • In the quasi-quotation: [fmt|{hello:+s}|]
-  |
-7 | main = putStrLn [fmt|{hello:+s}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/1179194556644623826.golden b/test/golden/1179194556644623826.golden
deleted file mode 100644
--- a/test/golden/1179194556644623826.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:30:
-  |
-7 | main = putStrLn [fmt|{hello:_s}|]
-  |                              ^
-String type is incompatible with grouping (_ or ,).
-
-    • In the quasi-quotation: [fmt|{hello:_s}|]
-  |
-7 | main = putStrLn [fmt|{hello:_s}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/1398692563425247859.golden b/test/golden/1398692563425247859.golden
deleted file mode 100644
--- a/test/golden/1398692563425247859.golden
+++ /dev/null
@@ -1,16 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:10:5:
-   |
-10 | {pi:l}|]
-   |     ^
-unexpected 'l'
-expecting '#', '%', '+', '-', '.', '0', 'E', 'F', 'G', 'X', 'b', 'c', 'd', 'e', 'f', 'g', 'n', 'o', 's', 'x', '}', integer, or space
-
-    • In the quasi-quotation: [fmt|hello
-
-
-{pi:l}|]
-  |
-7 | main = putStrLn [fmt|hello
-  |                      ^^^^^...
diff --git a/test/golden/1640634027373935614.golden b/test/golden/1640634027373935614.golden
deleted file mode 100644
--- a/test/golden/1640634027373935614.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (PyF.Internal.QQ.FormatAny2 (PyFClassify Bool) Bool 'PyF.Formatters.AlignAll) arising from a use of ‘PyF.Internal.QQ.formatAny’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) True)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) True)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) True)
-  |
-7 | main = putStrLn [fmt|{True}|]
-  |                      ^^^^^^^^
diff --git a/test/golden/2157463209259933381.golden b/test/golden/2157463209259933381.golden
deleted file mode 100644
--- a/test/golden/2157463209259933381.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:25:
-  |
-7 | main = putStrLn [fmt|foo\Pbar|]
-  |                         ^
-lexical error in literal section
-
-    • In the quasi-quotation: [fmt|foo\Pbar|]
-  |
-7 | main = putStrLn [fmt|foo\Pbar|]
-  |                      ^^^^^^^^^^
diff --git a/test/golden/2897849520519503487.golden b/test/golden/2897849520519503487.golden
deleted file mode 100644
--- a/test/golden/2897849520519503487.golden
+++ /dev/null
@@ -1,18 +0,0 @@
-
-INITIALPATH:26:3: error: [-Wmissing-fields, -Werror=missing-fields]
-    • Fields of ‘Foo’ not initialised: fieldA
-    • In the expression: Foo {fieldB = (Data.String.fromString ("hello what's up " <> ((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) x))}
-      In an equation for ‘yolo’:
-          yolo
-            = Foo {fieldB = (Data.String.fromString ("hello what's up " <> ((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) x))}
-            where
-                x :: T.Text
-                x = T.pack "hi"
-   |
-26 |   Foo
-   |   ^^^...
-
-INITIALPATH:33:1: error: [-Wmissing-signatures, -Werror=missing-signatures] Top-level binding with no type signature: main :: IO ()
-   |
-33 | main = print yolo
-   | ^^^^
diff --git a/test/golden/3089090107905559368.golden b/test/golden/3089090107905559368.golden
deleted file mode 100644
--- a/test/golden/3089090107905559368.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:30:
-  |
-7 | main = putStrLn [fmt|{hello:-s}|]
-  |                              ^
-Type incompatible with sign field (-), use any of {'b', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'x', 'X', '%'} or remove the sign field.
-
-    • In the quasi-quotation: [fmt|{hello:-s}|]
-  |
-7 | main = putStrLn [fmt|{hello:-s}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/3420956872570442319.golden b/test/golden/3420956872570442319.golden
deleted file mode 100644
--- a/test/golden/3420956872570442319.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:30:
-  |
-7 | main = putStrLn [fmt|{hello:,s}|]
-  |                              ^
-String type is incompatible with grouping (_ or ,).
-
-    • In the quasi-quotation: [fmt|{hello:,s}|]
-  |
-7 | main = putStrLn [fmt|{hello:,s}|]
-  |                      ^^^^^^^^^^^^
diff --git a/test/golden/5755283065522441917.golden b/test/golden/5755283065522441917.golden
deleted file mode 100644
--- a/test/golden/5755283065522441917.golden
+++ /dev/null
@@ -1,15 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • Ambiguous type variable ‘t0’ arising from a use of ‘pyfToString’
-      prevents the constraint ‘(PyFToString t0)’ from being solved.
-      Probable fix: use a type annotation to specify what ‘t0’ should be.
-      These potential instances exist:
-        instance [safe] PyFToString String -- Defined in ‘PyF.Class’
-        ...plus two instances involving out-of-scope types
-        (use -fprint-potential-instances to see them all)
-    • In the second argument of ‘(.)’, namely ‘pyfToString’
-      In the expression: (PyF.Formatters.formatString (PyF.Internal.QQ.newPaddingKForString PyF.Internal.QQ.PaddingDefaultK)) Nothing . pyfToString
-      In the first argument of ‘putStrLn’, namely ‘(((PyF.Formatters.formatString (PyF.Internal.QQ.newPaddingKForString PyF.Internal.QQ.PaddingDefaultK)) Nothing . pyfToString) (truncate number))’
-  |
-7 | main = putStrLn [fmt|{truncate number:s}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/6036259654057340493.golden b/test/golden/6036259654057340493.golden
deleted file mode 100644
--- a/test/golden/6036259654057340493.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) hello)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) hello)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) hello)
-  |
-7 | main = putStrLn [fmt|{hello:X}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6038793049108247706.golden b/test/golden/6038793049108247706.golden
deleted file mode 100644
--- a/test/golden/6038793049108247706.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-  |
-7 | main = putStrLn [fmt|{hello:G}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6039356029193947908.golden b/test/golden/6039356029193947908.golden
deleted file mode 100644
--- a/test/golden/6039356029193947908.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-  |
-7 | main = putStrLn [fmt|{hello:E}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6045267287109001261.golden b/test/golden/6045267287109001261.golden
deleted file mode 100644
--- a/test/golden/6045267287109001261.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) hello)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) hello)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) hello)
-  |
-7 | main = putStrLn [fmt|{hello:x}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6046956218641740019.golden b/test/golden/6046956218641740019.golden
deleted file mode 100644
--- a/test/golden/6046956218641740019.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) hello)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) hello)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) hello)
-  |
-7 | main = putStrLn [fmt|{hello:b}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6047800682159908474.golden b/test/golden/6047800682159908474.golden
deleted file mode 100644
--- a/test/golden/6047800682159908474.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-  |
-7 | main = putStrLn [fmt|{hello:g}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6048082170088778591.golden b/test/golden/6048082170088778591.golden
deleted file mode 100644
--- a/test/golden/6048082170088778591.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-  |
-7 | main = putStrLn [fmt|{hello:f}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6048363662245608676.golden b/test/golden/6048363662245608676.golden
deleted file mode 100644
--- a/test/golden/6048363662245608676.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-  |
-7 | main = putStrLn [fmt|{hello:e}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6048645150040257841.golden b/test/golden/6048645150040257841.golden
deleted file mode 100644
--- a/test/golden/6048645150040257841.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) hello)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) hello)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) hello)
-  |
-7 | main = putStrLn [fmt|{hello:d}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6050052589349056034.golden b/test/golden/6050052589349056034.golden
deleted file mode 100644
--- a/test/golden/6050052589349056034.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (Integral [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) hello)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) hello)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) hello)
-  |
-7 | main = putStrLn [fmt|{hello:o}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6066378924053859748.golden b/test/golden/6066378924053859748.golden
deleted file mode 100644
--- a/test/golden/6066378924053859748.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • No instance for (RealFloat [Char]) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
-    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
-      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
-  |
-7 | main = putStrLn [fmt|{hello:%}|]
-  |                      ^^^^^^^^^^^
diff --git a/test/golden/6097241206483808698.golden b/test/golden/6097241206483808698.golden
deleted file mode 100644
--- a/test/golden/6097241206483808698.golden
+++ /dev/null
@@ -1,13 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:28:
-  |
-7 | main = putStrLn [fmt|hello } world|]
-  |                            ^
-unexpected '}'
-expecting "{{", "}}", '{', or end of input
-
-    • In the quasi-quotation: [fmt|hello } world|]
-  |
-7 | main = putStrLn [fmt|hello } world|]
-  |                      ^^^^^^^^^^^^^^^
diff --git a/test/golden/6623238490461560947.golden b/test/golden/6623238490461560947.golden
deleted file mode 100644
--- a/test/golden/6623238490461560947.golden
+++ /dev/null
@@ -1,5 +0,0 @@
-
-INITIALPATH:7:22: error: Variable not in scope: b0001
-  |
-7 | main = putStrLn [fmt|{0b0001}|]
-  |                      ^^^^^^^^^^
diff --git a/test/golden/6874382203897971272.golden b/test/golden/6874382203897971272.golden
deleted file mode 100644
--- a/test/golden/6874382203897971272.golden
+++ /dev/null
@@ -1,13 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:35:
-  |
-7 | main = putStrLn [fmt|hello { world|]
-  |                                   ^
-unexpected end of input
-expecting ':' or '}'
-
-    • In the quasi-quotation: [fmt|hello { world|]
-  |
-7 | main = putStrLn [fmt|hello { world|]
-  |                      ^^^^^^^^^^^^^^^
diff --git a/test/golden/7717561110821329704.golden b/test/golden/7717561110821329704.golden
deleted file mode 100644
--- a/test/golden/7717561110821329704.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:41:
-  |
-7 | main = putStrLn [fmt|{truncate number:.3o}|]
-  |                                         ^
-Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
-
-    • In the quasi-quotation: [fmt|{truncate number:.3o}|]
-  |
-7 | main = putStrLn [fmt|{truncate number:.3o}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/7718968550063017457.golden b/test/golden/7718968550063017457.golden
deleted file mode 100644
--- a/test/golden/7718968550063017457.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:41:
-  |
-7 | main = putStrLn [fmt|{truncate number:.3b}|]
-  |                                         ^
-Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
-
-    • In the quasi-quotation: [fmt|{truncate number:.3b}|]
-  |
-7 | main = putStrLn [fmt|{truncate number:.3b}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/7719531530148717643.golden b/test/golden/7719531530148717643.golden
deleted file mode 100644
--- a/test/golden/7719531530148717643.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:41:
-  |
-7 | main = putStrLn [fmt|{truncate number:.3d}|]
-  |                                         ^
-Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
-
-    • In the quasi-quotation: [fmt|{truncate number:.3d}|]
-  |
-7 | main = putStrLn [fmt|{truncate number:.3d}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/7720657481595756207.golden b/test/golden/7720657481595756207.golden
deleted file mode 100644
--- a/test/golden/7720657481595756207.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:41:
-  |
-7 | main = putStrLn [fmt|{truncate number:.3x}|]
-  |                                         ^
-Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
-
-    • In the quasi-quotation: [fmt|{truncate number:.3x}|]
-  |
-7 | main = putStrLn [fmt|{truncate number:.3x}|]
-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/8270079288213682032.golden b/test/golden/8270079288213682032.golden
deleted file mode 100644
--- a/test/golden/8270079288213682032.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • String type is incompatible with inside padding (=).
-    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) ((PyF.Internal.QQ.PaddingK 100) (Just (Nothing, PyF.Formatters.AlignInside)))) Nothing) Nothing) hello)’
-      In the expression: putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) ((PyF.Internal.QQ.PaddingK 100) (Just (Nothing, PyF.Formatters.AlignInside)))) Nothing) Nothing) hello)
-      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) ((PyF.Internal.QQ.PaddingK 100) (Just (Nothing, PyF.Formatters.AlignInside)))) Nothing) Nothing) hello)
-  |
-7 | main = putStrLn [fmt|{hello:=100}|]
-  |                      ^^^^^^^^^^^^^^
diff --git a/test/golden/85163210393859768.golden b/test/golden/85163210393859768.golden
deleted file mode 100644
--- a/test/golden/85163210393859768.golden
+++ /dev/null
@@ -1,12 +0,0 @@
-
-INITIALPATH:7:22: error:
-    • INITIALPATH:7:29:
-  |
-7 | main = putStrLn [fmt|{1 + - / lalalal}|]
-  |                             ^
-Parse error: /
-
-    • In the quasi-quotation: [fmt|{1 + - / lalalal}|]
-  |
-7 | main = putStrLn [fmt|{1 + - / lalalal}|]
-  |                      ^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/fooBACKSLASHPbar.golden b/test/golden/fooBACKSLASHPbar.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/fooBACKSLASHPbar.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:25:
+  |
+7 | main = putStrLn [fmt|foo\Pbar|]
+  |                         ^
+Lexical error in literal section
+
+    • In the quasi-quotation: [fmt|foo\Pbar|]
+  |
+7 | main = putStrLn [fmt|foo\Pbar|]
+  |                      ^^^^^^^^^^
diff --git a/test/golden/fooNEWLINEbliBACKSLASHPbar.golden b/test/golden/fooNEWLINEbliBACKSLASHPbar.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/fooNEWLINEbliBACKSLASHPbar.golden
@@ -0,0 +1,13 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:8:4:
+  |
+8 | bli\Pbar|]
+  |    ^
+Lexical error in literal section
+
+    • In the quasi-quotation: [fmt|foo
+bli\Pbar|]
+  |
+7 | main = putStrLn [fmt|foo
+  |                      ^^^...
diff --git a/test/golden/hello { world.golden b/test/golden/hello { world.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/hello { world.golden
@@ -0,0 +1,14 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:35:
+  |
+7 | main = putStrLn [fmt|hello { world|]
+  |                                   ^
+
+unexpected end of input
+expecting ":" or "}"
+
+    • In the quasi-quotation: [fmt|hello { world|]
+  |
+7 | main = putStrLn [fmt|hello { world|]
+  |                      ^^^^^^^^^^^^^^^
diff --git a/test/golden/hello } world.golden b/test/golden/hello } world.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/hello } world.golden
@@ -0,0 +1,14 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:28:
+  |
+7 | main = putStrLn [fmt|hello } world|]
+  |                            ^
+
+unexpected '}'
+expecting "{{", "}}", "{" or end of input
+
+    • In the quasi-quotation: [fmt|hello } world|]
+  |
+7 | main = putStrLn [fmt|hello } world|]
+  |                      ^^^^^^^^^^^^^^^
diff --git a/test/golden/helloNEWLINE    {NEWLINElet a = 5NEWLINE    b = 10NEWLINEin 1 + - SLASH lalalal}.golden b/test/golden/helloNEWLINE    {NEWLINElet a = 5NEWLINE    b = 10NEWLINEin 1 + - SLASH lalalal}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/helloNEWLINE    {NEWLINElet a = 5NEWLINE    b = 10NEWLINEin 1 + - SLASH lalalal}.golden
@@ -0,0 +1,16 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:11:10:
+   |
+11 | in 1 + - / lalalal}|]
+   |          ^
+parse error on input `/' in haskell expression
+
+    • In the quasi-quotation: [fmt|hello
+    {
+let a = 5
+    b = 10
+in 1 + - / lalalal}|]
+  |
+7 | main = putStrLn [fmt|hello
+  |                      ^^^^^...
diff --git a/test/golden/helloNEWLINENEWLINENEWLINE{piCOLONl}.golden b/test/golden/helloNEWLINENEWLINENEWLINE{piCOLONl}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/helloNEWLINENEWLINENEWLINE{piCOLONl}.golden
@@ -0,0 +1,17 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:10:6:
+   |
+10 | {pi:l}|]
+   |      ^
+
+unexpected "}"
+expecting "<", ">", "^" or "="
+
+    • In the quasi-quotation: [fmt|hello
+
+
+{pi:l}|]
+  |
+7 | main = putStrLn [fmt|hello
+  |                      ^^^^^...
diff --git a/test/golden/{1 + - SLASH lalalal}.golden b/test/golden/{1 + - SLASH lalalal}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{1 + - SLASH lalalal}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:29:
+  |
+7 | main = putStrLn [fmt|{1 + - / lalalal}|]
+  |                             ^
+parse error on input `/' in haskell expression
+
+    • In the quasi-quotation: [fmt|{1 + - / lalalal}|]
+  |
+7 | main = putStrLn [fmt|{1 + - / lalalal}|]
+  |                      ^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/{TrueCOLONd}.golden b/test/golden/{TrueCOLONd}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{TrueCOLONd}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral Bool) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) True)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) True)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) True)
+  |
+7 | main = putStrLn [fmt|{True:d}|]
+  |                      ^^^^^^^^^^
diff --git a/test/golden/{TrueCOLONf}.golden b/test/golden/{TrueCOLONf}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{TrueCOLONf}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real Bool) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) True)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) True)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) True)
+  |
+7 | main = putStrLn [fmt|{True:f}|]
+  |                      ^^^^^^^^^^
diff --git a/test/golden/{True}.golden b/test/golden/{True}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{True}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (PyF.Internal.QQ.FormatAny2 (PyFClassify Bool) Bool 'PyF.Formatters.AlignAll) arising from a use of ‘PyF.Internal.QQ.formatAny’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) True)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) True)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) PyF.Internal.QQ.PaddingDefaultK) Nothing) Nothing) True)
+  |
+7 | main = putStrLn [fmt|{True}|]
+  |                      ^^^^^^^^
diff --git a/test/golden/{helloCOLON s}.golden b/test/golden/{helloCOLON s}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON s}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:30:
+  |
+7 | main = putStrLn [fmt|{hello: s}|]
+  |                              ^
+Type incompatible with sign field ( ), use any of {'b', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'x', 'X', '%'} or remove the sign field.
+
+    • In the quasi-quotation: [fmt|{hello: s}|]
+  |
+7 | main = putStrLn [fmt|{hello: s}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON%}.golden b/test/golden/{helloCOLON%}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON%}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real String) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+  |
+7 | main = putStrLn [fmt|{hello:%}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON+s}.golden b/test/golden/{helloCOLON+s}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON+s}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:30:
+  |
+7 | main = putStrLn [fmt|{hello:+s}|]
+  |                              ^
+Type incompatible with sign field (+), use any of {'b', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'x', 'X', '%'} or remove the sign field.
+
+    • In the quasi-quotation: [fmt|{hello:+s}|]
+  |
+7 | main = putStrLn [fmt|{hello:+s}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON,s}.golden b/test/golden/{helloCOLON,s}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON,s}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:30:
+  |
+7 | main = putStrLn [fmt|{hello:,s}|]
+  |                              ^
+String type is incompatible with grouping (_ or ,).
+
+    • In the quasi-quotation: [fmt|{hello:,s}|]
+  |
+7 | main = putStrLn [fmt|{hello:,s}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON-s}.golden b/test/golden/{helloCOLON-s}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON-s}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:30:
+  |
+7 | main = putStrLn [fmt|{hello:-s}|]
+  |                              ^
+Type incompatible with sign field (-), use any of {'b', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'x', 'X', '%'} or remove the sign field.
+
+    • In the quasi-quotation: [fmt|{hello:-s}|]
+  |
+7 | main = putStrLn [fmt|{hello:-s}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON=100s}.golden b/test/golden/{helloCOLON=100s}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON=100s}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:33:
+  |
+7 | main = putStrLn [fmt|{hello:=100s}|]
+  |                                 ^
+String type is incompatible with inside padding (=).
+
+    • In the quasi-quotation: [fmt|{hello:=100s}|]
+  |
+7 | main = putStrLn [fmt|{hello:=100s}|]
+  |                      ^^^^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON=100}.golden b/test/golden/{helloCOLON=100}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON=100}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • String type is incompatible with inside padding (=).
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) ((PyF.Internal.QQ.PaddingK 100) (Just (Nothing, PyF.Formatters.AlignInside)))) Nothing) Nothing) hello)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) ((PyF.Internal.QQ.PaddingK 100) (Just (Nothing, PyF.Formatters.AlignInside)))) Nothing) Nothing) hello)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAny PyF.Formatters.Minus) ((PyF.Internal.QQ.PaddingK 100) (Just (Nothing, PyF.Formatters.AlignInside)))) Nothing) Nothing) hello)
+  |
+7 | main = putStrLn [fmt|{hello:=100}|]
+  |                      ^^^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONE}.golden b/test/golden/{helloCOLONE}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONE}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real String) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+  |
+7 | main = putStrLn [fmt|{hello:E}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONG}.golden b/test/golden/{helloCOLONG}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONG}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real String) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic)) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+  |
+7 | main = putStrLn [fmt|{hello:G}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONX}.golden b/test/golden/{helloCOLONX}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONX}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral String) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) hello)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) hello)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) hello)
+  |
+7 | main = putStrLn [fmt|{hello:X}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLON_s}.golden b/test/golden/{helloCOLON_s}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLON_s}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:30:
+  |
+7 | main = putStrLn [fmt|{hello:_s}|]
+  |                              ^
+String type is incompatible with grouping (_ or ,).
+
+    • In the quasi-quotation: [fmt|{hello:_s}|]
+  |
+7 | main = putStrLn [fmt|{hello:_s}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONb}.golden b/test/golden/{helloCOLONb}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONb}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral String) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) hello)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) hello)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) hello)
+  |
+7 | main = putStrLn [fmt|{hello:b}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONd}.golden b/test/golden/{helloCOLONd}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONd}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral String) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) hello)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) hello)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) hello)
+  |
+7 | main = putStrLn [fmt|{hello:d}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONe}.golden b/test/golden/{helloCOLONe}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONe}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real String) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+  |
+7 | main = putStrLn [fmt|{hello:e}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONf}.golden b/test/golden/{helloCOLONf}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONf}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real String) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+  |
+7 | main = putStrLn [fmt|{hello:f}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONg}.golden b/test/golden/{helloCOLONg}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONg}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Real String) arising from a use of ‘PyF.Internal.QQ.formatAnyFractional’
+    • In the first argument of ‘putStrLn’, namely ‘((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)’
+      In the expression: putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+      In an equation for ‘main’: main = putStrLn ((((((PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic) PyF.Formatters.Minus) Nothing) Nothing) (Just 6)) hello)
+  |
+7 | main = putStrLn [fmt|{hello:g}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONo}.golden b/test/golden/{helloCOLONo}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONo}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral String) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) hello)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) hello)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) hello)
+  |
+7 | main = putStrLn [fmt|{hello:o}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{helloCOLONx}.golden b/test/golden/{helloCOLONx}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{helloCOLONx}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral String) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) hello)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) hello)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) hello)
+  |
+7 | main = putStrLn [fmt|{hello:x}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{numberCOLONX}.golden b/test/golden/{numberCOLONX}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{numberCOLONX}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) number)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) number)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa)) PyF.Formatters.Minus) Nothing) Nothing) number)
+  |
+7 | main = putStrLn [fmt|{number:X}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{numberCOLONb}.golden b/test/golden/{numberCOLONb}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{numberCOLONb}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) number)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) number)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary) PyF.Formatters.Minus) Nothing) Nothing) number)
+  |
+7 | main = putStrLn [fmt|{number:b}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{numberCOLONd}.golden b/test/golden/{numberCOLONd}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{numberCOLONd}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) number)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) number)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal) PyF.Formatters.Minus) Nothing) Nothing) number)
+  |
+7 | main = putStrLn [fmt|{number:d}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{numberCOLONo}.golden b/test/golden/{numberCOLONo}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{numberCOLONo}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) number)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) number)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal) PyF.Formatters.Minus) Nothing) Nothing) number)
+  |
+7 | main = putStrLn [fmt|{number:o}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{numberCOLONx}.golden b/test/golden/{numberCOLONx}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{numberCOLONx}.golden
@@ -0,0 +1,9 @@
+
+INITIALPATH:7:22: error:
+    • No instance for (Integral Float) arising from a use of ‘PyF.Internal.QQ.formatAnyIntegral’
+    • In the first argument of ‘putStrLn’, namely ‘(((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) number)’
+      In the expression: putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) number)
+      In an equation for ‘main’: main = putStrLn (((((PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa) PyF.Formatters.Minus) Nothing) Nothing) number)
+  |
+7 | main = putStrLn [fmt|{number:x}|]
+  |                      ^^^^^^^^^^^^
diff --git a/test/golden/{piCOLON.{SLASH}}.golden b/test/golden/{piCOLON.{SLASH}}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{piCOLON.{SLASH}}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:28:
+  |
+7 | main = putStrLn [fmt|{pi:.{/}}|]
+  |                            ^
+parse error on input `/' in haskell expression
+
+    • In the quasi-quotation: [fmt|{pi:.{/}}|]
+  |
+7 | main = putStrLn [fmt|{pi:.{/}}|]
+  |                      ^^^^^^^^^^^
diff --git a/test/golden/{piCOLON.{}}.golden b/test/golden/{piCOLON.{}}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{piCOLON.{}}.golden
@@ -0,0 +1,14 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:28:
+  |
+7 | main = putStrLn [fmt|{pi:.{}}|]
+  |                            ^
+
+unexpected "}"
+expecting an haskell expression
+
+    • In the quasi-quotation: [fmt|{pi:.{}}|]
+  |
+7 | main = putStrLn [fmt|{pi:.{}}|]
+  |                      ^^^^^^^^^^
diff --git a/test/golden/{truncate numberCOLON.3b}.golden b/test/golden/{truncate numberCOLON.3b}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{truncate numberCOLON.3b}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:41:
+  |
+7 | main = putStrLn [fmt|{truncate number:.3b}|]
+  |                                         ^
+Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
+
+    • In the quasi-quotation: [fmt|{truncate number:.3b}|]
+  |
+7 | main = putStrLn [fmt|{truncate number:.3b}|]
+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/{truncate numberCOLON.3d}.golden b/test/golden/{truncate numberCOLON.3d}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{truncate numberCOLON.3d}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:41:
+  |
+7 | main = putStrLn [fmt|{truncate number:.3d}|]
+  |                                         ^
+Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
+
+    • In the quasi-quotation: [fmt|{truncate number:.3d}|]
+  |
+7 | main = putStrLn [fmt|{truncate number:.3d}|]
+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/{truncate numberCOLON.3o}.golden b/test/golden/{truncate numberCOLON.3o}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{truncate numberCOLON.3o}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:41:
+  |
+7 | main = putStrLn [fmt|{truncate number:.3o}|]
+  |                                         ^
+Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
+
+    • In the quasi-quotation: [fmt|{truncate number:.3o}|]
+  |
+7 | main = putStrLn [fmt|{truncate number:.3o}|]
+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/{truncate numberCOLON.3x}.golden b/test/golden/{truncate numberCOLON.3x}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{truncate numberCOLON.3x}.golden
@@ -0,0 +1,12 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:41:
+  |
+7 | main = putStrLn [fmt|{truncate number:.3x}|]
+  |                                         ^
+Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', 'n', 's', '%'} or remove the precision field.
+
+    • In the quasi-quotation: [fmt|{truncate number:.3x}|]
+  |
+7 | main = putStrLn [fmt|{truncate number:.3x}|]
+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/test/golden/{}.golden b/test/golden/{}.golden
new file mode 100644
--- /dev/null
+++ b/test/golden/{}.golden
@@ -0,0 +1,14 @@
+
+INITIALPATH:7:22: error:
+    • INITIALPATH:7:23:
+  |
+7 | main = putStrLn [fmt|{}|]
+  |                       ^
+
+unexpected "}"
+expecting an haskell expression
+
+    • In the quasi-quotation: [fmt|{}|]
+  |
+7 | main = putStrLn [fmt|{}|]
+  |                      ^^^^
