packages feed

marvin-interpolate 0.0.1 → 0.1.0

raw patch · 7 files changed

+103/−48 lines, 7 filesdep ~marvin-interpolatePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: marvin-interpolate

API changes (from Hackage documentation)

+ Marvin.Interpolate: is :: String -> Q Exp
+ Marvin.Interpolate.String: isS :: String -> Q Exp
+ Marvin.Interpolate.Text: isT :: String -> Q Exp
+ Marvin.Interpolate.Text.Lazy: isLT :: String -> Q Exp

Files

README.md view
@@ -6,7 +6,7 @@ This string interpolation library originates from the [Marvin project](https://github.com/JustusAdam/marvin) where, in an attempt to make it easy for the user to write text with some generated data in it, I developed this string interpolation library. The design is very similar to the string interpolation in Scala and CoffeeScript, in that the hard work happens at compile time (no parsing overhead at runtime) and any valid Haskell expression can be interpolated. -The library uses the builtin Haskell compiler extension in the form of *QuasiQuoters* ([`QuasiQuotes`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell-quasi-quotation) language extension).+The library uses the builtin Haskell compiler extension in the form of *QuasiQuoters* ([`QuasiQuotes`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell-quasi-quotation) language extension) and/or *splices* ([`Template Haskell`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell) language extension)  ```haskell {-# LANGUAGE QuasiQuotes #-}@@ -17,19 +17,33 @@ -- "some string [2,3,4] and data" ``` -It basically transforms the interpolated string, which is anything between `[i|` and `|]` into a concatenation of all string bits and the expressions in `%{}`.+or alternatively as splice (which might be kinder to your code highlighting)++```haskell+{-# LANGUAGE TemplateHaskell #-}++import Marvin.Interpolate++myStr = $(is "some string %{show $ map succ [1,2,3]} and data")+-- "some string [2,3,4] and data"+```++It basically transforms the interpolated string  `[i|interpolated string|]`, or in splices `$(is "interpolated string")` into a concatenation of all string bits and the expressions in `%{}`. Therefore it is not limited to `String` alone, rather it produces a literal at compile time, which can either be interpreted as `String` or, using the [`OverloadedStrings`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#overloaded-string-literals) extension, as `Text` or `ByteString` or any other string type. -`i` is the basic interpolator, which inserts the expressions verbatim. Hence when using `i` all expressions must return the desired string type.+`i` (for *interpolate*) and `is` (for *interpolate splice*) is the basic interpolator, which inserts the expressions verbatim. Hence when using `i` or `is` all expressions must return the desired string type.  There are specialized interpolators, which also perform automatic conversion of non-string types into the desired string type. These specialized interpolators each have an associated typeclass, which converts string types (`String`, `Text` and lazy `Text`) to the target type, but leaves the contents unchanged and calls `show` on all other types before converting. This last instance, which is based on `Show`, can be overlapped by specifying a custom instance for your type, allowing the user to define the conversion. -- `iS` in `Marvin.Interpolate.String` converts to `String` via the `ShowS` typeclass-- `iT` in `Marvin.Interpolate.Text` converts to `Text` via the `ShowT` typeclass-- `iLT` in `Marvin.Interpolate.Text.Lazy` converts to lazy `Text` via the `ShowLT` typeclass+The naming scheme of the interpolators in general is `i<splice?><pecialization?>`.+I. e. `isS` expands to *interpolate splice to String* and `iLT` to *interpolate to Lazy Text*. +- `iS` and `isS` in `Marvin.Interpolate.String` converts to `String` via the `ShowS` typeclass+- `iT` and `isT` in `Marvin.Interpolate.Text` converts to `Text` via the `ShowT` typeclass+- `iLT` and `isLT` in `Marvin.Interpolate.Text.Lazy` converts to lazy `Text` via the `ShowLT` typeclass+ To import all interpolators, import `Marvin.Interpolate.All`.  ## Syntax@@ -45,17 +59,20 @@ let x = 5 in [iS|x equals %{x}|] -- > "x equals 5" ``` -There are three escape sequences to allow literal `%{` and `|]`+There are four escape sequences to allow literal `%{` and `|]`  | Input | Output | |-------|--------|-| `\]`  | `]`    |-| `\\`  | `\\`   |-| `\%`  | `%` (only outside `%{}`) |-| `\}`  | `}` (only inside `%{}`) | +| `~]`  | `]`    |+| `~%`  | `%`    |+| `~}`  | `}`    | +| `~~`  | `~`    |  -As a result the sequence `\%{` will show up as a literal `%{` in the output and `|\]` results in a literal `|]`.+As a result the sequence `~%{` will show up as a literal `%{` in the output and `|~]` results in a literal `|]`.+Note that these are simple substitutions. +In general the characters themselves, if not escaped, will not throw errors, aka `~` will be `~` again in the output.+Escaping is only necessary in cases where these cahracters would have a special meaning otherwise.   ## Differences to/Advantages over other libraries
marvin-interpolate.cabal view
@@ -1,5 +1,5 @@ name: marvin-interpolate-version: 0.0.1+version: 0.1.0 cabal-version: >=1.10 build-type: Simple license: BSD3@@ -9,7 +9,7 @@ homepage: http://marvin.readthedocs.io/en/latest/interpolation.html synopsis: Compile time string interpolation a la Scala and CoffeeScript description:-    The official documentation can be found on readthedocs http://marvin.readthedocs.io/en/latest/interpolation.html.+    The official documentation can be found on readthedocs https://marvin.readthedocs.io/en/latest/interpolation.html category: Text author: JustusAdam extra-source-files:@@ -43,7 +43,7 @@     main-is: Spec.hs     build-depends:         base >=4.9.0.0 && <4.10,-        marvin-interpolate >=0.0.1 && <0.1,+        marvin-interpolate >=0.1.0 && <0.2,         hspec >=2.2.4 && <2.3     default-language: Haskell2010     hs-source-dirs: test
src/Marvin/Interpolate.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE BangPatterns    #-} {-# LANGUAGE MultiWayIf      #-} {-# LANGUAGE TemplateHaskell #-} module Marvin.Interpolate   ( interpolateInto+  , is   , i   ) where @@ -21,38 +23,41 @@ type Parsed = [Either String String]  +escapeChar :: Char+escapeChar = '~'++ parser :: Parsec String () Parsed parser = manyTill (parseInterpolation <|> parseString) eof  parseString :: Parsec String () (Either String String)-parseString = Right <$> parseTillEscape '%' True+parseString = Right <$> parseTillEscape "%{" True  parseInterpolation :: Parsec String () (Either String String)-parseInterpolation = Left <$> between (string "%{") (char '}') (parseTillEscape '}' False)+parseInterpolation = Left <$> between (try $ string "%{") (char '}') (parseTillEscape "}" False) -parseTillEscape :: Char -> Bool -> Parsec String () String-parseTillEscape endChar allowEOF = do-    chunk <- many $ noneOf ['\\', endChar]-    rest <- eofEND <|> (lookAhead (try anyChar) >>= restEND)+parseTillEscape :: String -> Bool -> Parsec String () String+parseTillEscape endSeq@(endChar:_) allowEOF = do+    chunk <- many $ noneOf [escapeChar, endChar]+    !rest <- eofEND+              <|> (char escapeChar >> parseEscaped)+              <|> (lookAhead (try $ string endSeq) >> return "")+              <|> (return <$> char endChar)     return $ chunk <> rest   where     eofEND-        | allowEOF = eof >> return ""+        | allowEOF = eof >> return "" -- <|> (try (char escapeChar >> eof) >> char escapeChar >> return [escapeChar])         | otherwise = fail "EOF not allowed in interpolation" -    restEND c-        | c == '\\' = parseEscaped-        | c == endChar = return ""--    parseEscaped = do-        char '\\'+    parseEscaped = (eof >> return [escapeChar]) <|> do         next <- anyChar         let escaped-                | next == '\\' = "\\"+                | next == escapeChar = [escapeChar]+                | next == '%' = "%"                 | next == ']' = "]"-                | next == endChar = [endChar]-                | otherwise = "\\" ++ [next]-        rest <- parseTillEscape endChar allowEOF+                | next == '}' = "}"+                | otherwise = escapeChar : [next]+        rest <- parseTillEscape endSeq allowEOF         return $ escaped <> rest  @@ -87,5 +92,9 @@                       Left expr2 -> AppE converter expr2  +is :: String -> Q Exp+is = return . interpolateInto (VarE 'id)++ i :: QuasiQuoter-i = mqq { quoteExp = return . interpolateInto (VarE 'id) }+i = mqq { quoteExp = is }
src/Marvin/Interpolate/String.hs view
@@ -34,5 +34,9 @@     showStr = show  +isS :: String -> Q Exp+isS = return . interpolateInto (VarE 'show)++ iS :: QuasiQuoter-iS = mqq { quoteExp = return . interpolateInto (VarE 'show) }+iS = mqq { quoteExp = isS }
src/Marvin/Interpolate/Text.hs view
@@ -33,5 +33,9 @@     showT = pack . show  +isT :: String -> Q Exp+isT = return . interpolateInto (VarE 'showT)++ iT :: QuasiQuoter-iT = mqq { quoteExp = return . interpolateInto (VarE 'showT) }+iT = mqq { quoteExp = isT }
src/Marvin/Interpolate/Text/Lazy.hs view
@@ -33,5 +33,9 @@     showLT = L.pack . show  +isLT :: String -> Q Exp+isLT = return . interpolateInto (VarE 'showLT)++ iLT :: QuasiQuoter-iLT = mqq { quoteExp = return . interpolateInto (VarE 'showLT) }+iLT = mqq { quoteExp = isLT }
test/Spec.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE QuasiQuotes     #-}+{-# LANGUAGE TemplateHaskell #-}   import           Data.List              (intercalate)@@ -8,18 +9,28 @@  formatSpec :: Spec formatSpec = do-    describe "escape sequences" $ do-        it "parses \\\\ as \\" $-            [i|\\|] `shouldBe` "\\"-        it "parses \\% as %" $-            [i|\%|] `shouldBe` "%"-        it "parses \\] as ]" $-            [i|\]|] `shouldBe` "]"-        it "parses \\%{} as %{}" $-            [i|\%{}|] `shouldBe` "%{}"-        it "parses |\\] as |]" $-            [i||\]|] `shouldBe` "|]"+    describe "parsing to itself" $ do+        it "%" $+            [i|%|] `shouldBe` "%"+        it "%anything" $+            [i|%anything|] `shouldBe` "%anything"+        it "~" $+            [i|~|] `shouldBe` "~"+        it "~anything" $+            $(is "~anything") `shouldBe` "~anything" +    describe "parsing escape sequences" $ do+        it "parses ~% as %" $+            [i|~%|] `shouldBe` "%"+        it "parses ~] as ]" $+            [i|~]|] `shouldBe` "]"+        it "parses ~%{} as %{}" $+            [i|~%{}|] `shouldBe` "%{}"+        it "parses |~] as |]" $+            [i||~]|] `shouldBe` "|]"+        it "parses ~~ as ~" $+            [i|~~|] `shouldBe` "~"+     describe "interpolation substitution" $ do         it "leaves an empty string" $             [i||] `shouldBe` ""@@ -48,6 +59,12 @@                 y = " can be"             in [i|Any %{intercalate " " x ++ y} interpolated|]                  `shouldBe` "Any haskell expression can be interpolated"+++    describe "splice interpolation" $+        it "interpolates a splice" $+            let x = 5 in $(isS "%{x}") `shouldBe` "5"+  main :: IO () main = hspec formatSpec