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 +17/−0
- PyF.cabal +20/−21
- Readme.md +46/−5
- src/PyF.hs +107/−20
- src/PyF/Class.hs +131/−3
- src/PyF/Formatters.hs +18/−7
- src/PyF/Internal/Extensions.hs +0/−19
- src/PyF/Internal/Meta.hs +341/−0
- src/PyF/Internal/Parser.hs +82/−0
- src/PyF/Internal/ParserEx.hs +201/−0
- src/PyF/Internal/PythonSyntax.hs +75/−58
- src/PyF/Internal/QQ.hs +120/−56
- test/Spec.hs +221/−23
- test/SpecCustomDelimiters.hs +8/−1
- test/SpecFail.hs +38/−37
- test/SpecUtils.hs +3/−2
- test/failureCases/bug18.hs +0/−33
- test/golden/-4342647300489545826.golden +0/−16
- test/golden/-559153311506421916.golden +0/−12
- test/golden/-608734388740815869.golden +0/−12
- test/golden/-7029437438637288708.golden +0/−9
- test/golden/-7030000414293697510.golden +0/−9
- test/golden/-7033096789363194467.golden +0/−9
- test/golden/-8862740133902269087.golden +0/−9
- test/golden/-8863303109558677885.golden +0/−9
- test/golden/-8865836508904655568.golden +0/−9
- test/golden/-8866962460351694124.golden +0/−9
- test/golden/-8868932879679082057.golden +0/−9
- test/golden/-8877940512730742825.golden +0/−9
- test/golden/-955223088025738585.golden +0/−9
- test/golden/-972393886248710740.golden +0/−9
- test/golden/-972675374043359879.golden +0/−9
- test/golden/-973238354129060121.golden +0/−9
- test/golden/-981683007095020647.golden +0/−9
- test/golden/-982245987180720889.golden +0/−9
- test/golden/1039339902865820990.golden +0/−12
- test/golden/1179194556644623826.golden +0/−12
- test/golden/1398692563425247859.golden +0/−16
- test/golden/1640634027373935614.golden +0/−9
- test/golden/2157463209259933381.golden +0/−12
- test/golden/2897849520519503487.golden +0/−18
- test/golden/3089090107905559368.golden +0/−12
- test/golden/3420956872570442319.golden +0/−12
- test/golden/5755283065522441917.golden +0/−15
- test/golden/6036259654057340493.golden +0/−9
- test/golden/6038793049108247706.golden +0/−9
- test/golden/6039356029193947908.golden +0/−9
- test/golden/6045267287109001261.golden +0/−9
- test/golden/6046956218641740019.golden +0/−9
- test/golden/6047800682159908474.golden +0/−9
- test/golden/6048082170088778591.golden +0/−9
- test/golden/6048363662245608676.golden +0/−9
- test/golden/6048645150040257841.golden +0/−9
- test/golden/6050052589349056034.golden +0/−9
- test/golden/6066378924053859748.golden +0/−9
- test/golden/6097241206483808698.golden +0/−13
- test/golden/6623238490461560947.golden +0/−5
- test/golden/6874382203897971272.golden +0/−13
- test/golden/7717561110821329704.golden +0/−12
- test/golden/7718968550063017457.golden +0/−12
- test/golden/7719531530148717643.golden +0/−12
- test/golden/7720657481595756207.golden +0/−12
- test/golden/8270079288213682032.golden +0/−9
- test/golden/85163210393859768.golden +0/−12
- test/golden/fooBACKSLASHPbar.golden +12/−0
- test/golden/fooNEWLINEbliBACKSLASHPbar.golden +13/−0
- test/golden/hello { world.golden +14/−0
- test/golden/hello } world.golden +14/−0
- test/golden/helloNEWLINE {NEWLINElet a = 5NEWLINE b = 10NEWLINEin 1 + - SLASH lalalal}.golden +16/−0
- test/golden/helloNEWLINENEWLINENEWLINE{piCOLONl}.golden +17/−0
- test/golden/{1 + - SLASH lalalal}.golden +12/−0
- test/golden/{TrueCOLONd}.golden +9/−0
- test/golden/{TrueCOLONf}.golden +9/−0
- test/golden/{True}.golden +9/−0
- test/golden/{helloCOLON s}.golden +12/−0
- test/golden/{helloCOLON%}.golden +9/−0
- test/golden/{helloCOLON+s}.golden +12/−0
- test/golden/{helloCOLON,s}.golden +12/−0
- test/golden/{helloCOLON-s}.golden +12/−0
- test/golden/{helloCOLON=100s}.golden +12/−0
- test/golden/{helloCOLON=100}.golden +9/−0
- test/golden/{helloCOLONE}.golden +9/−0
- test/golden/{helloCOLONG}.golden +9/−0
- test/golden/{helloCOLONX}.golden +9/−0
- test/golden/{helloCOLON_s}.golden +12/−0
- test/golden/{helloCOLONb}.golden +9/−0
- test/golden/{helloCOLONd}.golden +9/−0
- test/golden/{helloCOLONe}.golden +9/−0
- test/golden/{helloCOLONf}.golden +9/−0
- test/golden/{helloCOLONg}.golden +9/−0
- test/golden/{helloCOLONo}.golden +9/−0
- test/golden/{helloCOLONx}.golden +9/−0
- test/golden/{numberCOLONX}.golden +9/−0
- test/golden/{numberCOLONb}.golden +9/−0
- test/golden/{numberCOLONd}.golden +9/−0
- test/golden/{numberCOLONo}.golden +9/−0
- test/golden/{numberCOLONx}.golden +9/−0
- test/golden/{piCOLON.{SLASH}}.golden +12/−0
- test/golden/{piCOLON.{}}.golden +14/−0
- test/golden/{truncate numberCOLON.3b}.golden +12/−0
- test/golden/{truncate numberCOLON.3d}.golden +12/−0
- test/golden/{truncate numberCOLON.3o}.golden +12/−0
- test/golden/{truncate numberCOLON.3x}.golden +12/−0
- test/golden/{}.golden +14/−0
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 -[](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|{}|]+ | ^^^^