]> gitweb.factorcode.org Git - factor.git/blob - basis/unicode/collation/collation.factor
Merge branch 'master' of git://github.com/inforichland/factor-id3
[factor.git] / basis / unicode / collation / collation.factor
1 ! Copyright (C) 2008 Daniel Ehrenberg.\r
2 ! See http://factorcode.org/license.txt for BSD license.\r
3 USING: combinators.short-circuit sequences io.files\r
4 io.encodings.ascii kernel values splitting accessors math.parser\r
5 ascii io assocs strings math namespaces make sorting combinators\r
6 math.order arrays unicode.normalize unicode.data locals\r
7 unicode.syntax macros sequences.deep words unicode.breaks\r
8 quotations combinators.short-circuit ;\r
9 IN: unicode.collation\r
10 \r
11 <PRIVATE\r
12 VALUE: ducet\r
13 \r
14 TUPLE: weight primary secondary tertiary ignorable? ;\r
15 \r
16 : parse-weight ( string -- weight )\r
17     "]" split but-last [\r
18         weight new swap rest unclip CHAR: * = swapd >>ignorable?\r
19         swap "." split first3 [ hex> ] tri@\r
20         [ >>primary ] [ >>secondary ] [ >>tertiary ] tri*\r
21     ] map ;\r
22 \r
23 : parse-line ( line -- code-poing weight )\r
24     ";" split1 [ [ blank? ] trim ] bi@\r
25     [ " " split [ hex> ] "" map-as ] [ parse-weight ] bi* ;\r
26 \r
27 : parse-ducet ( stream -- ducet )\r
28     lines filter-comments\r
29     [ parse-line ] H{ } map>assoc ;\r
30 \r
31 "vocab:unicode/collation/allkeys.txt"\r
32 ascii <file-reader> parse-ducet to: ducet\r
33 \r
34 ! Fix up table for long contractions\r
35 : help-one ( assoc key -- )\r
36     ! Need to be more general? Not for DUCET, apparently\r
37     2 head 2dup swap key? [ 2drop ] [\r
38         [ [ 1string swap at ] with { } map-as concat ]\r
39         [ swap set-at ] 2bi\r
40     ] if ;\r
41 \r
42 : insert-helpers ( assoc -- )\r
43     dup keys [ length 3 >= ] filter\r
44     [ help-one ] with each ;\r
45 \r
46 ducet insert-helpers\r
47 \r
48 : base ( char -- base )\r
49     {\r
50         { [ dup HEX: 3400 HEX:  4DB5 between? ] [ drop HEX: FB80 ] } ! Extension A\r
51         { [ dup HEX: 20000 HEX: 2A6D6 between? ] [ drop HEX: FB80 ] } ! Extension B\r
52         { [ dup HEX: 4E00 HEX: 9FC3 between? ] [ drop HEX: FB40 ] } ! CJK\r
53         [ drop HEX: FBC0 ] ! Other\r
54     } cond ;\r
55 \r
56 : AAAA ( char -- weight )\r
57     [ base ] [ -15 shift ] bi + HEX: 20 2 f weight boa ;\r
58 \r
59 : BBBB ( char -- weight )\r
60     HEX: 7FFF bitand HEX: 8000 bitor 0 0 f weight boa ;\r
61 \r
62 : illegal? ( char -- ? )\r
63     { [ "Noncharacter_Code_Point" property? ] [ category "Cs" = ] } 1|| ;\r
64 \r
65 : derive-weight ( char -- weights )\r
66     first dup illegal?\r
67     [ drop { } ]\r
68     [ [ AAAA ] [ BBBB ] bi 2array ] if ;\r
69 \r
70 : last ( -- char )\r
71     building get empty? [ 0 ] [ building get peek peek ] if ;\r
72 \r
73 : blocked? ( char -- ? )\r
74     combining-class dup { 0 f } member?\r
75     [ drop last non-starter? ]\r
76     [ last combining-class = ] if ;\r
77 \r
78 : possible-bases ( -- slice-of-building )\r
79     building get dup [ first non-starter? not ] find-last\r
80     drop [ 0 ] unless* tail-slice ;\r
81 \r
82 :: ?combine ( char slice i -- ? )\r
83     [let | str [ i slice nth char suffix ] |\r
84         str ducet key? dup\r
85         [ str i slice set-nth ] when\r
86     ] ;\r
87 \r
88 : add ( char -- )\r
89     dup blocked? [ 1string , ] [\r
90         dup possible-bases dup length\r
91         [ ?combine ] with with any?\r
92         [ drop ] [ 1string , ] if\r
93     ] if ;\r
94 \r
95 : string>graphemes ( string -- graphemes )\r
96     [ [ add ] each ] { } make ;\r
97 \r
98 : graphemes>weights ( graphemes -- weights )\r
99     [\r
100         dup weight? [ 1array ] ! From tailoring\r
101         [ dup ducet at [ ] [ derive-weight ] ?if ] if\r
102     ] { } map-as concat ;\r
103 \r
104 : append-weights ( weights quot -- )\r
105     [ [ ignorable?>> not ] filter ] dip\r
106     map [ zero? not ] filter % 0 , ; inline\r
107 \r
108 : variable-weight ( weight -- )\r
109     dup ignorable?>> [ primary>> ] [ drop HEX: FFFF ] if , ;\r
110 \r
111 : weights>bytes ( weights -- byte-array )\r
112     [\r
113         {\r
114             [ [ primary>> ] append-weights ]\r
115             [ [ secondary>> ] append-weights ]\r
116             [ [ tertiary>> ] append-weights ]\r
117             [ [ variable-weight ] each ]\r
118         } cleave\r
119     ] { } make ;\r
120 PRIVATE>\r
121 \r
122 : completely-ignorable? ( weight -- ? )\r
123     [ primary>> ] [ secondary>> ] [ tertiary>> ] tri\r
124     [ zero? ] tri@ and and ;\r
125 \r
126 : filter-ignorable ( weights -- weights' )\r
127     f swap [\r
128         [ nip ] [ primary>> zero? and ] 2bi\r
129         [ swap ignorable?>> or ]\r
130         [ swap completely-ignorable? or not ] 2bi\r
131     ] filter nip ;\r
132 \r
133 : collation-key ( string -- key )\r
134     nfd string>graphemes graphemes>weights\r
135     filter-ignorable weights>bytes ;\r
136 \r
137 <PRIVATE\r
138 : insensitive= ( str1 str2 levels-removed -- ? )\r
139     [\r
140         [ collation-key ] dip\r
141         [ [ 0 = not ] trim-tail but-last ] times\r
142     ] curry bi@ = ;\r
143 PRIVATE>\r
144 \r
145 : primary= ( str1 str2 -- ? )\r
146     3 insensitive= ;\r
147 \r
148 : secondary= ( str1 str2 -- ? )\r
149     2 insensitive= ;\r
150 \r
151 : tertiary= ( str1 str2 -- ? )\r
152     1 insensitive= ;\r
153 \r
154 : quaternary= ( str1 str2 -- ? )\r
155     0 insensitive= ;\r
156 \r
157 <PRIVATE\r
158 : w/collation-key ( str -- {str,key} )\r
159     [ collation-key ] keep 2array ;\r
160 PRIVATE>\r
161 \r
162 : sort-strings ( strings -- sorted )\r
163     [ w/collation-key ] map natural-sort values ;\r
164 \r
165 : string<=> ( str1 str2 -- <=> )\r
166     [ w/collation-key ] compare ;\r