interpolatedstring-perl6 0.1 → 0.2
raw patch · 3 files changed
+59/−21 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Text.InterpolatedString.Perl6: qq :: QuasiQuoter
Files
- interpolatedstring-perl6.cabal +3/−3
- src/Text/InterpolatedString/Perl6.hs +54/−18
- tests/Test.hs +2/−0
interpolatedstring-perl6.cabal view
@@ -1,5 +1,5 @@ Name: interpolatedstring-perl6-Version: 0.1+Version: 0.2 License: BSD3 License-file: LICENSE Category: Data@@ -10,8 +10,8 @@ Cabal-Version: >= 1.2 Build-Depends: base Build-Type: Custom-Synopsis: QuasiQuoter for Perl6-style multi-line interpolated strings.-Description: QuasiQuoter for Perl6-style multi-line interpolated strings.+Synopsis: 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 library
src/Text/InterpolatedString/Perl6.hs view
@@ -2,8 +2,8 @@ -- | 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 --- no interpolation at all: +-- The "q" form does one thin and does it well: It contains a multi-line string with +-- no interpolation at all: -- -- @ -- {-# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-} @@ -20,10 +20,10 @@ -- 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. -- --- Escaping of '{' is done with backslash. +-- Escapin of '{' is done with backslash. -- --- For interpolating numeric expressions without an explicit type signature, --- use the ExtendedDefaultRules language pragma, as shown below: +-- For interpolatin numeric expressions without an explicit type signature, +-- use the ExtendedDefaultRules lanuage pragma, as shown below: -- -- @ -- {-# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-} @@ -46,11 +46,23 @@ -- That way you interpolate bytestrings will not result in double quotes or -- character escapes. -- -module Text.InterpolatedString.Perl6 (qc, q, ShowQ(..)) where +-- The "qq" form adds to the "qc" form with a simple shorthand: '$foo' means '{foo}', +-- namely interpolating a single variable into the string. +-- +-- @ +-- {-# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-} +-- import Text.InterpolatedString.Perl6 (qq) +-- baz :: String +-- baz = [$qc| Hello, $who |] +-- where +-- who = "World" +-- @ +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 Data.Char (isAlpha, isAlphaNum) class Show a => ShowQ a where showQ :: a -> String @@ -66,18 +78,38 @@ data StringPart = Literal String | AntiQuote String deriving Show -parseHaskell a [] = [Literal (reverse a)] -parseHaskell a ('\\':x:xs) = parseHaskell (x:a) xs -parseHaskell a ('\\':[]) = parseHaskell ('\\':a) [] -parseHaskell a ('}':xs) = AntiQuote (reverse a) : parseStr [] xs -parseHaskell a (x:xs) = parseHaskell (x:a) xs +unQC a [] = [Literal (reverse a)] +unQC a ('\\':x:xs) = unQC (x:a) xs +unQC a ('\\':[]) = unQC ('\\':a) [] +unQC a ('}':xs) = AntiQuote (reverse a) : parseQC [] xs +unQC a (x:xs) = unQC (x:a) xs -parseStr a [] = [Literal (reverse a)] -parseStr a ('\\':x:xs) = parseStr (x:a) xs -parseStr a ('\\':[]) = parseStr ('\\':a) [] -parseStr a ('{':xs) = Literal (reverse a) : parseHaskell [] xs -parseStr a (x:xs) = parseStr (x:a) xs +parseQC a [] = [Literal (reverse a)] +parseQC a ('\\':x:xs) = parseQC (x:a) xs +parseQC a ('\\':[]) = parseQC ('\\':a) [] +parseQC a ('{':xs) = Literal (reverse a) : unQC [] xs +parseQC a (x:xs) = parseQC (x:a) xs +unQQ a [] = [Literal (reverse a)] +unQQ a ('\\':x:xs) = unQC (x:a) xs +unQQ a ('\\':[]) = unQC ('\\':a) [] +unQQ a ('}':xs) = AntiQuote (reverse a) : parseQQ [] xs +unQQ a (x:xs) = unQC (x:a) xs + +parseQQ a [] = [Literal (reverse a)] +parseQQ a ('\\':x:xs) = parseQQ (x:a) xs +parseQQ a ('\\':[]) = parseQQ ('\\':a) [] +parseQQ a ('$':x:xs) | x == '_' || isAlpha x = + Literal (reverse a) : AntiQuote (x:pre) : parseQQ [] post + where + (pre, post) = span isIdent xs +parseQQ a ('{':xs) = Literal (reverse a) : unQC [] xs +parseQQ a (x:xs) = parseQQ (x:a) xs + +isIdent '_' = True +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 @@ -87,9 +119,13 @@ Left s -> TH.report True s >> [| "" |] Right e -> return e --- | QuasiQuoter for interpolating Haskell values into a string literal. The pattern portion is undefined. +-- | QuasiQuoter for interpolatin Haskell values into a string literal. The pattern portion is undefined. +qq :: QuasiQuoter +qq = QuasiQuoter (makeExpr . parseQQ [] . filter (/= '\r')) + $ error "Cannot use qq as a pattern" + qc :: QuasiQuoter -qc = QuasiQuoter (makeExpr . parseStr [] . filter (/= '\r')) +qc = QuasiQuoter (makeExpr . parseQC [] . filter (/= '\r')) $ error "Cannot use qc as a pattern" q :: QuasiQuoter
tests/Test.hs view
@@ -12,6 +12,7 @@ testEmpty = assertBool "" ([$qc||] == "") testCharLiteral = assertBool "" ([$qc|{1+2}|] == "3") testString = assertBool "" ([$qc|a string {t1} is here|] == "a string 字元 is here")+testVariable = assertBool "" ([$qq|a string $t1 is here|] == "a string 字元 is here") testEscape = assertBool "" ([$qc|#\{}|] == "#{}" && [$qc|\{}|] == "{}") testComplex = assertBool "" ([$qc| ok@@ -27,6 +28,7 @@ [ TestLabel "Empty String" $ TestCase testEmpty , TestLabel "Character Literal" $ TestCase testCharLiteral , TestLabel "String Variable" $ TestCase testString+ , TestLabel "Dollar Variable" $ TestCase testVariable , TestLabel "Escape Sequences" $ TestCase testEscape , TestLabel "Complex Expression" $ TestCase testComplex ]