content: -- Character classification
  {-# LANGUAGE CPP #-}
  module Eta.Parser.Ctype
          ( is_ident      -- Char# -> Bool
          , is_symbol     -- Char# -> Bool
          , is_any        -- Char# -> Bool
          , is_space      -- Char# -> Bool
          , is_lower      -- Char# -> Bool
          , is_upper      -- Char# -> Bool
          , is_digit      -- Char# -> Bool
          , is_alphanum   -- Char# -> Bool
  
          , is_decdigit, is_hexdigit, is_octdigit, is_bindigit
          , hexDigit, octDecDigit
          ) where
  
  #include "HsVersions.h"
  
  import Data.Int         ( Int32 )
  import Data.Bits        ( Bits((.&.)) )
  import Data.Char        ( ord, chr )
  import Eta.Utils.Panic
  
  -- Bit masks
  
  cIdent, cSymbol, cAny, cSpace, cLower, cUpper, cDigit :: Int
  cIdent  =  1
  cSymbol =  2
  cAny    =  4
  cSpace  =  8
  cLower  = 16
  cUpper  = 32
  cDigit  = 64
  
  -- | The predicates below look costly, but aren't, GHC+GCC do a great job
  -- at the big case below.
  
  {-# INLINE is_ctype #-}
  is_ctype :: Int -> Char -> Bool
  is_ctype mask c = (fromIntegral (charType c) .&. fromIntegral mask) /= (0::Int32)
  
  is_ident, is_symbol, is_any, is_space, is_lower, is_upper, is_digit,
      is_alphanum :: Char -> Bool
  is_ident  = is_ctype cIdent
  is_symbol = is_ctype cSymbol
  is_any    = is_ctype cAny
  is_space  = is_ctype cSpace
  is_lower  = is_ctype cLower
  is_upper  = is_ctype cUpper
  is_digit  = is_ctype cDigit
  is_alphanum = is_ctype (cLower+cUpper+cDigit)
  
  -- Utils
  
  hexDigit :: Char -> Int
  hexDigit c | is_decdigit c = ord c - ord '0'
             | otherwise     = ord (to_lower c) - ord 'a' + 10
  
  octDecDigit :: Char -> Int
  octDecDigit c = ord c - ord '0'
  
  is_decdigit :: Char -> Bool
  is_decdigit c
          =  c >= '0' && c <= '9'
  
  is_hexdigit :: Char -> Bool
  is_hexdigit c
          =  is_decdigit c
          || (c >= 'a' && c <= 'f')
          || (c >= 'A' && c <= 'F')
  
  is_octdigit :: Char -> Bool
  is_octdigit c = c >= '0' && c <= '7'
  
  is_bindigit :: Char -> Bool
  is_bindigit c = c == '0' || c == '1'
  
  to_lower :: Char -> Char
  to_lower c
    | c >=  'A' && c <= 'Z' = chr (ord c - (ord 'A' - ord 'a'))
    | otherwise = c
  
  -- | We really mean .|. instead of + below, but GHC currently doesn't do
  --  any constant folding with bitops. *sigh*
  
  charType :: Char -> Int
  charType c = case c of
     '\0'   -> 0                         -- \000
     '\1'   -> 0                         -- \001
     '\2'   -> 0                         -- \002
     '\3'   -> 0                         -- \003
     '\4'   -> 0                         -- \004
     '\5'   -> 0                         -- \005
     '\6'   -> 0                         -- \006
     '\7'   -> 0                         -- \007
     '\8'   -> 0                         -- \010
     '\9'   -> cSpace                    -- \t  (not allowed in strings, so !cAny)
     '\10'  -> cSpace                    -- \n  (ditto)
     '\11'  -> cSpace                    -- \v  (ditto)
     '\12'  -> cSpace                    -- \f  (ditto)
     '\13'  -> cSpace                    --  ^M (ditto)
     '\14'  -> 0                         -- \016
     '\15'  -> 0                         -- \017
     '\16'  -> 0                         -- \020
     '\17'  -> 0                         -- \021
     '\18'  -> 0                         -- \022
     '\19'  -> 0                         -- \023
     '\20'  -> 0                         -- \024
     '\21'  -> 0                         -- \025
     '\22'  -> 0                         -- \026
     '\23'  -> 0                         -- \027
     '\24'  -> 0                         -- \030
     '\25'  -> 0                         -- \031
     '\26'  -> 0                         -- \032
     '\27'  -> 0                         -- \033
     '\28'  -> 0                         -- \034
     '\29'  -> 0                         -- \035
     '\30'  -> 0                         -- \036
     '\31'  -> 0                         -- \037
     '\32'  -> cAny + cSpace             --
     '\33'  -> cAny + cSymbol            -- !
     '\34'  -> cAny                      -- "
     '\35'  -> cAny + cSymbol            --  #
     '\36'  -> cAny + cSymbol            --  $
     '\37'  -> cAny + cSymbol            -- %
     '\38'  -> cAny + cSymbol            -- &
     '\39'  -> cAny + cIdent             -- '
     '\40'  -> cAny                      -- (
     '\41'  -> cAny                      -- )
     '\42'  -> cAny + cSymbol            --  *
     '\43'  -> cAny + cSymbol            -- +
     '\44'  -> cAny                      -- ,
     '\45'  -> cAny + cSymbol            -- -
     '\46'  -> cAny + cSymbol            -- .
     '\47'  -> cAny + cSymbol            --  /
     '\48'  -> cAny + cIdent  + cDigit   -- 0
     '\49'  -> cAny + cIdent  + cDigit   -- 1
     '\50'  -> cAny + cIdent  + cDigit   -- 2
     '\51'  -> cAny + cIdent  + cDigit   -- 3
     '\52'  -> cAny + cIdent  + cDigit   -- 4
     '\53'  -> cAny + cIdent  + cDigit   -- 5
     '\54'  -> cAny + cIdent  + cDigit   -- 6
     '\55'  -> cAny + cIdent  + cDigit   -- 7
     '\56'  -> cAny + cIdent  + cDigit   -- 8
     '\57'  -> cAny + cIdent  + cDigit   -- 9
     '\58'  -> cAny + cSymbol            -- :
     '\59'  -> cAny                      -- ;
     '\60'  -> cAny + cSymbol            -- <
     '\61'  -> cAny + cSymbol            -- =
     '\62'  -> cAny + cSymbol            -- >
     '\63'  -> cAny + cSymbol            -- ?
     '\64'  -> cAny + cSymbol            -- @
     '\65'  -> cAny + cIdent  + cUpper   -- A
     '\66'  -> cAny + cIdent  + cUpper   -- B
     '\67'  -> cAny + cIdent  + cUpper   -- C
     '\68'  -> cAny + cIdent  + cUpper   -- D
     '\69'  -> cAny + cIdent  + cUpper   -- E
     '\70'  -> cAny + cIdent  + cUpper   -- F
     '\71'  -> cAny + cIdent  + cUpper   -- G
     '\72'  -> cAny + cIdent  + cUpper   -- H
     '\73'  -> cAny + cIdent  + cUpper   -- I
     '\74'  -> cAny + cIdent  + cUpper   -- J
     '\75'  -> cAny + cIdent  + cUpper   -- K
     '\76'  -> cAny + cIdent  + cUpper   -- L
     '\77'  -> cAny + cIdent  + cUpper   -- M
     '\78'  -> cAny + cIdent  + cUpper   -- N
     '\79'  -> cAny + cIdent  + cUpper   -- O
     '\80'  -> cAny + cIdent  + cUpper   -- P
     '\81'  -> cAny + cIdent  + cUpper   -- Q
     '\82'  -> cAny + cIdent  + cUpper   -- R
     '\83'  -> cAny + cIdent  + cUpper   -- S
     '\84'  -> cAny + cIdent  + cUpper   -- T
     '\85'  -> cAny + cIdent  + cUpper   -- U
     '\86'  -> cAny + cIdent  + cUpper   -- V
     '\87'  -> cAny + cIdent  + cUpper   -- W
     '\88'  -> cAny + cIdent  + cUpper   -- X
     '\89'  -> cAny + cIdent  + cUpper   -- Y
     '\90'  -> cAny + cIdent  + cUpper   -- Z
     '\91'  -> cAny                      -- [
     '\92'  -> cAny + cSymbol            -- backslash
     '\93'  -> cAny                      -- ]
     '\94'  -> cAny + cSymbol            --  ^
     '\95'  -> cAny + cIdent  + cLower   -- _
     '\96'  -> cAny                      -- `
     '\97'  -> cAny + cIdent  + cLower   -- a
     '\98'  -> cAny + cIdent  + cLower   -- b
     '\99'  -> cAny + cIdent  + cLower   -- c
     '\100' -> cAny + cIdent  + cLower   -- d
     '\101' -> cAny + cIdent  + cLower   -- e
     '\102' -> cAny + cIdent  + cLower   -- f
     '\103' -> cAny + cIdent  + cLower   -- g
     '\104' -> cAny + cIdent  + cLower   -- h
     '\105' -> cAny + cIdent  + cLower   -- i
     '\106' -> cAny + cIdent  + cLower   -- j
     '\107' -> cAny + cIdent  + cLower   -- k
     '\108' -> cAny + cIdent  + cLower   -- l
     '\109' -> cAny + cIdent  + cLower   -- m
     '\110' -> cAny + cIdent  + cLower   -- n
     '\111' -> cAny + cIdent  + cLower   -- o
     '\112' -> cAny + cIdent  + cLower   -- p
     '\113' -> cAny + cIdent  + cLower   -- q
     '\114' -> cAny + cIdent  + cLower   -- r
     '\115' -> cAny + cIdent  + cLower   -- s
     '\116' -> cAny + cIdent  + cLower   -- t
     '\117' -> cAny + cIdent  + cLower   -- u
     '\118' -> cAny + cIdent  + cLower   -- v
     '\119' -> cAny + cIdent  + cLower   -- w
     '\120' -> cAny + cIdent  + cLower   -- x
     '\121' -> cAny + cIdent  + cLower   -- y
     '\122' -> cAny + cIdent  + cLower   -- z
     '\123' -> cAny                      -- {
     '\124' -> cAny + cSymbol            --  |
     '\125' -> cAny                      -- }
     '\126' -> cAny + cSymbol            -- ~
     '\127' -> 0                         -- \177
     _ -> panic ("charType: " ++ show c)
  