packages feed

PyF 0.9.0.3 → 0.10.0.1

raw patch · 104 files changed

+1866/−777 lines, 104 filesdep +ghcdep +ghc-bootdep +parsecdep −containersdep −directorydep −hashablePVP ok

version bump matches the API change (PVP)

Dependencies added: ghc, ghc-boot, parsec, time

Dependencies removed: containers, directory, hashable, haskell-src-exts, haskell-src-meta, megaparsec

API changes (from Hackage documentation)

- PyF: fmtWithDelimiters :: (Char, Char) -> QuasiQuoter
- PyF.Internal.Extensions: thExtToMetaExt :: Extension -> Maybe Extension
- PyF.Internal.QQ: instance GHC.Float.RealFloat t => PyF.Internal.QQ.FormatAny2 'PyF.Class.PyFFractional t k
- PyF.Internal.QQ: toExpPython :: String -> Q Exp
+ PyF: addFormatting :: (Char, Char) -> Config -> Config
+ PyF: addTrim :: Config -> Config
+ PyF: defaultConfig :: Config
+ PyF: fmtConfig :: Config
+ PyF: fmtTrim :: QuasiQuoter
+ PyF: mkFormatter :: String -> Config -> QuasiQuoter
+ PyF: raw :: QuasiQuoter
+ PyF: str :: QuasiQuoter
+ PyF: strConfig :: Config
+ PyF: strTrim :: QuasiQuoter
+ PyF: trimIndent :: String -> String
+ PyF.Class: class PyfFormatFractional a
+ PyF.Class: class PyfFormatIntegral i
+ PyF.Class: instance GHC.Real.Integral t => PyF.Class.PyfFormatIntegral t
+ PyF.Class: instance GHC.Real.Real t => PyF.Class.PyfFormatFractional t
+ PyF.Class: instance PyF.Class.PyFToString Data.ByteString.Internal.ByteString
+ PyF.Class: instance PyF.Class.PyFToString Data.ByteString.Lazy.Internal.ByteString
+ PyF.Class: instance PyF.Class.PyFToString GHC.Types.Char
+ PyF.Class: instance PyF.Class.PyfFormatFractional GHC.Types.Double
+ PyF.Class: instance PyF.Class.PyfFormatFractional GHC.Types.Float
+ PyF.Class: instance PyF.Class.PyfFormatIntegral GHC.Types.Char
+ PyF.Class: pyfFormatFractional :: PyfFormatFractional a => Format t t' 'Fractional -> SignMode -> Maybe (Int, AlignMode k, Char) -> Maybe (Int, Char) -> Maybe Int -> a -> String
+ PyF.Class: pyfFormatIntegral :: PyfFormatIntegral i => Format t t' 'Integral -> SignMode -> Maybe (Int, AlignMode k, Char) -> Maybe (Int, Char) -> i -> String
+ PyF.Formatters: instance GHC.Classes.Eq i => GHC.Classes.Eq (PyF.Formatters.ShowIntegral i)
+ PyF.Formatters: instance GHC.Classes.Ord i => GHC.Classes.Ord (PyF.Formatters.ShowIntegral i)
+ PyF.Formatters: instance GHC.Enum.Enum i => GHC.Enum.Enum (PyF.Formatters.ShowIntegral i)
+ PyF.Formatters: instance GHC.Num.Num i => GHC.Num.Num (PyF.Formatters.ShowIntegral i)
+ PyF.Formatters: instance GHC.Real.Integral i => GHC.Real.Integral (PyF.Formatters.ShowIntegral i)
+ PyF.Formatters: instance GHC.Real.Real i => GHC.Real.Real (PyF.Formatters.ShowIntegral i)
+ PyF.Formatters: instance GHC.Show.Show (PyF.Formatters.ShowIntegral i)
+ PyF.Internal.Meta: baseDynFlags :: [Extension] -> DynFlags
+ PyF.Internal.Meta: toExp :: DynFlags -> HsExpr GhcPs -> Exp
+ PyF.Internal.Meta: translateTHtoGHCExt :: Extension -> Extension
+ PyF.Internal.Parser: parseExpression :: String -> DynFlags -> Either (Int, Int, String) (HsExpr GhcPs)
+ PyF.Internal.ParserEx: fakeLlvmConfig :: LlvmConfig
+ PyF.Internal.ParserEx: fakeSettings :: Settings
+ PyF.Internal.ParserEx: parseExpression :: String -> DynFlags -> ParseResult (Located (HsExpr GhcPs))
+ PyF.Internal.QQ: Config :: Maybe (Char, Char) -> (Q Exp -> Q Exp) -> Config
+ PyF.Internal.QQ: [delimiters] :: Config -> Maybe (Char, Char)
+ PyF.Internal.QQ: [postProcess] :: Config -> Q Exp -> Q Exp
+ PyF.Internal.QQ: data Config
+ PyF.Internal.QQ: expQQ :: String -> (String -> Q Exp) -> QuasiQuoter
+ PyF.Internal.QQ: instance PyF.Class.PyfFormatFractional t => PyF.Internal.QQ.FormatAny2 'PyF.Class.PyFFractional t k
+ PyF.Internal.QQ: wrapFromString :: ExpQ -> Q Exp
- PyF.Class: pyfToString :: (PyFToString t, Show t) => t -> String
+ PyF.Class: pyfToString :: PyFToString t => t -> String
- PyF.Formatters: formatIntegral :: (Show i, Integral i) => Format t t' 'Integral -> SignMode -> Maybe (Int, AlignMode k, Char) -> Maybe (Int, Char) -> i -> String
+ PyF.Formatters: formatIntegral :: Integral i => Format t t' 'Integral -> SignMode -> Maybe (Int, AlignMode k, Char) -> Maybe (Int, Char) -> i -> String
- PyF.Internal.PythonSyntax: ParsingContext :: (Char, Char) -> [Extension] -> ParsingContext
+ PyF.Internal.PythonSyntax: ParsingContext :: Maybe (Char, Char) -> [Extension] -> ParsingContext
- PyF.Internal.PythonSyntax: [delimiters] :: ParsingContext -> (Char, Char)
+ PyF.Internal.PythonSyntax: [delimiters] :: ParsingContext -> Maybe (Char, Char)
- PyF.Internal.PythonSyntax: type Parser t = ParsecT Void String (Reader ParsingContext) t
+ PyF.Internal.PythonSyntax: type Parser t = ParsecT String () (Reader ParsingContext) t
- PyF.Internal.QQ: toExp :: (Char, Char) -> String -> Q Exp
+ PyF.Internal.QQ: toExp :: Config -> String -> Q Exp

Files

ChangeLog.md view
@@ -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
PyF.cabal view
@@ -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
Readme.md view
@@ -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.
src/PyF.hs view
@@ -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)
src/PyF/Class.hs view
@@ -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)
src/PyF/Formatters.hs view
@@ -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
− src/PyF/Internal/Extensions.hs
@@ -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
+ src/PyF/Internal/Meta.hs view
@@ -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
+ src/PyF/Internal/Parser.hs view
@@ -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
+ src/PyF/Internal/ParserEx.hs view
@@ -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
src/PyF/Internal/PythonSyntax.hs view
@@ -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)
src/PyF/Internal/QQ.hs view
@@ -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
test/Spec.hs view
@@ -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"
test/SpecCustomDelimiters.hs view
@@ -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 ('@', '!')+        }+    )
test/SpecFail.hs view
@@ -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}"
test/SpecUtils.hs view
@@ -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|]
− test/failureCases/bug18.hs
@@ -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
− test/golden/-4342647300489545826.golden
@@ -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-  |                      ^^^^^...
− test/golden/-559153311506421916.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^
− test/golden/-608734388740815869.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-7029437438637288708.golden
@@ -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}|]-  |                      ^^^^^^^^^^
− test/golden/-7030000414293697510.golden
@@ -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}|]-  |                      ^^^^^^^^^^
− test/golden/-7033096789363194467.golden
@@ -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}|]-  |                      ^^^^^^^^^^
− test/golden/-8862740133902269087.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-8863303109558677885.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-8865836508904655568.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-8866962460351694124.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-8868932879679082057.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-8877940512730742825.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/-955223088025738585.golden
@@ -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:%}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^
− test/golden/-972393886248710740.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^
− test/golden/-972675374043359879.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^
− test/golden/-973238354129060121.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^
− test/golden/-981683007095020647.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^
− test/golden/-982245987180720889.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^
− test/golden/1039339902865820990.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/1179194556644623826.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/1398692563425247859.golden
@@ -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-  |                      ^^^^^...
− test/golden/1640634027373935614.golden
@@ -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}|]-  |                      ^^^^^^^^
− test/golden/2157463209259933381.golden
@@ -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|]-  |                      ^^^^^^^^^^
− test/golden/2897849520519503487.golden
@@ -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-   | ^^^^
− test/golden/3089090107905559368.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/3420956872570442319.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^
− test/golden/5755283065522441917.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^
− test/golden/6036259654057340493.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6038793049108247706.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6039356029193947908.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6045267287109001261.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6046956218641740019.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6047800682159908474.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6048082170088778591.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6048363662245608676.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6048645150040257841.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6050052589349056034.golden
@@ -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}|]-  |                      ^^^^^^^^^^^
− test/golden/6066378924053859748.golden
@@ -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:%}|]-  |                      ^^^^^^^^^^^
− test/golden/6097241206483808698.golden
@@ -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|]-  |                      ^^^^^^^^^^^^^^^
− test/golden/6623238490461560947.golden
@@ -1,5 +0,0 @@--INITIALPATH:7:22: error: Variable not in scope: b0001-  |-7 | main = putStrLn [fmt|{0b0001}|]-  |                      ^^^^^^^^^^
− test/golden/6874382203897971272.golden
@@ -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|]-  |                      ^^^^^^^^^^^^^^^
− test/golden/7717561110821329704.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
− test/golden/7718968550063017457.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
− test/golden/7719531530148717643.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
− test/golden/7720657481595756207.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^^^^^
− test/golden/8270079288213682032.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^
− test/golden/85163210393859768.golden
@@ -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}|]-  |                      ^^^^^^^^^^^^^^^^^^^
+ test/golden/fooBACKSLASHPbar.golden view
@@ -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|]+  |                      ^^^^^^^^^^
+ test/golden/fooNEWLINEbliBACKSLASHPbar.golden view
@@ -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+  |                      ^^^...
+ test/golden/hello { world.golden view
@@ -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|]+  |                      ^^^^^^^^^^^^^^^
+ test/golden/hello } world.golden view
@@ -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|]+  |                      ^^^^^^^^^^^^^^^
+ test/golden/helloNEWLINE {NEWLINElet a = 5NEWLINE b = 10NEWLINEin 1 + - SLASH lalalal}.golden view
@@ -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+  |                      ^^^^^...
+ test/golden/helloNEWLINENEWLINENEWLINE{piCOLONl}.golden view
@@ -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+  |                      ^^^^^...
+ test/golden/{1 + - SLASH lalalal}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^^^^^^
+ test/golden/{TrueCOLONd}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^
+ test/golden/{TrueCOLONf}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^
+ test/golden/{True}.golden view
@@ -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}|]+  |                      ^^^^^^^^
+ test/golden/{helloCOLON s}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{helloCOLON%}.golden view
@@ -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:%}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLON+s}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{helloCOLON,s}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{helloCOLON-s}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{helloCOLON=100s}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^^
+ test/golden/{helloCOLON=100}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^
+ test/golden/{helloCOLONE}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONG}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONX}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLON_s}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{helloCOLONb}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONd}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONe}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONf}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONg}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONo}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{helloCOLONx}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^
+ test/golden/{numberCOLONX}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{numberCOLONb}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{numberCOLONd}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{numberCOLONo}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{numberCOLONx}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^
+ test/golden/{piCOLON.{SLASH}}.golden view
@@ -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:.{/}}|]+  |                      ^^^^^^^^^^^
+ test/golden/{piCOLON.{}}.golden view
@@ -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:.{}}|]+  |                      ^^^^^^^^^^
+ test/golden/{truncate numberCOLON.3b}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
+ test/golden/{truncate numberCOLON.3d}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
+ test/golden/{truncate numberCOLON.3o}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
+ test/golden/{truncate numberCOLON.3x}.golden view
@@ -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}|]+  |                      ^^^^^^^^^^^^^^^^^^^^^^^
+ test/golden/{}.golden view
@@ -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|{}|]+  |                      ^^^^