language-nix-2: src/Language/Nix/Identifier.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
module Language.Nix.Identifier
(
-- | Identifiers in Nix are essentially strings. They can be constructed
-- (and viewed) with the 'ident' isomorphism. For the sake of convenience,
-- @Identifier@s are an instance of the 'IsString' class.
--
-- Reasonable people restrict themselves to identifiers of the form
-- @[a-zA-Z\_][a-zA-Z0-9\_\'\-]*@, because these don't need quoting. The
-- methods of the 'Pretty' class can be used to pretty-print an identifier
-- with proper quoting:
--
-- >>> pPrint (ident # "test")
-- test
-- >>> pPrint (ident # "foo.bar")
-- "foo.bar"
Identifier
, -- | An isomorphism that allows conversion of 'Identifier' from/to the
-- standard 'String' type via 'review'.
--
-- prop> \str -> fromString str == ident # str
-- prop> \str -> set ident str undefined == ident # str
-- prop> \str -> view ident (review ident str) == str
ident
, -- | Helper function to quote a given identifier string if necessary.
--
-- >>> putStrLn (quote "abc")
-- abc
-- >>> putStrLn (quote "abc.def")
-- "abc.def"
quote
, -- | Checks whether a given string needs quoting when interpreted as an
-- 'Identifier'.
needsQuoting
) where
import Control.DeepSeq
import Control.Lens
import Data.String
import Text.PrettyPrint.HughesPJClass
import Text.Regex.Posix
declareLenses [d| newtype Identifier = Identifier { ident :: String }
deriving (Show, Eq, Ord, IsString)
|]
instance Pretty Identifier where
pPrint i = text (i ^. ident . to quote)
instance NFData Identifier where rnf (Identifier str) = rnf str
needsQuoting :: String -> Bool
needsQuoting str = not (str =~ grammar)
where grammar :: String -- TODO: should be a compiled regular expression
grammar = "^[a-zA-Z\\_][a-zA-Z0-9\\_\\'\\-]*$"
quote :: String -> String
quote s = if needsQuoting s then show s else s