[project @ 2003-08-27 08:41:07 by simonmar]
[ghc-base.git] / GHC / Unicode.hsc
1 {-# OPTIONS -fno-implicit-prelude #-}
2 -----------------------------------------------------------------------------
3 -- |
4 -- Module      :  GHC.Unicde
5 -- Copyright   :  (c) The University of Glasgow, 2003
6 -- License     :  see libraries/base/LICENSE
7 -- 
8 -- Maintainer  :  cvs-ghc@haskell.org
9 -- Stability   :  internal
10 -- Portability :  non-portable (GHC extensions)
11 --
12 -- Implementations for the character predicates (isLower, isUpper, etc.)
13 -- and the conversions (toUpper, toLower).  The implementation uses
14 -- libunicode on Unix systems if that is available.
15 --
16 -----------------------------------------------------------------------------
17
18 module GHC.Unicode (
19     isAscii, isLatin1, isControl,
20     isAsciiUpper, isAsciiLower,
21     isPrint, isSpace,  isUpper,
22     isLower, isAlpha,  isDigit,
23     isOctDigit, isHexDigit, isAlphaNum,
24     toUpper, toLower,
25   ) where
26
27 import GHC.Base
28 import GHC.Real  (fromIntegral)
29 import GHC.Int
30 import GHC.Word
31 import GHC.Num   (fromInteger)
32
33 #include "config.h"
34
35 -- | Selects the first 128 characters of the Unicode character set,
36 -- corresponding to the ASCII character set.
37 isAscii                 :: Char -> Bool
38 isAscii c               =  c <  '\x80'
39
40 -- | Selects the first 256 characters of the Unicode character set,
41 -- corresponding to the ISO 8859-1 (Latin-1) character set.
42 isLatin1                :: Char -> Bool
43 isLatin1 c              =  c <= '\xff'
44
45 isAsciiUpper, isAsciiLower :: Char -> Bool
46 isAsciiLower c          =  c >= 'a' && c <= 'z'
47 isAsciiUpper c          =  c >= 'A' && c <= 'Z'
48
49 -- | Selects control characters, which are the non-printing characters of
50 -- the Latin-1 subset of Unicode.
51 isControl               :: Char -> Bool
52
53 -- | Selects printable Unicode characters
54 -- (letters, numbers, marks, punctuation, symbols and spaces).
55 isPrint                 :: Char -> Bool
56
57 -- | Selects white-space characters in the Latin-1 range.
58 -- (In Unicode terms, this includes spaces and some control characters.)
59 isSpace                 :: Char -> Bool
60
61 -- | Selects alphabetic Unicode characters (letters) that are not lower-case.
62 -- (In Unicode terms, this includes letters in upper and title cases,
63 -- as well as modifier letters and other letters.)
64 isUpper                 :: Char -> Bool
65
66 -- | Selects lower-case alphabetic Unicode characters (letters).
67 isLower                 :: Char -> Bool
68
69 -- | Selects alphabetic Unicode characters (letters).
70 isAlpha                 :: Char -> Bool
71
72 -- | Selects alphabetic or numeric digit Unicode characters.
73 --
74 -- Note that numeric digits outside the ASCII range are selected by this
75 -- function but not by 'isDigit'.  Such digits may be part of identifiers
76 -- but are not used by the printer and reader to represent numbers.
77 isAlphaNum              :: Char -> Bool
78
79 -- | Selects ASCII digits, i.e. @\'0\'@..@\'9\'@.
80 isDigit                 :: Char -> Bool
81
82 -- | Selects ASCII octal digits, i.e. @\'0\'@..@\'7\'@.
83 isOctDigit              :: Char -> Bool
84 isOctDigit c            =  c >= '0' && c <= '7'
85
86 -- | Selects ASCII hexadecimal digits,
87 -- i.e. @\'0\'@..@\'9\'@, @\'a\'@..@\'f\'@, @\'A\'@..@\'F\'@.
88 isHexDigit              :: Char -> Bool
89 isHexDigit c            =  isDigit c || c >= 'A' && c <= 'F' ||
90                                         c >= 'a' && c <= 'f'
91
92 -- | Convert a letter to the corresponding upper-case letter, leaving any
93 -- other character unchanged.  Any Unicode letter which has an upper-case
94 -- equivalent is transformed.
95 toUpper                 :: Char -> Char
96
97 -- | Convert a letter to the corresponding lower-case letter, leaving any
98 -- other character unchanged.  Any Unicode letter which has a lower-case
99 -- equivalent is transformed.
100 toLower                 :: Char -> Char
101
102 -- -----------------------------------------------------------------------------
103 -- Win32 implementation
104
105 #if defined(HAVE_WCTYPE_H) || mingw32_TARGET_OS
106
107 -- Use the wide-char classification functions if available.  Glibc
108 -- seems to implement these properly, even for chars > 0xffff, as long
109 -- as you call setlocale() to set the locale to something other than
110 -- "C".  Therefore, we call setlocale() in hs_init().
111
112 -- Win32 uses UTF-16, so presumably the system-supplied iswlower() and
113 -- friends won't work properly with characters > 0xffff.  These
114 -- characters are represented as surrogate pairs in UTF-16.
115
116 type WInt = (#type wint_t)
117 type CInt = (#type int)
118
119 isDigit    c = iswdigit (fromIntegral (ord c)) /= 0
120 isAlpha    c = iswalpha (fromIntegral (ord c)) /= 0
121 isAlphaNum c = iswalnum (fromIntegral (ord c)) /= 0
122 isSpace    c = iswspace (fromIntegral (ord c)) /= 0
123 isControl  c = iswcntrl (fromIntegral (ord c)) /= 0
124 isPrint    c = iswprint (fromIntegral (ord c)) /= 0
125 isUpper    c = iswupper (fromIntegral (ord c)) /= 0
126 isLower    c = iswlower (fromIntegral (ord c)) /= 0
127
128 toLower c = chr (fromIntegral (towlower (fromIntegral (ord c))))
129 toUpper c = chr (fromIntegral (towupper (fromIntegral (ord c))))
130
131 foreign import ccall unsafe "iswdigit"
132   iswdigit :: WInt -> CInt
133
134 foreign import ccall unsafe "iswalpha"
135   iswalpha :: WInt -> CInt
136
137 foreign import ccall unsafe "iswalnum"
138   iswalnum :: WInt -> CInt
139
140 foreign import ccall unsafe "iswcntrl"
141   iswcntrl :: WInt -> CInt
142
143 foreign import ccall unsafe "iswspace"
144   iswspace :: WInt -> CInt
145
146 foreign import ccall unsafe "iswprint"
147   iswprint :: WInt -> CInt
148
149 foreign import ccall unsafe "iswlower"
150   iswlower :: WInt -> CInt
151
152 foreign import ccall unsafe "iswupper"
153   iswupper :: WInt -> CInt
154
155 foreign import ccall unsafe "towlower"
156   towlower :: WInt -> WInt
157
158 foreign import ccall unsafe "towupper"
159   towupper :: WInt -> WInt
160
161 -- -----------------------------------------------------------------------------
162 -- No libunicode, so fall back to the ASCII-only implementation
163
164 #else
165
166 isControl c             =  c < ' ' || c >= '\DEL' && c <= '\x9f'
167 isPrint c               =  not (isControl c)
168
169 -- isSpace includes non-breaking space
170 -- Done with explicit equalities both for efficiency, and to avoid a tiresome
171 -- recursion with GHC.List elem
172 isSpace c               =  c == ' '     ||
173                            c == '\t'    ||
174                            c == '\n'    ||
175                            c == '\r'    ||
176                            c == '\f'    ||
177                            c == '\v'    ||
178                            c == '\xa0'
179
180 -- The upper case ISO characters have the multiplication sign dumped
181 -- randomly in the middle of the range.  Go figure.
182 isUpper c               =  c >= 'A' && c <= 'Z' || 
183                            c >= '\xC0' && c <= '\xD6' ||
184                            c >= '\xD8' && c <= '\xDE'
185 -- The lower case ISO characters have the division sign dumped
186 -- randomly in the middle of the range.  Go figure.
187 isLower c               =  c >= 'a' && c <= 'z' ||
188                            c >= '\xDF' && c <= '\xF6' ||
189                            c >= '\xF8' && c <= '\xFF'
190
191 isAlpha c               =  isLower c || isUpper c
192 isDigit c               =  c >= '0' && c <= '9'
193 isAlphaNum c            =  isAlpha c || isDigit c
194
195 -- Case-changing operations
196
197 toUpper c@(C## c##)
198   | isAsciiLower c    = C## (chr## (ord## c## -## 32##))
199   | isAscii c         = c
200     -- fall-through to the slower stuff.
201   | isLower c   && c /= '\xDF' && c /= '\xFF'
202   = unsafeChr (ord c `minusInt` ord 'a' `plusInt` ord 'A')
203   | otherwise
204   = c
205
206
207 toLower c@(C## c##)
208   | isAsciiUpper c = C## (chr## (ord## c## +## 32##))
209   | isAscii c      = c
210   | isUpper c      = unsafeChr (ord c `minusInt` ord 'A' `plusInt` ord 'a')
211   | otherwise      =  c
212
213 #endif
214