diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30550cc..d61d81f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,23 +1,19 @@ name: (fast) CI - on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] - + branches: [main] jobs: build: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: cachix/install-nix-action@v21 - - uses: cachix/cachix-action@v12 - with: - name: guibou - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - # Builds - - name: Build current GHC - run: nix build .#pyf + - uses: actions/checkout@v2 + - uses: cachix/install-nix-action@v21 + - uses: cachix/cachix-action@v12 + with: + name: guibou + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + # Builds + - name: Build current GHC + run: nix build diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 497cb16..840f948 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -1,50 +1,42 @@ name: Complete CI - on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] - + branches: [main] jobs: nix_matrix: strategy: matrix: - # 88 is not in nixpkgs anymore - # 910 does not build yet for unknown reason - # We are not using nix build .#pyf_all because of github disk limitations - ghc: [86, 90, 92, 94, 96, 98] + ghc: [90, 92, 94, 96, 98, 910, 912] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: cachix/install-nix-action@v21 - - uses: cachix/cachix-action@v12 - with: - name: guibou - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - # Builds cabal (nix) - - name: Build with GHC ${{ matrix.ghc }} - run: nix build .#pyf_${{ matrix.ghc }} - + - uses: actions/checkout@v2 + - uses: cachix/install-nix-action@v21 + - uses: cachix/cachix-action@v12 + with: + name: guibou + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + # Builds cabal (nix) + - name: Build with GHC ${{ matrix.ghc }} + run: nix build .#pyf_${{ matrix.ghc }} stack_build: strategy: matrix: - resolver: [ lts-14.27 # 8.6 - , lts-16.31 # 8.8 - , lts-17.1 # 8.10 - , lts-19.1 # 9.0 - , lts-20.26 # 9.2 - , lts-21.17 # 9.4 - , lts-22.22 # 9.6 - , nightly-2024-05-15 # 9.8 nightly - ] - + resolver: [lts-14.27, # 8.6 + lts-16.31, # 8.8 + lts-17.1, # 8.10 + lts-19.1, # 9.0 + lts-20.26, # 9.2 + lts-21.17, # 9.4 + lts-22.22, # 9.6 + lts-23.2, # 9.8 + nightly-2025-01-03, # 9.10 nightly + ] runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup Stack - run: sudo apt-get install haskell-stack - - name: Stack resolver ${{ matrix.resolver }} - run: stack --resolver ${{ matrix.resolver }} test + - uses: actions/checkout@v2 + - name: Setup Stack + run: sudo apt-get install haskell-stack + - name: Stack resolver ${{ matrix.resolver }} + run: stack --resolver ${{ matrix.resolver }} test diff --git a/ChangeLog.md b/ChangeLog.md index 0a520fd..27a2ef0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,11 @@ # Revision history for PyF +- Support for GHC 9.12 +- No more "python" reference check in the test phase. I'm removing complexity, + and if it does not match the python implementation, we can just introduce a + new test case. Note that python checking can be reimplemented easilly by + parsing the AST. + ## 0.11.3.0 -- 2024-05-15 - Support for GHC 9.10. @@ -27,8 +33,8 @@ - Support for GHC 9.4. (Written with a pre-release of GHC 9.4, hopefully it won't change too much before the release). - Error reporting now uses the native GHC API. In summary, it means that - haskell-language-server will point to the correct location of the error, not - the beginning of the quasi quotes. + haskell-language-server will point to the correct location of the error, not + the beginning of the quasi quotes. - PyF will now correctly locate the error for variable not found in expression, even if the expression is complicated. The support for complex expression is limited, and PyF may return a false positive if you try to format a complex lambda / case expression. Please open a ticket if you need that. - Add support for literal `[]` and `()` in haskell expression. - Add support for overloaded labels, thank you Shimuuar. @@ -48,21 +54,30 @@ ## 0.10.0.1 -- 2021-10-30 - 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. +- 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. +- `fmtWithDelimiters` is gone and replaced by `mkFormatter` in `PyF` which is "more" generic. ## 0.9.0.3 -- 2021-02-06 @@ -115,7 +130,6 @@ - Template haskell splices are simpler. This leads to more efficient / small generated code and in the event of this code appears in a GHC error message, it is more readable. - PyF now longer emit unnecessary default typing. - ## 0.7.3.0 -- 2019-02-28 - Tests: fix non reproducible tests @@ -127,7 +141,7 @@ ## 0.7.1.0 -- 2019-02-11 - Fixed: PyF was wrongly ignoring everything located after a non-doubled closing delimiter. -- New Feature: line break can be escaped with \, thus allowing string to start on a new line ignoring the initial backspace +- New Feature: line break can be escaped with , thus allowing string to start on a new line ignoring the initial backspace ## 0.7.0.0 -- 2019-02-04 @@ -169,15 +183,14 @@ - Introduce `PyF.Formatters`, type safe generic number formatter solution - Remove dependency to `scientific` - ## 0.3.0.0 -- 2018-04-01 -* Support for haskell subexpression +- Support for haskell subexpression ## 0.1.1.0 -- 2018-01-07 -* Add support for the `sign` field. +- Add support for the `sign` field. ## 0.1.0.0 -- 2018-01-03 -* First version. Released on an unsuspecting world. +- First version. Released on an unsuspecting world. diff --git a/PyF.cabal b/PyF.cabal index bba61db..74406d2 100644 --- a/PyF.cabal +++ b/PyF.cabal @@ -1,75 +1,107 @@ -cabal-version: 2.4 -name: PyF -version: 0.11.3.0 -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/golden/*.golden test/golden96/*.golden - -Flag python_test - Description: Enable extensive python testing - Manual: True - Default: False +cabal-version: 2.4 +name: PyF +version: 0.11.3.0 +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/golden/*.golden + test/golden96/*.golden library exposed-modules: - PyF - PyF.Class - PyF.Internal.PythonSyntax - PyF.Internal.Meta - PyF.Internal.QQ - PyF.Formatters - PyF.Internal.ParserEx - PyF.Internal.Parser - - build-depends: base >= 4.12 && < 4.22 - , bytestring >= 0.10.8 && < 0.13 - , template-haskell >= 2.14.0 && < 2.24 - , text >= 1.2.3 && < 2.2 - , time >= 1.8.0 && < 1.14 - , parsec >= 3.1.13 && < 3.2 - , mtl >= 2.2.2 && < 2.4 - , ghc >= 8.6.1 - if impl(ghc < 9.2.1) - build-depends: - ghc-boot >= 8.6.1 && < 9.7 - hs-source-dirs: src - ghc-options: -Wall -Wunused-packages -Wincomplete-uni-patterns - default-language: Haskell2010 + PyF + PyF.Class + PyF.Formatters + PyF.Internal.Meta + PyF.Internal.Parser + PyF.Internal.ParserEx + PyF.Internal.PythonSyntax + PyF.Internal.QQ + + build-depends: + , base >=4.12 && <4.22 + , bytestring >=0.10.8 && <0.13 + , ghc >=8.6.1 + , mtl >=2.2.2 && <2.4 + , parsec >=3.1.13 && <3.2 + , template-haskell >=2.14.0 && <2.24 + , text >=1.2.3 && <2.2 + , time >=1.8.0 && <1.15 + + if impl(ghc <9.2.1) + build-depends: ghc-boot >=8.6.1 && <9.7 + + hs-source-dirs: src + 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, 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 - build-depends: process + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Spec.hs + other-modules: SpecCustomDelimiters + build-depends: + , base + , bytestring + , hspec + , PyF + , template-haskell + , text + , time + + ghc-options: + -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages + + default-language: Haskell2010 test-suite pyf-overloaded - type: exitcode-stdio-1.0 - hs-source-dirs: test - main-is: SpecOverloaded.hs - build-depends: base, PyF, hspec, text, bytestring - ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages - default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: SpecOverloaded.hs + build-depends: + , base + , bytestring + , hspec + , PyF + , text + + 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, process, hspec, temporary, filepath, deepseq, HUnit, PyF - ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages - default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: SpecFail.hs + build-depends: + , base + , deepseq + , filepath + , hspec + , HUnit + , process + , PyF + , temporary + , text + + ghc-options: + -Wall -threaded -rtsopts -with-rtsopts=-N -Wunused-packages + + default-language: Haskell2010 source-repository head type: git diff --git a/Readme.md b/Readme.md index af9672c..2609006 100644 --- a/Readme.md +++ b/Readme.md @@ -217,7 +217,7 @@ Type incompatible with precision (.3), use any of {'e', 'E', 'f', 'F', 'g', 'G', Note: error reporting uses the native GHC error infrastructure, so they will correctly appear in your editor (using [HLS](https://github.com/haskell/haskell-language-server)), for example: - ![Error reported in editor](error_example.png) +![Error reported in editor](error_example.png) - Error in variable name are also readable: @@ -297,7 +297,7 @@ Have a look at `PyF.mkFormatter` for all the details about customization. ## Differences 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. +The implementation *was* unit-tested against the reference Python implementation (Python 3.6.4) and should match its result. Since 2025, the standalone test suite is not cross checked with the standard python implementation. However some formatters are not supported or some (minor) differences can be observed. ### Not supported @@ -344,7 +344,7 @@ We also provide a few utility functions: # GHC compatibility -This library is tested in CI with ghc 8.6 to 9.2. +This library is tested in CI with ghc 8.6 to 9.12. # Conclusion @@ -355,7 +355,7 @@ Don't hesitate to make any suggestion, I'll be more than happy to work on it. Everything works with nix and flakes. But you can also try with manual cabal / stack if you wish. - `nix develop` 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 develop .#pyf_XY` opens a shell with a specific GHC version and without haskell-language-server. That's mostly to test compatibility with different GHC version or open a shell without HLS if you are in a hurry. Replace `pyf_XY` by `pyf_86`, `pyf_88`, `pyf_810`, `pyf_90` or `pyf_92`. +- `nix develop .#pyf_XY` opens a shell with a specific GHC version and without haskell-language-server. That's mostly to test compatibility with different GHC version or open a shell without HLS if you are in a hurry. Replace `pyf_XY` by a GHC version, e.g. `pyf_912`. Once in the shell, use `cabal build`, `cabal test`, `cabal repl`. @@ -363,15 +363,15 @@ There is a cachix available, used by CI, and already configured in flakes. You c You can locally build and test everything using: -- `nix build .#pyf_all`. +- `nix flake check` Don't hesitate to submit a PR not tested on all GHC versions. ## Formatting -The codebase is formatted with `ormolu`. Please run: +Please run: -- `nix run .\#run-ormolu` +- `nix run fmt` Before submitting. diff --git a/flake.lock b/flake.lock index c67ca9b..cf8678d 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1715759831, - "narHash": "sha256-IqSVO1C30USnIVzDoRZdBNC7uYPuRbxBeaty32CU4p8=", + "lastModified": 1735863392, + "narHash": "sha256-jdyYQ7GquKhseXmwG6lyx9ebCLo9lMX9iJ1htpbpOF8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "54b7ab5b45c62a29e1c5a6dab12df27d857ce3d1", + "rev": "f371cdec06dbc42781824c5324657294e7ac581b", "type": "github" }, "original": { @@ -34,10 +34,27 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1735554305, + "narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "treefmt-nix": "treefmt-nix" } }, "systems": { @@ -54,6 +71,24 @@ "repo": "default", "type": "github" } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1735905407, + "narHash": "sha256-1hKMRIT+QZNWX46e4gIovoQ7H8QRb7803ZH4qSKI45o=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "29806abab803e498df96d82dd6f34b32eb8dd2c8", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index d30acc3..9408a3a 100644 --- a/flake.nix +++ b/flake.nix @@ -3,165 +3,97 @@ inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.nixpkgs.url = "github:nixos/nixpkgs/haskell-updates"; + inputs.treefmt-nix.url = "github:numtide/treefmt-nix"; - # Broken: see https://github.com/NixOS/nix/issues/5621 - #nixConfig.allow-import-from-derivation = true; nixConfig.extra-substituters = [ "https://guibou.cachix.org" ]; - nixConfig.extra-trusted-public-keys = - [ "guibou.cachix.org-1:GcGQvWEyTx8t0KfQac05E1mrlPNHqs5fGMExiN9/pbM=" ]; - - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: - let pkgs = nixpkgs.legacyPackages.${system}; - in with pkgs; rec { - inherit pkgs; - # Explicit list of used files. Else there is always too much and - # cache is invalidated. - sources = lib.sourceByRegex ./. [ - "PyF.cabal$" - ".*.hs$" - ".*.md$" - ".*.golden$" - "src" - "app" - "src/PyF" - "src/PyF/Internal" - "test" - "test/golden" - "test/golden96" - "LICENSE" - ]; - - pyfBuilder = hPkgs: + nixConfig.extra-trusted-public-keys = [ + "guibou.cachix.org-1:GcGQvWEyTx8t0KfQac05E1mrlPNHqs5fGMExiN9/pbM=" + ]; + + outputs = + { + self, + nixpkgs, + flake-utils, + treefmt-nix, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + pyfBuilder = + hPkgs: let shell = pkg.env.overrideAttrs (old: { - nativeBuildInputs = old.nativeBuildInputs - ++ [ cabal-install python3 ]; + nativeBuildInputs = old.nativeBuildInputs ++ (with pkgs; [ cabal-install ]); }); # Shell with haskell language server shell_hls = shell.overrideAttrs (old: { - nativeBuildInputs = old.nativeBuildInputs - ++ [ hPkgs.haskell-language-server ]; - }); - - pkg = ( - (hPkgs.callCabal2nix "PyF" sources { })).overrideAttrs - (oldAttrs: { - buildInputs = oldAttrs.buildInputs; - passthru = oldAttrs.passthru // { inherit shell shell_hls; }; - }); - # Add the GHC version in the package name - in pkg.overrideAttrs (old: { name = "PyF-ghc${hPkgs.ghc.version}"; }); - - packages = rec { - pyf_86 = (pyfBuilder (haskell.packages.ghc865Binary.override { - overrides = self: super: with haskell.lib; { }; - })).overrideAttrs (old: { - passthru.shell = old.passthru.shell.overrideAttrs (old: { - # for some reasons, ncurses is not part of the dependencies of ghc... - buildInputs = old.buildInputs ++ [ pkgs.ncurses ]; + nativeBuildInputs = old.nativeBuildInputs ++ [ hPkgs.haskell-language-server ]; }); - }); - - # GHC 8.8 is not in nixpkgs anymore. - - pyf_810 = pyfBuilder (haskell.packages.ghc810.override { - overrides = self: super: with haskell.lib; { }; - }); - - pyf_90 = pyfBuilder (haskell.packages.ghc90.override { - overrides = self: super: with haskell.lib; { }; - }); - - pyf_92 = pyfBuilder (haskell.packages.ghc92.override { - overrides = self: super: with haskell.lib; rec { }; - }); - - # The current version for debug - pyf_current = pyfBuilder (haskellPackages.override { - overrides = self: super: with haskell.lib; rec { - hls-floskell-plugin = doJailbreak super.hls-floskell-plugin; - hls-formolu-plugin = doJailbreak super.hls-formolu-plugin; - }; - }); - # GHC 9.4 - pyf_94 = pyfBuilder ((haskell.packages.ghc94.override { - overrides = self: super: - with haskell.lib; { + pkg = ((hPkgs.callCabal2nix "PyF" ./. { })).overrideAttrs (oldAttrs: { + buildInputs = oldAttrs.buildInputs; + passthru = oldAttrs.passthru // { + inherit shell shell_hls; }; - })); - - pyf_96 = pyfBuilder (haskell.packages.ghc96.override { - overrides = self: super: with haskell.lib; rec { - }; - }); - - pyf_98 = pkgs.haskell.lib.dontCheck (pyfBuilder ((haskell.packages.ghc98.override { - overrides = self: super: - with haskell.lib; { - # Bump hspec (and dependencies) - #hspec-core = super.callHackage "hspec-core" "2.11.6" {}; - #hspec-meta = super.callHackage "hspec-meta" "2.11.6" {}; - #hspec = super.callHackage "hspec" "2.11.6" {}; - #hspec-discover = super.callHackage "hspec-discover" "2.11.6" {}; - #hspec-expectations = super.callHackage "hspec-expectations" "0.8.4" {}; - #tagged = doJailbreak super.tagged; - - ## Disabling tests breaks the loop with hspec - #base-orphans = dontCheck super.base-orphans; - #splitmix = doJailbreak super.splitmix; - - }; - }))); - - pyf_910 = pyfBuilder (haskell.packages.ghc910.override { - overrides = self: super: with haskell.lib; rec { - primitive = dontCheck super.primitive_0_9_0_0; - HUnit = dontCheck super.HUnit; - call-stack = dontCheck super.call-stack; - hspec-expectations = dontCheck super.hspec-expectations; - QuickCheck = dontCheck super.QuickCheck; - hspec-discover = dontCheck super.hspec-discover; - }; + }); + in + # Add the GHC version in the package name + pkg.overrideAttrs (old: { + pname = "PyF-ghc${hPkgs.ghc.version}"; + name = "PyF-ghc${hPkgs.ghc.version}-${old.version}"; }); - pyf_all = linkFarmFromDrvs "all_pyf" [ - pyf_86 + in + with pkgs; + rec { + checks = { + inherit (packages) pyf_810 pyf_90 pyf_92 pyf_94 pyf_96 pyf_98 - - # https://github.com/NixOS/nixpkgs/pull/311912/files - # For some reason, nixpkgs does not build correctly with recent cabal pyf_910 - ]; + pyf_912 + ; - # Only the current build is built with python3 support - # (i.e. extended tests) - pyf = haskell.lib.enableCabalFlag (pyf_current.overrideAttrs - (old: { buildInputs = old.buildInputs ++ [ python3 ]; })) - "python_test"; + formatting = treefmtEval.config.build.check self; }; - apps = { - run-ormolu = { - type = "app"; - program = "${writeScript "pyf-ormolu" '' - ${ormolu}/bin/ormolu --mode inplace $(git ls-files | grep '\.hs$') - exit 0 - ''}"; - }; + packages = { + # GHC 8.6 is tested with stack, I'm stopping the testing with nix. + # GHC 8.8 is not in nixpkgs anymore. + + pyf_810 = pyfBuilder haskell.packages.ghc810; + pyf_90 = pyfBuilder haskell.packages.ghc90; + pyf_92 = pyfBuilder haskell.packages.ghc92; + pyf_94 = pyfBuilder haskell.packages.ghc94; + pyf_96 = pyfBuilder haskell.packages.ghc96; + pyf_98 = pkgs.haskell.lib.dontCheck (pyfBuilder haskell.packages.ghc98); + + pyf_910 = pyfBuilder haskell.packages.ghc910; + pyf_912 = pyfBuilder haskell.packages.ghc912; + + default = pyfBuilder haskellPackages; }; - defaultPackage = packages.pyf; - devShell = packages.pyf.shell_hls; + formatter = treefmtEval.config.build.wrapper; + devShells = (builtins.mapAttrs (name: value: value.shell) packages) // { - treesitter = pkgs.mkShell { buildInputs = [ pkgs.tree-sitter pkgs.nodejs ]; }; + treesitter = pkgs.mkShell { + buildInputs = [ + pkgs.tree-sitter + pkgs.nodejs + ]; + }; + default = packages.default.shell_hls; }; - }); + } + ); } diff --git a/missing_variable b/missing_variable deleted file mode 100644 index b6be8a9..0000000 Binary files a/missing_variable and /dev/null differ diff --git a/src/PyF/Class.hs b/src/PyF/Class.hs index 95e6e08..3c83485 100644 --- a/src/PyF/Class.hs +++ b/src/PyF/Class.hs @@ -135,7 +135,7 @@ instance PyFToString Data.ByteString.Lazy.ByteString where pyfToString = pyfToSt 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 +instance {-# OVERLAPPABLE #-} (Show t) => PyFToString t where pyfToString = show -- * Real formatting (with optional fractional part) @@ -163,7 +163,7 @@ class PyfFormatFractional a where String -- | Default instance working for any 'Real'. Internally it converts the type to 'Double'. -instance {-# OVERLAPPABLE #-} Real t => PyfFormatFractional t where +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. @@ -183,7 +183,7 @@ instance PyfFormatFractional Float where pyfFormatFractional = formatFractional -- 'Integral' constraint. class PyfFormatIntegral i where pyfFormatIntegral :: - Integral paddingWidth => + (Integral paddingWidth) => Format t t' 'Integral -> -- | Sign formatting SignMode -> @@ -195,7 +195,7 @@ class PyfFormatIntegral i where String -- | Default instance for any 'Integral'. -instance {-# OVERLAPPABLE #-} Integral t => PyfFormatIntegral t where +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' diff --git a/src/PyF/Formatters.hs b/src/PyF/Formatters.hs index 99d7631..cc15d38 100644 --- a/src/PyF/Formatters.hs +++ b/src/PyF/Formatters.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} @@ -9,7 +10,6 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE DeriveDataTypeable #-} -- | -- @@ -53,10 +53,10 @@ module PyF.Formatters where import Data.Char (chr, toUpper) +import Data.Data (Data) import Data.List (intercalate) import Language.Haskell.TH.Syntax import qualified Numeric -import Data.Data (Data) -- ADT for API @@ -84,7 +84,6 @@ data AlignMode (k :: AlignForString) where -- | Padding will be added around the value AlignCenter :: AlignMode 'AlignAll - deriving instance Show (AlignMode k) -- The generic version @@ -174,8 +173,8 @@ reprFractional fmt precision f | isInfinite f = Infinite sign (upperIt "inf") | isNaN f = NaN (upperIt "nan") | isNegativeZero f = case reprFractional fmt precision (abs f) of - FractionalRepr Positive aa bb cc -> FractionalRepr Negative aa bb cc - other -> error $ "reprFractional (isNegativeZero f): The impossible happened : " ++ show other ++ ". Please open an issue at https://github.com/guibou/PyF/issues/" + FractionalRepr Positive aa bb cc -> FractionalRepr Negative aa bb cc + other -> error $ "reprFractional (isNegativeZero f): The impossible happened : " ++ show other ++ ". Please open an issue at https://github.com/guibou/PyF/issues/" | otherwise = FractionalRepr sign decimalPart fractionalPart suffixPart where upperIt s = case fmt of @@ -189,8 +188,8 @@ reprFractional fmt precision f Exponent -> overrideExponent precision $ splitFractionalExp (Numeric.showEFloat precision iAbs "") Generic -> splitFractionalExp (Numeric.showGFloatAlt precision iAbs "") Percent -> case splitFractional (Numeric.showFFloatAlt precision (iAbs * 100) "") of - (a, b, "") -> (a, b, "%") - other -> error $ "reprFractional (format): The impossible happened : " ++ show other ++ ". Please open an issue at https://github.com/guibou/PyF/issues/" + (a, b, "") -> (a, b, "%") + other -> error $ "reprFractional (format): The impossible happened : " ++ show other ++ ". Please open an issue at https://github.com/guibou/PyF/issues/" Alternate fmt' -> format fmt' Upper fmt' -> let (a, b, c) = format fmt' @@ -229,7 +228,7 @@ group (IntegralRepr s str) (Just (size, c)) = IntegralRepr s (groupIntercalate c group (FractionalRepr s a b d) (Just (size, c)) = FractionalRepr s (groupIntercalate c size a) b d group i _ = i -padAndSign :: Integral paddingWidth => Format t t' t'' -> String -> SignMode -> Maybe (paddingWidth, AlignMode k, Char) -> Repr -> String +padAndSign :: (Integral paddingWidth) => Format t t' t'' -> String -> SignMode -> Maybe (paddingWidth, AlignMode k, Char) -> Repr -> String padAndSign format prefix sign padding repr = leftAlignMode <> prefixStr <> middleAlignMode <> content <> rightAlignMode where (signStr, content) = case repr of @@ -282,8 +281,8 @@ groupIntercalate c i s = intercalate [c] (reverse (pack (reverse s))) -- | Format an integral number. formatIntegral :: - Integral paddingWidth => - Integral i => + (Integral paddingWidth) => + (Integral i) => Format t t' 'Integral -> SignMode -> -- | Padding diff --git a/src/PyF/Internal/Meta.hs b/src/PyF/Internal/Meta.hs index 53cbf12..0f4c329 100644 --- a/src/PyF/Internal/Meta.hs +++ b/src/PyF/Internal/Meta.hs @@ -180,7 +180,9 @@ toExp d (Expr.ExprWithTySig HsWC{hswc_body=HsIB{hsib_body}} e) = TH.SigE (toExp 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 -#if MIN_VERSION_ghc(9,10,0) +#if MIN_VERSION_ghc(9,12,0) +toExp d (Expr.HsLam _ _ (Expr.MG _ (unLoc -> (map unLoc -> [Expr.Match _ _ (unLoc -> map unLoc -> ps) (Expr.GRHSs _ [unLoc -> Expr.GRHS _ _ (unLoc -> e)] _)])))) = TH.LamE (fmap (toPat d) ps) (toExp d e) +#elif MIN_VERSION_ghc(9,10,0) 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) #elif MIN_VERSION_ghc(9,6,0) 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) @@ -246,7 +248,9 @@ toExp d (Expr.ArithSeq _ _ e) = TH.ArithSeqE $ case e of (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) -#if MIN_VERSION_ghc(9,7,0) +#if MIN_VERSION_ghc(9,12,0) +toExp _ (HsOverLabel _ fs) = TH.LabelE (unpackFS fs) +#elif MIN_VERSION_ghc(9,7,0) toExp _ (HsOverLabel _ lbl _) = TH.LabelE (fromSourceText lbl) where fromSourceText :: SourceText -> String @@ -265,7 +269,10 @@ toExp _ (HsOverLabel _ lbl) = TH.LabelE (unpackFS lbl) -- enabled thus match on Nothing toExp _ (HsOverLabel _ Nothing lbl) = TH.LabelE (unpackFS lbl) #endif -#if MIN_VERSION_ghc(9,6,0) +#if MIN_VERSION_ghc(9,12,0) +toExp dynFlags (HsGetField _ expr field) = TH.GetFieldE (toExp dynFlags (unLoc expr)) (unpackFS . field_label . unLoc . dfoLabel . unLoc $ field) +toExp _ (HsProjection _ fields) = TH.ProjectionE (fmap (unpackFS . unLoc . fmap field_label . dfoLabel) fields) +#elif MIN_VERSION_ghc(9,6,0) toExp dynFlags (HsGetField _ expr field) = TH.GetFieldE (toExp dynFlags (unLoc expr)) (unpackFS . field_label . unLoc . dfoLabel . unLoc $ field) toExp _ (HsProjection _ fields) = TH.ProjectionE (fmap (unpackFS . unLoc . fmap field_label . dfoLabel . unLoc) fields) #elif MIN_VERSION_ghc(9, 4, 0) @@ -295,4 +302,3 @@ baseDynFlags exts = foldl xopt_set dynFlags enable #else dynFlags = defaultDynFlags fakeSettings fakeLlvmConfig #endif - diff --git a/src/PyF/Internal/Parser.hs b/src/PyF/Internal/Parser.hs index ba3d886..e5910be 100644 --- a/src/PyF/Internal/Parser.hs +++ b/src/PyF/Internal/Parser.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} -- | This module is here to parse Haskell expression using the GHC Api module PyF.Internal.Parser (parseExpression) where diff --git a/src/PyF/Internal/PythonSyntax.hs b/src/PyF/Internal/PythonSyntax.hs index d7794e5..0950445 100644 --- a/src/PyF/Internal/PythonSyntax.hs +++ b/src/PyF/Internal/PythonSyntax.hs @@ -1,11 +1,11 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE CPP #-} -- | -- This module provides a parser for . @@ -28,6 +28,7 @@ import Control.Applicative (some) import Control.Monad (replicateM_, void) import Control.Monad.Reader (Reader, asks) import qualified Data.Char +import Data.Data (Data) import Data.Maybe (fromMaybe) import GHC (GhcPs, HsExpr) import Language.Haskell.TH.LanguageExtensions (Extension (..)) @@ -36,7 +37,6 @@ import PyF.Formatters import PyF.Internal.Meta import qualified PyF.Internal.Parser as ParseExp import Text.Parsec -import Data.Data (Data) #if MIN_VERSION_ghc(9,7,0) @@ -332,7 +332,7 @@ parsePrecision = do -- | 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 :: (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 diff --git a/src/PyF/Internal/QQ.hs b/src/PyF/Internal/QQ.hs index 76c6508..88c90f9 100644 --- a/src/PyF/Internal/QQ.hs +++ b/src/PyF/Internal/QQ.hs @@ -35,7 +35,6 @@ import Data.Maybe (catMaybes, fromMaybe, isJust) import Data.Proxy import Data.String (fromString) - #if MIN_VERSION_ghc(9,0,0) import GHC.Tc.Utils.Monad (addErrAt) import GHC.Tc.Types (TcM) @@ -63,8 +62,6 @@ import GHC.Parser.Errors.Types #endif #endif - - #if MIN_VERSION_ghc(9,0,0) import GHC.Types.Name.Reader #else @@ -170,9 +167,10 @@ toExp Config {delimiters = expressionDelimiters, postProcess} s = do findFreeVariablesInFormatMode :: Maybe FormatMode -> [(SrcSpan, RdrName)] findFreeVariablesInFormatMode Nothing = [] -findFreeVariablesInFormatMode (Just (FormatMode padding tf _ )) = findFreeVariables tf <> case padding of - PaddingDefault -> [] - Padding eoi _ -> findFreeVariables eoi +findFreeVariablesInFormatMode (Just (FormatMode padding tf _)) = + findFreeVariables tf <> case padding of + PaddingDefault -> [] + Padding eoi _ -> findFreeVariables eoi checkOneItem :: Item -> Q (Maybe (SrcSpan, String)) checkOneItem (Raw _) = pure Nothing @@ -185,7 +183,7 @@ checkOneItem (Replacement (hsExpr, _) formatMode) = do [] -> pure Nothing ((err, span) : _) -> pure $ Just (span, err) - +{- ORMOLU_DISABLE -} findFreeVariables :: Data a => a -> [(SrcSpan, RdrName)] findFreeVariables item = allNames where @@ -198,17 +196,31 @@ findFreeVariables item = allNames Just (HsVar _ l) -> [l] #endif -#if MIN_VERSION_ghc(9,10,0) +#if MIN_VERSION_ghc(9,12,0) + Just (HsLam _ _ (MG _ (unLoc -> (map unLoc -> [Expr.Match _ _ (unLoc -> map unLoc -> ps) (GRHSs _ [unLoc -> GRHS _ _ (unLoc -> e)] _)])))) -> filter keepVar subVars + where + keepVar (L _ n) = n `notElem` subPats + subVars = concat $ gmapQ f [e] + subPats = concat $ gmapQ findPats ps +#elif MIN_VERSION_ghc(9,10,0) Just (HsLam _ _ (MG _ (unLoc -> (map unLoc -> [Expr.Match _ _ (map unLoc -> ps) (GRHSs _ [unLoc -> GRHS _ _ (unLoc -> e)] _)])))) -> filter keepVar subVars + where + keepVar (L _ n) = n `notElem` subPats + subVars = concat $ gmapQ f [e] + subPats = concat $ gmapQ findPats ps #elif MIN_VERSION_ghc(9,6,0) Just (HsLam _ (MG _ (unLoc -> (map unLoc -> [Expr.Match _ _ (map unLoc -> ps) (GRHSs _ [unLoc -> GRHS _ _ (unLoc -> e)] _)])))) -> filter keepVar subVars + where + keepVar (L _ n) = n `notElem` subPats + subVars = concat $ gmapQ f [e] + subPats = concat $ gmapQ findPats ps #else Just (HsLam _ (MG _ (unLoc -> (map unLoc -> [Expr.Match _ _ (map unLoc -> ps) (GRHSs _ [unLoc -> GRHS _ _ (unLoc -> e)] _)])) _)) -> filter keepVar subVars -#endif where keepVar (L _ n) = n `notElem` subPats subVars = concat $ gmapQ f [e] subPats = concat $ gmapQ findPats ps +#endif _ -> concat $ gmapQ f e -- Find all Variables bindings (i.e. patterns) in an HsExpr @@ -221,6 +233,7 @@ findFreeVariables item = allNames -- level expression: gmapQ only checks sub constructors. allVars = concat $ gmapQ f [item] allNames = map (\(L l e) -> (l, e)) allVars +{- ORMOLU_ENABLE -} lookupName :: RdrName -> Q Bool lookupName n = case n of @@ -302,6 +315,7 @@ formatErrorMessages err (_sysUnExpect, msgs1) = span (SysUnExpect "" ==) (errorMessages err) (_unExpect, msgs2) = span (UnExpect "" ==) msgs1 (_expect, messages) = span (Expect "" ==) msgs2 + {- Note: Empty String Lifting @@ -321,7 +335,7 @@ sappendQ s0 s1 = InfixE (Just s0) (VarE '(<>)) (Just s1) toFormat :: Item -> Q Exp toFormat (Raw x) = pure $ LitE (StringL x) -- see [Empty String Lifting] -toFormat (Replacement ( _, expr) y) = do +toFormat (Replacement (_, expr) y) = do formatExpr <- padAndFormat (fromMaybe DefaultFormatMode y) pure (formatExpr `AppE` expr) @@ -395,7 +409,7 @@ paddingKToPadding p = case p of Just (Nothing, a) -> Just (i, AnyAlign a, ' ') Just (Just c, a) -> Just (i, AnyAlign a, c) -formatAnyIntegral :: forall i paddingWidth t t'. Integral paddingWidth => PyfFormatIntegral i => Formatters.Format t t' 'Formatters.Integral -> Formatters.SignMode -> Maybe (paddingWidth, AnyAlign, Char) -> Maybe (Int, Char) -> i -> String +formatAnyIntegral :: forall i paddingWidth t t'. (Integral paddingWidth) => (PyfFormatIntegral i) => Formatters.Format t t' 'Formatters.Integral -> Formatters.SignMode -> Maybe (paddingWidth, AnyAlign, Char) -> Maybe (Int, Char) -> i -> String formatAnyIntegral f s Nothing grouping i = pyfFormatIntegral @i @paddingWidth f s Nothing grouping i formatAnyIntegral f s (Just (padSize, AnyAlign alignMode, c)) grouping i = pyfFormatIntegral f s (Just (padSize, alignMode, c)) grouping i @@ -418,7 +432,7 @@ instance (Show t, Integral t) => FormatAny2 'PyFIntegral t k where instance (PyfFormatFractional t) => FormatAny2 'PyFFractional t k where formatAny2 _ s a = formatAnyFractional Formatters.Generic s (paddingKToPadding a) -newPaddingKForString :: Integral i => PaddingK 'Formatters.AlignAll i -> Maybe (Int, Formatters.AlignMode 'Formatters.AlignAll, Char) +newPaddingKForString :: (Integral i) => PaddingK 'Formatters.AlignAll i -> Maybe (Int, Formatters.AlignMode 'Formatters.AlignAll, Char) newPaddingKForString padding = case padding of PaddingDefaultK -> Nothing PaddingK i Nothing -> Just (fromIntegral i, Formatters.AlignLeft, ' ') -- default align left and fill with space for string @@ -428,5 +442,5 @@ newPaddingKForString padding = case padding of instance (PyFToString t) => FormatAny2 'PyFString t 'Formatters.AlignAll where formatAny2 _ _s a _grouping precision t = Formatters.formatString (newPaddingKForString a) precision (pyfToString t) -instance TypeError ('Text "String type is incompatible with inside padding (=).") => FormatAny2 'PyFString t 'Formatters.AlignNumber where +instance (TypeError ('Text "String type is incompatible with inside padding (=).")) => FormatAny2 'PyFString t 'Formatters.AlignNumber where formatAny2 = error "Unreachable" diff --git a/stack.yaml b/stack.yaml index b7f23a8..f067734 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,9 +1,7 @@ resolver: lts-15.4 - packages: -- . - + - . nix: # Not enabled by default. It will be on nixos, but not on CI enable: false - packages: [ python3 ] + packages: [python3] diff --git a/test/Spec.hs b/test/Spec.hs index cde9bb1..f9133ac 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -27,25 +27,17 @@ import qualified Data.List as List import Data.Proxy (Proxy (..)) import qualified Data.Ratio import qualified Data.Text +import qualified Data.Text as Text import qualified Data.Text.Lazy import qualified Data.Time import GHC.OverloadedLabels import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) import PyF import SpecCustomDelimiters -import SpecUtils import Test.Hspec -import qualified Data.Text as Text - -{- - - Normal tests are done using the recommanded API: [fmt|.....|] - - Test with $(checkExample formatString result) are checked against the python reference implementation. Result is provided as documentation. - - Test with $(checkExampleDiff formatString result) are not checked against the python reference implementation. This is known (and documented) differences. - - Test with $(check formatString) are only tested against the python reference implementation. --} main :: IO () -main = hspec spec +main = hspec $ parallel spec newtype FooFloating t = FooFloating t deriving newtype (Show, RealFloat, RealFrac, Floating, Fractional, Real, Enum, Num, Ord, Eq, PyfFormatFractional) @@ -72,13 +64,13 @@ type instance PyFClassify FooDefault = 'PyFString -- Data type to test overloaded labels data V (a :: Symbol) = V -instance KnownSymbol a => Show (V a) where +instance (KnownSymbol a) => Show (V a) where show V = "V=" ++ symbolVal (Proxy @a) instance (a ~ a') => IsLabel a (V a') where fromLabel = V -showV :: KnownSymbol a => V a -> String +showV :: (KnownSymbol a) => V a -> String showV = show globalName :: String @@ -95,159 +87,161 @@ spec = do it "string" $ [fmt|{aString}|] `shouldBe` "hello" describe "only expression" $ do describe "default" $ do - it "int" $(checkExample "{123}" "123") - it "float" $(checkExample "{0.234}" "0.234") - it "string" $(checkExample "{\"hello\"}" "hello") - it "float precision" $(checkExample "{0.234:.1}" "0.2") - it "string precision" $(checkExample "{\"hello\":.1}" "h") - it "sign +" $(checkExample "{0.234:+}" "+0.234") - it "sign space" $(checkExample "{0.234: }" " 0.234") - it "sign neg" $(checkExample "{-123:+}" "-123") + it "int" $ [fmt|{123}|] `shouldBe` "123" + it "float" $ [fmt|{0.234}|] `shouldBe` "0.234" + it "string" $ [fmt|{"hello"}|] `shouldBe` "hello" + it "float precision" $ [fmt|{0.234:.1}|] `shouldBe` "0.2" + it "string precision" $ [fmt|{"hello":.1}|] `shouldBe` "h" + it "sign +" $ [fmt|{0.234:+}|] `shouldBe` "+0.234" + it "sign space" $ [fmt|{0.234: }|] `shouldBe` " 0.234" + it "sign neg" $ [fmt|{-123:+}|] `shouldBe` "-123" describe "binary" $ do - it "simple" $(checkExample "{123:b}" "1111011") - it "alt" $(checkExample "{123:#b}" "0b1111011") - it "sign" $(checkExample "{123:+#b}" "+0b1111011") + it "simple" $ [fmt|{123:b}|] `shouldBe` "1111011" + it "alt" $ [fmt|{123:#b}|] `shouldBe` "0b1111011" + it "sign" $ [fmt|{123:+#b}|] `shouldBe` "+0b1111011" describe "character" $ - it "simple" $(checkExample "{123:c}" "{") + it "simple" $ + [fmt|{123:c}|] `shouldBe` "{" describe "decimal" $ do - it "simple" $(checkExample "{123:d}" "123") - it "sign" $(checkExample "{123:+d}" "+123") + it "simple" $ [fmt|{123:d}|] `shouldBe` "123" + it "sign" $ [fmt|{123:+d}|] `shouldBe` "+123" describe "exponentiel" $ do - it "simple > 1" $(checkExample "{234.0:e}" "2.340000e+02") - it "precision > 1" $(checkExample "{234.0:.1e}" "2.3e+02") - it "simple < 1" $(checkExample "{0.234:e}" "2.340000e-01") - it "precision < 1 " $(checkExample "{0.234:.1e}" "2.3e-01") + it "simple > 1" $ [fmt|{234.0:e}|] `shouldBe` "2.340000e+02" + it "precision > 1" $ [fmt|{234.0:.1e}|] `shouldBe` "2.3e+02" + it "simple < 1" $ [fmt|{0.234:e}|] `shouldBe` "2.340000e-01" + it "precision < 1 " $ [fmt|{0.234:.1e}|] `shouldBe` "2.3e-01" describe "exponentiel caps" $ do - it "simple > 1" $(checkExample "{234.0:E}" "2.340000E+02") - it "precision > 1" $(checkExample "{234.0:.1E}" "2.3E+02") - it "simple < 1" $(checkExample "{0.234:E}" "2.340000E-01") - it "precision < 1 " $(checkExample "{0.234:.1E}" "2.3E-01") + it "simple > 1" $ [fmt|{234.0:E}|] `shouldBe` "2.340000E+02" + it "precision > 1" $ [fmt|{234.0:.1E}|] `shouldBe` "2.3E+02" + it "simple < 1" $ [fmt|{0.234:E}|] `shouldBe` "2.340000E-01" + it "precision < 1 " $ [fmt|{0.234:.1E}|] `shouldBe` "2.3E-01" describe "general" $ do - it "simple small" $(checkExampleDiff "{123.02:g}" "123.020000") - it "precision small" $(checkExampleDiff "{123.02:.1g}" "123.0") - it "simple big" $(checkExampleDiff "{1234567890.23:g}" "1.234568e+09") - it "precision big" $(checkExampleDiff "{1234567890.23:.1g}" "1.2e+09") + it "simple small" $ [fmt|{123.02:g}|] `shouldBe` "123.020000" + it "precision small" $ [fmt|{123.02:.1g}|] `shouldBe` "123.0" + it "simple big" $ [fmt|{1234567890.23:g}|] `shouldBe` "1.234568e+09" + it "precision big" $ [fmt|{1234567890.23:.1g}|] `shouldBe` "1.2e+09" describe "general caps" $ do - it "simple small" $(checkExampleDiff "{123.02:G}" "123.020000") - it "precision small" $(checkExampleDiff "{123.02:.1G}" "123.0") - it "simple big" $(checkExampleDiff "{1234567890.23:G}" "1.234568E+09") - it "precision big" $(checkExampleDiff "{1234567890.23:.1G}" "1.2E+09") + it "simple small" $ [fmt|{123.02:G}|] `shouldBe` "123.020000" + it "precision small" $ [fmt|{123.02:.1G}|] `shouldBe` "123.0" + it "simple big" $ [fmt|{1234567890.23:G}|] `shouldBe` "1.234568E+09" + it "precision big" $ [fmt|{1234567890.23:.1G}|] `shouldBe` "1.2E+09" describe "fixed" $ do - it "simple" $(checkExample "{0.234:f}" "0.234000") - it "precision" $(checkExample "{0.234:.1f}" "0.2") + it "simple" $ [fmt|{0.234:f}|] `shouldBe` "0.234000" + it "precision" $ [fmt|{0.234:.1f}|] `shouldBe` "0.2" describe "fixed caps" $ do - it "simple" $(checkExample "{0.234:F}" "0.234000") - it "precision" $(checkExample "{0.234:.1F}" "0.2") + it "simple" $ [fmt|{0.234:F}|] `shouldBe` "0.234000" + it "precision" $ [fmt|{0.234:.1F}|] `shouldBe` "0.2" describe "octal" $ do - it "simple" $(checkExample "{123:o}" "173") - it "alt" $(checkExample "{123:#o}" "0o173") + it "simple" $ [fmt|{123:o}|] `shouldBe` "173" + it "alt" $ [fmt|{123:#o}|] `shouldBe` "0o173" describe "string" $ do - it "string" $(checkExample "{\"hello\":s}" "hello") - it "precision" $(checkExample "{\"hello\":.2s}" "he") + it "string" $ [fmt|{"hello":s}|] `shouldBe` "hello" + it "precision" $ [fmt|{"hello":.2s}|] `shouldBe` "he" describe "hex" $ do - it "simple" $(checkExample "{123:x}" "7b") - it "alt" $(checkExample "{123:#x}" "0x7b") + it "simple" $ [fmt|{123:x}|] `shouldBe` "7b" + it "alt" $ [fmt|{123:#x}|] `shouldBe` "0x7b" describe "hex caps" $ do - it "simple" $(checkExample "{123:X}" "7B") - it "alt" $(checkExample "{123:#X}" "0X7B") + it "simple" $ [fmt|{123:X}|] `shouldBe` "7B" + it "alt" $ [fmt|{123:#X}|] `shouldBe` "0X7B" describe "percent" $ do - it "simple" $(checkExample "{0.234:%}" "23.400000%") - it "precision" $(checkExample "{0.234:.2%}" "23.40%") + it "simple" $ [fmt|{0.234:%}|] `shouldBe` "23.400000%" + it "precision" $ [fmt|{0.234:.2%}|] `shouldBe` "23.40%" describe "string truncating" $ - it "works" $(checkExample "{\"hello\":.3}" "hel") + it "works" $ + [fmt|{"hello":.3}|] `shouldBe` "hel" describe "padding" $ do describe "default char" $ do - it "left" $(checkExample "{\"hello\":<10}" "hello ") - it "right" $(checkExample "{\"hello\":>10}" " hello") - it "center" $(checkExample "{\"hello\":^10}" " hello ") + it "left" $ [fmt|{"hello":<10}|] `shouldBe` "hello " + it "right" $ [fmt|{"hello":>10}|] `shouldBe` " hello" + it "center" $ [fmt|{"hello":^10}|] `shouldBe` " hello " describe "a char" $ do - it "left" $(checkExample "{\"hello\":-<10}" "hello-----") - it "right" $(checkExample "{\"hello\":->10}" "-----hello") - it "center" $(checkExample "{\"hello\":-^10}" "--hello---") + it "left" $ [fmt|{"hello":-<10}|] `shouldBe` "hello-----" + it "right" $ [fmt|{"hello":->10}|] `shouldBe` "-----hello" + it "center" $ [fmt|{"hello":-^10}|] `shouldBe` "--hello---" describe "inside" $ do - it "inside" $(checkExample "{123:=+10}" "+ 123") - it "inside" $(checkExample "{123:=10}" " 123") - it "inside" $(checkExample "{- 123:=10}" "- 123") - it "inside" $(checkExample "{- 123:|= 10}" "-||||||123") - it "inside" $(checkExample "{123:|= 10}" " ||||||123") + it "inside" $ [fmt|{123:=+10}|] `shouldBe` "+ 123" + it "inside" $ [fmt|{123:=10}|] `shouldBe` " 123" + it "inside" $ [fmt|{- 123:=10}|] `shouldBe` "- 123" + it "inside" $ [fmt|{- 123:|= 10}|] `shouldBe` "-||||||123" + it "inside" $ [fmt|{123:|= 10}|] `shouldBe` " ||||||123" describe "default padding" $ do - it "floating" $(checkExample "{1:10f}" " 1.000000") - it "integral" $(checkExample "{1:10d}" " 1") - it "string" $(checkExample "{\"h\":10s}" "h ") - it "default" $(checkExample "{1:10}" " 1") - it "default" $(checkExample "{1.0:10}" " 1.0") - it "default" $(checkExample "{\"h\":10}" "h ") + it "floating" $ [fmt|{1:10f}|] `shouldBe` " 1.000000" + it "integral" $ [fmt|{1:10d}|] `shouldBe` " 1" + it "string" $ [fmt|{"h":10s}|] `shouldBe` "h " + it "default" $ [fmt|{1:10}|] `shouldBe` " 1" + it "default" $ [fmt|{1.0:10}|] `shouldBe` " 1.0" + it "default" $ [fmt|{"h":10}|] `shouldBe` "h " describe "NaN" $ do 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") + it "nan" $ [fmt|{nan}|] `shouldBe` "nan" + it "nan f" $ [fmt|{nan:f}|] `shouldBe` "nan" + it "nan e" $ [fmt|{nan:e}|] `shouldBe` "nan" + it "nan g" $ [fmt|{nan:g}|] `shouldBe` "nan" + it "nan F" $ [fmt|{nan:F}|] `shouldBe` "NAN" + it "nan G" $ [fmt|{nan:G}|] `shouldBe` "NAN" + it "nan E" $ [fmt|{nan:E}|] `shouldBe` "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") + it "nan" $ [fmt|{nan}|] `shouldBe` "nan" + it "nan f" $ [fmt|{nan:f}|] `shouldBe` "nan" + it "nan e" $ [fmt|{nan:e}|] `shouldBe` "nan" + it "nan g" $ [fmt|{nan:g}|] `shouldBe` "nan" + it "nan F" $ [fmt|{nan:F}|] `shouldBe` "NAN" + it "nan G" $ [fmt|{nan:G}|] `shouldBe` "NAN" + it "nan E" $ [fmt|{nan:E}|] `shouldBe` "NAN" describe "Infinite" $ do 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") + it "infinite" $ [fmt|{inf}|] `shouldBe` "inf" + it "infinite f" $ [fmt|{inf:f}|] `shouldBe` "inf" + it "infinite e" $ [fmt|{inf:e}|] `shouldBe` "inf" + it "infinite g" $ [fmt|{inf:g}|] `shouldBe` "inf" + it "infinite F" $ [fmt|{inf:F}|] `shouldBe` "INF" + it "infinite G" $ [fmt|{inf:G}|] `shouldBe` "INF" + it "infinite E" $ [fmt|{inf:E}|] `shouldBe` "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") + it "infinite" $ [fmt|{inf}|] `shouldBe` "inf" + it "infinite f" $ [fmt|{inf:f}|] `shouldBe` "inf" + it "infinite e" $ [fmt|{inf:e}|] `shouldBe` "inf" + it "infinite g" $ [fmt|{inf:g}|] `shouldBe` "inf" + it "infinite F" $ [fmt|{inf:F}|] `shouldBe` "INF" + it "infinite G" $ [fmt|{inf:G}|] `shouldBe` "INF" + it "infinite E" $ [fmt|{inf:E}|] `shouldBe` "INF" describe "Grouping" $ do - it "groups int" $(checkExample "{123456789:,d}" "123,456,789") - it "groups int with _" $(checkExample "{123456789:_d}" "123_456_789") - it "groups float" $(checkExample "{123456789.234:,f}" "123,456,789.234000") - it "groups bin" $(checkExample "{123456789:_b}" "111_0101_1011_1100_1101_0001_0101") - it "groups hex" $(checkExample "{123456789:_x}" "75b_cd15") - it "groups oct" $(checkExample "{123456789:_o}" "7_2674_6425") + it "groups int" $ [fmt|{123456789:,d}|] `shouldBe` "123,456,789" + it "groups int with _" $ [fmt|{123456789:_d}|] `shouldBe` "123_456_789" + it "groups float" $ [fmt|{123456789.234:,f}|] `shouldBe` "123,456,789.234000" + it "groups bin" $ [fmt|{123456789:_b}|] `shouldBe` "111_0101_1011_1100_1101_0001_0101" + it "groups hex" $ [fmt|{123456789:_x}|] `shouldBe` "75b_cd15" + it "groups oct" $ [fmt|{123456789:_o}|] `shouldBe` "7_2674_6425" describe "negative zero" $ do - it "f" $(checkExample "{-0.0:f}" "-0.000000") - it "e" $(checkExample "{-0.0:e}" "-0.000000e+00") - it "g" $(checkExampleDiff "{-0.0:g}" "-0.000000") - it "F" $(checkExample "{-0.0:F}" "-0.000000") - it "G" $(checkExampleDiff "{-0.0:G}" "-0.000000") - it "E" $(checkExample "{-0.0:E}" "-0.000000E+00") + it "f" $ [fmt|{-0.0:f}|] `shouldBe` "-0.000000" + it "e" $ [fmt|{-0.0:e}|] `shouldBe` "-0.000000e+00" + it "g" $ [fmt|{-0.0:g}|] `shouldBe` "-0.000000" + it "F" $ [fmt|{-0.0:F}|] `shouldBe` "-0.000000" + it "G" $ [fmt|{-0.0:G}|] `shouldBe` "-0.000000" + it "E" $ [fmt|{-0.0:E}|] `shouldBe` "-0.000000E+00" describe "0" $ do - it "works" $(checkExample "{123:010}" "0000000123") - it "works with sign" $(checkExample "{-123:010}" "-000000123") - it "accept mode override" $(checkExample "{-123:<010}" "-123000000") - it "accept mode and char override" $(checkExample "{-123:.<010}" "-123......") + it "works" $ [fmt|{123:010}|] `shouldBe` "0000000123" + it "works with sign" $ [fmt|{-123:010}|] `shouldBe` "-000000123" + it "accept mode override" $ [fmt|{-123:<010}|] `shouldBe` "-123000000" + it "accept mode and char override" $ [fmt|{-123:.<010}|] `shouldBe` "-123......" describe "no digit no dot" $ do - it "f" $(checkExample "{1.0:.0f}" "1") - it "e" $(checkExample "{1.0:.0e}" "1e+00") - it "g" $(checkExample "{1.0:.0g}" "1") - it "E" $(checkExample "{1.0:.0E}" "1E+00") - it "G" $(checkExample "{1.0:.0G}" "1") - it "percent" $(checkExample "{1.0:.0%}" "100%") + it "f" $ [fmt|{1.0:.0f}|] `shouldBe` "1" + it "e" $ [fmt|{1.0:.0e}|] `shouldBe` "1e+00" + it "g" $ [fmt|{1.0:.0g}|] `shouldBe` "1" + it "E" $ [fmt|{1.0:.0E}|] `shouldBe` "1E+00" + it "G" $ [fmt|{1.0:.0G}|] `shouldBe` "1" + it "percent" $ [fmt|{1.0:.0%}|] `shouldBe` "100%" describe "no digit alt -> dot" $ do - it "f" $(checkExample "{1.0:#.0f}" "1.") - it "e" $(checkExample "{1.0:#.0e}" "1.e+00") - it "g" $(checkExample "{1.0:#.0g}" "1.") - it "E" $(checkExample "{1.0:#.0E}" "1.E+00") - it "G" $(checkExample "{1.0:#.0G}" "1.") - it "percent" $(checkExample "{1.0:#.0%}" "100.%") + it "f" $ [fmt|{1.0:#.0f}|] `shouldBe` "1." + it "e" $ [fmt|{1.0:#.0e}|] `shouldBe` "1.e+00" + it "g" $ [fmt|{1.0:#.0g}|] `shouldBe` "1." + it "E" $ [fmt|{1.0:#.0E}|] `shouldBe` "1.E+00" + it "G" $ [fmt|{1.0:#.0G}|] `shouldBe` "1." + it "percent" $ [fmt|{1.0:#.0%}|] `shouldBe` "100.%" describe "complex" $ it "works with many things at once" $ let name = "Guillaume" diff --git a/test/SpecFail.hs b/test/SpecFail.hs index 32ce3ed..1c81aaf 100644 --- a/test/SpecFail.hs +++ b/test/SpecFail.hs @@ -1,24 +1,22 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExtendedDefaultRules #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE ScopedTypeVariables #-} import Control.DeepSeq import Control.Exception import Data.Bits (Bits (..)) import Data.Char (ord) import qualified Data.Text as Text +import PyF import System.Exit import System.FilePath -import qualified System.IO as IO import System.IO.Temp import System.Process (readProcessWithExitCode) import Test.HUnit.Lang import Test.Hspec -import PyF - -- * Check compilation with external GHC (this is usefull to test compilation failure) data CompilationStatus @@ -28,24 +26,25 @@ data CompilationStatus | Ok String deriving (Show, Eq) - makeTemplate :: String -> String -makeTemplate s = [fmt|{{-# LANGUAGE QuasiQuotes, ExtendedDefaultRules, TypeApplications #-}} +makeTemplate s = + [fmt|{{-# LANGUAGE QuasiQuotes, ExtendedDefaultRules, TypeApplications #-}} import PyF truncate' = truncate @Float @Int hello = "hello" number = 3.14 :: Float main :: IO () -main = putStrLn [fmt|{s}|] <> "|]\n" +main = putStrLn [fmt|{s}|] + <> "|]\n" -- | Compile a formatting string -- -- >>> checkCompile fileContent -- CompileError "Bla bla bla, Floating cannot be formatted as hexa (`x`) -checkCompile :: HasCallStack => String -> IO CompilationStatus -checkCompile content = withSystemTempFile "PyFTest.hs" $ \path fd -> do - IO.hPutStr fd content - IO.hFlush fd +checkCompile :: (HasCallStack) => String -> IO CompilationStatus +checkCompile content = withSystemTempDirectory "PyF" $ \dirPath -> do + let path = dirPath "PyFTest.hs" + writeFile path content (ecode, _stdout, stderr) <- readProcessWithExitCode "ghc" @@ -55,6 +54,11 @@ checkCompile content = withSystemTempFile "PyFTest.hs" $ \path fd -> do -- Disable the usage of the annoying .ghc environment file "-package-env", "-", + -- Move the ".o" in the temporary dir + "-odir", + dirPath, + "-hidir", + dirPath, -- Tests use a filename in a temporary directory which may have a long filename which triggers -- line wrapping, reducing the reproducibility of error message -- By setting the column size to a high value, we ensure reproducible error messages @@ -92,6 +96,7 @@ sanitize path = . Text.replace (Text.pack "[Char]") (Text.pack "String") . Text.pack +{- ORMOLU_DISABLE -} golden :: HasCallStack => String -> String -> IO () golden name output = do let @@ -109,20 +114,27 @@ golden name output = do Left _ -> output -- Flush lazy IO _ <- evaluate (force goldenContent) - if output /= goldenContent + -- Apparently the whitespaces in GHC 9.10 changed + -- By stripping, we just keep the test working as expected, but we are + -- compatible with different version of GHC. + -- + -- TODO: use GHC API to build directly the example and gather errors in a + -- more reproducible way. + if Text.strip (Text.pack output) /= Text.strip (Text.pack goldenContent) then do writeFile actualFile output - (_, diffOutput, _) <- readProcessWithExitCode "diff" [goldenFile, actualFile] "" + (_, diffOutput, _) <- readProcessWithExitCode "diff" ["-b", goldenFile, actualFile] "" putStrLn diffOutput -- Update golden file - writeFile goldenFile output + writeFile goldenFile (Text.unpack $ Text.strip (Text.pack output)) assertFailure diffOutput - else writeFile goldenFile output + else writeFile goldenFile (Text.unpack $ Text.strip (Text.pack output)) +{- ORMOLU_ENABLE -} -failCompile :: HasCallStack => String -> Spec +failCompile :: (HasCallStack) => String -> Spec failCompile s = failCompileContent s s (makeTemplate s) -failCompileContent :: HasCallStack => String -> String -> String -> Spec +failCompileContent :: (HasCallStack) => String -> String -> String -> Spec failCompileContent h caption fileContent = before (checkCompile fileContent) $ do let goldenName = concatMap cleanSpecialChars h @@ -150,7 +162,7 @@ cleanSpecialChars '\n' = "NL" cleanSpecialChars e = pure e main :: IO () -main = hspec spec +main = hspec $ parallel spec spec :: Spec spec = diff --git a/test/SpecUtils.hs b/test/SpecUtils.hs deleted file mode 100644 index 926ee7e..0000000 --- a/test/SpecUtils.hs +++ /dev/null @@ -1,77 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE TemplateHaskell #-} - -module SpecUtils - ( checkExample, - checkExampleDiff, - ) -where - -import Language.Haskell.TH -#ifdef PYTHON_TEST -import Language.Haskell.TH.Syntax -import System.Exit -import System.Process -#endif - -import PyF (fmtConfig) -import PyF.Internal.QQ -import Test.Hspec - --- * Utils - -#ifdef PYTHON_TEST --- | Runs a python formatter example --- --- For conveniance, it exports a few python symbols, `inf`, `nan` and pi. --- --- >>> runPythonExample "{3.14159:.1f} --- "3.1 -runPythonExample :: String -> IO (Maybe String) -runPythonExample s = do - let pythonPath = "python3" - args = ["-c", "from math import pi;nan = float('NaN');inf = float('inf');print(f\'''" ++ s ++ "''', end='')"] - (ecode, stdout, _stderr) <- readProcessWithExitCode pythonPath args "" - pure $ case ecode of - ExitSuccess -> Just stdout - ExitFailure _ -> Nothing - --- | `pyCheck formatString reference` compares a format string against --- a reference (if `Just`) and against the python implementation --- --- This TH expression will return an expression compatible with `Hspec` `SpecM`. --- --- This expression is a failure if python cannot format this formatString --- or if the python result does not match the (provided) reference. -pyCheck :: String -> Maybe String -> Q Exp -pyCheck s exampleStr = do - pythonRes <- Language.Haskell.TH.Syntax.runIO (runPythonExample s) - case pythonRes of - Nothing -> [|expectationFailure $ "Expression: `" ++ s ++ "` fails in python"|] - Just res -> do - let qexp = [|$(toExp fmtConfig s) `shouldBe` res|] - case exampleStr of - Nothing -> qexp - Just e -> - if res == e - then qexp - else [|expectationFailure $ "Provided result `" ++ e ++ "` does not match the python result `" ++ res ++ "`"|] - --- * Exported - --- | `checkExample formatString result` checks if, once formated, --- `formatString` is equal to result. It also checks that the result is --- the same as the one provided by python. -checkExample :: String -> String -> Q Exp -checkExample s res = pyCheck s (Just res) -#else --- | Alias for checkExampleDiff -checkExample :: String -> String -> Q Exp -checkExample = checkExampleDiff -#endif - --- | `checkExampleDiff formatString result` checks if, once formated, --- `formatString` is equal to result. It does not check the result --- against the python implementation -checkExampleDiff :: String -> String -> Q Exp -checkExampleDiff s res = [|$(toExp fmtConfig s) `shouldBe` res|] diff --git a/test/golden96/Hello {length name}.16675806454852491955.golden b/test/golden96/Hello {length name}.16675806454852491955.golden index 1382ba8..96c3ea1 100644 --- a/test/golden96/Hello {length name}.16675806454852491955.golden +++ b/test/golden96/Hello {length name}.16675806454852491955.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:36: error: • Variable not in scope: name • In the quasi-quotation: [fmt|Hello {length name}|] | 7 | main = putStrLn [fmt|Hello {length name}|] - | ^^^^ + | ^^^^ \ No newline at end of file diff --git a/test/golden96/Hello {name}.16618004304593959603.golden b/test/golden96/Hello {name}.16618004304593959603.golden index 0213489..2e00f95 100644 --- a/test/golden96/Hello {name}.16618004304593959603.golden +++ b/test/golden96/Hello {name}.16618004304593959603.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:29: error: • Variable not in scope: name • In the quasi-quotation: [fmt|Hello {name}|] | 7 | main = putStrLn [fmt|Hello {name}|] - | ^^^^ + | ^^^^ \ No newline at end of file diff --git a/test/golden96/Hello {piCL.{precision}}.9628757040831863475.golden b/test/golden96/Hello {piCL.{precision}}.9628757040831863475.golden index 068c2a4..c8e504f 100644 --- a/test/golden96/Hello {piCL.{precision}}.9628757040831863475.golden +++ b/test/golden96/Hello {piCL.{precision}}.9628757040831863475.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:34: error: • Variable not in scope: precision • In the quasi-quotation: [fmt|Hello {pi:.{precision}}|] | 7 | main = putStrLn [fmt|Hello {pi:.{precision}}|] - | ^^^^^^^^^ + | ^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/Hello {piCL.{truncate number + precision}}.18094049741103110835.golden b/test/golden96/Hello {piCL.{truncate number + precision}}.18094049741103110835.golden index 9ea99da..1dfc2d3 100644 --- a/test/golden96/Hello {piCL.{truncate number + precision}}.18094049741103110835.golden +++ b/test/golden96/Hello {piCL.{truncate number + precision}}.18094049741103110835.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:52: error: • Variable not in scope: precision • In the quasi-quotation: [fmt|Hello {pi:.{truncate number + precision}}|] | 7 | main = putStrLn [fmt|Hello {pi:.{truncate number + precision}}|] - | ^^^^^^^^^ + | ^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/Hello {piCL{width}}.15463239215289408179.golden b/test/golden96/Hello {piCL{width}}.15463239215289408179.golden index cc2bb32..2679ab6 100644 --- a/test/golden96/Hello {piCL{width}}.15463239215289408179.golden +++ b/test/golden96/Hello {piCL{width}}.15463239215289408179.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:33: error: • Variable not in scope: width • In the quasi-quotation: [fmt|Hello {pi:{width}}|] | 7 | main = putStrLn [fmt|Hello {pi:{width}}|] - | ^^^^^ + | ^^^^^ \ No newline at end of file diff --git a/test/golden96/fooBSPbar.17645057532673886893.golden b/test/golden96/fooBSPbar.17645057532673886893.golden index c278dbd..683d8b6 100644 --- a/test/golden96/fooBSPbar.17645057532673886893.golden +++ b/test/golden96/fooBSPbar.17645057532673886893.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:25: error: • Lexical error in literal section • In the quasi-quotation: [fmt|foo\Pbar|] | 7 | main = putStrLn [fmt|foo\Pbar|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/fooNLbliBSPbar.16759496276764189145.golden b/test/golden96/fooNLbliBSPbar.16759496276764189145.golden index 6779bf7..28eb04a 100644 --- a/test/golden96/fooNLbliBSPbar.16759496276764189145.golden +++ b/test/golden96/fooNLbliBSPbar.16759496276764189145.golden @@ -1,8 +1,7 @@ - INITIALPATH:8:4: error: • Lexical error in literal section • In the quasi-quotation: [fmt|foo bli\Pbar|] | 8 | bli\Pbar|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/hello { world.10778336899993839283.golden b/test/golden96/hello { world.10778336899993839283.golden index 2a73b56..10d9b00 100644 --- a/test/golden96/hello { world.10778336899993839283.golden +++ b/test/golden96/hello { world.10778336899993839283.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:35: error: • unexpected end of input @@ -6,4 +5,4 @@ expecting "::", ":" or "}" • In the quasi-quotation: [fmt|hello { world|] | 7 | main = putStrLn [fmt|hello { world|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/hello } world.5295037443422799539.golden b/test/golden96/hello } world.5295037443422799539.golden index 7bab6d5..3750ccf 100644 --- a/test/golden96/hello } world.5295037443422799539.golden +++ b/test/golden96/hello } world.5295037443422799539.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:28: error: • unexpected '}' @@ -6,4 +5,4 @@ expecting "{{", "}}", "{" or end of input • In the quasi-quotation: [fmt|hello } world|] | 7 | main = putStrLn [fmt|hello } world|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/helloNL {NLlet a = 5NL b = 10NLin 1 + - SL lalalal}.3018767237106994099.golden b/test/golden96/helloNL {NLlet a = 5NL b = 10NLin 1 + - SL lalalal}.3018767237106994099.golden index a8b518a..5918bce 100644 --- a/test/golden96/helloNL {NLlet a = 5NL b = 10NLin 1 + - SL lalalal}.3018767237106994099.golden +++ b/test/golden96/helloNL {NLlet a = 5NL b = 10NLin 1 + - SL lalalal}.3018767237106994099.golden @@ -1,4 +1,3 @@ - INITIALPATH:11:10: error: • parse error on input `/' in haskell expression • In the quasi-quotation: [fmt|hello @@ -8,4 +7,4 @@ let a = 5 in 1 + - / lalalal}|] | 11 | in 1 + - / lalalal}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/helloNLNLNL{piCLl}.13123157148160021427.golden b/test/golden96/helloNLNLNL{piCLl}.13123157148160021427.golden index becbd32..8078e50 100644 --- a/test/golden96/helloNLNLNL{piCLl}.13123157148160021427.golden +++ b/test/golden96/helloNLNLNL{piCLl}.13123157148160021427.golden @@ -1,4 +1,3 @@ - INITIALPATH:10:6: error: • unexpected "}" @@ -9,4 +8,4 @@ expecting "<", ">", "^" or "=" {pi:l}|] | 10 | {pi:l}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{1 + - SL lalalal}.14923086665437293731.golden b/test/golden96/{1 + - SL lalalal}.14923086665437293731.golden index 0d38fa0..0dca75b 100644 --- a/test/golden96/{1 + - SL lalalal}.14923086665437293731.golden +++ b/test/golden96/{1 + - SL lalalal}.14923086665437293731.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:29: error: • parse error on input `/' in haskell expression • In the quasi-quotation: [fmt|{1 + - / lalalal}|] | 7 | main = putStrLn [fmt|{1 + - / lalalal}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{TrueCLd}.12627313193367841398.golden b/test/golden96/{TrueCLd}.12627313193367841398.golden index e3d33d5..f354cb7 100644 --- a/test/golden96/{TrueCLd}.12627313193367841398.golden +++ b/test/golden96/{TrueCLd}.12627313193367841398.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing True)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing True) | 7 | main = putStrLn [fmt|{True:d}|] - | ^^^^^^^^^^ + | ^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{TrueCLf}.18281408089045870326.golden b/test/golden96/{TrueCLf}.18281408089045870326.golden index de1365e..bf6f0c9 100644 --- a/test/golden96/{TrueCLf}.18281408089045870326.golden +++ b/test/golden96/{TrueCLf}.18281408089045870326.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) True)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) True) | 7 | main = putStrLn [fmt|{True:f}|] - | ^^^^^^^^^^ + | ^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{True}.16254223077612353942.golden b/test/golden96/{True}.16254223077612353942.golden index acbef24..990b9bd 100644 --- a/test/golden96/{True}.16254223077612353942.golden +++ b/test/golden96/{True}.16254223077612353942.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe Int) True)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAny PyF.Formatters.Minus PyF.Internal.QQ.PaddingDefaultK Nothing (Nothing :: Maybe Int) True) | 7 | main = putStrLn [fmt|{True}|] - | ^^^^^^^^ + | ^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCL s}.13047921915648718386.golden b/test/golden96/{helloCL s}.13047921915648718386.golden index 8c2fa63..27d0c23 100644 --- a/test/golden96/{helloCL s}.13047921915648718386.golden +++ b/test/golden96/{helloCL s}.13047921915648718386.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:30: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{helloCL%}.1257653362598537778.golden b/test/golden96/{helloCL%}.1257653362598537778.golden index fcd735a..e28cd7c 100644 --- a/test/golden96/{helloCL%}.1257653362598537778.golden +++ b/test/golden96/{helloCL%}.1257653362598537778.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Percent PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:%}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCL+s}.1657517030647448626.golden b/test/golden96/{helloCL+s}.1657517030647448626.golden index 1681a8d..cdbabe3 100644 --- a/test/golden96/{helloCL+s}.1657517030647448626.golden +++ b/test/golden96/{helloCL+s}.1657517030647448626.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:30: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{helloCL,s}.14139635988852178482.golden b/test/golden96/{helloCL,s}.14139635988852178482.golden index 6761ce5..289529c 100644 --- a/test/golden96/{helloCL,s}.14139635988852178482.golden +++ b/test/golden96/{helloCL,s}.14139635988852178482.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:30: error: • String type is incompatible with grouping (_ or ,). • In the quasi-quotation: [fmt|{hello:,s}|] | 7 | main = putStrLn [fmt|{hello:,s}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{helloCL-s}.12627805606214404146.golden b/test/golden96/{helloCL-s}.12627805606214404146.golden index 954c97b..66f071f 100644 --- a/test/golden96/{helloCL-s}.12627805606214404146.golden +++ b/test/golden96/{helloCL-s}.12627805606214404146.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:30: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{helloCL=100s}.14374776122070431282.golden b/test/golden96/{helloCL=100s}.14374776122070431282.golden index 5aa8d10..b3975e4 100644 --- a/test/golden96/{helloCL=100s}.14374776122070431282.golden +++ b/test/golden96/{helloCL=100s}.14374776122070431282.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:33: error: • String type is incompatible with inside padding (=). • In the quasi-quotation: [fmt|{hello:=100s}|] | 7 | main = putStrLn [fmt|{hello:=100s}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{helloCL=100}.9444838110946424370.golden b/test/golden96/{helloCL=100}.9444838110946424370.golden index 54d8c1e..762ab93 100644 --- a/test/golden96/{helloCL=100}.9444838110946424370.golden +++ b/test/golden96/{helloCL=100}.9444838110946424370.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-64725] • 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 :: Int) (Just (Nothing, PyF.Formatters.AlignInside))) Nothing (Nothing :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-64725] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAny PyF.Formatters.Minus (PyF.Internal.QQ.PaddingK (100 :: Int) (Just (Nothing, PyF.Formatters.AlignInside))) Nothing (Nothing :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:=100}|] - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLE}.15676531368138664498.golden b/test/golden96/{helloCLE}.15676531368138664498.golden index 8620be1..28b95f7 100644 --- a/test/golden96/{helloCLE}.15676531368138664498.golden +++ b/test/golden96/{helloCLE}.15676531368138664498.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Exponent) PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:E}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLG}.17442699390234010162.golden b/test/golden96/{helloCLG}.17442699390234010162.golden index 6cc582c..492403e 100644 --- a/test/golden96/{helloCLG}.17442699390234010162.golden +++ b/test/golden96/{helloCLG}.17442699390234010162.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional (PyF.Formatters.Upper PyF.Formatters.Generic) PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:G}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLX}.8447528333473699378.golden b/test/golden96/{helloCLX}.8447528333473699378.golden index 29b04f1..090f12a 100644 --- a/test/golden96/{helloCLX}.8447528333473699378.golden +++ b/test/golden96/{helloCLX}.8447528333473699378.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa) PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello) | 7 | main = putStrLn [fmt|{hello:X}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCL_s}.1094067961907256370.golden b/test/golden96/{helloCL_s}.1094067961907256370.golden index f6fcbff..2543d67 100644 --- a/test/golden96/{helloCL_s}.1094067961907256370.golden +++ b/test/golden96/{helloCL_s}.1094067961907256370.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:30: error: • String type is incompatible with grouping (_ or ,). • In the quasi-quotation: [fmt|{hello:_s}|] | 7 | main = putStrLn [fmt|{hello:_s}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{helloCLb}.14869862508711808562.golden b/test/golden96/{helloCLb}.14869862508711808562.golden index 7508adb..6cf3d4a 100644 --- a/test/golden96/{helloCLb}.14869862508711808562.golden +++ b/test/golden96/{helloCLb}.14869862508711808562.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello) | 7 | main = putStrLn [fmt|{hello:b}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLd}.1892681375540151858.golden b/test/golden96/{helloCLd}.1892681375540151858.golden index a75d78d..0e4c20f 100644 --- a/test/golden96/{helloCLd}.1892681375540151858.golden +++ b/test/golden96/{helloCLd}.1892681375540151858.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello) | 7 | main = putStrLn [fmt|{hello:d}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLe}.13933826712837941810.golden b/test/golden96/{helloCLe}.13933826712837941810.golden index d4dadbe..39231d8 100644 --- a/test/golden96/{helloCLe}.13933826712837941810.golden +++ b/test/golden96/{helloCLe}.13933826712837941810.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Exponent PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:e}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLf}.14332487603622862386.golden b/test/golden96/{helloCLf}.14332487603622862386.golden index 19e7827..2d8d8da 100644 --- a/test/golden96/{helloCLf}.14332487603622862386.golden +++ b/test/golden96/{helloCLf}.14332487603622862386.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Fixed PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:f}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLg}.9607247906229690930.golden b/test/golden96/{helloCLg}.9607247906229690930.golden index 1a811ba..72e0fdf 100644 --- a/test/golden96/{helloCLg}.9607247906229690930.golden +++ b/test/golden96/{helloCLg}.9607247906229690930.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyFractional PyF.Formatters.Generic PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing (Just 6 :: Maybe Int) hello) | 7 | main = putStrLn [fmt|{hello:g}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLo}.9389880575827657266.golden b/test/golden96/{helloCLo}.9389880575827657266.golden index c76cb58..1fe353e 100644 --- a/test/golden96/{helloCLo}.9389880575827657266.golden +++ b/test/golden96/{helloCLo}.9389880575827657266.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello) | 7 | main = putStrLn [fmt|{hello:o}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{helloCLx}.14710080644372944434.golden b/test/golden96/{helloCLx}.14710080644372944434.golden index 389236d..fa4174a 100644 --- a/test/golden96/{helloCLx}.14710080644372944434.golden +++ b/test/golden96/{helloCLx}.14710080644372944434.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing hello) | 7 | main = putStrLn [fmt|{hello:x}|] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{numberCLX}.4609648040604121432.golden b/test/golden96/{numberCLX}.4609648040604121432.golden index 45f0d0b..5fac5c0 100644 --- a/test/golden96/{numberCLX}.4609648040604121432.golden +++ b/test/golden96/{numberCLX}.4609648040604121432.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral (PyF.Formatters.Upper PyF.Formatters.Hexa) PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number) | 7 | main = putStrLn [fmt|{number:X}|] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{numberCLb}.8801685868342243288.golden b/test/golden96/{numberCLb}.8801685868342243288.golden index 84d25d4..0b44bf3 100644 --- a/test/golden96/{numberCLb}.8801685868342243288.golden +++ b/test/golden96/{numberCLb}.8801685868342243288.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Binary PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number) | 7 | main = putStrLn [fmt|{number:b}|] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{numberCLd}.13336740346716692056.golden b/test/golden96/{numberCLd}.13336740346716692056.golden index 4f735a2..2a8ef51 100644 --- a/test/golden96/{numberCLd}.13336740346716692056.golden +++ b/test/golden96/{numberCLd}.13336740346716692056.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Decimal PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number) | 7 | main = putStrLn [fmt|{number:d}|] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{numberCLo}.12467189151987896600.golden b/test/golden96/{numberCLo}.12467189151987896600.golden index 4016f32..50a1d5f 100644 --- a/test/golden96/{numberCLo}.12467189151987896600.golden +++ b/test/golden96/{numberCLo}.12467189151987896600.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Octal PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number) | 7 | main = putStrLn [fmt|{number:o}|] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{numberCLx}.14457861675063419224.golden b/test/golden96/{numberCLx}.14457861675063419224.golden index caebdb1..5f2a288 100644 --- a/test/golden96/{numberCLx}.14457861675063419224.golden +++ b/test/golden96/{numberCLx}.14457861675063419224.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:22: error: [GHC-39999] • 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 :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number)’ @@ -6,4 +5,4 @@ INITIALPATH:7:22: error: [GHC-39999] In an equation for ‘main’: main = putStrLn (PyF.Internal.QQ.formatAnyIntegral PyF.Formatters.Hexa PyF.Formatters.Minus (Nothing :: Maybe (Int, PyF.Formatters.AnyAlign, Char)) Nothing number) | 7 | main = putStrLn [fmt|{number:x}|] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ \ No newline at end of file diff --git a/test/golden96/{piCL.{SL}}.6840925804160914882.golden b/test/golden96/{piCL.{SL}}.6840925804160914882.golden index 4cd065f..bfc6ec7 100644 --- a/test/golden96/{piCL.{SL}}.6840925804160914882.golden +++ b/test/golden96/{piCL.{SL}}.6840925804160914882.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:28: error: • parse error on input `/' in haskell expression • In the quasi-quotation: [fmt|{pi:.{/}}|] | 7 | main = putStrLn [fmt|{pi:.{/}}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{piCL.{}}.9894464503607709506.golden b/test/golden96/{piCL.{}}.9894464503607709506.golden index 66af9d5..1a93ee0 100644 --- a/test/golden96/{piCL.{}}.9894464503607709506.golden +++ b/test/golden96/{piCL.{}}.9894464503607709506.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:28: error: • unexpected "}" @@ -6,4 +5,4 @@ expecting an haskell expression • In the quasi-quotation: [fmt|{pi:.{}}|] | 7 | main = putStrLn [fmt|{pi:.{}}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{truncate numberCL.3b}.11608798523190422838.golden b/test/golden96/{truncate numberCL.3b}.11608798523190422838.golden index e0000d9..28108ae 100644 --- a/test/golden96/{truncate numberCL.3b}.11608798523190422838.golden +++ b/test/golden96/{truncate numberCL.3b}.11608798523190422838.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:41: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{truncate numberCL.3d}.9142976352287206710.golden b/test/golden96/{truncate numberCL.3d}.9142976352287206710.golden index 28380ba..844cf9e 100644 --- a/test/golden96/{truncate numberCL.3d}.9142976352287206710.golden +++ b/test/golden96/{truncate numberCL.3d}.9142976352287206710.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:41: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{truncate numberCL.3o}.1443712191031422262.golden b/test/golden96/{truncate numberCL.3o}.1443712191031422262.golden index 2356c43..99a8cff 100644 --- a/test/golden96/{truncate numberCL.3o}.1443712191031422262.golden +++ b/test/golden96/{truncate numberCL.3o}.1443712191031422262.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:41: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{truncate numberCL.3x}.12613302271643495734.golden b/test/golden96/{truncate numberCL.3x}.12613302271643495734.golden index a5bf1df..6be9518 100644 --- a/test/golden96/{truncate numberCL.3x}.12613302271643495734.golden +++ b/test/golden96/{truncate numberCL.3x}.12613302271643495734.golden @@ -1,7 +1,6 @@ - INITIALPATH:7:41: error: • 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}|] - | ^ + | ^ \ No newline at end of file diff --git a/test/golden96/{}.14986928820806517861.golden b/test/golden96/{}.14986928820806517861.golden index 34420f7..f68b5fb 100644 --- a/test/golden96/{}.14986928820806517861.golden +++ b/test/golden96/{}.14986928820806517861.golden @@ -1,4 +1,3 @@ - INITIALPATH:7:23: error: • unexpected "}" @@ -6,4 +5,4 @@ expecting an haskell expression • In the quasi-quotation: [fmt|{}|] | 7 | main = putStrLn [fmt|{}|] - | ^ + | ^ \ No newline at end of file diff --git a/tree-sitter-pyf/tree-sitter.json b/tree-sitter-pyf/tree-sitter.json new file mode 100644 index 0000000..3cda0b2 --- /dev/null +++ b/tree-sitter-pyf/tree-sitter.json @@ -0,0 +1,34 @@ +{ + "grammars": [ + { + "name": "pyf", + "camelcase": "Pyf", + "scope": "source.pyf", + "path": ".", + "file-types": [ + "pyf" + ] + } + ], + "metadata": { + "version": "1.0.0", + "license": "ISC", + "description": "Parser for PyF interpolated strings", + "authors": [ + { + "name": "Guillaume Bouchard" + } + ], + "links": { + "repository": "https://github.com/guibou/PyF/tree-sitter-pyf" + } + }, + "bindings": { + "c": true, + "go": true, + "node": true, + "python": true, + "rust": true, + "swift": true + } +} diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 0000000..2e8da75 --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,20 @@ +{ ... }: +{ + projectRootFile = "flake.nix"; + + programs.ormolu.enable = true; + programs.nixfmt.enable = true; + programs.yamlfmt.enable = true; + programs.cabal-fmt.enable = true; + programs.mdformat.enable = true; + + settings.excludes = [ + "*.golden" + "flake.lock" + "*.png" + ".gitignore" + ".envrc" + "LICENSE" + "tree-sitter-pyf/*" + ]; +}