content: 
  module DDC.Core.Codec.Text.Lexer.Token.Names
          ( -- * Variable names
            scanVarName
          , matchVarName
          , acceptVarName
          , isVarName
          , isVarStart
          , isVarBody
  
            -- * Constructor names
          , scanConName
          , matchConName
          , acceptConName
          , isConName
          , isConStart
          , isConBody)
  where
  import Text.Lexer.Inchworm.Char
  import DDC.Data.ListUtils
  import qualified Data.Char      as Char
  
  
  -- Variable names ---------------------------------------------------------------------------------
  -- | Scanner for variable anmes.
  scanVarName   :: Scanner IO Location [Char] (Range Location, String)
  scanVarName
   = munchPred Nothing matchVarName acceptVarName
  
  
  -- | Match a variable name.
  matchVarName :: Int -> Char -> Bool
  matchVarName 0 c        = isVarStart c
  matchVarName _ c        = isVarBody  c
  
  
  -- | Accept a variable name.
  acceptVarName :: String -> Maybe String
  acceptVarName ss
          | isVarName ss  = Just ss
          | otherwise     = Nothing
  
  
  -- | Check if this string is a variable name.
  isVarName :: String -> Bool
  isVarName str
   = case str of
       []          -> False
       c : cs
          |  isVarStart c
          ,  and (map isVarBody cs)
          -> True
  
          | _ : _         <- cs
          ,  Just initCs   <- takeInit cs
          ,  isVarStart c
          ,  and (map isVarBody initCs)
          ,  last cs == '#'
          -> True
  
          | otherwise
          -> False
  
  
  -- | Charater can start a variable name.
  isVarStart :: Char -> Bool
  isVarStart c
          =  Char.isLower c
          || c == '?'
          || c == '_'
  
  
  -- | Character can be part of a variable body.
  isVarBody  :: Char -> Bool
  isVarBody c
          =  Char.isUpper c
          || Char.isLower c
          || Char.isDigit c
          || c == '_'
          || c == '\''
          || c == '$'
          || c == '#'
  
  
  -- Constructor names ------------------------------------------------------------------------------
  -- | Scanner for constructor names.
  scanConName   :: Scanner IO Location [Char] (Range Location, String)
  scanConName
   = munchPred Nothing matchConName acceptConName
  
  
  -- | Match a constructor name.
  matchConName  :: Int -> Char -> Bool
  matchConName 0 c        = isConStart c
  matchConName _ c        = isConBody  c
  
  
  -- | Accept a constructor name.
  acceptConName :: String -> Maybe String
  acceptConName ss
          | isConName ss  = Just ss
          | otherwise     = Nothing
  
  
  -- | String is a constructor name.
  isConName :: String -> Bool
  isConName str
   = case str of
       []          -> False
       c : cs
          |  isConStart c
          ,  and (map isConBody cs)
          -> True
  
          | _ : _         <- cs
          ,  Just initCs   <- takeInit cs
          ,  isConStart c
          ,  and (map isConBody initCs)
          ,  last cs == '#'
          -> True
  
          | otherwise
          -> False
  
  
  -- | Character can start a constructor name.
  isConStart :: Char -> Bool
  isConStart
          = Char.isUpper
  
  
  -- | Charater can be part of a constructor body.
  isConBody  :: Char -> Bool
  isConBody c
          =  Char.isUpper c
          || Char.isLower c
          || Char.isDigit c
          || c == '_'
          || c == '\''
          || c == '#'
  
  