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