[gforth] / gforth / utf-8.fs  

gforth: gforth/utf-8.fs


1 : pazsan 1.1 \ UTF-8 handling 12dec04py
2 :    
3 : anton 1.44 \ Copyright (C) 2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
4 : pazsan 1.1
5 :     \ This file is part of Gforth.
6 :    
7 :     \ Gforth is free software; you can redistribute it and/or
8 :     \ modify it under the terms of the GNU General Public License
9 : anton 1.34 \ as published by the Free Software Foundation, either version 3
10 : pazsan 1.1 \ of the License, or (at your option) any later version.
11 :    
12 :     \ This program is distributed in the hope that it will be useful,
13 :     \ but WITHOUT ANY WARRANTY; without even the implied warranty of
14 :     \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 :     \ GNU General Public License for more details.
16 :    
17 :     \ You should have received a copy of the GNU General Public License
18 : anton 1.34 \ along with this program. If not, see http://www.gnu.org/licenses/.
19 : pazsan 1.1
20 :     \ short: u8 means utf-8 encoded address
21 :    
22 : pazsan 1.7 s" malformed UTF-8 character" exception Constant UTF-8-err
23 :    
24 : anton 1.13 $80 Value max-single-byte
25 : pazsan 1.10
26 : pazsan 1.4 : u8len ( u8 -- n )
27 : anton 1.13 dup max-single-byte u< IF drop 1 EXIT THEN \ special case ASCII
28 : pazsan 1.1 $800 2 >r
29 : pazsan 1.17 BEGIN 2dup u>= WHILE 5 lshift r> 1+ >r dup 0= UNTIL THEN
30 : pazsan 1.1 2drop r> ;
31 :    
32 :     : u8@+ ( u8addr -- u8addr' u )
33 : anton 1.13 count dup max-single-byte u< ?EXIT \ special case ASCII
34 : pazsan 1.17 dup $C2 u< IF UTF-8-err throw THEN \ malformed character
35 : pazsan 1.1 $7F and $40 >r
36 :     BEGIN dup r@ and WHILE r@ xor
37 :     6 lshift r> 5 lshift >r >r count
38 : pazsan 1.7 dup $C0 and $80 <> IF UTF-8-err throw THEN
39 : pazsan 1.1 $3F and r> or
40 :     REPEAT rdrop ;
41 :    
42 :     : u8!+ ( u u8addr -- u8addr' )
43 : anton 1.13 over max-single-byte u< IF tuck c! 1+ EXIT THEN \ special case ASCII
44 : pazsan 1.1 >r 0 swap $3F
45 :     BEGIN 2dup u> WHILE
46 :     2/ >r dup $3F and $80 or swap 6 rshift r>
47 :     REPEAT $7F xor 2* or r>
48 :     BEGIN over $80 u>= WHILE tuck c! 1+ REPEAT nip ;
49 :    
50 : pazsan 1.8 \ plug-in so that char and '<char> work for UTF-8
51 :    
52 : anton 1.9 [ifundef] char@ \ !! bootstrapping help
53 :     Defer char@ ( addr u -- char addr' u' )
54 :     :noname over c@ -rot 1 /string ; IS char@
55 :     [then]
56 :    
57 : pazsan 1.8 :noname ( addr u -- char addr' u' )
58 : anton 1.9 \ !! the if here seems to work around some breakage, but not
59 : pazsan 1.24 \ entirely; e.g., try 'รง' with LANG=C.
60 : anton 1.9 dup 1 u<= IF defers char@ EXIT THEN
61 : pazsan 1.8 over + >r u8@+ swap r> over - ; IS char@
62 :    
63 : pazsan 1.1 \ scan to next/previous character
64 :    
65 : anton 1.13 \ alternative names: u8char+ u8char-
66 :    
67 : pazsan 1.12 : u8>> ( u8addr -- u8addr' ) u8@+ drop ;
68 : pazsan 1.1 : u8<< ( u8addr -- u8addr' )
69 : anton 1.13 BEGIN 1- dup c@ $C0 and max-single-byte <> UNTIL ;
70 : pazsan 1.1
71 :     \ utf key and emit
72 :    
73 : pazsan 1.27 Defer check-xy ' noop IS check-xy
74 :    
75 : pazsan 1.1 : u8key ( -- u )
76 : pazsan 1.37 key dup max-single-byte u< ?EXIT \ special case ASCII
77 : pazsan 1.27 dup $FF = ?EXIT \ special resize character
78 : pazsan 1.17 dup $C2 u< IF UTF-8-err throw THEN \ malformed character
79 : pazsan 1.1 $7F and $40 >r
80 :     BEGIN dup r@ and WHILE r@ xor
81 : pazsan 1.37 6 lshift r> 5 lshift >r >r key
82 : pazsan 1.7 dup $C0 and $80 <> IF UTF-8-err throw THEN
83 : pazsan 1.1 $3F and r> or
84 :     REPEAT rdrop ;
85 :    
86 :     : u8emit ( u -- )
87 : pazsan 1.37 dup max-single-byte u< IF emit EXIT THEN \ special case ASCII
88 : pazsan 1.1 0 swap $3F
89 :     BEGIN 2dup u> WHILE
90 :     2/ >r dup $3F and $80 or swap 6 rshift r>
91 :     REPEAT $7F xor 2* or
92 : pazsan 1.37 BEGIN dup $80 u>= WHILE emit REPEAT drop ;
93 : anton 1.13
94 :     \ utf-8 stuff for xchars
95 :    
96 : pazsan 1.29 : +u8/string ( xc-addr1 u1 -- xc-addr2 u2 )
97 :     over dup u8>> swap - /string ;
98 :     : u8\string- ( xcaddr u -- xcaddr u' )
99 : pazsan 1.26 over + u8<< over - ;
100 :    
101 : anton 1.13 : u8@ ( c-addr -- u )
102 :     u8@+ nip ;
103 :    
104 :     : u8!+? ( xc xc-addr1 u1 -- xc-addr2 u2 f )
105 :     >r over u8len r@ over u< if ( xc xc-addr1 len r: u1 )
106 :     \ not enough space
107 :     drop nip r> false
108 :     else
109 :     >r u8!+ r> r> swap - true
110 :     then ;
111 :    
112 : pazsan 1.28 : u8addrlen ( u8-addr u -- u ) drop
113 : anton 1.13 \ length of UTF-8 char starting at u8-addr (accesses only u8-addr)
114 :     c@
115 :     dup $80 u< if drop 1 exit endif
116 :     dup $c0 u< if UTF-8-err throw endif
117 :     dup $e0 u< if drop 2 exit endif
118 :     dup $f0 u< if drop 3 exit endif
119 :     dup $f8 u< if drop 4 exit endif
120 :     dup $fc u< if drop 5 exit endif
121 :     dup $fe u< if drop 6 exit endif
122 : pazsan 1.38 dup $ff u< if drop 7 exit endif
123 :     drop 8 ;
124 : anton 1.13
125 :     : -u8trailing-garbage ( addr u1 -- addr u2 )
126 :     2dup + dup u8<< ( addr u1 end1 end2 )
127 : pazsan 1.28 2dup dup over over - u8addrlen + = if \ last character ok
128 : anton 1.13 2drop
129 :     else
130 :     nip nip over -
131 :     then ;
132 :    
133 : anton 1.22 [IFUNDEF] wcwidth
134 : pazsan 1.24 : wc,3 ( n low high -- ) 1+ , , , ;
135 :    
136 :     Create wc-table \ derived from wcwidth source code, for UCS32
137 : anton 1.35 0 $0300 $0357 wc,3
138 :     0 $035D $036F wc,3
139 :     0 $0483 $0486 wc,3
140 :     0 $0488 $0489 wc,3
141 :     0 $0591 $05A1 wc,3
142 :     0 $05A3 $05B9 wc,3
143 :     0 $05BB $05BD wc,3
144 :     0 $05BF $05BF wc,3
145 :     0 $05C1 $05C2 wc,3
146 :     0 $05C4 $05C4 wc,3
147 :     0 $0600 $0603 wc,3
148 :     0 $0610 $0615 wc,3
149 :     0 $064B $0658 wc,3
150 :     0 $0670 $0670 wc,3
151 :     0 $06D6 $06E4 wc,3
152 :     0 $06E7 $06E8 wc,3
153 :     0 $06EA $06ED wc,3
154 :     0 $070F $070F wc,3
155 :     0 $0711 $0711 wc,3
156 :     0 $0730 $074A wc,3
157 :     0 $07A6 $07B0 wc,3
158 :     0 $0901 $0902 wc,3
159 :     0 $093C $093C wc,3
160 :     0 $0941 $0948 wc,3
161 :     0 $094D $094D wc,3
162 :     0 $0951 $0954 wc,3
163 :     0 $0962 $0963 wc,3
164 :     0 $0981 $0981 wc,3
165 :     0 $09BC $09BC wc,3
166 :     0 $09C1 $09C4 wc,3
167 :     0 $09CD $09CD wc,3
168 :     0 $09E2 $09E3 wc,3
169 :     0 $0A01 $0A02 wc,3
170 :     0 $0A3C $0A3C wc,3
171 :     0 $0A41 $0A42 wc,3
172 :     0 $0A47 $0A48 wc,3
173 :     0 $0A4B $0A4D wc,3
174 :     0 $0A70 $0A71 wc,3
175 :     0 $0A81 $0A82 wc,3
176 :     0 $0ABC $0ABC wc,3
177 :     0 $0AC1 $0AC5 wc,3
178 :     0 $0AC7 $0AC8 wc,3
179 :     0 $0ACD $0ACD wc,3
180 :     0 $0AE2 $0AE3 wc,3
181 :     0 $0B01 $0B01 wc,3
182 :     0 $0B3C $0B3C wc,3
183 :     0 $0B3F $0B3F wc,3
184 :     0 $0B41 $0B43 wc,3
185 :     0 $0B4D $0B4D wc,3
186 :     0 $0B56 $0B56 wc,3
187 :     0 $0B82 $0B82 wc,3
188 :     0 $0BC0 $0BC0 wc,3
189 :     0 $0BCD $0BCD wc,3
190 :     0 $0C3E $0C40 wc,3
191 :     0 $0C46 $0C48 wc,3
192 :     0 $0C4A $0C4D wc,3
193 :     0 $0C55 $0C56 wc,3
194 :     0 $0CBC $0CBC wc,3
195 :     0 $0CBF $0CBF wc,3
196 :     0 $0CC6 $0CC6 wc,3
197 :     0 $0CCC $0CCD wc,3
198 :     0 $0D41 $0D43 wc,3
199 :     0 $0D4D $0D4D wc,3
200 :     0 $0DCA $0DCA wc,3
201 :     0 $0DD2 $0DD4 wc,3
202 :     0 $0DD6 $0DD6 wc,3
203 :     0 $0E31 $0E31 wc,3
204 :     0 $0E34 $0E3A wc,3
205 :     0 $0E47 $0E4E wc,3
206 :     0 $0EB1 $0EB1 wc,3
207 :     0 $0EB4 $0EB9 wc,3
208 :     0 $0EBB $0EBC wc,3
209 :     0 $0EC8 $0ECD wc,3
210 :     0 $0F18 $0F19 wc,3
211 :     0 $0F35 $0F35 wc,3
212 :     0 $0F37 $0F37 wc,3
213 :     0 $0F39 $0F39 wc,3
214 :     0 $0F71 $0F7E wc,3
215 :     0 $0F80 $0F84 wc,3
216 :     0 $0F86 $0F87 wc,3
217 :     0 $0F90 $0F97 wc,3
218 :     0 $0F99 $0FBC wc,3
219 :     0 $0FC6 $0FC6 wc,3
220 :     0 $102D $1030 wc,3
221 :     0 $1032 $1032 wc,3
222 :     0 $1036 $1037 wc,3
223 :     0 $1039 $1039 wc,3
224 :     0 $1058 $1059 wc,3
225 :     1 $0000 $1100 wc,3
226 :     2 $1100 $115f wc,3
227 :     0 $1160 $11FF wc,3
228 :     0 $1712 $1714 wc,3
229 :     0 $1732 $1734 wc,3
230 :     0 $1752 $1753 wc,3
231 :     0 $1772 $1773 wc,3
232 :     0 $17B4 $17B5 wc,3
233 :     0 $17B7 $17BD wc,3
234 :     0 $17C6 $17C6 wc,3
235 :     0 $17C9 $17D3 wc,3
236 :     0 $17DD $17DD wc,3
237 :     0 $180B $180D wc,3
238 :     0 $18A9 $18A9 wc,3
239 :     0 $1920 $1922 wc,3
240 :     0 $1927 $1928 wc,3
241 :     0 $1932 $1932 wc,3
242 :     0 $1939 $193B wc,3
243 :     0 $200B $200F wc,3
244 :     0 $202A $202E wc,3
245 :     0 $2060 $2063 wc,3
246 :     0 $206A $206F wc,3
247 :     0 $20D0 $20EA wc,3
248 :     2 $2329 $232A wc,3
249 :     0 $302A $302F wc,3
250 :     2 $2E80 $303E wc,3
251 :     0 $3099 $309A wc,3
252 :     2 $3040 $A4CF wc,3
253 :     2 $AC00 $D7A3 wc,3
254 :     2 $F900 $FAFF wc,3
255 :     0 $FB1E $FB1E wc,3
256 :     0 $FE00 $FE0F wc,3
257 :     0 $FE20 $FE23 wc,3
258 :     2 $FE30 $FE6F wc,3
259 :     0 $FEFF $FEFF wc,3
260 :     2 $FF00 $FF60 wc,3
261 :     2 $FFE0 $FFE6 wc,3
262 :     0 $FFF9 $FFFB wc,3
263 :     0 $1D167 $1D169 wc,3
264 :     0 $1D173 $1D182 wc,3
265 :     0 $1D185 $1D18B wc,3
266 :     0 $1D1AA $1D1AD wc,3
267 :     2 $20000 $2FFFD wc,3
268 :     2 $30000 $3FFFD wc,3
269 :     0 $E0001 $E0001 wc,3
270 :     0 $E0020 $E007F wc,3
271 :     0 $E0100 $E01EF wc,3
272 : pazsan 1.24 here wc-table - Constant #wc-table
273 :    
274 :     \ inefficient table walk:
275 :    
276 : pazsan 1.41 : xc-width ( xc -- n )
277 : pazsan 1.24 wc-table #wc-table over + swap ?DO
278 :     dup I 2@ within IF I 2 cells + @ UNLOOP EXIT THEN
279 :     3 cells +LOOP 1 ;
280 : pazsan 1.41 [ELSE]
281 :     ' wcwidth Alias xc-width
282 : anton 1.22 [THEN]
283 :    
284 : pazsan 1.19 : u8width ( xcaddr u -- n )
285 :     0 rot rot over + swap ?DO
286 : pazsan 1.41 I xc@+ swap >r xc-width +
287 : pazsan 1.19 r> I - +LOOP ;
288 :    
289 : anton 1.13 : set-encoding-utf-8 ( -- )
290 :     ['] u8emit is xemit
291 :     ['] u8key is xkey
292 :     ['] u8>> is xchar+
293 :     ['] u8<< is xchar-
294 : pazsan 1.26 [ [IFDEF] xstring+ ]
295 : pazsan 1.29 ['] u8\string- is xstring-
296 :     ['] +u8/string is +xstring
297 :     [ [THEN] ]
298 : pazsan 1.32 [ [IFDEF] +x/string ]
299 : pazsan 1.29 ['] u8\string- is x\string-
300 :     ['] +u8/string is +x/string
301 : pazsan 1.26 [ [THEN] ]
302 : anton 1.13 ['] u8@ is xc@
303 : pazsan 1.43 [ [IFDEF] xc!+ ]
304 :     ['] u8!+ is xc!+
305 :     [ [THEN] ]
306 : anton 1.13 ['] u8!+? is xc!+?
307 :     ['] u8@+ is xc@+
308 :     ['] u8len is xc-size
309 : pazsan 1.20 [ [IFDEF] x-width ]
310 : pazsan 1.19 ['] u8width is x-width
311 : pazsan 1.20 [ [THEN] ]
312 : pazsan 1.28 [ [IFDEF] x-size ]
313 :     ['] u8addrlen is x-size
314 :     [ [THEN] ]
315 : anton 1.13 ['] -u8trailing-garbage is -trailing-garbage
316 :     ;
317 : pazsan 1.1
318 : anton 1.15 : utf-8-cold ( -- )
319 : pazsan 1.16 s" LC_ALL" getenv 2dup d0= IF 2drop
320 :     s" LC_CTYPE" getenv 2dup d0= IF 2drop
321 :     s" LANG" getenv 2dup d0= IF 2drop
322 :     s" C" THEN THEN THEN
323 :     s" UTF-8" search nip nip
324 : anton 1.15 IF set-encoding-utf-8 ELSE set-encoding-fixed-width THEN ;
325 :    
326 : pazsan 1.30 environment-wordlist set-current
327 : pazsan 1.31 : xchar-encoding ( -- addr u ) \ xchar-ext
328 :     \G Returns a printable ASCII string that reperesents the encoding,
329 :     \G and use the preferred MIME name (if any) or the name in
330 :     \G @url{http://www.iana.org/assignments/character-sets} like
331 :     \G ``ISO-LATIN-1'' or ``UTF-8'', with the exception of ``ASCII'', where
332 :     \G we prefer the alias ``ASCII''.
333 :     max-single-byte $80 = IF s" UTF-8" ELSE s" ISO-LATIN-1" THEN ;
334 : pazsan 1.39 : max-xchar ( -- xchar )
335 :     max-single-byte $80 = IF $7FFFFFFF ELSE $FF THEN ;
336 :     ' noop Alias X:xchar
337 : pazsan 1.30 forth definitions
338 :    
339 : anton 1.23 :noname ( -- )
340 :     defers 'cold
341 :     utf-8-cold
342 :     ; is 'cold
343 : anton 1.15
344 :     utf-8-cold

CVS Admin

Powered by ViewCVS 1.0-dev
(Powered by ViewCVS)

ViewCVS and CVS Help