git.iko.soy/typeable/cabal2nix
on 1663fb8 help sign in
81 lines 2.6 kB history raw
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3module Language.Nix.Identifier ( Identifier(..), ident, quote, needsQuoting ) where
5import Control.Lens
6import Data.Char
7import Data.Function ( on )
8import Data.String
9import Distribution.Nixpkgs.Util.PrettyPrinting ( Pretty(..), text )
10import Text.Regex.Posix
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-- because these don't need quoting. The @Identifier@ type is an instance of
15-- the 'IsString' class for convenience. The methods of the 'Pretty' class can
16-- be used to pretty-print an identifier with proper quoting.
17--
18-- >>> let i = Identifier "test" in (i, pPrint i)
19-- (Identifier "test",test)
20-- >>> let i = Identifier "foo.bar" in (i, pPrint i)
21-- (Identifier "foo.bar","foo.bar")
22--
23-- The 'Ord' instance for identifiers is unusual in that it's aware of
24-- character's case:
25--
26-- >>> Identifier "abc" == Identifier "ABC"
27-- False
28-- >>> Identifier "abc" > Identifier "ABC"
29-- True
30-- >>> Identifier "abc" > Identifier "ABC"
31-- True
32-- >>> Identifier "X" > Identifier "a"
33-- True
34-- >>> Identifier "x" > Identifier "A"
35-- True
36--
37-- prop> \str -> Identifier str == Identifier str
38-- prop> \str -> any (`elem` ['a'..'z']) str ==> Identifier (map toLower str) /= Identifier (map toUpper str)
40newtype Identifier = Identifier String
41 deriving (Show, Eq, IsString)
43instance Pretty Identifier where
44 pPrint i = text (i ^. ident . to quote)
46instance Ord Identifier where
47 compare (Identifier a) (Identifier b) =
48 case (compare `on` map toLower) a b of
49 EQ -> compare a b
50 r -> r
52-- | Checks whether a given string would need quoting when interpreted as an
53-- intentifier.
55needsQuoting :: String -> Bool
56needsQuoting str = not (str =~ grammar)
57 where grammar :: String -- TODO: should be a compiled regular expression
58 grammar = "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$"
60-- | Lens that allows conversion from/to the standard 'String' type. The setter
61-- does not evaluate its input, so it's safe to use with 'undefined'.
62--
63-- >>> putStrLn $ Identifier "abc.def" ^. ident
64-- abc.def
65--
66-- >>> pPrint $ undefined & ident .~ "abcdef"
67-- abcdef
69ident :: Lens' Identifier String
70ident f (Identifier str) = Identifier `fmap` f str
72-- | Help function that quotes a given identifier string (if necessary).
73--
74-- >>> putStrLn (quote "abc")
75-- abc
76--
77-- >>> putStrLn (quote "abc.def")
78-- "abc.def"
80quote :: String -> String
81quote s = if needsQuoting s then show s else s