git.iko.soy/typeable/cabal2nix
on master help sign in
wip: define proper data types for Nix language constructs
commit3d528a2dfa5980c4adcb751ce7ca01e136c1d9a8 authorPeter Simons <simons@cryp.to> dateSun 25 Jan 2015, 20:39 (12 years ago) parent86547cf
5 files changed+105−3
GNUmakefile+5−2 generate-cabal-file.hs+1 release.nix+1−1 src/Language/Nix/Identifier.hs+85 src/Language/Nix/Path.hs+13
GNUmakefile+5−2
@@ -1,6 +1,6 @@
11# GNUmakefile
22
3−.PHONY: all configure build check check
3+.PHONY: all configure build check haddock
44
55all:: check
66
@@ -10,7 +10,10 @@ configure:: cabal2nix.cabal
1010build:: configure
1111 @cabal build -j
1212
13−check:: build
13+haddock:: build
14+ @cabal haddock
15+
16+check:: haddock
1417 @cabal test -j
1518
1619cabal2nix.cabal: generate-cabal-file.hs
generate-cabal-file.hs+1
@@ -64,6 +64,7 @@ commonBuildDepends = buildDepends
6464 , unconstrained "directory"
6565 , unconstrained "filepath"
6666 , unconstrained "hackage-db"
67+ , unconstrained "lens"
6768 , unconstrained "monad-par"
6869 , unconstrained "monad-par-extras"
6970 , unconstrained "mtl"
release.nix+1−1
@@ -30,7 +30,7 @@ in
3030 aeson base bytestring Cabal containers deepseq deepseq-generics
3131 directory filepath hackage-db monad-par monad-par-extras mtl pretty
3232 prettyclass process QuickCheck regex-posix SHA split transformers
33− utf8-string
33+ utf8-string lens
3434 ];
3535 testDepends = with haskellPackages; [
3636 aeson base bytestring Cabal containers deepseq deepseq-generics
src/Language/Nix/Identifier.hsnew file+85
@@ -0,0 +1,85 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+
3+module Language.Nix.Identifier ( Identifier, string, quotedString, needsQuoting ) where
4+
5+import Data.Char
6+import Data.Function
7+import Control.Lens
8+import Data.String
9+import Distribution.Nix.Util.PrettyPrinting ( Pretty(..), text )
10+import Text.Regex.Posix
11+
12+-- | Identifiers in Nix are essentially strings. Reasonable people restrict
13+-- themselves to identifiers of the form @[a-zA-Z\_][a-zA-Z0-9\_\'\-]*@,
14+-- though, because these don't need quoting. The @Identifier@ type is an
15+-- instance of the 'IsString' class for construction. The methods of the
16+-- 'Pretty' class can be used to pretty-print an identifier with proper
17+-- quoting.
18+--
19+-- >>> let i = fromString "test" :: Identifier in (i, pPrint i)
20+-- (Identifier "test",test)
21+-- >>> let i = fromString "foo.bar" :: Identifier in (i, pPrint i)
22+-- (Identifier "foo.bar","foo.bar")
23+--
24+-- The 'Ord' instance for identifiers is unusual in that it's aware of
25+-- character's case:
26+--
27+-- >>> (fromString "abc" :: Identifier) == fromString "abc"
28+-- True
29+-- >>> (fromString "abc" :: Identifier) == fromString "ABC"
30+-- False
31+-- >>> (fromString "abc" :: Identifier) > fromString "ABC"
32+-- True
33+-- >>> (fromString "abc" :: Identifier) > fromString "ABC"
34+-- True
35+-- >>> (fromString "X" :: Identifier) > fromString "a"
36+-- True
37+-- >>> (fromString "x" :: Identifier) > fromString "A"
38+-- True
39+--
40+-- prop> \str -> Identifier str == Identifier str
41+-- prop> \str -> any (`elem` ['a'..'z']) str ==> Identifier (map toLower str) /= Identifier (map toUpper str)
42+-- prop> \(NonEmpty str) -> head str `elem` ['a'..'z'] ==> Identifier (map toLower str) >= Identifier (map toUpper str)
43+
44+newtype Identifier = Identifier String
45+ deriving (Show, Eq, IsString)
46+
47+instance Pretty Identifier where
48+ pPrint i = text (i ^. string)
49+
50+instance Ord Identifier where
51+ compare (Identifier a) (Identifier b) =
52+ case (compare `on` map toLower) a b of
53+ EQ -> compare a b
54+ r -> r
55+
56+needsQuoting :: Identifier -> Bool
57+needsQuoting (Identifier str) = not (str =~ "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$")
58+
59+-- | Lens that allows conversion from/to the standard 'String' type.
60+--
61+-- >>> putStrLn $ fromString "abc" ^. string
62+-- abc
63+--
64+-- >>> putStrLn $ fromString "abc.def" ^. string
65+-- "abc.def"
66+--
67+-- >>> pPrint $ fromString "" & string .~ "abcdef"
68+-- abcdef
69+
70+string :: Lens' Identifier String
71+string f (Identifier str) = (\str' -> fromString str') `fmap` f s
72+ where s = if needsQuoting (Identifier str) then show str else str
73+
74+-- | A variant of 'string' that gives read-only access to a string-representation
75+-- of the identifier that is always quoted, even if the identifier actually
76+-- doesn't need quoting.
77+--
78+-- >>> putStrLn $ fromString "abc" ^. quotedString
79+-- "abc"
80+--
81+-- >>> putStrLn $ fromString "abc.def" ^. quotedString
82+-- "abc.def"
83+
84+quotedString :: Getter Identifier String
85+quotedString f (Identifier str) = fmap fromString (f (show str))
src/Language/Nix/Path.hsnew file+13
@@ -0,0 +1,13 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+
3+module Language.Nix.Path where
4+
5+import Control.Lens
6+import Data.String
7+import Distribution.Nix.Util.PrettyPrinting -- ( Pretty(..), text )
8+import Language.Nix.Identifier
9+
10+-- | Paths are non-empty lists of identifiers in Nix.
11+
12+newtype Path = Path [Identifier]
13+ deriving (Show, Eq)