]> gitweb.factorcode.org Git - factor.git/blob - basis/unicode/breaks/breaks.factor
slots: pre-create the "at", "nth", and "global" slots to make deterministic the curre...
[factor.git] / basis / unicode / breaks / breaks.factor
1 ! Copyright (C) 2008 Daniel Ehrenberg.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: combinators.short-circuit unicode.categories kernel math
4 combinators splitting sequences math.parser io.files io assocs
5 arrays namespaces make math.ranges unicode.normalize
6 unicode.normalize.private values io.encodings.ascii
7 unicode.data compiler.units fry unicode.categories.syntax
8 alien.syntax sets accessors interval-maps memoize locals words
9 simple-flat-file ;
10 FROM: sequences => change-nth ;
11 IN: unicode.breaks
12
13 <PRIVATE
14 ! Grapheme breaks
15
16 CONSTANT: Any 0
17 CONSTANT: L 1
18 CONSTANT: V 2
19 CONSTANT: T 3
20 CONSTANT: LV 4
21 CONSTANT: LVT 5
22 CONSTANT: Extend 6
23 CONSTANT: Control 7
24 CONSTANT: CR 8
25 CONSTANT: LF 9
26 CONSTANT: SpacingMark 10
27 CONSTANT: Prepend 11
28 CONSTANT: graphemes 12
29
30 : jamo-class ( ch -- class )
31     dup initial? [ drop L ]
32     [ dup medial? [ drop V ] [ final? T Any ? ] if ] if ;
33
34 : hangul-class ( ch -- class )
35     hangul-base - HEX: 1C mod zero? LV LVT ? ;
36
37 CATEGORY: grapheme-control Zl Zp Cc Cf ;
38 : control-class ( ch -- class )
39     {
40         { CHAR: \r [ CR ] }
41         { CHAR: \n [ LF ] }
42         { HEX: 200C [ Extend ] }
43         { HEX: 200D [ Extend ] }
44         [ drop Control ]
45     } case ;
46
47 CATEGORY: extend
48     Me Mn |
49     "Other_Grapheme_Extend" property? ;
50
51 : loe? ( ch -- ? )
52     "Logical_Order_Exception" property? ;
53
54 CATEGORY: spacing Mc ;
55
56 : grapheme-class ( ch -- class )
57     {
58         { [ dup jamo? ] [ jamo-class ] }
59         { [ dup hangul? ] [ hangul-class ] }
60         { [ dup grapheme-control? ] [ control-class ] }
61         { [ dup extend? ] [ drop Extend ] }
62         { [ dup spacing? ] [ drop SpacingMark ] }
63         { [ loe? ] [ Prepend ] }
64         [ Any ]
65     } cond ;
66
67 : init-table ( size -- table )
68     dup [ f <array> ] curry replicate ;
69
70 SYMBOL: table
71
72 : finish-table ( -- table )
73     table get [ [ 1 = ] map ] map ;
74
75 : eval-seq ( seq -- seq ) [ ?execute ] map ;
76
77 : (set-table) ( class1 class2 val -- )
78     [ table get nth ] dip '[ _ or ] change-nth ;
79
80 : set-table ( classes1 classes2 val -- )
81     [ [ eval-seq ] bi@ ] dip
82     [ [ (set-table) ] curry with each ] 2curry each ;
83
84 : connect ( class1 class2 -- ) 1 set-table ;
85 : disconnect ( class1 class2 -- ) 0 set-table ;
86   
87 : make-grapheme-table ( -- )
88     { CR } { LF } connect
89     { Control CR LF } graphemes iota disconnect
90     graphemes iota { Control CR LF } disconnect
91     { L } { L V LV LVT } connect
92     { LV V } { V T } connect
93     { LVT T } { T } connect
94     graphemes iota { Extend } connect
95     graphemes iota { SpacingMark } connect
96     { Prepend } graphemes iota connect ;
97
98 VALUE: grapheme-table
99
100 : grapheme-break? ( class1 class2 -- ? )
101     grapheme-table nth nth not ;
102
103 PRIVATE>
104
105 : first-grapheme ( str -- i )
106     unclip-slice grapheme-class over
107     [ grapheme-class [ nip ] [ grapheme-break? ] 2bi ] find drop
108     nip swap length or 1 + ;
109
110 : first-grapheme-from ( start str -- i )
111     over tail-slice first-grapheme + ;
112
113 : last-grapheme ( str -- i )
114     unclip-last-slice grapheme-class swap
115     [ grapheme-class dup rot grapheme-break? ] find-last drop ?1+ nip ;
116
117 : last-grapheme-from ( end str -- i )
118     swap head-slice last-grapheme ;
119
120 <PRIVATE
121
122 : >pieces ( str quot: ( str -- i ) -- graphemes )
123     [ dup empty? not ] swap '[ dup @ cut-slice swap ] produce nip ; inline
124
125 PRIVATE>
126
127 : >graphemes ( str -- graphemes )
128     [ first-grapheme ] >pieces ;
129
130 : string-reverse ( str -- rts )
131     >graphemes reverse concat ;
132
133 <PRIVATE
134
135 graphemes init-table table
136 [ make-grapheme-table finish-table ] with-variable
137 to: grapheme-table
138
139 ! Word breaks
140
141 VALUE: word-break-table
142
143 "vocab:unicode/data/WordBreakProperty.txt" load-interval-file
144 to: word-break-table
145
146 CONSTANT: wOther 0
147 CONSTANT: wCR 1
148 CONSTANT: wLF 2
149 CONSTANT: wNewline 3
150 CONSTANT: wExtend 4
151 CONSTANT: wFormat 5
152 CONSTANT: wKatakana 6
153 CONSTANT: wALetter 7
154 CONSTANT: wMidLetter 8
155 CONSTANT: wMidNum 9
156 CONSTANT: wMidNumLet 10
157 CONSTANT: wNumeric 11
158 CONSTANT: wExtendNumLet 12
159 CONSTANT: words 13
160
161 : word-break-classes ( -- table ) ! Is there a way to avoid this?
162     H{
163         { "Other" 0 } { "CR" 1 } { "LF" 2 } { "Newline" 3 }
164         { "Extend" 4 } { "Format" 5 } { "Katakana" 6 }
165         { "ALetter" 7 } { "MidLetter" 8 }
166         { "MidNum" 9 } { "MidNumLet" 10 } { "Numeric" 11 }
167         { "ExtendNumLet" 12 }
168     } ;
169
170 : word-break-prop ( char -- word-break-prop )
171     word-break-table interval-at
172     word-break-classes at [ wOther ] unless* ;
173
174 SYMBOL: check-letter-before
175 SYMBOL: check-letter-after
176 SYMBOL: check-number-before
177 SYMBOL: check-number-after
178
179 : make-word-table ( -- )
180     { wCR } { wLF } connect
181     { wNewline wCR wLF } words iota disconnect
182     words iota { wNewline wCR wLF } disconnect
183     { wALetter } { wMidLetter wMidNumLet } check-letter-after set-table
184     { wMidLetter wMidNumLet } { wALetter } check-letter-before set-table
185     { wNumeric wALetter } { wNumeric wALetter } connect
186     { wNumeric } { wMidNum wMidNumLet } check-number-after set-table
187     { wMidNum wMidNumLet } { wNumeric } check-number-before set-table
188     { wKatakana } { wKatakana } connect
189     { wALetter wNumeric wKatakana wExtendNumLet } { wExtendNumLet }
190     [ connect ] [ swap connect ] 2bi ;
191
192 VALUE: word-table
193
194 : finish-word-table ( -- table )
195     table get [
196         [ { { 0 [ f ] } { 1 [ t ] } [ ] } case ] map
197     ] map ;
198
199 words init-table table
200 [ make-word-table finish-word-table ] with-variable
201 to: word-table
202
203 : word-table-nth ( class1 class2 -- ? )
204     word-table nth nth ;
205
206 :: property-not= ( str i property -- ? )
207     i [
208         i str ?nth [ word-break-prop property = not ]
209         [ f ] if*
210     ] [ t ] if ;
211
212 : format/extended? ( ch -- ? )
213     word-break-prop { 4 5 } member? ;
214
215 : (walk-up) ( str i -- j )
216     swap [ format/extended? not ] find-from drop ;
217
218 : walk-up ( str i -- j )
219     dupd 1 + (walk-up) [ 1 + (walk-up) ] [ drop f ] if* ;
220
221 : (walk-down) ( str i -- j )
222     swap [ format/extended? not ] find-last-from drop ;
223
224 : walk-down ( str i -- j )
225     dupd (walk-down) [ 1 - (walk-down) ] [ drop f ] if* ;
226
227 : word-break? ( str i table-entry -- ? )
228     {
229         { t [ 2drop f ] }
230         { f [ 2drop t ] }
231         { check-letter-after
232             [ dupd walk-up wALetter property-not= ] }
233         { check-letter-before
234             [ dupd walk-down wALetter property-not= ] }
235         { check-number-after
236             [ dupd walk-up wNumeric property-not= ] }
237         { check-number-before
238             [ dupd walk-down wNumeric property-not= ] }
239     } case ;
240
241 :: word-break-next ( old-class new-char i str -- next-class ? )
242     new-char format/extended?
243     [ old-class dup { 1 2 3 } member? ] [
244         new-char word-break-prop old-class over word-table-nth
245         [ str i ] dip word-break?
246     ] if ;
247
248 PRIVATE>
249
250 : first-word ( str -- i )
251     [ unclip-slice word-break-prop over <enum> ] keep
252     '[ swap _ word-break-next ] assoc-find 2drop
253     nip swap length or 1 + ;
254
255 : >words ( str -- words )
256     [ first-word ] >pieces ;
257
258 <PRIVATE
259
260 : nth-next ( i str -- str[i-1] str[i] )
261     [ [ 1 - ] keep ] dip '[ _ nth ] bi@ ;
262
263 PRIVATE>
264
265 : word-break-at? ( i str -- ? )
266     {
267         [ drop zero? ]
268         [ length = ]
269         [
270             [ nth-next [ word-break-prop ] dip ] 2keep
271             word-break-next nip
272         ]
273     } 2|| ;
274
275 : first-word-from ( start str -- i )
276     over tail-slice first-word + ;
277
278 : last-word ( str -- i )
279     [ length iota ] keep '[ _ word-break-at? ] find-last drop 0 or ;
280
281 : last-word-from ( end str -- i )
282     swap head-slice last-word ;