1{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4module Language.Nix.Identifier ( Identifier(..), ident, quote, needsQuoting ) where
6import Control.DeepSeq.Generics
9import Data.Function ( on )
11import Distribution.Nixpkgs.Util.PrettyPrinting ( Pretty(..), text )
12import Text.Regex.Posix
13import GHC.Generics ( Generic )
15-- | Identifiers in Nix are essentially strings. Reasonable people restrict
16-- themselves to identifiers of the form @[a-zA-Z\_][a-zA-Z0-9\_\'\-]*@,
17-- because these don't need quoting. The @Identifier@ type is an instance of
18-- the 'IsString' class for convenience. The methods of the 'Pretty' class can
19-- be used to pretty-print an identifier with proper quoting.
21-- >>> let i = Identifier "test" in (i, pPrint i)
22-- (Identifier "test",test)
23-- >>> let i = Identifier "foo.bar" in (i, pPrint i)
24-- (Identifier "foo.bar","foo.bar")
26-- The 'Ord' instance for identifiers is respects character case in less-than
29-- >>> Identifier "abc" == Identifier "ABC"
31-- >>> Identifier "abc" < Identifier "ABC"
33-- >>> Identifier "abc" > Identifier "ABC"
35-- >>> Identifier "X" > Identifier "a"
37-- >>> Identifier "x" > Identifier "A"
40-- prop> \str -> Identifier str == Identifier str
41-- prop> \str -> any (`elem` ['a'..'z']) str ==> Identifier (map toLower str) /= Identifier (map toUpper str)
43newtype Identifier = Identifier String
44 deriving (Show, Eq, IsString, Generic)
46instance Pretty Identifier where
47 pPrint i = text (i ^. ident . to quote)
49instance Ord Identifier where
50 compare (Identifier a) (Identifier b) =
51 case (compare `on` map toLower) a b of
55instance NFData Identifier where rnf = genericRnf
57-- | Checks whether a given string would need quoting when interpreted as an
60needsQuoting :: String -> Bool
61needsQuoting str = not (str =~ grammar)
62 where grammar :: String -- TODO: should be a compiled regular expression
63 grammar = "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$"
65-- | Lens that allows conversion from/to the standard 'String' type. The setter
66-- does not evaluate its input, so it's safe to use with 'undefined'.
68-- >>> putStrLn $ Identifier "abc.def" ^. ident
71-- >>> pPrint $ undefined & ident .~ "abcdef"
74ident :: Lens' Identifier String
75ident f (Identifier str) = Identifier `fmap` f str
77-- | Help function that quotes a given identifier string (if necessary).
79-- >>> putStrLn (quote "abc")
82-- >>> putStrLn (quote "abc.def")
85quote :: String -> String
86quote s = if needsQuoting s then show s else s
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list