content: {-# language DataKinds, KindSignatures #-}
  {-# language DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric #-}
  {-# language FlexibleInstances #-}
  {-# language InstanceSigs, ScopedTypeVariables, TypeApplications #-}
  
  {-|
  Module      : Language.Python.Syntax.Ident
  Copyright   : (C) CSIRO 2017-2019
  License     : BSD3
  Maintainer  : Isaac Elliott <isaace71295@gmail.com>
  Stability   : experimental
  Portability : non-portable
  -}
  
  module Language.Python.Syntax.Ident
    ( Ident(..)
      -- * Lenses
    , identAnn
    , identValue
    , identWhitespace
      -- * Extra functions
    , isIdentifierStart
    , isIdentifierChar
    )
  where
  
  import Control.Lens.Lens (Lens, Lens', lens)
  import Data.Char (isDigit, isLetter)
  import Data.Generics.Product.Typed (typed)
  import Data.String (IsString(..))
  import GHC.Generics (Generic)
  
  import Language.Python.Optics.Validated (Validated)
  import Language.Python.Syntax.Ann
  import Language.Python.Syntax.Raw
  import Language.Python.Syntax.Whitespace
  
  -- | An identifier. Like many types in hpython, it has an optional annotation
  -- and tracks its trailing whitespace.
  --
  -- 'Raw' 'Ident's have an 'IsString' instance.
  --
  -- See <https://docs.python.org/3.5/reference/lexical_analysis.html#identifiers>
  data Ident (v :: [*]) a
    = MkIdent
    { _identAnn :: Ann a
    , _identValue :: String
    , _identWhitespace :: [Whitespace]
    } deriving (Eq, Show, Functor, Foldable, Traversable, Generic)
  
  instance HasAnn (Ident v) where
    annot :: forall a. Lens' (Ident v a) (Ann a)
    annot = typed @(Ann a)
  
  -- | Determine whether this character could start a valid identifier
  isIdentifierStart :: Char -> Bool
  isIdentifierStart = do
    a <- isLetter
    b <- (=='_')
    pure $ a || b
  
  -- | Determine whether this character could be part of a valid identifier
  isIdentifierChar :: Char -> Bool
  isIdentifierChar = do
    a <- isIdentifierStart
    b <- isDigit
    pure $ a || b
  
  instance IsString (Raw Ident) where
    fromString s = MkIdent (Ann ()) s []
  
  identValue :: Lens (Ident v a) (Ident '[] a) String String
  identValue = lens _identValue (\s a -> s { _identValue = a })
  
  identAnn :: Lens (Ident v a) (Ident v a) a a
  identAnn = annot_
  
  identWhitespace :: Lens (Ident v a) (Ident v a) [Whitespace] [Whitespace]
  identWhitespace = lens _identWhitespace (\s ws -> s { _identWhitespace = ws })
  
  instance HasTrailingWhitespace (Ident v a) where
    trailingWhitespace = identWhitespace
  
  instance Validated Ident where
  