interpolatedstring-perl6 0.8.1 → 0.9.0
raw patch · 3 files changed
+93/−27 lines, 3 filesdep +bytestringdep +text
Dependencies added: bytestring, text
Files
- interpolatedstring-perl6.cabal +3/−3
- src/Text/InterpolatedString/Perl6.hs +83/−23
- tests/Test.hs +7/−1
interpolatedstring-perl6.cabal view
@@ -1,5 +1,5 @@ Name: interpolatedstring-perl6-Version: 0.8.1+Version: 0.9.0 License: BSD3 License-file: LICENSE Category: Data@@ -11,7 +11,7 @@ Build-Depends: base Build-Type: Custom Synopsis: QuasiQuoter for Perl6-style multi-line interpolated strings-Description: QuasiQuoter for Perl6-style multi-line interpolated strings with "q", "qq" and "qc" support.+Description: QuasiQuoter for Perl6-style multi-line interpolated strings with \"q\", \"qq\" and \"qc\" support. Data-Files: tests/Test.hs Source-Repository head@@ -20,6 +20,6 @@ library extensions: TemplateHaskell, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, OverlappingInstances- build-depends: base > 4 && < 5, template-haskell >= 2.5, haskell-src-meta >= 0.3+ build-depends: base > 4 && < 5, template-haskell >= 2.5, haskell-src-meta >= 0.3, text, bytestring hs-source-dirs: src exposed-modules: Text.InterpolatedString.Perl6
src/Text/InterpolatedString/Perl6.hs view
@@ -1,14 +1,17 @@-{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, OverlappingInstances #-}+{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, + UndecidableInstances, OverlappingInstances, MultiParamTypeClasses,+ IncoherentInstances+ #-} -- | QuasiQuoter for interpolated strings using Perl 6 syntax. ----- The "q" form does one thing and does it well: It contains a multi-line string with+-- The 'q' form does one thing and does it well: It contains a multi-line string with -- no interpolation at all: -- -- @ -- {-\# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-} -- import Text.InterpolatedString.Perl6 (q)--- foo :: String+-- foo :: String -- 'Text', 'ByteString' etc also works -- foo = [q| -- -- Well here is a@@ -17,11 +20,14 @@ -- |] -- @ ----- The "qc" form interpolates curly braces: Expressions inside {} will be--- directly interpolated if it's a String, or have 'show' called if it is not.+-- Any instance of the 'IsString' class is permitted. ----- Escaping of '{' is done with backslash.+-- The 'qc' form interpolates curly braces: expressions inside {} will be+-- directly interpolated if it's a 'Char', 'String', 'Text' or 'ByteString', or +-- it will have 'show' called if it is not. --+-- Escaping of '{' is done with backslash. +-- -- For interpolating numeric expressions without an explicit type signature, -- use the ExtendedDefaultRules lanuage pragma, as shown below: --@@ -37,16 +43,17 @@ -- If you want control over how 'show' works on your types, define a custom -- 'ShowQ' instance: --+-- For example, this instance allows you to display interpolated lists of strings as +-- a sequence of words, removing those pesky brackets, quotes, and escape sequences.+-- -- @+-- {-\# LANGUAGE FlexibleInstances #-} -- import Text.InterpolatedString.Perl6 (qc, ShowQ(..))--- instance ShowQ ByteString where--- showQ = unpack+-- instance ShowQ [String] where+-- showQ = unwords -- @ ----- That way you interpolate bytestrings will not result in double quotes or--- character escapes.------ The "qq" form adds to the "qc" form with a simple shorthand: '$foo' means '{foo}',+-- The 'qq' form adds to the 'qc' form with a simple shorthand: '$foo' means '{foo}', -- namely interpolating a single variable into the string. -- -- @@@ -55,12 +62,24 @@ -- baz :: String -- baz = [qc| Hello, $who |] -- where--- who = "World"+-- who = \"World\" -- @ ----- The ability to define custom "ShowQ" instances is particularly powerful with--- cascading instances using "qq".+-- Both 'qc' and 'qq' permit output to any types with both 'IsString' and 'Monoid' +-- instances.+-- +-- @+-- {-# LANGUAGE QuasiQuotes, OverloadedStrings #-}+-- import Text.InterpolatedString.Perl6 (qc)+-- import Data.Text (Text)+-- import Data.ByteString.Char8 (ByteString)+-- qux :: ByteString+-- qux = [qc| This will convert {\"Text\" :: Text} to {\"ByteString\" :: ByteString} |]+-- @ --+-- The ability to define custom 'ShowQ' instances is particularly powerful with+-- cascading instances using 'qq'.+-- -- Below is a sample snippet from a script that converts Shape objects into -- AppleScript suitable for drawing in OmniGraffle: --@@ -121,25 +140,65 @@ -- |] -- @ --+ module Text.InterpolatedString.Perl6 (qq, qc, q, ShowQ(..)) where import qualified Language.Haskell.TH as TH import Language.Haskell.TH.Quote import Language.Haskell.Meta.Parse+import GHC.Exts (IsString(..))+import Data.Monoid (Monoid(..))+import Data.ByteString.Char8 as Strict (ByteString, unpack)+import Data.ByteString.Lazy.Char8 as Lazy (ByteString, unpack)+import Data.Text as T (Text, unpack)+import Data.Text.Lazy as LazyT(Text, unpack) import Data.Char (isAlpha, isAlphaNum) +-- |A class for types that use special interpolation rules.+-- Instances of 'ShowQ' that are also instances of 'IsString' should obey the +-- following law: +--+-- @+-- fromString (showQ s) == s+-- @+--+-- because this library relies on this fact to optimize +-- away needless string conversions. class ShowQ a where showQ :: a -> String instance ShowQ Char where showQ = (:[])-+ instance ShowQ String where showQ = id -instance (Show a) => ShowQ a where+instance ShowQ Strict.ByteString where+ showQ = Strict.unpack++instance ShowQ Lazy.ByteString where+ showQ = Lazy.unpack++instance ShowQ T.Text where+ showQ = T.unpack++instance ShowQ LazyT.Text where+ showQ = LazyT.unpack++instance Show a => ShowQ a where showQ = show +-- todo: this should really be rewritten into RULES pragmas, but so far+-- I can't convince GHC to let the rules fire.+class QQ a string where+ toQQ :: a -> string++instance IsString s => QQ s s where+ toQQ = id++instance (ShowQ a, IsString s) => QQ a s where + toQQ = fromString . showQ+ data StringPart = Literal String | AntiQuote String deriving Show unQC a [] = [Literal (reverse a)]@@ -174,13 +233,15 @@ isIdent '\'' = True isIdent x = isAlphaNum x -makeExpr [] = [| "" |]-makeExpr ((Literal a):xs) = TH.appE [| (++) a |] $ makeExpr xs-makeExpr ((AntiQuote a):xs) = TH.appE [| (++) (showQ $(reify a)) |] $ makeExpr xs+makeExpr [] = [| mempty |]+makeExpr ((Literal a):xs) = TH.appE [| mappend (fromString a) |] + $ makeExpr xs+makeExpr ((AntiQuote a):xs) = TH.appE [| mappend (toQQ $(reify a)) |] + $ makeExpr xs reify s = case parseExp s of- Left s -> TH.report True s >> [| "" |]+ Left s -> TH.report True s >> [| mempty |] Right e -> return e -- | QuasiQuoter for interpolating '$var' and '{expr}' into a string literal. The pattern portion is undefined.@@ -199,8 +260,7 @@ -- | QuasiQuoter for a non-interpolating string literal. The pattern portion is undefined. q :: QuasiQuoter-q = QuasiQuoter ((\a -> [|a|]) . filter (/= '\r'))+q = QuasiQuoter ((\a -> [|fromString a|]) . filter (/= '\r')) (error "Cannot use q as a pattern") (error "Cannot use q as a type") (error "Cannot use q as a dec")-
tests/Test.hs view
@@ -4,6 +4,9 @@ import Text.InterpolatedString.Perl6 import Test.HUnit+import GHC.Exts(fromString)+import Data.ByteString.Char8 as BS(ByteString, pack)+import Data.Text as T(Text, pack) data Foo = Foo Int String deriving Show @@ -22,7 +25,9 @@ " ok\n" ++ "[Foo 4 \"Great!\",Foo 3 \"Scott!\"]\n" ++ " then\n"))-+testConvert = assertBool "" + (([$qc|{fromString "a"::Text} {fromString "b"::ByteString}|] :: String)+ == "a b") tests = TestList [ TestLabel "Empty String" $ TestCase testEmpty@@ -31,6 +36,7 @@ , TestLabel "Dollar Variable" $ TestCase testVariable , TestLabel "Escape Sequences" $ TestCase testEscape , TestLabel "Complex Expression" $ TestCase testComplex+ , TestLabel "String Conversion" $ TestCase testConvert ] main = runTestTT tests