interpolate 0.1.1 → 0.2.1
raw patch · 7 files changed
Files
- interpolate.cabal +14/−11
- src/Data/String/Interpolate.hs +39/−14
- src/Data/String/Interpolate/Compat.hs +0/−10
- src/Data/String/Interpolate/Parse.hs +13/−9
- test/Data/String/Interpolate/ParseSpec.hs +8/−2
- test/Data/String/Interpolate/UtilSpec.hs +3/−1
- test/Data/String/InterpolateSpec.hs +3/−0
interpolate.cabal view
@@ -1,9 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.17.0.+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.2. -- -- see: https://github.com/sol/hpack name: interpolate-version: 0.1.1+version: 0.2.1 homepage: https://github.com/sol/interpolate#readme bug-reports: https://github.com/sol/interpolate/issues license: MIT@@ -12,7 +14,6 @@ author: Simon Hengel <sol@typeful.net> maintainer: Simon Hengel <sol@typeful.net> build-type: Simple-cabal-version: >= 1.10 category: Data, Text stability: experimental synopsis: String interpolation done right@@ -35,9 +36,9 @@ Data.String.Interpolate.Parse Paths_interpolate build-depends:- base == 4.*+ base ==4.*+ , haskell-src-meta >=0.8 , template-haskell- , haskell-src-meta >= 0.8 ghc-options: -Wall default-language: Haskell2010 @@ -59,14 +60,16 @@ Data.String.Interpolate.ParseSpec Data.String.Interpolate.UtilSpec Data.String.InterpolateSpec+ Paths_interpolate build-depends:- base == 4.*- , template-haskell- , haskell-src-meta >= 0.8- , text+ QuickCheck+ , base ==4.*+ , base-compat , bytestring- , hspec >= 1.5- , QuickCheck+ , haskell-src-meta >=0.8+ , hspec >=1.5 , quickcheck-instances+ , template-haskell+ , text ghc-options: -Wall default-language: Haskell2010
src/Data/String/Interpolate.hs view
@@ -15,7 +15,7 @@ import Data.String.Interpolate.Internal.Util import Data.String.Interpolate.Parse-import Data.String.Interpolate.Compat (Q, Exp, appE, reportError)+import Language.Haskell.TH -- | -- A `QuasiQuoter` for string interpolation. Expression enclosed within@@ -40,7 +40,7 @@ -- profession: Marvin the λ-scientist i :: QuasiQuoter i = QuasiQuoter {- quoteExp = toExp . parseNodes+ quoteExp = toExp . parseNodes . decodeNewlines , quotePat = err "pattern" , quoteType = err "type" , quoteDec = err "declaration"@@ -48,17 +48,42 @@ where err name = error ("Data.String.Interpolate.i: This QuasiQuoter can not be used as a " ++ name ++ "!") - toExp:: [Node] -> Q Exp- toExp nodes = case nodes of- [] -> [|""|]- (x:xs) -> f x `appE` toExp xs+ toExp :: [Node ()] -> Q Exp+ toExp input = do+ nodes <- mapM generateName input+ e <- go nodes+ return $ foldr lambda e [name | Abstraction name <- nodes] where- f (Literal s) = [|showString s|]- f (Expression e) = [|(showString . toString) $(reifyExpression e)|]+ lambda :: Name -> Exp -> Exp+ lambda name e = LamE [VarP name] e - reifyExpression :: String -> Q Exp- reifyExpression s = case parseExp s of- Left _ -> do- reportError "Parse error in expression!"- [|""|]- Right e -> return e+ generateName :: Node () -> Q (Node Name)+ generateName (Abstraction ()) = Abstraction <$> newName "x"+ generateName (Literal s) = return (Literal s)+ generateName (Expression e) = return (Expression e)++ go :: [Node Name] -> Q Exp+ go nodes = case nodes of+ [] -> [|""|]+ (x:xs) -> eval x `appE` go xs+ where+ eval (Literal s) = [|showString s|]+ eval (Expression e) = interpolate (reifyExpression e)+ eval (Abstraction name) = interpolate $ (return $ VarE name)++ interpolate :: Q Exp -> Q Exp+ interpolate e = [|(showString . toString) $(e)|]++ reifyExpression :: String -> Q Exp+ reifyExpression s = case parseExp s of+ Left _ -> do+ fail "Parse error in expression!" :: Q Exp+ Right e -> return e++decodeNewlines :: String -> String+decodeNewlines = go+ where+ go xs = case xs of+ '\r' : '\n' : ys -> '\n' : go ys+ y : ys -> y : go ys+ [] -> []
src/Data/String/Interpolate/Compat.hs view
@@ -1,13 +1,8 @@ {-# LANGUAGE CPP #-} module Data.String.Interpolate.Compat ( readMaybe-, module Language.Haskell.TH-#if !MIN_VERSION_template_haskell(2,8,0)-, reportError-#endif ) where -import Language.Haskell.TH import Text.Read #if !MIN_VERSION_base(4,6,0)@@ -36,9 +31,4 @@ readMaybe s = case readEither s of Left _ -> Nothing Right a -> Just a-#endif--#if !MIN_VERSION_template_haskell(2,8,0)-reportError :: String -> Q ()-reportError = report True #endif
src/Data/String/Interpolate/Parse.hs view
@@ -2,19 +2,23 @@ import Data.String.Interpolate.Internal.Util -data Node = Literal String | Expression String+data Node a = Literal String | Expression String | Abstraction a -parseNodes :: String -> [Node]+parseNodes :: String -> [Node ()] parseNodes = go "" where- go :: String -> String -> [Node]+ go :: String -> String -> [Node ()] go acc input = case input of- "" -> [(lit . reverse) acc]+ "" -> lit [] '\\':x:xs -> go (x:'\\':acc) xs- '#':'{':xs -> case span (/= '}') xs of- (ys, _:zs) -> (lit . reverse) acc : Expression ys : go "" zs- (_, "") -> [lit (reverse acc ++ input)]+ '#':'{':xs | (e, '}':ys) <- span (/= '}') xs -> lit $ expression e : go "" ys x:xs -> go (x:acc) xs+ where+ expression e+ | null e = Abstraction ()+ | otherwise = Expression e - lit :: String -> Node- lit = Literal . unescape+ lit :: [Node ()] -> [Node ()]+ lit nodes+ | null acc = nodes+ | otherwise = (Literal . unescape $ reverse acc) : nodes
test/Data/String/Interpolate/ParseSpec.hs view
@@ -6,8 +6,8 @@ import Data.String.Interpolate.Parse -deriving instance Eq Node-deriving instance Show Node+deriving instance Eq a => Eq (Node a)+deriving instance Show a => Show (Node a) main :: IO () main = hspec spec@@ -17,6 +17,12 @@ describe "parseNodes" $ do it "parses string literals" $ do parseNodes "foo" `shouldBe` [Literal "foo"]++ it "parses abstractions" $ do+ parseNodes "#{}" `shouldBe` [Abstraction ()]++ it "parses expressions" $ do+ parseNodes "#{foo}" `shouldBe` [Expression "foo"] it "parses embedded expressions" $ do parseNodes "foo #{bar} baz" `shouldBe` [Literal "foo ", Expression "bar", Literal " baz"]
test/Data/String/Interpolate/UtilSpec.hs view
@@ -1,8 +1,10 @@ module Data.String.Interpolate.UtilSpec (main, spec) where +import Prelude ()+import Prelude.Compat+ import Test.Hspec import Test.QuickCheck-import Control.Applicative import Data.String.Interpolate.Util
test/Data/String/InterpolateSpec.hs view
@@ -18,6 +18,9 @@ it "interpolates an expression of type String" $ do property $ \xs ys -> [i|foo #{xs ++ ys} bar|] `shouldBe` "foo " ++ xs ++ ys ++ " bar" + it "interpolates abstractions" $ do+ [i|foo #{} bar #{} baz|] (23 :: Int) "test" `shouldBe` "foo 23 bar test baz"+ it "accepts character escapes" $ do [i|foo \955 bar|] `shouldBe` "foo \955 bar"