git.iko.soy/typeable/cabal2nix
on master help sign in
language-nix: re-factoring and cleanup
commit33917c35d6de17ef3f79e604da8f81ce06d12c27 authorPeter Simons <simons@cryp.to> dateThu 3 Sep 2015, 17:07 (11 years ago) parent87526c2
6 files changed+104−89
language-nix/language-nix.cabal+18−4 language-nix/package.yaml+8−3 language-nix/src/Language/Nix/Binding.hs+9−13 language-nix/src/Language/Nix/Identifier.hs+44−47 language-nix/src/Language/Nix/Path.hs+19−22 language-nix/test/Main.hs+6
language-nix/language-nix.cabal+18−4
@@ -1,4 +1,4 @@
1−-- This file has been generated from package.yaml by hpack version 0.5.4.
1+-- This file has been generated from package.yaml by hpack version 0.7.0.
22--
33-- see: https://github.com/sol/hpack
44
@@ -25,10 +25,9 @@ library
2525 ghc-options: -Wall
2626 build-depends:
2727 base < 5
28− , data-default-class
29− , deepseq-generics
28+ , deepseq
29+ , doctest
3030 , lens
31− , lens-construction-helper
3231 , pretty >= 1.1.2
3332 , regex-posix
3433 exposed-modules:
@@ -37,3 +36,18 @@ library
3736 Language.Nix.Identifier
3837 Language.Nix.Path
3938 default-language: Haskell2010
39+
40+test-suite spec
41+ type: exitcode-stdio-1.0
42+ main-is: Main.hs
43+ hs-source-dirs:
44+ test
45+ ghc-options: -Wall
46+ build-depends:
47+ base < 5
48+ , deepseq
49+ , doctest
50+ , lens
51+ , pretty >= 1.1.2
52+ , regex-posix
53+ default-language: Haskell2010
language-nix/package.yaml+8−3
@@ -11,10 +11,9 @@ ghc-options: -Wall
1111
1212dependencies:
1313 - base < 5
14− - data-default-class
15− - deepseq-generics
14+ - deepseq
15+ - doctest
1616 - lens
17− - lens-construction-helper
1817 - pretty >= 1.1.2
1918 - regex-posix
2019
@@ -25,3 +24,9 @@ library:
2524 - Language.Nix.Binding
2625 - Language.Nix.Identifier
2726 - Language.Nix.Path
27+
28+tests:
29+ spec:
30+ main: Main.hs
31+ source-dirs:
32+ - test
language-nix/src/Language/Nix/Binding.hs+9−13
@@ -3,26 +3,22 @@
33
44module Language.Nix.Binding ( Binding, binding, localName, reference ) where
55
6−import Control.DeepSeq.Generics
7−import GHC.Generics ( Generic )
8−import Control.Lens.Create
6+import Control.DeepSeq
97import Control.Lens
108import Text.PrettyPrint.HughesPJClass
119import Language.Nix.Identifier
1210import Language.Nix.Path
1311
14−data Binding = Bind { _localName :: Identifier, _reference :: Path }
15− deriving (Show, Eq, Ord, Generic)
12+-- | A 'Binding' represents an identifier that refers to some other 'Path'.
1613
17−makeLenses ''Binding
14+declareLenses [d| data Binding = Bind { localName :: Identifier, reference :: Path }
15+ deriving (Show, Eq, Ord)
16+ |]
1817
19−binding :: Lens' Binding (Identifier,Path)
20−binding f (Bind l r) = uncurry Bind `fmap` f (l, r)
18+binding :: Iso' Binding (Identifier,Path)
19+binding = iso (\(Bind l r) -> (l,r)) (uncurry Bind)
2120
22−instance NFData Binding where rnf = genericRnf
23−
24−instance Default Binding where
25− def = Bind def def
21+instance NFData Binding where rnf (Bind l r) = l `deepseq` rnf r
2622
2723instance Pretty Binding where
2824 pPrint b = case (init ps, last ps) of
@@ -30,7 +26,7 @@ instance Pretty Binding where
3026 then text "inherit" <+> pPrint i' <> semi
3127 else pPrint i <+> equals <+> pPrint p <> semi
3228 (p', i') -> if i == i'
33− then text "inherit" <+> parens (pPrint (create path p')) <+> pPrint i' <> semi
29+ then text "inherit" <+> parens (pPrint (path # p')) <+> pPrint i' <> semi
3430 else pPrint i <+> equals <+> pPrint p <> semi
3531
3632 where
language-nix/src/Language/Nix/Identifier.hs+44−47
@@ -1,67 +1,64 @@
1−{-# LANGUAGE DeriveGeneric #-}
21{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE TemplateHaskell #-}
33
4−module Language.Nix.Identifier ( Identifier, ident, quote, needsQuoting ) where
4+module Language.Nix.Identifier
5+ (
6+ -- | Identifiers in Nix are essentially strings. They can be constructed
7+ -- (and viewed) with the 'ident' isomorphism. For the sake of convenience,
8+ -- @Identifier@s are an instance of the 'IsString' class.
9+ --
10+ -- Reasonable people restrict themselves to identifiers of the form
11+ -- @[a-zA-Z\_][a-zA-Z0-9\_\'\-]*@, because these don't need quoting. The
12+ -- methods of the 'Pretty' class can be used to pretty-print an identifier
13+ -- with proper quoting:
14+ --
15+ -- >>> pPrint (ident # "test")
16+ -- test
17+ -- >>> pPrint (ident # "foo.bar")
18+ -- "foo.bar"
19+ Identifier
520
6−import Control.DeepSeq.Generics
21+ , -- | An isomorphism that allows conversion of 'Identifier' from/to the
22+ -- standard 'String' type via 'review'.
23+ --
24+ -- prop> \str -> fromString str == ident # str
25+ -- prop> \str -> set ident str undefined == ident # str
26+ -- prop> \str -> view ident (review ident str) == str
27+ ident
28+
29+ , -- | Helper function to quote a given identifier string if necessary.
30+ --
31+ -- >>> putStrLn (quote "abc")
32+ -- abc
33+ -- >>> putStrLn (quote "abc.def")
34+ -- "abc.def"
35+ quote
36+
37+ , -- | Checks whether a given string needs quoting when interpreted as an
38+ -- 'Identifier'.
39+ needsQuoting
40+
41+ ) where
42+
43+import Control.DeepSeq
744import Control.Lens
8−import Data.Default.Class
945import Data.String
10−import GHC.Generics ( Generic )
1146import Text.PrettyPrint.HughesPJClass
1247import Text.Regex.Posix
1348
14−-- | Identifiers in Nix are essentially strings. Reasonable people restrict
15−-- themselves to identifiers of the form @[a-zA-Z\_][a-zA-Z0-9\_\'\-]*@,
16−-- because these don't need quoting. The @Identifier@ type is an instance of
17−-- the 'IsString' class for convenience. The methods of the 'Pretty' class can
18−-- be used to pretty-print an identifier with proper quoting.
19−--
20−-- >>> let i = Identifier "test" in (i, pPrint i)
21−-- (Identifier "test",test)
22−-- >>> let i = Identifier "foo.bar" in (i, pPrint i)
23−-- (Identifier "foo.bar","foo.bar")
24−
25−newtype Identifier = Identifier String
26− deriving (Show, Eq, IsString, Generic)
27−
28−instance Default Identifier where def = Identifier ""
49+declareLenses [d| newtype Identifier = Identifier { ident :: String }
50+ deriving (Show, Eq, Ord, IsString)
51+ |]
2952
3053instance Pretty Identifier where
3154 pPrint i = text (i ^. ident . to quote)
3255
33−instance Ord Identifier where
34− compare (Identifier a) (Identifier b) = compare a b
35−
36−instance NFData Identifier where rnf = genericRnf
37−
38−-- | Checks whether a given string would need quoting when interpreted as an
39−-- intentifier.
56+instance NFData Identifier where rnf (Identifier str) = rnf str
4057
4158needsQuoting :: String -> Bool
4259needsQuoting str = not (str =~ grammar)
4360 where grammar :: String -- TODO: should be a compiled regular expression
4461 grammar = "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$"
4562
46−-- | Lens that allows conversion from/to the standard 'String' type. The setter
47−-- does not evaluate its input, so it's safe to use with 'undefined'.
48−--
49−-- >>> putStrLn $ Identifier "abc.def" ^. ident
50−-- abc.def
51−--
52−-- >>> pPrint $ undefined & ident .~ "abcdef"
53−-- abcdef
54−
55−ident :: Lens' Identifier String
56−ident f (Identifier str) = Identifier `fmap` f str
57−
58−-- | Helper function to quote a given identifier string if necessary.
59−--
60−-- >>> putStrLn (quote "abc")
61−-- abc
62−--
63−-- >>> putStrLn (quote "abc.def")
64−-- "abc.def"
65−
6663quote :: String -> String
6764quote s = if needsQuoting s then show s else s
language-nix/src/Language/Nix/Path.hs+19−22
@@ -1,54 +1,51 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE TemplateHaskell #-}
22
33module Language.Nix.Path ( Path, path ) where
44
5−import Data.Default.Class
5+import Control.DeepSeq
66import Control.Lens
7−import Text.PrettyPrint.HughesPJClass ( Pretty (..), char, hcat, punctuate )
87import Language.Nix.Identifier
9−import Control.DeepSeq.Generics
10−import GHC.Generics ( Generic )
8+import Text.PrettyPrint.HughesPJClass ( Pretty (..), char, hcat, punctuate )
119
1210-- | Paths are non-empty lists of identifiers in Nix.
1311--
14−-- >>> set path [set ident "yo" undefined] undefined
15−-- Path {_segments = [Identifier "yo"]}
12+-- >>> path # [ident # "yo"]
13+-- Path [Identifier "yo"]
1614--
1715-- Any attempt to construct the empty path throws an 'error':
1816--
19−-- >>> set path [] undefined
20−-- Path {_segments = *** Exception: Nix paths cannot be empty
17+-- >>> path # []
18+-- Path *** Exception: Nix paths cannot be empty
2119--
2220-- 'Identifier' is an instance of 'IsString':
2321--
2422-- >>> :set -XOverloadedStrings
25−-- >>> pPrint $ set path ["yo","bar"] undefined
23+-- >>> pPrint $ path # ["yo","bar"]
2624-- yo.bar
2725--
2826-- Freaky quoted identifiers are fine except in the first segment:
2927--
30−-- >>> pPrint $ set path ["yo","b\"ar"] undefined
28+-- >>> pPrint $ path # ["yo","b\"ar"]
3129-- yo."b\"ar"
32−-- >>> pPrint $ set path ["5ident"] undefined
30+-- >>> pPrint $ path # ["5ident"]
3331-- *** Exception: invalid Nix path: [Identifier "5ident"]
34−-- >>> pPrint $ set path ["5ident","foo","bar"] undefined
32+-- >>> pPrint $ path # ["5ident","foo","bar"]
3533-- *** Exception: invalid Nix path: [Identifier "5ident",Identifier "foo",Identifier "bar"]
3634
37−newtype Path = Path { _segments :: [Identifier] }
38− deriving (Show, Eq, Ord, Generic)
35+declareLenses [d| newtype Path = Path [Identifier]
36+ deriving (Show, Eq, Ord)
37+ |]
3938
40−instance NFData Path where rnf = genericRnf
39+instance NFData Path where rnf (Path p) = rnf p
4140
42−instance Default Path where def = Path (error "undefined Nix.Path")
41+instance Pretty Path where
42+ pPrint p = hcat $ punctuate (char '.') $ pPrint <$> p^.path
4343
44−path :: Lens' Path [Identifier]
45−path f (Path ids) = mkPath `fmap` f ids
44+path :: Iso' Path [Identifier]
45+path = iso (\(Path p) -> p) mkPath
4646 where
4747 mkPath :: [Identifier] -> Path
4848 mkPath [] = error "Nix paths cannot be empty"
4949 mkPath p@(s0:_)
5050 | s0^.ident.to needsQuoting = error ("invalid Nix path: " ++ show p)
5151 | otherwise = Path p
52−
53−instance Pretty Path where
54− pPrint p = hcat $ punctuate (char '.') $ pPrint <$> p^.path
language-nix/test/Main.hsnew file+6
@@ -0,0 +1,6 @@
1+module Main ( main ) where
2+
3+import Test.DocTest
4+
5+main :: IO ()
6+main = doctest [ "-isrc", "src" ]