1{-# LANGUAGE TemplateHaskell #-}
4-- Module : Streamly.Internal.Unicode.String
5-- Copyright : (c) 2022 Composewell Technologies
6-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11-- Convenient template Haskell quasiquoters to format strings.
15-- Essential requirements are:
17-- Haskell expression expansion
18-- Newline treatment (continue without introducing a newline)
19-- Indentation treatment
21-- We choose #{expr} for patching a Haskell expression's value in a string. "$"
22-- instead of "#" was another option (like in neat-interpolation package) but
23-- we did not use that to avoid conflict with strings that are used as shell
24-- commands. Another option was to use just "{}" (like in PyF package) but we
25-- did not use that to avoid conflict with "${}" used in shell.
27-- We use a "#" at the end of line to continue the line. We could use a "\"
28-- as well but that may interfere with CPP.
30-- Stripping is not part of the quasiquoter as it can be done by a Haskell
31-- function. Other type of formatting on the Haskell expression can be done
32-- using Haskell functions.
34module Streamly.Internal.Unicode.String
37 -- | To execute the code examples provided in this module in ghci, please
38 -- run the following commands first.
46import Control.Applicative (Alternative(..))
47import Control.Exception (displayException)
48import Data.Functor.Identity (runIdentity)
49import Streamly.Internal.Data.Parser (Parser)
51import Language.Haskell.TH
52import Language.Haskell.TH.Quote
54import qualified Streamly.Data.Fold as Fold
55import qualified Streamly.Internal.Data.Parser as Parser
56 (some, many, takeWhile1)
57import qualified Streamly.Data.Stream as Stream (fromList, parse)
58import qualified Streamly.Internal.Unicode.Parser as Parser
60#include "DocTestUnicodeString.hs"
62--------------------------------------------------------------------------------
64--------------------------------------------------------------------------------
71haskellIdentifier :: Monad m => Parser Char m String
73 let p = Parser.alphaNum <|> Parser.char '\'' <|> Parser.char '_'
74 in Parser.some p Fold.toList
76strParser :: Monad m => Parser Char m [StrSegment]
77strParser = Parser.many content Fold.toList
81 plainText = StrText <$> Parser.takeWhile1 (/= '#') Fold.toList
82 escHash = StrText . (: []) <$> (Parser.char '#' *> Parser.char '#')
83 lineCont = StrText [] <$ (Parser.char '#' *> Parser.char '\n')
90 plainHash = StrText . (: []) <$> Parser.char '#'
93 content = plainText <|> escHash <|> lineCont <|> var <|> plainHash
95strSegmentExp :: StrSegment -> Q Exp
96strSegmentExp (StrText text) = stringE text
97strSegmentExp (StrVar name) = do
98 valueName <- lookupValueName name
103 $ "str quote: Haskell symbol `" ++ name
104 ++ "` is not in scope"
106strExp :: [StrSegment] -> Q Exp
107strExp xs = appE [| concat |] $ listE $ map strSegmentExp xs
109expandVars :: String -> Q Exp
111 case runIdentity $ Stream.parse strParser (Stream.fromList ln) of
113 fail $ "str QuasiQuoter parse error: " ++ displayException e
117-- | A QuasiQuoter that treats the input as a string literal:
122-- Any @#{symbol}@ is replaced by the value of the Haskell symbol @symbol@
126-- >>> [str|#{x} world!|]
129-- @##@ means a literal @#@ without the special meaning for referencing
132-- >>> [str|##{x} world!|]
135-- A @#@ at the end of line means the line continues to the next line without
136-- introducing a newline character:
144-- Bugs: because of a bug in parsers, a lone # at the end of input gets
150 { quoteExp = expandVars
151 , quotePat = notSupported
152 , quoteType = notSupported
153 , quoteDec = notSupported
158 notSupported = error "str: Not supported."
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list