interpolate 0.0.4 → 0.1.0
raw patch · 6 files changed
+195/−134 lines, 6 filesdep −doctest
Dependencies removed: doctest
Files
- interpolate.cabal +3/−15
- src/Data/String/Interpolate.hs +1/−1
- src/Data/String/Interpolate/Internal/Util.hs +118/−0
- src/Data/String/Interpolate/Parse.hs +1/−1
- src/Data/String/Interpolate/Util.hs +72/−111
- test/doctests.hs +0/−6
interpolate.cabal view
@@ -1,5 +1,5 @@ name: interpolate-version: 0.0.4+version: 0.1.0 license: MIT license-file: LICENSE copyright: (c) 2013 Simon Hengel@@ -24,8 +24,9 @@ exposed-modules: Data.String.Interpolate Data.String.Interpolate.IsString- other-modules: Data.String.Interpolate.Util+ other-modules:+ Data.String.Interpolate.Internal.Util Data.String.Interpolate.Parse Data.String.Interpolate.Compat build-depends:@@ -52,16 +53,3 @@ , hspec >= 1.5 , QuickCheck , quickcheck-instances--test-suite doctests- type:- exitcode-stdio-1.0- ghc-options:- -Wall- hs-source-dirs:- test- main-is:- doctests.hs- build-depends:- base == 4.*- , doctest == 0.9.*
src/Data/String/Interpolate.hs view
@@ -13,7 +13,7 @@ import Language.Haskell.TH.Quote (QuasiQuoter(..)) import Language.Haskell.Meta.Parse.Careful (parseExp) -import Data.String.Interpolate.Util+import Data.String.Interpolate.Internal.Util import Data.String.Interpolate.Parse import Data.String.Interpolate.Compat (Q, Exp, appE, reportError)
+ src/Data/String/Interpolate/Internal/Util.hs view
@@ -0,0 +1,118 @@+module Data.String.Interpolate.Internal.Util where++import Data.Char+import Data.Maybe+import qualified Numeric as N++import Data.String.Interpolate.Compat++toString :: Show a => a -> String+toString a = let s = show a in fromMaybe s (readMaybe s)+{-# NOINLINE toString #-}+{-# RULES "toString/String" toString = id #-}+{-# RULES "toString/Int" toString = show :: Int -> String #-}+{-# RULES "toString/Integer" toString = show :: Integer -> String #-}+{-# RULES "toString/Float" toString = show :: Float -> String #-}+{-# RULES "toString/Double" toString = show :: Double -> String #-}++-- Haskell 2010 character unescaping, see:+-- http://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6+unescape :: String -> String+unescape = go+ where+ go input = case input of+ "" -> ""+ '\\' : 'x' : x : xs | isHexDigit x -> case span isHexDigit xs of+ (ys, zs) -> (chr . readHex $ x:ys) : go zs+ '\\' : 'o' : x : xs | isOctDigit x -> case span isOctDigit xs of+ (ys, zs) -> (chr . readOct $ x:ys) : go zs+ '\\' : x : xs | isDigit x -> case span isDigit xs of+ (ys, zs) -> (chr . read $ x:ys) : go zs+ '\\' : input_ -> case input_ of+ '\\' : xs -> '\\' : go xs+ 'a' : xs -> '\a' : go xs+ 'b' : xs -> '\b' : go xs+ 'f' : xs -> '\f' : go xs+ 'n' : xs -> '\n' : go xs+ 'r' : xs -> '\r' : go xs+ 't' : xs -> '\t' : go xs+ 'v' : xs -> '\v' : go xs+ '&' : xs -> go xs+ 'N':'U':'L' : xs -> '\NUL' : go xs+ 'S':'O':'H' : xs -> '\SOH' : go xs+ 'S':'T':'X' : xs -> '\STX' : go xs+ 'E':'T':'X' : xs -> '\ETX' : go xs+ 'E':'O':'T' : xs -> '\EOT' : go xs+ 'E':'N':'Q' : xs -> '\ENQ' : go xs+ 'A':'C':'K' : xs -> '\ACK' : go xs+ 'B':'E':'L' : xs -> '\BEL' : go xs+ 'B':'S' : xs -> '\BS' : go xs+ 'H':'T' : xs -> '\HT' : go xs+ 'L':'F' : xs -> '\LF' : go xs+ 'V':'T' : xs -> '\VT' : go xs+ 'F':'F' : xs -> '\FF' : go xs+ 'C':'R' : xs -> '\CR' : go xs+ 'S':'O' : xs -> '\SO' : go xs+ 'S':'I' : xs -> '\SI' : go xs+ 'D':'L':'E' : xs -> '\DLE' : go xs+ 'D':'C':'1' : xs -> '\DC1' : go xs+ 'D':'C':'2' : xs -> '\DC2' : go xs+ 'D':'C':'3' : xs -> '\DC3' : go xs+ 'D':'C':'4' : xs -> '\DC4' : go xs+ 'N':'A':'K' : xs -> '\NAK' : go xs+ 'S':'Y':'N' : xs -> '\SYN' : go xs+ 'E':'T':'B' : xs -> '\ETB' : go xs+ 'C':'A':'N' : xs -> '\CAN' : go xs+ 'E':'M' : xs -> '\EM' : go xs+ 'S':'U':'B' : xs -> '\SUB' : go xs+ 'E':'S':'C' : xs -> '\ESC' : go xs+ 'F':'S' : xs -> '\FS' : go xs+ 'G':'S' : xs -> '\GS' : go xs+ 'R':'S' : xs -> '\RS' : go xs+ 'U':'S' : xs -> '\US' : go xs+ 'S':'P' : xs -> '\SP' : go xs+ 'D':'E':'L' : xs -> '\DEL' : go xs+ '^':'@' : xs -> '\^@' : go xs+ '^':'A' : xs -> '\^A' : go xs+ '^':'B' : xs -> '\^B' : go xs+ '^':'C' : xs -> '\^C' : go xs+ '^':'D' : xs -> '\^D' : go xs+ '^':'E' : xs -> '\^E' : go xs+ '^':'F' : xs -> '\^F' : go xs+ '^':'G' : xs -> '\^G' : go xs+ '^':'H' : xs -> '\^H' : go xs+ '^':'I' : xs -> '\^I' : go xs+ '^':'J' : xs -> '\^J' : go xs+ '^':'K' : xs -> '\^K' : go xs+ '^':'L' : xs -> '\^L' : go xs+ '^':'M' : xs -> '\^M' : go xs+ '^':'N' : xs -> '\^N' : go xs+ '^':'O' : xs -> '\^O' : go xs+ '^':'P' : xs -> '\^P' : go xs+ '^':'Q' : xs -> '\^Q' : go xs+ '^':'R' : xs -> '\^R' : go xs+ '^':'S' : xs -> '\^S' : go xs+ '^':'T' : xs -> '\^T' : go xs+ '^':'U' : xs -> '\^U' : go xs+ '^':'V' : xs -> '\^V' : go xs+ '^':'W' : xs -> '\^W' : go xs+ '^':'X' : xs -> '\^X' : go xs+ '^':'Y' : xs -> '\^Y' : go xs+ '^':'Z' : xs -> '\^Z' : go xs+ '^':'[' : xs -> '\^[' : go xs+ '^':'\\' : xs -> '\^\' : go xs+ '^':']' : xs -> '\^]' : go xs+ '^':'^' : xs -> '\^^' : go xs+ '^':'_' : xs -> '\^_' : go xs+ xs -> go xs+ x:xs -> x : go xs++ readHex :: String -> Int+ readHex xs = case N.readHex xs of+ [(n, "")] -> n+ _ -> error "Data.String.Interpolate.Util.readHex: no parse"++ readOct :: String -> Int+ readOct xs = case N.readOct xs of+ [(n, "")] -> n+ _ -> error "Data.String.Interpolate.Util.readHex: no parse"
src/Data/String/Interpolate/Parse.hs view
@@ -1,6 +1,6 @@ module Data.String.Interpolate.Parse where -import Data.String.Interpolate.Util+import Data.String.Interpolate.Internal.Util data Node = Literal String | Expression String
src/Data/String/Interpolate/Util.hs view
@@ -1,118 +1,79 @@-module Data.String.Interpolate.Util where+module Data.String.Interpolate.Util (unindent) where +import Control.Arrow ((>>>)) import Data.Char-import Data.Maybe-import qualified Numeric as N -import Data.String.Interpolate.Compat+-- | Remove indentation as much as possible while preserving relative+-- indentation levels.+--+-- `unindent` is useful in combination with `Data.String.Interpolate.i` to remove leading spaces that+-- resulted from code indentation. That way you can freely indent your string+-- literals without the indentation ending up in the resulting strings.+--+-- Here is an example:+--+-- >>> :set -XQuasiQuotes+-- >>> import Data.String.Interpolate+-- >>> import Data.String.Interpolate.Util+-- >>> :{+-- putStr $ unindent [i|+-- def foo+-- 23+-- end+-- |]+-- :}+-- def foo+-- 23+-- end+--+-- To allow this, two additional things are being done, apart from removing+-- indentation:+--+-- - One empty line at the beginning will be removed and+-- - if the last newline character (@"\\n"@) is followed by spaces, the spaces are removed.+unindent :: String -> String+unindent =+ lines_+ >>> removeLeadingEmptyLine+ >>> trimLastLine+ >>> removeIndentation+ >>> concat+ where+ isEmptyLine :: String -> Bool+ isEmptyLine = all isSpace -toString :: Show a => a -> String-toString a = let s = show a in fromMaybe s (readMaybe s)-{-# NOINLINE toString #-}-{-# RULES "toString/String" toString = id #-}-{-# RULES "toString/Int" toString = show :: Int -> String #-}-{-# RULES "toString/Integer" toString = show :: Integer -> String #-}-{-# RULES "toString/Float" toString = show :: Float -> String #-}-{-# RULES "toString/Double" toString = show :: Double -> String #-}+ lines_ :: String -> [String]+ lines_ [] = []+ lines_ s = case span (/= '\n') s of+ (first, '\n' : rest) -> (first ++ "\n") : lines_ rest+ (first, rest) -> first : lines_ rest --- Haskell 2010 character unescaping, see:--- http://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6-unescape :: String -> String-unescape = go- where- go input = case input of- "" -> ""- '\\' : 'x' : x : xs | isHexDigit x -> case span isHexDigit xs of- (ys, zs) -> (chr . readHex $ x:ys) : go zs- '\\' : 'o' : x : xs | isOctDigit x -> case span isOctDigit xs of- (ys, zs) -> (chr . readOct $ x:ys) : go zs- '\\' : x : xs | isDigit x -> case span isDigit xs of- (ys, zs) -> (chr . read $ x:ys) : go zs- '\\' : input_ -> case input_ of- '\\' : xs -> '\\' : go xs- 'a' : xs -> '\a' : go xs- 'b' : xs -> '\b' : go xs- 'f' : xs -> '\f' : go xs- 'n' : xs -> '\n' : go xs- 'r' : xs -> '\r' : go xs- 't' : xs -> '\t' : go xs- 'v' : xs -> '\v' : go xs- '&' : xs -> go xs- 'N':'U':'L' : xs -> '\NUL' : go xs- 'S':'O':'H' : xs -> '\SOH' : go xs- 'S':'T':'X' : xs -> '\STX' : go xs- 'E':'T':'X' : xs -> '\ETX' : go xs- 'E':'O':'T' : xs -> '\EOT' : go xs- 'E':'N':'Q' : xs -> '\ENQ' : go xs- 'A':'C':'K' : xs -> '\ACK' : go xs- 'B':'E':'L' : xs -> '\BEL' : go xs- 'B':'S' : xs -> '\BS' : go xs- 'H':'T' : xs -> '\HT' : go xs- 'L':'F' : xs -> '\LF' : go xs- 'V':'T' : xs -> '\VT' : go xs- 'F':'F' : xs -> '\FF' : go xs- 'C':'R' : xs -> '\CR' : go xs- 'S':'O' : xs -> '\SO' : go xs- 'S':'I' : xs -> '\SI' : go xs- 'D':'L':'E' : xs -> '\DLE' : go xs- 'D':'C':'1' : xs -> '\DC1' : go xs- 'D':'C':'2' : xs -> '\DC2' : go xs- 'D':'C':'3' : xs -> '\DC3' : go xs- 'D':'C':'4' : xs -> '\DC4' : go xs- 'N':'A':'K' : xs -> '\NAK' : go xs- 'S':'Y':'N' : xs -> '\SYN' : go xs- 'E':'T':'B' : xs -> '\ETB' : go xs- 'C':'A':'N' : xs -> '\CAN' : go xs- 'E':'M' : xs -> '\EM' : go xs- 'S':'U':'B' : xs -> '\SUB' : go xs- 'E':'S':'C' : xs -> '\ESC' : go xs- 'F':'S' : xs -> '\FS' : go xs- 'G':'S' : xs -> '\GS' : go xs- 'R':'S' : xs -> '\RS' : go xs- 'U':'S' : xs -> '\US' : go xs- 'S':'P' : xs -> '\SP' : go xs- 'D':'E':'L' : xs -> '\DEL' : go xs- '^':'@' : xs -> '\^@' : go xs- '^':'A' : xs -> '\^A' : go xs- '^':'B' : xs -> '\^B' : go xs- '^':'C' : xs -> '\^C' : go xs- '^':'D' : xs -> '\^D' : go xs- '^':'E' : xs -> '\^E' : go xs- '^':'F' : xs -> '\^F' : go xs- '^':'G' : xs -> '\^G' : go xs- '^':'H' : xs -> '\^H' : go xs- '^':'I' : xs -> '\^I' : go xs- '^':'J' : xs -> '\^J' : go xs- '^':'K' : xs -> '\^K' : go xs- '^':'L' : xs -> '\^L' : go xs- '^':'M' : xs -> '\^M' : go xs- '^':'N' : xs -> '\^N' : go xs- '^':'O' : xs -> '\^O' : go xs- '^':'P' : xs -> '\^P' : go xs- '^':'Q' : xs -> '\^Q' : go xs- '^':'R' : xs -> '\^R' : go xs- '^':'S' : xs -> '\^S' : go xs- '^':'T' : xs -> '\^T' : go xs- '^':'U' : xs -> '\^U' : go xs- '^':'V' : xs -> '\^V' : go xs- '^':'W' : xs -> '\^W' : go xs- '^':'X' : xs -> '\^X' : go xs- '^':'Y' : xs -> '\^Y' : go xs- '^':'Z' : xs -> '\^Z' : go xs- '^':'[' : xs -> '\^[' : go xs- '^':'\\' : xs -> '\^\' : go xs- '^':']' : xs -> '\^]' : go xs- '^':'^' : xs -> '\^^' : go xs- '^':'_' : xs -> '\^_' : go xs- xs -> go xs- x:xs -> x : go xs+ removeLeadingEmptyLine :: [String] -> [String]+ removeLeadingEmptyLine xs = case xs of+ y:ys | isEmptyLine y -> ys+ _ -> xs - readHex :: String -> Int- readHex xs = case N.readHex xs of- [(n, "")] -> n- _ -> error "Data.String.Interpolate.Util.readHex: no parse"+ trimLastLine :: [String] -> [String]+ trimLastLine (a : b : r) = a : trimLastLine (b : r)+ trimLastLine [a] = if all (== ' ') a+ then []+ else [a]+ trimLastLine [] = [] - readOct :: String -> Int- readOct xs = case N.readOct xs of- [(n, "")] -> n- _ -> error "Data.String.Interpolate.Util.readHex: no parse"+ removeIndentation :: [String] -> [String]+ removeIndentation ys = map (dropSpaces indentation) ys+ where+ dropSpaces 0 s = s+ dropSpaces n (' ' : r) = dropSpaces (n - 1) r+ dropSpaces _ s = s+ indentation = minimalIndentation ys+ minimalIndentation =+ safeMinimum 0+ . map (length . takeWhile (== ' '))+ . removeEmptyLines+ removeEmptyLines = filter (not . isEmptyLine)++ safeMinimum :: Ord a => a -> [a] -> a+ safeMinimum x xs = case xs of+ [] -> x+ _ -> minimum xs
− test/doctests.hs
@@ -1,6 +0,0 @@-module Main where--import Test.DocTest--main :: IO ()-main = doctest ["-isrc", "-optP-include", "-optPdist/build/autogen/cabal_macros.h", "src/Data/String/Interpolate.hs"]