interpolatedstring-perl6 0.3 → 0.4
raw patch · 2 files changed
+202/−137 lines, 2 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
interpolatedstring-perl6.cabal view
@@ -1,5 +1,5 @@ Name: interpolatedstring-perl6-Version: 0.3+Version: 0.4 License: BSD3 License-file: LICENSE Category: Data
src/Text/InterpolatedString/Perl6.hs view
@@ -1,136 +1,201 @@-{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, OverlappingInstances #-} - --- | QuasiQuoter for interpolated strings using Perl 6 syntax. --- --- The "q" form does one thin 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 = [$q| --- --- Well here is a --- multi-line string! --- --- |] --- @ --- --- 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. --- --- Escapin of '{' is done with backslash. --- --- For interpolatin numeric expressions without an explicit type signature, --- use the ExtendedDefaultRules lanuage pragma, as shown below: --- --- @ --- {-# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-} --- import Text.InterpolatedString.Perl6 (qc) --- bar :: String --- bar = [$qc| Well {\"hello\" ++ \" there\"} {6 * 7} |] --- @ --- --- bar will have the value \" Well hello there 42 \". --- --- If you want control over how 'show' works on your types, define a custom --- 'ShowQ' instance: --- --- @ --- import Text.InterpolatedString.Perl6 (qc, ShowQ(..)) --- instance ShowQ ByteString where --- showQ = unpack --- @ --- --- 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}', --- 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 ShowQ a where - showQ :: a -> String - -instance ShowQ Char where - showQ = (:[]) - -instance ShowQ String where - showQ = id - -instance (Show a) => ShowQ a where - showQ = show - -data StringPart = Literal String | AntiQuote String deriving Show - -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 - -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 - -reify s = - case parseExp s of - Left s -> TH.report True s >> [| "" |] - Right e -> return e - --- | QuasiQuoter for interpolating '$var' and '{expr}' into a string literal. The pattern portion is undefined. -qq :: QuasiQuoter -qq = QuasiQuoter (makeExpr . parseQQ [] . filter (/= '\r')) - $ error "Cannot use qq as a pattern" - --- | QuasiQuoter for interpolating '{expr}' into a string literal. The pattern portion is undefined. -qc :: QuasiQuoter -qc = QuasiQuoter (makeExpr . parseQC [] . filter (/= '\r')) - $ error "Cannot use qc as a pattern" - --- | QuasiQuoter for a non-interpolating string literal. The pattern portion is undefined. -q :: QuasiQuoter -q = QuasiQuoter ((\a -> [|a|]) . filter (/= '\r')) - $ error "Cannot use q as a pattern" - +{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, OverlappingInstances #-}++-- | 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:+--+-- @+-- {-\# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-}+-- import Text.InterpolatedString.Perl6 (q)+-- foo :: String+-- foo = [$q|+-- +-- Well here is a+-- multi-line string!+-- +-- |]+-- @+--+-- 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.+--+-- For interpolating numeric expressions without an explicit type signature,+-- use the ExtendedDefaultRules lanuage pragma, as shown below:+--+-- @+-- {-\# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-}+-- import Text.InterpolatedString.Perl6 (qc)+-- bar :: String+-- bar = [$qc| Well {\"hello\" ++ \" there\"} {6 * 7} |]+-- @+--+-- bar will have the value \" Well hello there 42 \".+--+-- If you want control over how 'show' works on your types, define a custom+-- 'ShowQ' instance:+--+-- @+-- import Text.InterpolatedString.Perl6 (qc, ShowQ(..))+-- instance ShowQ ByteString where+-- showQ = unpack+-- @+--+-- 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}',+-- 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"+-- @+--+-- 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:+--+-- @+-- {-\# LANGUAGE QuasiQuotes, ExtendedDefaultRules, NamedFieldPuns, RecordWildCards #-}+-- import qualified System.IO.UTF8 as UTF8+-- import Text.InterpolatedString.Perl6+-- @ +--+-- @ +-- data Shape = Shape+-- { originX :: Int+-- , originY :: Int+-- , width :: Int+-- , height :: Int+-- , stroke :: Stroke+-- , text :: Text+-- }+-- instance ShowQ Shape where+-- showQ Shape{..} = [$qq|+-- make new shape at end of graphics with properties+-- \\{ $text, $stroke, _size, $_origin }+-- |]+-- where +-- _size = [$qq|size: \{$width, $height}|]+-- _origin = [$qq|origin: \{$originX, $originY}|]+-- @ +--+-- @ +-- data Stroke = StrokeWhite | StrokeNone+-- instance ShowQ Stroke where+-- showQ StrokeNone = \"draws stroke:false\"+-- showQ StrokeWhite = \"stroke color: {1, 1, 1}\"+-- @ +--+-- @ +-- data Text = Text+-- { txt :: String+-- , color :: Color+-- }+-- instance ShowQ Text where+-- showQ Text{..} = [$qq|text: \\{ text: \"$txt\", $color, alignment: center } |]+-- @ +--+-- @ +-- data Color = Color { red :: Float, green :: Float, blue :: Float }+-- instance ShowQ Color where+-- showQ Color{..} = [$qq|color: \{$red, $green, $blue}|]+-- @ +--+-- @ +-- main :: IO ()+-- main = UTF8.putStrLn [$qq|+-- tell application \"OmniGraffle Professional 5\"+-- tell canvas of front window+-- { makeShape ... }+-- end tell+-- end tell+-- |]+-- @+--+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 ShowQ a where+ showQ :: a -> String++instance ShowQ Char where+ showQ = (:[])++instance ShowQ String where+ showQ = id++instance (Show a) => ShowQ a where+ showQ = show++data StringPart = Literal String | AntiQuote String deriving Show++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++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++reify s = + case parseExp s of+ Left s -> TH.report True s >> [| "" |]+ Right e -> return e++-- | QuasiQuoter for interpolating '$var' and '{expr}' into a string literal. The pattern portion is undefined.+qq :: QuasiQuoter+qq = QuasiQuoter (makeExpr . parseQQ [] . filter (/= '\r'))+ $ error "Cannot use qq as a pattern"++-- | QuasiQuoter for interpolating '{expr}' into a string literal. The pattern portion is undefined.+qc :: QuasiQuoter+qc = QuasiQuoter (makeExpr . parseQC [] . filter (/= '\r'))+ $ error "Cannot use qc as a pattern"++-- | QuasiQuoter for a non-interpolating string literal. The pattern portion is undefined.+q :: QuasiQuoter+q = QuasiQuoter ((\a -> [|a|]) . filter (/= '\r'))+ $ error "Cannot use q as a pattern"+