@@ -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