interpol 0.1.0 → 0.2.0
raw patch · 7 files changed
+143/−43 lines, 7 filesdep +haskell-src-extsdep −preprocessor-tools
Dependencies added: haskell-src-exts
Dependencies removed: preprocessor-tools
Files
- Main.hs +50/−26
- Makefile +11/−5
- README.md +27/−5
- Test/Pure.hs +7/−0
- Test/String.hs +7/−0
- Text/Interpol.hs +22/−0
- interpol.cabal +19/−7
Main.hs view
@@ -3,40 +3,64 @@ ) where import Data.Generics ( mkT, everywhere )-import Data.Monoid ( Monoid(..) )-import Language.Haskell.Preprocessor ( Extension(..)- , Ast(..), Token(..), Tag(..)- , transform, base )+import Language.Haskell.Exts ( prettyPrint+ , ParseResult(..), parseFileWithExts+ , Module(..), Exp(..), QOp(..)+ , QName(..), Name(..), Literal(..)+ , ImportDecl(..), ModuleName(..), SrcLoc(..) ) import System.Environment ( getArgs )+import Text.Printf ( printf ) import Text.Regex.Posix ( (=~) ) -extension :: Extension-extension = interpolExt `mappend` base+main :: IO ()+main = do+ [_, fin, fout] <- getArgs+ res <- parseFileWithExts [] fin+ case res of+ ParseFailed loc reason -> do+ printf "%s:%s: Parse Failed: %s" fin (show loc) reason+ writeFile fout =<< readFile fin+ ParseOk m -> do+ let magicLine = "{-# LINE 1 \"" ++ fin ++ "\" #-}\n"+ writeFile fout $ magicLine ++ prettyPrint (transform m) -interpolExt :: Extension-interpolExt = mempty { transformer = everywhere (mkT trans) }+transform :: Module -> Module+transform = addDecl . everywhere (mkT trans) where- trans :: Ast -> Ast- trans Single{item = tok@Token{tag = StringLit, val = s}} =- let s' = '(' : concat (unsafeInterpol $ ti s) ++ ")"- in Single tok{ val = s' }- trans ast = ast+ addDecl :: Module -> Module+ addDecl (Module sl mn mps mwt mes ids ds) =+ let ids' = (ImportDecl{ importLoc = SrcLoc { srcFilename = ""+ , srcLine = 0+ , srcColumn = 0 }+ , importModule = ModuleName "Text.Interpol"+ , importQualified = False+ , importSrc = False+ , importPkg = Nothing+ , importAs = Nothing+ , importSpecs = Nothing }) : ids+ in Module sl mn mps mwt mes ids' ds - ti = tail . init+ trans :: Exp -> Exp+ trans (Lit (String s)) = interpol s+ trans e = e+ identRE = "\\{[A-z_][A-z0-9_]*}" - unsafeInterpol :: String -> [String]- unsafeInterpol s =- let (before, ident, after) = s =~ identRE- in case ident of- "" -> [norm before]- _ -> norm before : showIdent ident : unsafeInterpol after+ interpol :: String -> Exp+ interpol s+ | s =~ identRE = Paren $ go s+ | otherwise = Lit (String s) - showIdent "" = ""- showIdent i = concat [" ++ show ", ti i, " ++ "]+ go :: String -> Exp+ go s = let (before, ident, after) = s =~ identRE+ e = InfixApp (Lit (String before))+ appendOp+ (Var (UnQual (Ident $ ti ident)))+ in case ident of+ "" -> (Lit (String before))+ _ -> InfixApp e appendOp $ go after - norm "" = "\"\""- norm s = '"' : s ++ "\""+ appendOp :: QOp+ appendOp = QVarOp (UnQual (Symbol "^-^")) -main :: IO ()-main = transform extension =<< getArgs+ ti = tail . init
Makefile view
@@ -1,6 +1,7 @@-TESTS := Test/Simple Test/One+TESTS1 := Simple One String+TESTS2 := Standalone Pure -.PHONY: all build dist install clean clean-tests test+.PHONY: all build dist install clean clean-tests test doc all: build @@ -17,7 +18,8 @@ cabal clean clean-tests:- rm -f $(TESTS) Test/*.{hi,o}+ rm -f Test/*.{hi,o}+ $(foreach t,$(TESTS1) $(TESTS2),rm -f Test/$(t)${\n}) define \n @@ -25,8 +27,12 @@ endef test: install clean-tests- $(foreach t,$(TESTS),ghc -F -pgmF interpol $(t)${\n})- $(foreach t,$(TESTS),[ "`$(t)`" = "I have 23 apples." ]${\n})+ $(foreach t,$(TESTS1),cd Test && ghc -F -pgmF interpol $(t)${\n})+ $(foreach t,$(TESTS2),cd Test && ghc $(t)${\n})+ $(foreach t,$(TESTS1) $(TESTS2),cd Test && [ "`./$(t)`" = "I have 23 apples." ]${\n}) dist/setup-config: interpol.cabal cabal configure++doc: build+ cabal haddock
README.md view
@@ -7,7 +7,7 @@ Examples -------- -The `interpol` pre-processor parses Haskell source file *before* GHC+The `interpol` preprocessor parses Haskell source file *before* GHC and performs variable interpolation statically. Concretely, it replaces `{identifier}` patterns in literal strings with `show identifier`. For instance,@@ -25,6 +25,17 @@ not have a `Show` instance will result in the appropriate error). +Installation+------------++This package is on+[Hackage](http://hackage.haskell.org/package/interpol). To install+it, run:++ cabal update+ cabal install interpol++ Usage ----- @@ -44,14 +55,25 @@ Operation --------- -The `interpol` pre-processor effectively replaces-`"\\{[A-z_][A-z0-9_]*}"` with `"++ show <ident> ++"`. So,+The `interpol` preprocessor effectively does two things: + 1. it adds an import declaration for `Text.Interpol`, in order to+ bring the `(^-^)` operator into scope, and++ 1. it replaces any occurrence of `"\\{[A-z_][A-z0-9_]*}"` in string+ literals with `"^-^ <ident> ^-^"`.++So,+ "I have {okVal} apples." actually becomes - ("I have " ++ show okVal ++ " apples.")+ ("I have " ^-^ okVal ^-^ " apples.") -Run the pre-processor manually and check out the source for details+The `(^-^)` operator is a smarter version of `(++)`: it shows its+second argument before appending, but only if it is not already a+`String` (i.e. it does not quote `String` values when interpolating).++Run the preprocessor manually and check out the source for details (seriously now, this README is longer than the source).
+ Test/Pure.hs view
@@ -0,0 +1,7 @@+import Text.Interpol++myVar :: Int+myVar = 23++main :: IO ()+main = putStrLn $ "I have " ^-^ myVar ^-^ " apples."
+ Test/String.hs view
@@ -0,0 +1,7 @@+module Main where++myVar :: String+myVar = "23"++main :: IO ()+main = putStrLn "I have {myVar} apples."
+ Text/Interpol.hs view
@@ -0,0 +1,22 @@+-- | Support module for the @interpol@ preprocessor.+module Text.Interpol (+ (^-^)+ ) where++import Data.Typeable ( Typeable, cast )++infixl 3 ^-^++-- | Append a showable value to a 'String' in a smart way. In+-- particular, do /not/ 'show' a 'String', as this encloses it in+-- \"quotes\". So, depending on the type of the second parameter,+-- '^-^' is equivalent to one of the following+--+-- @+-- x ^-^ y = x ++ y+-- x ^-^ y = x ++ show y+-- @+(^-^) :: (Typeable a, Show a) => String -> a -> String+s ^-^ st = case cast st of+ Just s' -> s ++ s'+ Nothing -> s ++ show st
interpol.cabal view
@@ -1,23 +1,35 @@ Name: interpol-Version: 0.1.0-Cabal-Version: >= 1.2-License: GPL+Version: 0.2.0+Cabal-Version: >= 1.6+License: GPL-3 License-File: LICENSE Stability: experimental Author: Alexandru Scvortov <scvalex@gmail.com> Maintainer: scvalex@gmail.com Homepage: https://github.com/scvalex/interpol Category: Source-tools, Language-Synopsis: GHC pre-processor to enable variable interpolation in strings+Synopsis: GHC preprocessor to enable variable interpolation in strings Build-type: Simple-Description: This pre-processor enables variable interpolation in strings. See the README.md file for details.+Description: This preprocessor enables variable interpolation in strings.+ See the README.md file for details. Extra-source-files: Test/Simple.hs, Test/One.hs,- Test/Standalone.hs+ Test/Standalone.hs,+ Test/Pure.hs,+ Test/String.hs Data-files: Makefile, README.md +Source-repository head+ Type: git+ Location: git://github.com/scvalex/interpol.git+ Executable interpol- Build-Depends: base >=4 && <5, syb, preprocessor-tools, regex-posix+ Build-Depends: base >=4 && <5, syb, haskell-src-exts, regex-posix Main-Is: Main.hs++Library+ Build-Depends: base >= 4 && <5+ Ghc-options: -Wall+ Exposed-modules: Text.Interpol