├── .gitattributes ├── .gitignore ├── Setup.hs ├── README.md ├── ChangeLog ├── test └── Test.hs ├── examples └── RawRegex.hs ├── LICENSE ├── raw-strings-qq.cabal ├── Text └── RawString │ └── QQ.hs └── .travis.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | test/Test.hs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cabal-sandbox 2 | cabal.sandbox.config 3 | dist 4 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # raw-strings-qq [![Build Status](https://secure.travis-ci.org/23Skidoo/raw-strings-qq.png?branch=master)](http://travis-ci.org/23Skidoo/raw-strings-qq) 2 | 3 | Raw string literals for Haskell. Documentation can be found 4 | [on Hackage](http://hackage.haskell.org/package/raw-strings-qq). 5 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | -*-change-log-*- 2 | 3 | 1.1 Mikhail Glushenkov 4 | * CRLF line endings are now normalised to LF (#1). 5 | 6 | 1.0.2 Mikhail Glushenkov 7 | * Added an ability to escape '|~...]' sequences. 8 | 9 | 1.0.1 Mikhail Glushenkov 10 | * Tweaked documentation. 11 | 12 | 1.0 Mikhail Glushenkov 13 | * Initial release. 14 | -------------------------------------------------------------------------------- /test/Test.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | module Main 4 | where 5 | 6 | import Test.HUnit 7 | import Text.RawString.QQ 8 | import System.Exit 9 | 10 | multilineUnixNewlines :: String 11 | multilineUnixNewlines = [r|FOO 12 | BAR|] 13 | 14 | multilineWindowsNewlines :: String 15 | multilineWindowsNewlines = [r|FOO 16 | BAR|] 17 | 18 | main :: IO () 19 | main = defaultMain $ test [ 20 | "Windows newlines" ~: (multilineUnixNewlines ~=? multilineWindowsNewlines) 21 | ] 22 | 23 | defaultMain :: Test -> IO () 24 | defaultMain t = do 25 | cnts <- runTestTT t 26 | case failures cnts + errors cnts of 27 | 0 -> exitWith $ ExitSuccess 28 | n -> exitWith $ ExitFailure n 29 | -------------------------------------------------------------------------------- /examples/RawRegex.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | module Main 4 | where 5 | 6 | import Text.Regex.Posix 7 | import Text.RawString.QQ 8 | 9 | haystack :: String 10 | haystack = "My e-mail address is user@example.com" 11 | 12 | needle :: String 13 | needle = [r|\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}|] 14 | 15 | multiline :: String 16 | multiline = [r| 17 | 18 | Auto-generated html formated source 19 | 20 | 21 | 22 |

23 |
|]
24 | 
25 | main :: IO ()
26 | main = do
27 |   print multiline
28 |   print ""
29 |   print $ ((haystack =~ needle) :: String)
30 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013, Mikhail Glushenkov
 2 | 
 3 | All rights reserved.
 4 | 
 5 | Redistribution and use in source and binary forms, with or without
 6 | modification, are permitted provided that the following conditions are met:
 7 | 
 8 |     * Redistributions of source code must retain the above copyright
 9 |       notice, this list of conditions and the following disclaimer.
10 | 
11 |     * Redistributions in binary form must reproduce the above
12 |       copyright notice, this list of conditions and the following
13 |       disclaimer in the documentation and/or other materials provided
14 |       with the distribution.
15 | 
16 |     * Neither the name of Mikhail Glushenkov nor the names of other
17 |       contributors may be used to endorse or promote products derived
18 |       from this software without specific prior written permission.
19 | 
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | 


--------------------------------------------------------------------------------
/raw-strings-qq.cabal:
--------------------------------------------------------------------------------
 1 | name:                raw-strings-qq
 2 | version:             1.1
 3 | synopsis:            Raw string literals for Haskell.
 4 | description:
 5 | 
 6 |     A quasiquoter for raw string literals - that is, string literals that don't
 7 |     recognise the standard escape sequences (such as @\'\\n\'@). Basically, they
 8 |     make your code more readable by freeing you from the responsibility to
 9 |     escape backslashes. They are useful when working with regular expressions,
10 |     DOS/Windows paths and markup languages (such as XML).
11 |     .
12 |     See @examples/RawRegex.hs@ for a usage example.
13 | 
14 | homepage:            https://github.com/23Skidoo/raw-strings-qq
15 | bug-reports:         https://github.com/23Skidoo/raw-strings-qq/issues
16 | license:             BSD3
17 | license-file:        LICENSE
18 | author:              Mikhail Glushenkov
19 | maintainer:          mikhail.glushenkov@gmail.com
20 | copyright:           (c) Mikhail Glushenkov 2013-2015
21 | category:            Text
22 | build-type:          Simple
23 | cabal-version:       >=1.8
24 | extra-source-files:  examples/RawRegex.hs
25 |                      ChangeLog
26 | tested-with:         GHC == 7.0.4, GHC == 7.2.2, GHC == 7.4.2,
27 |                      GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3,
28 |                      GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1
29 | 
30 | source-repository head
31 |   type:     git
32 |   location: https://github.com/23Skidoo/raw-strings-qq.git
33 | 
34 | library
35 |   exposed-modules:     Text.RawString.QQ
36 |   build-depends:       base <= 10, template-haskell >= 2.5
37 | 
38 | test-suite tests
39 |   hs-source-dirs: test
40 |   main-is:        Test.hs
41 |   type:           exitcode-stdio-1.0
42 |   build-depends:  base,
43 |                   raw-strings-qq,
44 |                   HUnit
45 |   ghc-options:    -Wall
46 | 


--------------------------------------------------------------------------------
/Text/RawString/QQ.hs:
--------------------------------------------------------------------------------
  1 | -- | Raw string literals, implemented using Template Haskell's quasiquotation
  2 | -- feature.
  3 | module Text.RawString.QQ (r, rQ)
  4 |        where
  5 | 
  6 | import Language.Haskell.TH
  7 | import Language.Haskell.TH.Quote
  8 | 
  9 | {-|
 10 | 
 11 | A quasiquoter for raw string literals - that is, string literals that don't
 12 | recognise the standard escape sequences (such as @\'\\n\'@). Basically, they
 13 | make your code more readable by freeing you from the responsibility to escape
 14 | backslashes. They are useful when working with regular expressions, DOS/Windows
 15 | paths and markup languages (such as XML).
 16 | 
 17 | Don't forget the @LANGUAGE QuasiQuotes@ pragma if you're using this
 18 | module in your code.
 19 | 
 20 | Usage:
 21 | 
 22 | @
 23 |     ghci> :set -XQuasiQuotes
 24 |     ghci> import Text.RawString.QQ
 25 |     ghci> let s = [r|\\w+\@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}|]
 26 |     ghci> s
 27 |     \"\\\\w+\@[a-zA-Z_]+?\\\\.[a-zA-Z]{2,3}\"
 28 |     ghci> [r|C:\\Windows\\SYSTEM|] ++ [r|\\user32.dll|]
 29 |     \"C:\\\\Windows\\\\SYSTEM\\\\user32.dll\"
 30 | @
 31 | 
 32 | Multiline raw string literals are also supported:
 33 | 
 34 | @
 35 |     multiline :: String
 36 |     multiline = [r|\
 37 |     \
 38 |     \Auto-generated html formated source\
 39 |     \
 40 |     \
 41 |     \
 42 |     \ \
 43 |     \|]
 44 | @
 45 | 
 46 | Caveat: since the @\"|]\"@ character sequence is used to terminate the
 47 | quasiquotation, you can't use it inside the raw string literal. Use 'rQ' if you
 48 | want to embed that character sequence inside the raw string.
 49 | 
 50 | For more on raw strings, see e.g.
 51 | 
 52 | 
 53 | For more on quasiquotation, see
 54 | 
 55 | 
 56 | -}
 57 | r :: QuasiQuoter
 58 | r = QuasiQuoter {
 59 |     -- Extracted from dead-simple-json.
 60 |     quoteExp  = return . LitE . StringL . normaliseNewlines,
 61 | 
 62 |     quotePat  = \_ -> fail "illegal raw string QuasiQuote \
 63 |                            \(allowed as expression only, used as a pattern)",
 64 |     quoteType = \_ -> fail "illegal raw string QuasiQuote \
 65 |                            \(allowed as expression only, used as a type)",
 66 |     quoteDec  = \_ -> fail "illegal raw string QuasiQuote \
 67 |                            \(allowed as expression only, used as a declaration)"
 68 | }
 69 | 
 70 | {-| A variant of 'r' that interprets the @\"|~]\"@ sequence as @\"|]\"@,
 71 | @\"|~~]\"@ as @\"|~]\"@ and, in general, @\"|~^n]\"@ as @\"|~^(n-1)]\"@
 72 | for n >= 1.
 73 | 
 74 | Usage:
 75 | 
 76 | @
 77 |     ghci> [rQ||~]|~]|]
 78 |     \"|]|]\"
 79 |     ghci> [rQ||~~]|]
 80 |     \"|~]\"
 81 |     ghci> [rQ||~~~~]|]
 82 |     \"|~~~]\"
 83 | @
 84 | -}
 85 | rQ :: QuasiQuoter
 86 | rQ = QuasiQuoter {
 87 |     quoteExp  = return . LitE . StringL . escape_rQ . normaliseNewlines,
 88 | 
 89 |     quotePat  = \_ -> fail "illegal raw string QuasiQuote \
 90 |                            \(allowed as expression only, used as a pattern)",
 91 |     quoteType = \_ -> fail "illegal raw string QuasiQuote \
 92 |                            \(allowed as expression only, used as a type)",
 93 |     quoteDec  = \_ -> fail "illegal raw string QuasiQuote \
 94 |                            \(allowed as expression only, used as a declaration)"
 95 | }
 96 | 
 97 | escape_rQ :: String -> String
 98 | escape_rQ [] = []
 99 | escape_rQ ('|':'~':xs) =
100 |   let (tildas, rest) = span (== '~') xs
101 |   in case rest of
102 |     []       -> '|':'~':tildas
103 |     (']':rs) -> '|':tildas ++ ']':escape_rQ rs
104 |     rs       -> '|':'~':tildas ++ escape_rQ rs
105 | escape_rQ (x:xs) = x : escape_rQ xs
106 | 
107 | -- See https://github.com/23Skidoo/raw-strings-qq/issues/1 and
108 | -- https://ghc.haskell.org/trac/ghc/ticket/11215.
109 | normaliseNewlines :: String -> String
110 | normaliseNewlines []             = []
111 | normaliseNewlines ('\r':'\n':cs) = '\n':normaliseNewlines cs
112 | normaliseNewlines (c:cs)         = c:normaliseNewlines cs
113 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
  1 | # This Travis job script has been generated by a script via
  2 | #
  3 | #   runghc make_travis_yml_2.hs 'raw-strings-qq.cabal'
  4 | #
  5 | # For more information, see https://github.com/hvr/multi-ghc-travis
  6 | #
  7 | language: c
  8 | sudo: false
  9 | 
 10 | git:
 11 |   submodules: false  # whether to recursively clone submodules
 12 | 
 13 | cache:
 14 |   directories:
 15 |     - $HOME/.cabal/packages
 16 |     - $HOME/.cabal/store
 17 | 
 18 | before_cache:
 19 |   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
 20 |   # remove files that are regenerated by 'cabal update'
 21 |   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
 22 |   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
 23 |   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
 24 |   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
 25 |   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
 26 | 
 27 |   - rm -rfv $HOME/.cabal/packages/head.hackage
 28 | 
 29 | matrix:
 30 |   include:
 31 |     - compiler: "ghc-7.0.4"
 32 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 33 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.0.4], sources: [hvr-ghc]}}
 34 |     - compiler: "ghc-7.2.2"
 35 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 36 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.2.2], sources: [hvr-ghc]}}
 37 |     - compiler: "ghc-7.4.2"
 38 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 39 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.4.2], sources: [hvr-ghc]}}
 40 |     - compiler: "ghc-7.6.3"
 41 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 42 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.6.3], sources: [hvr-ghc]}}
 43 |     - compiler: "ghc-7.8.4"
 44 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 45 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.8.4], sources: [hvr-ghc]}}
 46 |     - compiler: "ghc-7.10.3"
 47 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 48 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.10.3], sources: [hvr-ghc]}}
 49 |     - compiler: "ghc-8.0.2"
 50 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 51 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
 52 |     - compiler: "ghc-8.2.2"
 53 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 54 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
 55 |     - compiler: "ghc-8.4.1"
 56 |     # env: TEST=--disable-tests BENCH=--disable-benchmarks
 57 |       addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.4.1], sources: [hvr-ghc]}}
 58 | 
 59 | before_install:
 60 |   - HC=${CC}
 61 |   - HCPKG=${HC/ghc/ghc-pkg}
 62 |   - unset CC
 63 |   - ROOTDIR=$(pwd)
 64 |   - mkdir -p $HOME/.local/bin
 65 |   - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
 66 |   - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
 67 |   - echo $HCNUMVER
 68 | 
 69 | install:
 70 |   - cabal --version
 71 |   - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
 72 |   - BENCH=${BENCH---enable-benchmarks}
 73 |   - TEST=${TEST---enable-tests}
 74 |   - HADDOCK=${HADDOCK-true}
 75 |   - INSTALLED=${INSTALLED-true}
 76 |   - GHCHEAD=${GHCHEAD-false}
 77 |   - travis_retry cabal update -v
 78 |   - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
 79 |   - rm -fv cabal.project cabal.project.local
 80 |   - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
 81 |   - "printf 'packages: \".\"\\n' > cabal.project"
 82 |   - cat cabal.project
 83 |   - if [ -f "./configure.ac" ]; then
 84 |       (cd "." && autoreconf -i);
 85 |     fi
 86 |   - rm -f cabal.project.freeze
 87 |   - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
 88 |   - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
 89 |   - rm -rf .ghc.environment.* "."/dist
 90 |   - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
 91 | 
 92 | # Here starts the actual work to be performed for the package under test;
 93 | # any command which exits with a non-zero exit code causes the build to fail.
 94 | script:
 95 |   # test that source-distributions can be generated
 96 |   - (cd "." && cabal sdist)
 97 |   - mv "."/dist/raw-strings-qq-*.tar.gz ${DISTDIR}/
 98 |   - cd ${DISTDIR} || false
 99 |   - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
100 |   - "printf 'packages: raw-strings-qq-*/*.cabal\\n' > cabal.project"
101 |   - cat cabal.project
102 |   # this builds all libraries and executables (without tests/benchmarks)
103 |   - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
104 | 
105 |   # Build with installed constraints for packages in global-db
106 |   - if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi
107 | 
108 |   # build & run tests, build benchmarks
109 |   - cabal new-build -w ${HC} ${TEST} ${BENCH} all
110 |   - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi
111 | 
112 |   # cabal check
113 |   - (cd raw-strings-qq-* && cabal check)
114 | 
115 |   # haddock
116 |   - rm -rf ./dist-newstyle
117 |   - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
118 | 
119 | # REGENDATA ["raw-strings-qq.cabal"]
120 | # EOF
121 | 


--------------------------------------------------------------------------------