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