git.iko.soy/typeable/cabal2nix
on master help sign in
Clean up the Identified type.
55ab810Peter Simons
src/Language/Nix/Identifier.hs+32−36
@@ -1,6 +1,6 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2
2
3−module Language.Nix.Identifier ( Identifier, string, quotedString, needsQuoting ) where
3+module Language.Nix.Identifier ( Identifier(..), ident, quote, needsQuoting ) where
4
4
5import Data.Char
5import Data.Char
6import Data.Function
6import Data.Function
@@ -11,41 +11,37 @@ import Text.Regex.Posix
11
11
12-- | Identifiers in Nix are essentially strings. Reasonable people restrict
12-- | Identifiers in Nix are essentially strings. Reasonable people restrict
13-- themselves to identifiers of the form @[a-zA-Z\_][a-zA-Z0-9\_\'\-]*@,
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
14+-- because these don't need quoting. The @Identifier@ type is an instance of
15−-- instance of the 'IsString' class for construction. The methods of the
15+-- the 'IsString' class for convenience. The methods of the 'Pretty' class can
16−-- 'Pretty' class can be used to pretty-print an identifier with proper
16+-- be used to pretty-print an identifier with proper quoting.
17−-- quoting.
18--
17--
19−-- >>> let i = fromString "test" :: Identifier in (i, pPrint i)
18+-- >>> let i = Identifier "test" in (i, pPrint i)
20-- (Identifier "test",test)
19-- (Identifier "test",test)
21−-- >>> let i = fromString "foo.bar" :: Identifier in (i, pPrint i)
20+-- >>> let i = Identifier "foo.bar" in (i, pPrint i)
22-- (Identifier "foo.bar","foo.bar")
21-- (Identifier "foo.bar","foo.bar")
23--
22--
24-- The 'Ord' instance for identifiers is unusual in that it's aware of
23-- The 'Ord' instance for identifiers is unusual in that it's aware of
25-- character's case:
24-- character's case:
26--
25--
27−-- >>> (fromString "abc" :: Identifier) == fromString "abc"
28−-- True
29−-- >>> (fromString "abc" :: Identifier) == fromString "ABC"
26+-- >>> Identifier "abc" == Identifier "ABC"
30-- False
27-- False
31−-- >>> (fromString "abc" :: Identifier) > fromString "ABC"
28+-- >>> Identifier "abc" > Identifier "ABC"
32-- True
29-- True
33−-- >>> (fromString "abc" :: Identifier) > fromString "ABC"
30+-- >>> Identifier "abc" > Identifier "ABC"
34-- True
31-- True
35−-- >>> (fromString "X" :: Identifier) > fromString "a"
32+-- >>> Identifier "X" > Identifier "a"
36-- True
33-- True
37−-- >>> (fromString "x" :: Identifier) > fromString "A"
34+-- >>> Identifier "x" > Identifier "A"
38-- True
35-- True
39--
36--
40-- prop> \str -> Identifier str == Identifier str
37-- prop> \str -> Identifier str == Identifier str
41-- prop> \str -> any (`elem` ['a'..'z']) str ==> Identifier (map toLower str) /= Identifier (map toUpper str)
38-- 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
39
44newtype Identifier = Identifier String
40newtype Identifier = Identifier String
45 deriving (Show, Eq, IsString)
41 deriving (Show, Eq, IsString)
46
42
47instance Pretty Identifier where
43instance Pretty Identifier where
48− pPrint i = text (i ^. string)
44+ pPrint i = text (i ^. ident . to quote)
49
45
50instance Ord Identifier where
46instance Ord Identifier where
51 compare (Identifier a) (Identifier b) =
47 compare (Identifier a) (Identifier b) =
@@ -53,33 +49,33 @@ instance Ord Identifier where
53 EQ -> compare a b
49 EQ -> compare a b
54 r -> r
50 r -> r
55
51
56−needsQuoting :: Identifier -> Bool
52+-- | Checks whether a given string would need quoting when interpreted as an
57−needsQuoting (Identifier str) = not (str =~ "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$")
53+-- intentifier.
58
54
55+needsQuoting :: String -> Bool
56+needsQuoting str = not (str =~ grammar)
57+ where grammar :: String -- TODO: should be a compiled regular expression
58+ grammar = "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$"
59+
59−-- | Lens that allows conversion from/to the standard 'String' type.
60+-- | Lens that allows conversion from/to the standard 'String' type. The setter
60−--
61+-- does not evaluate its input, so it's safe to use with 'undefined'.
61−-- >>> putStrLn $ fromString "abc" ^. string
62−-- abc
63--
62--
64−-- >>> putStrLn $ fromString "abc.def" ^. string
63+-- >>> putStrLn $ Identifier "abc.def" ^. ident
65−-- "abc.def"
64+-- abc.def
66--
65--
67−-- >>> pPrint $ fromString "" & string .~ "abcdef"
66+-- >>> pPrint $ undefined & ident .~ "abcdef"
68-- abcdef
67-- abcdef
69
68
70−string :: Lens' Identifier String
69+ident :: Lens' Identifier String
71−string f (Identifier str) = (\str' -> fromString str') `fmap` f s
70+ident f (Identifier str) = Identifier `fmap` f str
72− where s = if needsQuoting (Identifier str) then show str else str
73
71
74−-- | A variant of 'string' that gives read-only access to a string-representation
72+-- | Help function that quotes a given identifier string (if necessary).
75−-- of the identifier that is always quoted, even if the identifier actually
76−-- doesn't need quoting.
77--
73--
78−-- >>> putStrLn $ fromString "abc" ^. quotedString
74+-- >>> putStrLn (quote "abc")
79−-- "abc"
75+-- abc
80--
76--
81−-- >>> putStrLn $ fromString "abc.def" ^. quotedString
77+-- >>> putStrLn (quote "abc.def")
82-- "abc.def"
78-- "abc.def"
83
79
84−quotedString :: Getter Identifier String
80+quote :: String -> String
85−quotedString f (Identifier str) = fmap fromString (f (show str))
81+quote s = if needsQuoting s then show s else s