content: 
  module DDC.Core.Codec.Text.Lexer.Token.Operator
          ( scanPrefixOperator
          , scanInfixOperator
          , acceptInfixOperator
          , isOpName
          , isOpStart
          , isOpBody)
  where
  import Text.Lexer.Inchworm.Char
  import DDC.Core.Codec.Text.Lexer.Unicode
  import qualified Data.Set       as Set
  import qualified Data.List      as List
  
  
  -------------------------------------------------------------------------------
  -- | Scanner for operators used prefix.
  scanPrefixOperator :: Scanner IO Location [Char] (Range Location, String)
  scanPrefixOperator
   = munchPred Nothing matchPrefixOperator acceptPrefixOperator
  
  
  -- | Patch a prefix operator name.
  matchPrefixOperator :: Int -> Char -> Bool
  matchPrefixOperator 0 c         = c == '('
  matchPrefixOperator _ c         = isOpBody c || c == ')'
  
  
  -- | Accept a prefix operator name.
  acceptPrefixOperator :: String -> Maybe String
  acceptPrefixOperator str
          | '(' : cs1     <- str
          , c   : cs2     <- cs1
          , isOpStart c
          , (body , cs3)  <- List.span isOpBody cs2
          , ')' : []      <- cs3
          , not $ null (c : body)
          = Just (c : body)
  
          | otherwise
          = Nothing
  
  
  -------------------------------------------------------------------------------
  -- | Scanner for operators used infix.
  scanInfixOperator  :: Scanner IO Location [Char] (Range Location, String)
  scanInfixOperator
   = munchPred Nothing matchInfixOperator acceptInfixOperator
  
  
  -- | Match an operator name.
  matchInfixOperator  :: Int -> Char -> Bool
  matchInfixOperator 0 c       = isOpStart c
  matchInfixOperator _ c       = isOpBody  c
  
  
  -- | Accept an operator name.
  acceptInfixOperator :: String -> Maybe String
  acceptInfixOperator str
   = case str of
          "="     -> Nothing
          "|"     -> Nothing
          "~>"    -> Nothing
          "->"    -> Nothing
          "<-"    -> Nothing
          "=>"    -> Nothing
          "/\\"   -> Nothing
          _       -> Just str
  
  
  -- | String is the name of some operator.
  isOpName :: String -> Bool
  isOpName str
   = case str of
          []      -> False
          c : cs
           | isOpStart c
           , and (map isOpBody cs)
           -> True
  
           | otherwise
           -> False
  
  
  -- | Character can start an operator.
  isOpStart :: Char -> Bool
  isOpStart c
          =  c == '~'     || c == '!'
          || c == '$'     || c == '%'                     || c == '&'
          || c == '*'     || c == '-'     || c == '+'     || c == '='
          || c == ':'                     || c == '/'     || c == '|'
          || c == '<'     || c == '>'
          || Set.member c unicodeOperatorsInfix
  
  
  -- | Character can be part of an operator body.
  isOpBody :: Char -> Bool
  isOpBody c
          =  c == '~'     || c == '!'                     || c == '#'
          || c == '$'     || c == '%'                     || c == '&'
          || c == '*'     || c == '-'     || c == '+'     || c == '='
          || c == ':'     || c == '?'     || c == '/'     || c == '|'
          || c == '<'     || c == '>'
          || c == '\\'
          || Set.member c unicodeOperatorsInfix
  
  
  
  
  