• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4 *******************************************************************************
5 * Copyright (C) 2003-2011, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9 package com.ibm.icu.text;
10 
11 import java.io.IOException;
12 import java.io.OutputStream;
13 import java.util.ArrayList;
14 import java.util.List;
15 
16 import com.ibm.icu.impl.Assert;
17 import com.ibm.icu.impl.Trie2Writable;
18 import com.ibm.icu.impl.Trie2_16;
19 import com.ibm.icu.text.RBBIRuleBuilder.IntPair;
20 
21 //
22 //  RBBISetBuilder   Handles processing of Unicode Sets from RBBI rules
23 //                   (part of the rule building process.)
24 //
25 //      Starting with the rules parse tree from the scanner,
26 //
27 //                   -  Enumerate the set of UnicodeSets that are referenced
28 //                      by the RBBI rules.
29 //                   -  compute a set of non-overlapping character ranges
30 //                      with all characters within a range belonging to the same
31 //                      set of input uniocde sets.
32 //                   -  Derive a set of non-overlapping UnicodeSet (like things)
33 //                      that will correspond to columns in the state table for
34 //                      the RBBI execution engine.  All characters within one
35 //                      of these sets belong to the same set of the original
36 //                      UnicodeSets from the user's rules.
37 //                   -  construct the trie table that maps input characters
38 //                      to the index of the matching non-overlapping set of set from
39 //                      the previous step.
40 //
41 class RBBISetBuilder {
42     static class RangeDescriptor  {
43            int                fStartChar;      // Start of range, unicode 32 bit value.
44            int                fEndChar;        // End of range, unicode 32 bit value.
45            int                fNum;            // runtime-mapped input value for this range.
46            List<RBBINode>     fIncludesSets;    // vector of the the original
47                                                  //   Unicode sets that include this range.
48                                                 //    (Contains ptrs to uset nodes)
49             RangeDescriptor   fNext;           // Next RangeDescriptor in the linked list.
50 
RangeDescriptor()51             RangeDescriptor() {
52                 fIncludesSets = new ArrayList<RBBINode>();
53             }
54 
RangeDescriptor(RangeDescriptor other)55             RangeDescriptor(RangeDescriptor other) {
56                 fStartChar = other.fStartChar;
57                 fEndChar   = other.fEndChar;
58                 fNum       = other.fNum;
59                 fIncludesSets = new ArrayList<RBBINode>(other.fIncludesSets);
60             }
61 
62             //-------------------------------------------------------------------------------------
63             //
64             //          RangeDesriptor::split()
65             //
66             //-------------------------------------------------------------------------------------
split(int where)67             void split(int where) {
68                 Assert.assrt(where>fStartChar && where<=fEndChar);
69                 RangeDescriptor nr = new RangeDescriptor(this);
70 
71                 //  RangeDescriptor copy constructor copies all fields.
72                 //  Only need to update those that are different after the split.
73                 nr.fStartChar = where;
74                 this.fEndChar = where-1;
75                 nr.fNext      = this.fNext;
76                 this.fNext    = nr;
77 
78                 // TODO:  fIncludesSets is not updated.  Check it out.
79                 //         Probably because they haven't been populated yet,
80                 //         but still sloppy.
81             }
82 
83 
84             //-------------------------------------------------------------------------------------
85             //
86             //          RangeDescriptor::setDictionaryFlag
87             //
88             //          Character Category Numbers that include characters from
89             //          the original Unicode Set named "dictionary" have bit 14
90             //          set to 1.  The RBBI runtime engine uses this to trigger
91             //          use of the word dictionary.
92             //
93             //          This function looks through the Unicode Sets that it
94             //          (the range) includes, and sets the bit in fNum when
95             //          "dictionary" is among them.
96             //
97             //          TODO:  a faster way would be to find the set node for
98             //          "dictionary" just once, rather than looking it
99             //          up by name every time.
100             //
101             // -------------------------------------------------------------------------------------
setDictionaryFlag()102             void setDictionaryFlag() {
103                 int i;
104 
105                 for (i=0; i<this.fIncludesSets.size(); i++) {
106                     RBBINode        usetNode    = fIncludesSets.get(i);
107                     String          setName = "";
108                     RBBINode        setRef = usetNode.fParent;
109                     if (setRef != null) {
110                         RBBINode varRef = setRef.fParent;
111                         if (varRef != null  &&  varRef.fType == RBBINode.varRef) {
112                             setName = varRef.fText;
113                         }
114                     }
115                     if (setName.equals("dictionary")) {
116                         this.fNum |= DICT_BIT;
117                         break;
118                     }
119                 }
120 
121         }
122     }
123 
124 
125     RBBIRuleBuilder       fRB;             // The RBBI Rule Compiler that owns us.
126     RangeDescriptor       fRangeList;      // Head of the linked list of RangeDescriptors
127 
128     Trie2Writable         fTrie;           // The mapping TRIE that is the end result of processing
129                                            //  the Unicode Sets.
130     Trie2_16              fFrozenTrie;
131 
132     // Groups correspond to character categories -
133     //       groups of ranges that are in the same original UnicodeSets.
134     //       fGroupCount is the index of the last used group.
135     //       fGroupCount+1 is also the number of columns in the RBBI state table being compiled.
136     //       State table column 0 is not used.  Column 1 is for end-of-input.
137     //       column 2 is for group 0.  Funny counting.
138     int                fGroupCount;
139 
140     boolean             fSawBOF;
141 
142     static final int    DICT_BIT = 0x4000;
143 
144 
145     //------------------------------------------------------------------------
146     //
147     //       RBBISetBuilder Constructor
148     //
149     //------------------------------------------------------------------------
RBBISetBuilder(RBBIRuleBuilder rb)150     RBBISetBuilder(RBBIRuleBuilder rb)
151     {
152         fRB             = rb;
153     }
154 
155 
156     //------------------------------------------------------------------------
157     //
158     //           build          Build the list of non-overlapping character ranges
159     //                          from the Unicode Sets.
160     //
161     //------------------------------------------------------------------------
buildRanges()162     void buildRanges() {
163         RangeDescriptor rlRange;
164 
165         if (fRB.fDebugEnv!=null  && fRB.fDebugEnv.indexOf("usets")>=0) {printSets();}
166 
167         //  Initialize the process by creating a single range encompassing all characters
168         //  that is in no sets.
169         //
170         fRangeList               = new RangeDescriptor();
171         fRangeList.fStartChar    = 0;
172         fRangeList.fEndChar      = 0x10ffff;
173 
174         //
175         //  Find the set of non-overlapping ranges of characters
176         //
177         for (RBBINode usetNode : fRB.fUSetNodes) {
178             UnicodeSet      inputSet             = usetNode.fInputSet;
179             int            inputSetRangeCount   = inputSet.getRangeCount();
180             int            inputSetRangeIndex   = 0;
181             rlRange              = fRangeList;
182 
183             for (;;) {
184                 if (inputSetRangeIndex >= inputSetRangeCount) {
185                     break;
186                 }
187                 int      inputSetRangeBegin  = inputSet.getRangeStart(inputSetRangeIndex);
188                 int      inputSetRangeEnd    = inputSet.getRangeEnd(inputSetRangeIndex);
189 
190                 // skip over ranges from the range list that are completely
191                 //   below the current range from the input unicode set.
192                 while (rlRange.fEndChar < inputSetRangeBegin) {
193                     rlRange = rlRange.fNext;
194                 }
195 
196                 // If the start of the range from the range list is before with
197                 //   the start of the range from the unicode set, split the range list range
198                 //   in two, with one part being before (wholly outside of) the unicode set
199                 //   and the other containing the rest.
200                 //   Then continue the loop; the post-split current range will then be skipped
201                 //     over
202                 if (rlRange.fStartChar < inputSetRangeBegin) {
203                     rlRange.split(inputSetRangeBegin);
204                      continue;
205                 }
206 
207                 // Same thing at the end of the ranges...
208                 // If the end of the range from the range list doesn't coincide with
209                 //   the end of the range from the unicode set, split the range list
210                 //   range in two.  The first part of the split range will be
211                 //   wholly inside the Unicode set.
212                 if (rlRange.fEndChar > inputSetRangeEnd) {
213                     rlRange.split(inputSetRangeEnd+1);
214                  }
215 
216                 // The current rlRange is now entirely within the UnicodeSet range.
217                 // Add this unicode set to the list of sets for this rlRange
218                 if (rlRange.fIncludesSets.indexOf(usetNode) == -1) {
219                     rlRange.fIncludesSets.add(usetNode);
220                 }
221 
222                 // Advance over ranges that we are finished with.
223                 if (inputSetRangeEnd == rlRange.fEndChar) {
224                     inputSetRangeIndex++;
225                 }
226                 rlRange = rlRange.fNext;
227             }
228         }
229 
230         if (fRB.fDebugEnv!=null && fRB.fDebugEnv.indexOf("range")>=0) { printRanges();}
231 
232         //
233         //  Group the above ranges, with each group consisting of one or more
234         //    ranges that are in exactly the same set of original UnicodeSets.
235         //    The groups are numbered, and these group numbers are the set of
236         //    input symbols recognized by the run-time state machine.
237         //
238         //    Numbering: # 0  (state table column 0) is unused.
239         //               # 1  is reserved - table column 1 is for end-of-input
240         //               # 2  is reserved - table column 2 is for beginning-in-input
241         //               # 3  is the first range list.
242         //
243         RangeDescriptor rlSearchRange;
244         for (rlRange = fRangeList; rlRange!=null; rlRange=rlRange.fNext) {
245             for (rlSearchRange=fRangeList; rlSearchRange != rlRange; rlSearchRange=rlSearchRange.fNext) {
246                 if (rlRange.fIncludesSets.equals(rlSearchRange.fIncludesSets)) {
247                     rlRange.fNum = rlSearchRange.fNum;
248                     break;
249                 }
250             }
251             if (rlRange.fNum == 0) {
252                 fGroupCount ++;
253                 rlRange.fNum = fGroupCount+2;
254                 rlRange.setDictionaryFlag();
255                 addValToSets(rlRange.fIncludesSets, fGroupCount+2);
256             }
257         }
258 
259         // Handle input sets that contain the special string {eof}.
260         //   Column 1 of the state table is reserved for EOF on input.
261         //   Column 2 is reserved for before-the-start-input.
262         //            (This column can be optimized away later if there are no rule
263         //             references to {bof}.)
264         //   Add this column value (1 or 2) to the equivalent expression
265         //     subtree for each UnicodeSet that contains the string {eof}
266         //   Because {bof} and {eof} are not a characters in the normal sense,
267         //   they doesn't affect the computation of ranges or TRIE.
268 
269         String eofString = "eof";
270         String bofString = "bof";
271 
272         for (RBBINode usetNode : fRB.fUSetNodes) {
273             UnicodeSet      inputSet = usetNode.fInputSet;
274             if (inputSet.contains(eofString)) {
275                 addValToSet(usetNode, 1);
276             }
277             if (inputSet.contains(bofString)) {
278                 addValToSet(usetNode, 2);
279                 fSawBOF = true;
280             }
281         }
282 
283 
284         if (fRB.fDebugEnv!=null  && fRB.fDebugEnv.indexOf("rgroup")>=0) {printRangeGroups();}
285         if (fRB.fDebugEnv!=null  && fRB.fDebugEnv.indexOf("esets")>=0) {printSets();}
286     }
287 
288 
289     /**
290      * Build the Trie table for mapping UChar32 values to the corresponding
291      * range group number.
292      */
buildTrie()293     void buildTrie() {
294         RangeDescriptor rlRange;
295 
296         fTrie = new Trie2Writable(0,       //   Initial value for all code points.
297                                   0);      //   Error value for out-of-range input.
298 
299         for (rlRange = fRangeList; rlRange!=null; rlRange=rlRange.fNext) {
300             fTrie.setRange(
301                     rlRange.fStartChar,     // Range start
302                     rlRange.fEndChar,       // Range end (inclusive)
303                     rlRange.fNum,           // value for range
304                     true                    // Overwrite previously written values
305                     );
306         }
307     }
308 
309     /**
310      * Merge two character categories that have been identified as having equivalent behavior.
311      * The ranges belonging to the second category (table column) will be added to the first.
312      * @param categories the pair of categories to be merged.
313      */
mergeCategories(IntPair categories)314     void mergeCategories(IntPair categories) {
315         assert(categories.first >= 1);
316         assert(categories.second > categories.first);
317         for (RangeDescriptor rd = fRangeList; rd != null; rd = rd.fNext) {
318             int rangeNum = rd.fNum & ~DICT_BIT;
319             int rangeDict = rd.fNum & DICT_BIT;
320             if (rangeNum == categories.second) {
321                 rd.fNum = categories.first | rangeDict;
322             } else if (rangeNum > categories.second) {
323                 rd.fNum--;
324             }
325         }
326         --fGroupCount;
327     }
328 
329     //-----------------------------------------------------------------------------------
330     //
331     //          getTrieSize()    Return the size that will be required to serialize the Trie.
332     //
333     //-----------------------------------------------------------------------------------
getTrieSize()334     int getTrieSize()  {
335         if (fFrozenTrie == null) {
336             fFrozenTrie = fTrie.toTrie2_16();
337             fTrie = null;
338         }
339         return fFrozenTrie.getSerializedLength();
340     }
341 
342 
343     //-----------------------------------------------------------------------------------
344     //
345     //          serializeTrie()   Write the serialized trie to an output stream
346     //
347     //-----------------------------------------------------------------------------------
serializeTrie(OutputStream os)348     void serializeTrie(OutputStream os) throws IOException {
349         if (fFrozenTrie == null) {
350             fFrozenTrie = fTrie.toTrie2_16();
351             fTrie = null;
352         }
353         fFrozenTrie.serialize(os);
354    }
355 
356     //------------------------------------------------------------------------
357     //
358     //      addValToSets     Add a runtime-mapped input value to each uset from a
359     //      list of uset nodes. (val corresponds to a state table column.)
360     //      For each of the original Unicode sets - which correspond
361     //      directly to uset nodes - a logically equivalent expression
362     //      is constructed in terms of the remapped runtime input
363     //      symbol set.  This function adds one runtime input symbol to
364     //      a list of sets.
365     //
366     //      The "logically equivalent expression" is the tree for an
367     //      or-ing together of all of the symbols that go into the set.
368     //
369     //------------------------------------------------------------------------
addValToSets(List<RBBINode> sets, int val)370     void  addValToSets(List<RBBINode> sets, int val) {
371         for (RBBINode usetNode : sets) {
372             addValToSet(usetNode, val);
373         }
374     }
375 
addValToSet(RBBINode usetNode, int val)376     void  addValToSet(RBBINode usetNode, int val) {
377         RBBINode leafNode = new RBBINode(RBBINode.leafChar);
378         leafNode.fVal = val;
379         if (usetNode.fLeftChild == null) {
380             usetNode.fLeftChild = leafNode;
381             leafNode.fParent    = usetNode;
382         } else {
383             // There are already input symbols present for this set.
384             // Set up an OR node, with the previous stuff as the left child
385             //   and the new value as the right child.
386             RBBINode orNode = new RBBINode(RBBINode.opOr);
387             orNode.fLeftChild  = usetNode.fLeftChild;
388             orNode.fRightChild = leafNode;
389             orNode.fLeftChild.fParent  = orNode;
390             orNode.fRightChild.fParent = orNode;
391             usetNode.fLeftChild = orNode;
392             orNode.fParent = usetNode;
393         }
394     }
395 
396 
397     //------------------------------------------------------------------------
398     //
399     //           getNumCharCategories
400     //
401     //------------------------------------------------------------------------
getNumCharCategories()402     int  getNumCharCategories()  {
403         return fGroupCount + 3;
404     }
405 
406 
407     //------------------------------------------------------------------------
408     //
409     //           sawBOF
410     //
411     //------------------------------------------------------------------------
sawBOF()412     boolean  sawBOF()  {
413         return fSawBOF;
414     }
415 
416 
417     //------------------------------------------------------------------------
418     //
419     //           getFirstChar      Given a runtime RBBI character category, find
420     //                             the first UChar32 that is in the set of chars
421     //                             in the category.
422     //------------------------------------------------------------------------
getFirstChar(int category)423     int  getFirstChar(int category)  {
424         RangeDescriptor   rlRange;
425         int            retVal = -1;
426         for (rlRange = fRangeList; rlRange!=null; rlRange=rlRange.fNext) {
427             if (rlRange.fNum == category) {
428                 retVal = rlRange.fStartChar;
429                 break;
430             }
431         }
432         return retVal;
433     }
434 
435 
436 
437     //------------------------------------------------------------------------
438     //
439     //           printRanges        A debugging function.
440     //                              dump out all of the range definitions.
441     //
442     //------------------------------------------------------------------------
443     ///CLOVER:OFF
printRanges()444     void printRanges() {
445         RangeDescriptor       rlRange;
446         int                    i;
447 
448         System.out.print("\n\n Nonoverlapping Ranges ...\n");
449         for (rlRange = fRangeList; rlRange!=null; rlRange=rlRange.fNext) {
450             System.out.print(" " + rlRange.fNum + "   " + rlRange.fStartChar + "-" + rlRange.fEndChar);
451 
452             for (i=0; i<rlRange.fIncludesSets.size(); i++) {
453                 RBBINode       usetNode    = rlRange.fIncludesSets.get(i);
454                 String         setName = "anon";
455                 RBBINode       setRef = usetNode.fParent;
456                 if (setRef != null) {
457                     RBBINode varRef = setRef.fParent;
458                     if (varRef != null  &&  varRef.fType == RBBINode.varRef) {
459                         setName = varRef.fText;
460                     }
461                 }
462                 System.out.print(setName); System.out.print("  ");
463             }
464             System.out.println("");
465         }
466     }
467     ///CLOVER:ON
468 
469 
470     //------------------------------------------------------------------------
471     //
472     //           printRangeGroups     A debugging function.
473     //                                dump out all of the range groups.
474     //
475     //------------------------------------------------------------------------
476     ///CLOVER:OFF
printRangeGroups()477     void printRangeGroups() {
478         RangeDescriptor       rlRange;
479         RangeDescriptor       tRange;
480         int                    i;
481         int                    lastPrintedGroupNum = 0;
482 
483         System.out.print("\nRanges grouped by Unicode Set Membership...\n");
484         for (rlRange = fRangeList; rlRange!=null; rlRange=rlRange.fNext) {
485             int groupNum = rlRange.fNum & 0xbfff;
486             if (groupNum > lastPrintedGroupNum) {
487                 lastPrintedGroupNum = groupNum;
488                 if (groupNum<10) {System.out.print(" ");}
489                 System.out.print(groupNum + " ");
490 
491                 if ((rlRange.fNum & DICT_BIT) != 0) { System.out.print(" <DICT> ");}
492 
493                 for (i=0; i<rlRange.fIncludesSets.size(); i++) {
494                     RBBINode       usetNode    = rlRange.fIncludesSets.get(i);
495                     String         setName = "anon";
496                     RBBINode       setRef = usetNode.fParent;
497                     if (setRef != null) {
498                         RBBINode varRef = setRef.fParent;
499                         if (varRef != null  &&  varRef.fType == RBBINode.varRef) {
500                             setName = varRef.fText;
501                         }
502                     }
503                     System.out.print(setName); System.out.print(" ");
504                 }
505 
506                 i = 0;
507                 for (tRange = rlRange; tRange != null; tRange = tRange.fNext) {
508                     if (tRange.fNum == rlRange.fNum) {
509                         if (i++ % 5 == 0) {
510                             System.out.print("\n    ");
511                         }
512                         RBBINode.printHex(tRange.fStartChar, -1);
513                         System.out.print("-");
514                         RBBINode.printHex(tRange.fEndChar, 0);
515                     }
516                 }
517                 System.out.print("\n");
518             }
519         }
520         System.out.print("\n");
521     }
522     ///CLOVER:ON
523 
524 
525     //------------------------------------------------------------------------
526     //
527     //           printSets          A debugging function.
528     //                              dump out all of the set definitions.
529     //
530     //------------------------------------------------------------------------
531     ///CLOVER:OFF
printSets()532     void printSets() {
533         int                   i;
534         System.out.print("\n\nUnicode Sets List\n------------------\n");
535         for (i=0; i<fRB.fUSetNodes.size(); i++) {
536             RBBINode        usetNode;
537             RBBINode        setRef;
538             RBBINode        varRef;
539             String          setName;
540 
541             usetNode = fRB.fUSetNodes.get(i);
542 
543             //System.out.print(" " + i + "   ");
544             RBBINode.printInt(2, i);
545             setName = "anonymous";
546             setRef = usetNode.fParent;
547             if (setRef != null) {
548                 varRef = setRef.fParent;
549                 if (varRef != null  &&  varRef.fType == RBBINode.varRef) {
550                     setName = varRef.fText;
551                 }
552             }
553             System.out.print("  " + setName);
554             System.out.print("   ");
555             System.out.print(usetNode.fText);
556             System.out.print("\n");
557             if (usetNode.fLeftChild != null) {
558                 usetNode.fLeftChild.printTree(true);
559             }
560         }
561         System.out.print("\n");
562     }
563     ///CLOVER:ON
564 }
565