]> git.saurik.com Git - apple/icu.git/blob - icuSources/common/uniset_props.cpp
ICU-62109.0.1.tar.gz
[apple/icu.git] / icuSources / common / uniset_props.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 1999-2014, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 * file name: uniset_props.cpp
11 * encoding: UTF-8
12 * tab size: 8 (not used)
13 * indentation:4
14 *
15 * created on: 2004aug25
16 * created by: Markus W. Scherer
17 *
18 * Character property dependent functions moved here from uniset.cpp
19 */
20
21 #include "unicode/utypes.h"
22 #include "unicode/uniset.h"
23 #include "unicode/parsepos.h"
24 #include "unicode/uchar.h"
25 #include "unicode/uscript.h"
26 #include "unicode/symtable.h"
27 #include "unicode/uset.h"
28 #include "unicode/locid.h"
29 #include "unicode/brkiter.h"
30 #include "uset_imp.h"
31 #include "ruleiter.h"
32 #include "cmemory.h"
33 #include "ucln_cmn.h"
34 #include "util.h"
35 #include "uvector.h"
36 #include "uprops.h"
37 #include "propname.h"
38 #include "normalizer2impl.h"
39 #include "ucase.h"
40 #include "ubidi_props.h"
41 #include "uinvchar.h"
42 #include "uprops.h"
43 #include "charstr.h"
44 #include "cstring.h"
45 #include "mutex.h"
46 #include "umutex.h"
47 #include "uassert.h"
48 #include "hash.h"
49
50 U_NAMESPACE_USE
51
52 // initial storage. Must be >= 0
53 // *** same as in uniset.cpp ! ***
54 #define START_EXTRA 16
55
56 // Define UChar constants using hex for EBCDIC compatibility
57 // Used #define to reduce private static exports and memory access time.
58 #define SET_OPEN ((UChar)0x005B) /*[*/
59 #define SET_CLOSE ((UChar)0x005D) /*]*/
60 #define HYPHEN ((UChar)0x002D) /*-*/
61 #define COMPLEMENT ((UChar)0x005E) /*^*/
62 #define COLON ((UChar)0x003A) /*:*/
63 #define BACKSLASH ((UChar)0x005C) /*\*/
64 #define INTERSECTION ((UChar)0x0026) /*&*/
65 #define UPPER_U ((UChar)0x0055) /*U*/
66 #define LOWER_U ((UChar)0x0075) /*u*/
67 #define OPEN_BRACE ((UChar)123) /*{*/
68 #define CLOSE_BRACE ((UChar)125) /*}*/
69 #define UPPER_P ((UChar)0x0050) /*P*/
70 #define LOWER_P ((UChar)0x0070) /*p*/
71 #define UPPER_N ((UChar)78) /*N*/
72 #define EQUALS ((UChar)0x003D) /*=*/
73
74 //static const UChar POSIX_OPEN[] = { SET_OPEN,COLON,0 }; // "[:"
75 static const UChar POSIX_CLOSE[] = { COLON,SET_CLOSE,0 }; // ":]"
76 //static const UChar PERL_OPEN[] = { BACKSLASH,LOWER_P,0 }; // "\\p"
77 //static const UChar PERL_CLOSE[] = { CLOSE_BRACE,0 }; // "}"
78 //static const UChar NAME_OPEN[] = { BACKSLASH,UPPER_N,0 }; // "\\N"
79 static const UChar HYPHEN_RIGHT_BRACE[] = {HYPHEN,SET_CLOSE,0}; /*-]*/
80
81 // Special property set IDs
82 static const char ANY[] = "ANY"; // [\u0000-\U0010FFFF]
83 static const char ASCII[] = "ASCII"; // [\u0000-\u007F]
84 static const char ASSIGNED[] = "Assigned"; // [:^Cn:]
85
86 // Unicode name property alias
87 #define NAME_PROP "na"
88 #define NAME_PROP_LENGTH 2
89
90 /**
91 * Delimiter string used in patterns to close a category reference:
92 * ":]". Example: "[:Lu:]".
93 */
94 //static const UChar CATEGORY_CLOSE[] = {COLON, SET_CLOSE, 0x0000}; /* ":]" */
95
96 // Cached sets ------------------------------------------------------------- ***
97
98 U_CDECL_BEGIN
99 static UBool U_CALLCONV uset_cleanup();
100
101 struct Inclusion {
102 UnicodeSet *fSet;
103 UInitOnce fInitOnce;
104 };
105 static Inclusion gInclusions[UPROPS_SRC_COUNT]; // cached getInclusions()
106
107 static UnicodeSet *uni32Singleton;
108 static icu::UInitOnce uni32InitOnce = U_INITONCE_INITIALIZER;
109
110 //----------------------------------------------------------------
111 // Inclusions list
112 //----------------------------------------------------------------
113
114 // USetAdder implementation
115 // Does not use uset.h to reduce code dependencies
116 static void U_CALLCONV
117 _set_add(USet *set, UChar32 c) {
118 ((UnicodeSet *)set)->add(c);
119 }
120
121 static void U_CALLCONV
122 _set_addRange(USet *set, UChar32 start, UChar32 end) {
123 ((UnicodeSet *)set)->add(start, end);
124 }
125
126 static void U_CALLCONV
127 _set_addString(USet *set, const UChar *str, int32_t length) {
128 ((UnicodeSet *)set)->add(UnicodeString((UBool)(length<0), str, length));
129 }
130
131 /**
132 * Cleanup function for UnicodeSet
133 */
134 static UBool U_CALLCONV uset_cleanup(void) {
135 for(int32_t i = UPROPS_SRC_NONE; i < UPROPS_SRC_COUNT; ++i) {
136 Inclusion &in = gInclusions[i];
137 delete in.fSet;
138 in.fSet = NULL;
139 in.fInitOnce.reset();
140 }
141
142 delete uni32Singleton;
143 uni32Singleton = NULL;
144 uni32InitOnce.reset();
145 return TRUE;
146 }
147
148 U_CDECL_END
149
150 U_NAMESPACE_BEGIN
151
152 /*
153 Reduce excessive reallocation, and make it easier to detect initialization problems.
154 Usually you don't see smaller sets than this for Unicode 5.0.
155 */
156 #define DEFAULT_INCLUSION_CAPACITY 3072
157
158 void U_CALLCONV UnicodeSet_initInclusion(int32_t src, UErrorCode &status) {
159 // This function is invoked only via umtx_initOnce().
160 // This function is a friend of class UnicodeSet.
161
162 U_ASSERT(src >=0 && src<UPROPS_SRC_COUNT);
163 UnicodeSet * &incl = gInclusions[src].fSet;
164 U_ASSERT(incl == NULL);
165
166 incl = new UnicodeSet();
167 if (incl == NULL) {
168 status = U_MEMORY_ALLOCATION_ERROR;
169 return;
170 }
171 USetAdder sa = {
172 (USet *)incl,
173 _set_add,
174 _set_addRange,
175 _set_addString,
176 NULL, // don't need remove()
177 NULL // don't need removeRange()
178 };
179
180 incl->ensureCapacity(DEFAULT_INCLUSION_CAPACITY, status);
181 switch(src) {
182 case UPROPS_SRC_CHAR:
183 uchar_addPropertyStarts(&sa, &status);
184 break;
185 case UPROPS_SRC_PROPSVEC:
186 upropsvec_addPropertyStarts(&sa, &status);
187 break;
188 case UPROPS_SRC_CHAR_AND_PROPSVEC:
189 uchar_addPropertyStarts(&sa, &status);
190 upropsvec_addPropertyStarts(&sa, &status);
191 break;
192 #if !UCONFIG_NO_NORMALIZATION
193 case UPROPS_SRC_CASE_AND_NORM: {
194 const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(status);
195 if(U_SUCCESS(status)) {
196 impl->addPropertyStarts(&sa, status);
197 }
198 ucase_addPropertyStarts(&sa, &status);
199 break;
200 }
201 case UPROPS_SRC_NFC: {
202 const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(status);
203 if(U_SUCCESS(status)) {
204 impl->addPropertyStarts(&sa, status);
205 }
206 break;
207 }
208 case UPROPS_SRC_NFKC: {
209 const Normalizer2Impl *impl=Normalizer2Factory::getNFKCImpl(status);
210 if(U_SUCCESS(status)) {
211 impl->addPropertyStarts(&sa, status);
212 }
213 break;
214 }
215 case UPROPS_SRC_NFKC_CF: {
216 const Normalizer2Impl *impl=Normalizer2Factory::getNFKC_CFImpl(status);
217 if(U_SUCCESS(status)) {
218 impl->addPropertyStarts(&sa, status);
219 }
220 break;
221 }
222 case UPROPS_SRC_NFC_CANON_ITER: {
223 const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(status);
224 if(U_SUCCESS(status)) {
225 impl->addCanonIterPropertyStarts(&sa, status);
226 }
227 break;
228 }
229 #endif
230 case UPROPS_SRC_CASE:
231 ucase_addPropertyStarts(&sa, &status);
232 break;
233 case UPROPS_SRC_BIDI:
234 ubidi_addPropertyStarts(&sa, &status);
235 break;
236 default:
237 status = U_INTERNAL_PROGRAM_ERROR;
238 break;
239 }
240
241 if (U_FAILURE(status)) {
242 delete incl;
243 incl = NULL;
244 return;
245 }
246 // Compact for caching
247 incl->compact();
248 ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup);
249 }
250
251
252
253 const UnicodeSet* UnicodeSet::getInclusions(int32_t src, UErrorCode &status) {
254 U_ASSERT(src >=0 && src<UPROPS_SRC_COUNT);
255 Inclusion &i = gInclusions[src];
256 umtx_initOnce(i.fInitOnce, &UnicodeSet_initInclusion, src, status);
257 return i.fSet;
258 }
259
260 namespace {
261
262 // Cache some sets for other services -------------------------------------- ***
263 void U_CALLCONV createUni32Set(UErrorCode &errorCode) {
264 U_ASSERT(uni32Singleton == NULL);
265 uni32Singleton = new UnicodeSet(UNICODE_STRING_SIMPLE("[:age=3.2:]"), errorCode);
266 if(uni32Singleton==NULL) {
267 errorCode=U_MEMORY_ALLOCATION_ERROR;
268 } else {
269 uni32Singleton->freeze();
270 }
271 ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup);
272 }
273
274
275 U_CFUNC UnicodeSet *
276 uniset_getUnicode32Instance(UErrorCode &errorCode) {
277 umtx_initOnce(uni32InitOnce, &createUni32Set, errorCode);
278 return uni32Singleton;
279 }
280
281 // helper functions for matching of pattern syntax pieces ------------------ ***
282 // these functions are parallel to the PERL_OPEN etc. strings above
283
284 // using these functions is not only faster than UnicodeString::compare() and
285 // caseCompare(), but they also make UnicodeSet work for simple patterns when
286 // no Unicode properties data is available - when caseCompare() fails
287
288 static inline UBool
289 isPerlOpen(const UnicodeString &pattern, int32_t pos) {
290 UChar c;
291 return pattern.charAt(pos)==BACKSLASH && ((c=pattern.charAt(pos+1))==LOWER_P || c==UPPER_P);
292 }
293
294 /*static inline UBool
295 isPerlClose(const UnicodeString &pattern, int32_t pos) {
296 return pattern.charAt(pos)==CLOSE_BRACE;
297 }*/
298
299 static inline UBool
300 isNameOpen(const UnicodeString &pattern, int32_t pos) {
301 return pattern.charAt(pos)==BACKSLASH && pattern.charAt(pos+1)==UPPER_N;
302 }
303
304 static inline UBool
305 isPOSIXOpen(const UnicodeString &pattern, int32_t pos) {
306 return pattern.charAt(pos)==SET_OPEN && pattern.charAt(pos+1)==COLON;
307 }
308
309 /*static inline UBool
310 isPOSIXClose(const UnicodeString &pattern, int32_t pos) {
311 return pattern.charAt(pos)==COLON && pattern.charAt(pos+1)==SET_CLOSE;
312 }*/
313
314 // TODO memory debugging provided inside uniset.cpp
315 // could be made available here but probably obsolete with use of modern
316 // memory leak checker tools
317 #define _dbgct(me)
318
319 } // namespace
320
321 //----------------------------------------------------------------
322 // Constructors &c
323 //----------------------------------------------------------------
324
325 /**
326 * Constructs a set from the given pattern, optionally ignoring
327 * white space. See the class description for the syntax of the
328 * pattern language.
329 * @param pattern a string specifying what characters are in the set
330 */
331 UnicodeSet::UnicodeSet(const UnicodeString& pattern,
332 UErrorCode& status) :
333 len(0), capacity(START_EXTRA), list(0), bmpSet(0), buffer(0),
334 bufferCapacity(0), patLen(0), pat(NULL), strings(NULL), stringSpan(NULL),
335 fFlags(0)
336 {
337 if(U_SUCCESS(status)){
338 list = (UChar32*) uprv_malloc(sizeof(UChar32) * capacity);
339 /* test for NULL */
340 if(list == NULL) {
341 status = U_MEMORY_ALLOCATION_ERROR;
342 }else{
343 allocateStrings(status);
344 applyPattern(pattern, status);
345 }
346 }
347 _dbgct(this);
348 }
349
350 //----------------------------------------------------------------
351 // Public API
352 //----------------------------------------------------------------
353
354 UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern,
355 UErrorCode& status) {
356 // Equivalent to
357 // return applyPattern(pattern, USET_IGNORE_SPACE, NULL, status);
358 // but without dependency on closeOver().
359 ParsePosition pos(0);
360 applyPatternIgnoreSpace(pattern, pos, NULL, status);
361 if (U_FAILURE(status)) return *this;
362
363 int32_t i = pos.getIndex();
364 // Skip over trailing whitespace
365 ICU_Utility::skipWhitespace(pattern, i, TRUE);
366 if (i != pattern.length()) {
367 status = U_ILLEGAL_ARGUMENT_ERROR;
368 }
369 return *this;
370 }
371
372 void
373 UnicodeSet::applyPatternIgnoreSpace(const UnicodeString& pattern,
374 ParsePosition& pos,
375 const SymbolTable* symbols,
376 UErrorCode& status) {
377 if (U_FAILURE(status)) {
378 return;
379 }
380 if (isFrozen()) {
381 status = U_NO_WRITE_PERMISSION;
382 return;
383 }
384 // Need to build the pattern in a temporary string because
385 // _applyPattern calls add() etc., which set pat to empty.
386 UnicodeString rebuiltPat;
387 RuleCharacterIterator chars(pattern, symbols, pos);
388 applyPattern(chars, symbols, rebuiltPat, USET_IGNORE_SPACE, NULL, 0, status);
389 if (U_FAILURE(status)) return;
390 if (chars.inVariable()) {
391 // syntaxError(chars, "Extra chars in variable value");
392 status = U_MALFORMED_SET;
393 return;
394 }
395 setPattern(rebuiltPat);
396 }
397
398 /**
399 * Return true if the given position, in the given pattern, appears
400 * to be the start of a UnicodeSet pattern.
401 */
402 UBool UnicodeSet::resemblesPattern(const UnicodeString& pattern, int32_t pos) {
403 return ((pos+1) < pattern.length() &&
404 pattern.charAt(pos) == (UChar)91/*[*/) ||
405 resemblesPropertyPattern(pattern, pos);
406 }
407
408 //----------------------------------------------------------------
409 // Implementation: Pattern parsing
410 //----------------------------------------------------------------
411
412 namespace {
413
414 /**
415 * A small all-inline class to manage a UnicodeSet pointer. Add
416 * operator->() etc. as needed.
417 */
418 class UnicodeSetPointer {
419 UnicodeSet* p;
420 public:
421 inline UnicodeSetPointer() : p(0) {}
422 inline ~UnicodeSetPointer() { delete p; }
423 inline UnicodeSet* pointer() { return p; }
424 inline UBool allocate() {
425 if (p == 0) {
426 p = new UnicodeSet();
427 }
428 return p != 0;
429 }
430 };
431
432 constexpr int32_t MAX_DEPTH = 100;
433
434 } // namespace
435
436 /**
437 * Parse the pattern from the given RuleCharacterIterator. The
438 * iterator is advanced over the parsed pattern.
439 * @param chars iterator over the pattern characters. Upon return
440 * it will be advanced to the first character after the parsed
441 * pattern, or the end of the iteration if all characters are
442 * parsed.
443 * @param symbols symbol table to use to parse and dereference
444 * variables, or null if none.
445 * @param rebuiltPat the pattern that was parsed, rebuilt or
446 * copied from the input pattern, as appropriate.
447 * @param options a bit mask of zero or more of the following:
448 * IGNORE_SPACE, CASE.
449 */
450 void UnicodeSet::applyPattern(RuleCharacterIterator& chars,
451 const SymbolTable* symbols,
452 UnicodeString& rebuiltPat,
453 uint32_t options,
454 UnicodeSet& (UnicodeSet::*caseClosure)(int32_t attribute),
455 int32_t depth,
456 UErrorCode& ec) {
457 if (U_FAILURE(ec)) return;
458 if (depth > MAX_DEPTH) {
459 ec = U_ILLEGAL_ARGUMENT_ERROR;
460 return;
461 }
462
463 // Syntax characters: [ ] ^ - & { }
464
465 // Recognized special forms for chars, sets: c-c s-s s&s
466
467 int32_t opts = RuleCharacterIterator::PARSE_VARIABLES |
468 RuleCharacterIterator::PARSE_ESCAPES;
469 if ((options & USET_IGNORE_SPACE) != 0) {
470 opts |= RuleCharacterIterator::SKIP_WHITESPACE;
471 }
472
473 UnicodeString patLocal, buf;
474 UBool usePat = FALSE;
475 UnicodeSetPointer scratch;
476 RuleCharacterIterator::Pos backup;
477
478 // mode: 0=before [, 1=between [...], 2=after ]
479 // lastItem: 0=none, 1=char, 2=set
480 int8_t lastItem = 0, mode = 0;
481 UChar32 lastChar = 0;
482 UChar op = 0;
483
484 UBool invert = FALSE;
485
486 clear();
487
488 while (mode != 2 && !chars.atEnd()) {
489 U_ASSERT((lastItem == 0 && op == 0) ||
490 (lastItem == 1 && (op == 0 || op == HYPHEN /*'-'*/)) ||
491 (lastItem == 2 && (op == 0 || op == HYPHEN /*'-'*/ ||
492 op == INTERSECTION /*'&'*/)));
493
494 UChar32 c = 0;
495 UBool literal = FALSE;
496 UnicodeSet* nested = 0; // alias - do not delete
497
498 // -------- Check for property pattern
499
500 // setMode: 0=none, 1=unicodeset, 2=propertypat, 3=preparsed
501 int8_t setMode = 0;
502 if (resemblesPropertyPattern(chars, opts)) {
503 setMode = 2;
504 }
505
506 // -------- Parse '[' of opening delimiter OR nested set.
507 // If there is a nested set, use `setMode' to define how
508 // the set should be parsed. If the '[' is part of the
509 // opening delimiter for this pattern, parse special
510 // strings "[", "[^", "[-", and "[^-". Check for stand-in
511 // characters representing a nested set in the symbol
512 // table.
513
514 else {
515 // Prepare to backup if necessary
516 chars.getPos(backup);
517 c = chars.next(opts, literal, ec);
518 if (U_FAILURE(ec)) return;
519
520 if (c == 0x5B /*'['*/ && !literal) {
521 if (mode == 1) {
522 chars.setPos(backup); // backup
523 setMode = 1;
524 } else {
525 // Handle opening '[' delimiter
526 mode = 1;
527 patLocal.append((UChar) 0x5B /*'['*/);
528 chars.getPos(backup); // prepare to backup
529 c = chars.next(opts, literal, ec);
530 if (U_FAILURE(ec)) return;
531 if (c == 0x5E /*'^'*/ && !literal) {
532 invert = TRUE;
533 patLocal.append((UChar) 0x5E /*'^'*/);
534 chars.getPos(backup); // prepare to backup
535 c = chars.next(opts, literal, ec);
536 if (U_FAILURE(ec)) return;
537 }
538 // Fall through to handle special leading '-';
539 // otherwise restart loop for nested [], \p{}, etc.
540 if (c == HYPHEN /*'-'*/) {
541 literal = TRUE;
542 // Fall through to handle literal '-' below
543 } else {
544 chars.setPos(backup); // backup
545 continue;
546 }
547 }
548 } else if (symbols != 0) {
549 const UnicodeFunctor *m = symbols->lookupMatcher(c);
550 if (m != 0) {
551 const UnicodeSet *ms = dynamic_cast<const UnicodeSet *>(m);
552 if (ms == NULL) {
553 ec = U_MALFORMED_SET;
554 return;
555 }
556 // casting away const, but `nested' won't be modified
557 // (important not to modify stored set)
558 nested = const_cast<UnicodeSet*>(ms);
559 setMode = 3;
560 }
561 }
562 }
563
564 // -------- Handle a nested set. This either is inline in
565 // the pattern or represented by a stand-in that has
566 // previously been parsed and was looked up in the symbol
567 // table.
568
569 if (setMode != 0) {
570 if (lastItem == 1) {
571 if (op != 0) {
572 // syntaxError(chars, "Char expected after operator");
573 ec = U_MALFORMED_SET;
574 return;
575 }
576 add(lastChar, lastChar);
577 _appendToPat(patLocal, lastChar, FALSE);
578 lastItem = 0;
579 op = 0;
580 }
581
582 if (op == HYPHEN /*'-'*/ || op == INTERSECTION /*'&'*/) {
583 patLocal.append(op);
584 }
585
586 if (nested == 0) {
587 // lazy allocation
588 if (!scratch.allocate()) {
589 ec = U_MEMORY_ALLOCATION_ERROR;
590 return;
591 }
592 nested = scratch.pointer();
593 }
594 switch (setMode) {
595 case 1:
596 nested->applyPattern(chars, symbols, patLocal, options, caseClosure, depth + 1, ec);
597 break;
598 case 2:
599 chars.skipIgnored(opts);
600 nested->applyPropertyPattern(chars, patLocal, ec);
601 if (U_FAILURE(ec)) return;
602 break;
603 case 3: // `nested' already parsed
604 nested->_toPattern(patLocal, FALSE);
605 break;
606 }
607
608 usePat = TRUE;
609
610 if (mode == 0) {
611 // Entire pattern is a category; leave parse loop
612 *this = *nested;
613 mode = 2;
614 break;
615 }
616
617 switch (op) {
618 case HYPHEN: /*'-'*/
619 removeAll(*nested);
620 break;
621 case INTERSECTION: /*'&'*/
622 retainAll(*nested);
623 break;
624 case 0:
625 addAll(*nested);
626 break;
627 }
628
629 op = 0;
630 lastItem = 2;
631
632 continue;
633 }
634
635 if (mode == 0) {
636 // syntaxError(chars, "Missing '['");
637 ec = U_MALFORMED_SET;
638 return;
639 }
640
641 // -------- Parse special (syntax) characters. If the
642 // current character is not special, or if it is escaped,
643 // then fall through and handle it below.
644
645 if (!literal) {
646 switch (c) {
647 case 0x5D /*']'*/:
648 if (lastItem == 1) {
649 add(lastChar, lastChar);
650 _appendToPat(patLocal, lastChar, FALSE);
651 }
652 // Treat final trailing '-' as a literal
653 if (op == HYPHEN /*'-'*/) {
654 add(op, op);
655 patLocal.append(op);
656 } else if (op == INTERSECTION /*'&'*/) {
657 // syntaxError(chars, "Trailing '&'");
658 ec = U_MALFORMED_SET;
659 return;
660 }
661 patLocal.append((UChar) 0x5D /*']'*/);
662 mode = 2;
663 continue;
664 case HYPHEN /*'-'*/:
665 if (op == 0) {
666 if (lastItem != 0) {
667 op = (UChar) c;
668 continue;
669 } else {
670 // Treat final trailing '-' as a literal
671 add(c, c);
672 c = chars.next(opts, literal, ec);
673 if (U_FAILURE(ec)) return;
674 if (c == 0x5D /*']'*/ && !literal) {
675 patLocal.append(HYPHEN_RIGHT_BRACE, 2);
676 mode = 2;
677 continue;
678 }
679 }
680 }
681 // syntaxError(chars, "'-' not after char or set");
682 ec = U_MALFORMED_SET;
683 return;
684 case INTERSECTION /*'&'*/:
685 if (lastItem == 2 && op == 0) {
686 op = (UChar) c;
687 continue;
688 }
689 // syntaxError(chars, "'&' not after set");
690 ec = U_MALFORMED_SET;
691 return;
692 case 0x5E /*'^'*/:
693 // syntaxError(chars, "'^' not after '['");
694 ec = U_MALFORMED_SET;
695 return;
696 case 0x7B /*'{'*/:
697 if (op != 0) {
698 // syntaxError(chars, "Missing operand after operator");
699 ec = U_MALFORMED_SET;
700 return;
701 }
702 if (lastItem == 1) {
703 add(lastChar, lastChar);
704 _appendToPat(patLocal, lastChar, FALSE);
705 }
706 lastItem = 0;
707 buf.truncate(0);
708 {
709 UBool ok = FALSE;
710 while (!chars.atEnd()) {
711 c = chars.next(opts, literal, ec);
712 if (U_FAILURE(ec)) return;
713 if (c == 0x7D /*'}'*/ && !literal) {
714 ok = TRUE;
715 break;
716 }
717 buf.append(c);
718 }
719 if (buf.length() < 1 || !ok) {
720 // syntaxError(chars, "Invalid multicharacter string");
721 ec = U_MALFORMED_SET;
722 return;
723 }
724 }
725 // We have new string. Add it to set and continue;
726 // we don't need to drop through to the further
727 // processing
728 add(buf);
729 patLocal.append((UChar) 0x7B /*'{'*/);
730 _appendToPat(patLocal, buf, FALSE);
731 patLocal.append((UChar) 0x7D /*'}'*/);
732 continue;
733 case SymbolTable::SYMBOL_REF:
734 // symbols nosymbols
735 // [a-$] error error (ambiguous)
736 // [a$] anchor anchor
737 // [a-$x] var "x"* literal '$'
738 // [a-$.] error literal '$'
739 // *We won't get here in the case of var "x"
740 {
741 chars.getPos(backup);
742 c = chars.next(opts, literal, ec);
743 if (U_FAILURE(ec)) return;
744 UBool anchor = (c == 0x5D /*']'*/ && !literal);
745 if (symbols == 0 && !anchor) {
746 c = SymbolTable::SYMBOL_REF;
747 chars.setPos(backup);
748 break; // literal '$'
749 }
750 if (anchor && op == 0) {
751 if (lastItem == 1) {
752 add(lastChar, lastChar);
753 _appendToPat(patLocal, lastChar, FALSE);
754 }
755 add(U_ETHER);
756 usePat = TRUE;
757 patLocal.append((UChar) SymbolTable::SYMBOL_REF);
758 patLocal.append((UChar) 0x5D /*']'*/);
759 mode = 2;
760 continue;
761 }
762 // syntaxError(chars, "Unquoted '$'");
763 ec = U_MALFORMED_SET;
764 return;
765 }
766 default:
767 break;
768 }
769 }
770
771 // -------- Parse literal characters. This includes both
772 // escaped chars ("\u4E01") and non-syntax characters
773 // ("a").
774
775 switch (lastItem) {
776 case 0:
777 lastItem = 1;
778 lastChar = c;
779 break;
780 case 1:
781 if (op == HYPHEN /*'-'*/) {
782 if (lastChar >= c) {
783 // Don't allow redundant (a-a) or empty (b-a) ranges;
784 // these are most likely typos.
785 // syntaxError(chars, "Invalid range");
786 ec = U_MALFORMED_SET;
787 return;
788 }
789 add(lastChar, c);
790 _appendToPat(patLocal, lastChar, FALSE);
791 patLocal.append(op);
792 _appendToPat(patLocal, c, FALSE);
793 lastItem = 0;
794 op = 0;
795 } else {
796 add(lastChar, lastChar);
797 _appendToPat(patLocal, lastChar, FALSE);
798 lastChar = c;
799 }
800 break;
801 case 2:
802 if (op != 0) {
803 // syntaxError(chars, "Set expected after operator");
804 ec = U_MALFORMED_SET;
805 return;
806 }
807 lastChar = c;
808 lastItem = 1;
809 break;
810 }
811 }
812
813 if (mode != 2) {
814 // syntaxError(chars, "Missing ']'");
815 ec = U_MALFORMED_SET;
816 return;
817 }
818
819 chars.skipIgnored(opts);
820
821 /**
822 * Handle global flags (invert, case insensitivity). If this
823 * pattern should be compiled case-insensitive, then we need
824 * to close over case BEFORE COMPLEMENTING. This makes
825 * patterns like /[^abc]/i work.
826 */
827 if ((options & USET_CASE_INSENSITIVE) != 0) {
828 (this->*caseClosure)(USET_CASE_INSENSITIVE);
829 }
830 else if ((options & USET_ADD_CASE_MAPPINGS) != 0) {
831 (this->*caseClosure)(USET_ADD_CASE_MAPPINGS);
832 }
833 if (invert) {
834 complement();
835 }
836
837 // Use the rebuilt pattern (patLocal) only if necessary. Prefer the
838 // generated pattern.
839 if (usePat) {
840 rebuiltPat.append(patLocal);
841 } else {
842 _generatePattern(rebuiltPat, FALSE);
843 }
844 if (isBogus() && U_SUCCESS(ec)) {
845 // We likely ran out of memory. AHHH!
846 ec = U_MEMORY_ALLOCATION_ERROR;
847 }
848 }
849
850 //----------------------------------------------------------------
851 // Property set implementation
852 //----------------------------------------------------------------
853
854 namespace {
855
856 static UBool numericValueFilter(UChar32 ch, void* context) {
857 return u_getNumericValue(ch) == *(double*)context;
858 }
859
860 static UBool generalCategoryMaskFilter(UChar32 ch, void* context) {
861 int32_t value = *(int32_t*)context;
862 return (U_GET_GC_MASK((UChar32) ch) & value) != 0;
863 }
864
865 static UBool versionFilter(UChar32 ch, void* context) {
866 static const UVersionInfo none = { 0, 0, 0, 0 };
867 UVersionInfo v;
868 u_charAge(ch, v);
869 UVersionInfo* version = (UVersionInfo*)context;
870 return uprv_memcmp(&v, &none, sizeof(v)) > 0 && uprv_memcmp(&v, version, sizeof(v)) <= 0;
871 }
872
873 typedef struct {
874 UProperty prop;
875 int32_t value;
876 } IntPropertyContext;
877
878 static UBool intPropertyFilter(UChar32 ch, void* context) {
879 IntPropertyContext* c = (IntPropertyContext*)context;
880 return u_getIntPropertyValue((UChar32) ch, c->prop) == c->value;
881 }
882
883 static UBool scriptExtensionsFilter(UChar32 ch, void* context) {
884 return uscript_hasScript(ch, *(UScriptCode*)context);
885 }
886
887 } // namespace
888
889 /**
890 * Generic filter-based scanning code for UCD property UnicodeSets.
891 */
892 void UnicodeSet::applyFilter(UnicodeSet::Filter filter,
893 void* context,
894 int32_t src,
895 UErrorCode &status) {
896 if (U_FAILURE(status)) return;
897
898 // Logically, walk through all Unicode characters, noting the start
899 // and end of each range for which filter.contain(c) is
900 // true. Add each range to a set.
901 //
902 // To improve performance, use an inclusions set which
903 // encodes information about character ranges that are known
904 // to have identical properties.
905 // getInclusions(src) contains exactly the first characters of
906 // same-value ranges for the given properties "source".
907 const UnicodeSet* inclusions = getInclusions(src, status);
908 if (U_FAILURE(status)) {
909 return;
910 }
911
912 clear();
913
914 UChar32 startHasProperty = -1;
915 int32_t limitRange = inclusions->getRangeCount();
916
917 for (int j=0; j<limitRange; ++j) {
918 // get current range
919 UChar32 start = inclusions->getRangeStart(j);
920 UChar32 end = inclusions->getRangeEnd(j);
921
922 // for all the code points in the range, process
923 for (UChar32 ch = start; ch <= end; ++ch) {
924 // only add to this UnicodeSet on inflection points --
925 // where the hasProperty value changes to false
926 if ((*filter)(ch, context)) {
927 if (startHasProperty < 0) {
928 startHasProperty = ch;
929 }
930 } else if (startHasProperty >= 0) {
931 add(startHasProperty, ch-1);
932 startHasProperty = -1;
933 }
934 }
935 }
936 if (startHasProperty >= 0) {
937 add((UChar32)startHasProperty, (UChar32)0x10FFFF);
938 }
939 if (isBogus() && U_SUCCESS(status)) {
940 // We likely ran out of memory. AHHH!
941 status = U_MEMORY_ALLOCATION_ERROR;
942 }
943 }
944
945 namespace {
946
947 static UBool mungeCharName(char* dst, const char* src, int32_t dstCapacity) {
948 /* Note: we use ' ' in compiler code page */
949 int32_t j = 0;
950 char ch;
951 --dstCapacity; /* make room for term. zero */
952 while ((ch = *src++) != 0) {
953 if (ch == ' ' && (j==0 || (j>0 && dst[j-1]==' '))) {
954 continue;
955 }
956 if (j >= dstCapacity) return FALSE;
957 dst[j++] = ch;
958 }
959 if (j > 0 && dst[j-1] == ' ') --j;
960 dst[j] = 0;
961 return TRUE;
962 }
963
964 } // namespace
965
966 //----------------------------------------------------------------
967 // Property set API
968 //----------------------------------------------------------------
969
970 #define FAIL(ec) {ec=U_ILLEGAL_ARGUMENT_ERROR; return *this;}
971
972 UnicodeSet&
973 UnicodeSet::applyIntPropertyValue(UProperty prop, int32_t value, UErrorCode& ec) {
974 if (U_FAILURE(ec) || isFrozen()) return *this;
975
976 if (prop == UCHAR_GENERAL_CATEGORY_MASK) {
977 applyFilter(generalCategoryMaskFilter, &value, UPROPS_SRC_CHAR, ec);
978 } else if (prop == UCHAR_SCRIPT_EXTENSIONS) {
979 UScriptCode script = (UScriptCode)value;
980 applyFilter(scriptExtensionsFilter, &script, UPROPS_SRC_PROPSVEC, ec);
981 } else {
982 IntPropertyContext c = {prop, value};
983 applyFilter(intPropertyFilter, &c, uprops_getSource(prop), ec);
984 }
985 return *this;
986 }
987
988 UnicodeSet&
989 UnicodeSet::applyPropertyAlias(const UnicodeString& prop,
990 const UnicodeString& value,
991 UErrorCode& ec) {
992 if (U_FAILURE(ec) || isFrozen()) return *this;
993
994 // prop and value used to be converted to char * using the default
995 // converter instead of the invariant conversion.
996 // This should not be necessary because all Unicode property and value
997 // names use only invariant characters.
998 // If there are any variant characters, then we won't find them anyway.
999 // Checking first avoids assertion failures in the conversion.
1000 if( !uprv_isInvariantUString(prop.getBuffer(), prop.length()) ||
1001 !uprv_isInvariantUString(value.getBuffer(), value.length())
1002 ) {
1003 FAIL(ec);
1004 }
1005 CharString pname, vname;
1006 pname.appendInvariantChars(prop, ec);
1007 vname.appendInvariantChars(value, ec);
1008 if (U_FAILURE(ec)) return *this;
1009
1010 UProperty p;
1011 int32_t v;
1012 UBool invert = FALSE;
1013
1014 if (value.length() > 0) {
1015 p = u_getPropertyEnum(pname.data());
1016 if (p == UCHAR_INVALID_CODE) FAIL(ec);
1017
1018 // Treat gc as gcm
1019 if (p == UCHAR_GENERAL_CATEGORY) {
1020 p = UCHAR_GENERAL_CATEGORY_MASK;
1021 }
1022
1023 if ((p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) ||
1024 (p >= UCHAR_INT_START && p < UCHAR_INT_LIMIT) ||
1025 (p >= UCHAR_MASK_START && p < UCHAR_MASK_LIMIT)) {
1026 v = u_getPropertyValueEnum(p, vname.data());
1027 if (v == UCHAR_INVALID_CODE) {
1028 // Handle numeric CCC
1029 if (p == UCHAR_CANONICAL_COMBINING_CLASS ||
1030 p == UCHAR_TRAIL_CANONICAL_COMBINING_CLASS ||
1031 p == UCHAR_LEAD_CANONICAL_COMBINING_CLASS) {
1032 char* end;
1033 double value = uprv_strtod(vname.data(), &end);
1034 // Anything between 0 and 255 is valid even if unused.
1035 // Cast double->int only after range check.
1036 // We catch NaN here because comparing it with both 0 and 255 will be false
1037 // (as are all comparisons with NaN).
1038 if (*end != 0 || !(0 <= value && value <= 255) ||
1039 (v = (int32_t)value) != value) {
1040 // non-integral value or outside 0..255, or trailing junk
1041 FAIL(ec);
1042 }
1043 } else {
1044 FAIL(ec);
1045 }
1046 }
1047 }
1048
1049 else {
1050
1051 switch (p) {
1052 case UCHAR_NUMERIC_VALUE:
1053 {
1054 char* end;
1055 double value = uprv_strtod(vname.data(), &end);
1056 if (*end != 0) {
1057 FAIL(ec);
1058 }
1059 applyFilter(numericValueFilter, &value, UPROPS_SRC_CHAR, ec);
1060 return *this;
1061 }
1062 case UCHAR_NAME:
1063 {
1064 // Must munge name, since u_charFromName() does not do
1065 // 'loose' matching.
1066 char buf[128]; // it suffices that this be > uprv_getMaxCharNameLength
1067 if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec);
1068 UChar32 ch = u_charFromName(U_EXTENDED_CHAR_NAME, buf, &ec);
1069 if (U_SUCCESS(ec)) {
1070 clear();
1071 add(ch);
1072 return *this;
1073 } else {
1074 FAIL(ec);
1075 }
1076 }
1077 case UCHAR_UNICODE_1_NAME:
1078 // ICU 49 deprecates the Unicode_1_Name property APIs.
1079 FAIL(ec);
1080 case UCHAR_AGE:
1081 {
1082 // Must munge name, since u_versionFromString() does not do
1083 // 'loose' matching.
1084 char buf[128];
1085 if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec);
1086 UVersionInfo version;
1087 u_versionFromString(version, buf);
1088 applyFilter(versionFilter, &version, UPROPS_SRC_PROPSVEC, ec);
1089 return *this;
1090 }
1091 case UCHAR_SCRIPT_EXTENSIONS:
1092 v = u_getPropertyValueEnum(UCHAR_SCRIPT, vname.data());
1093 if (v == UCHAR_INVALID_CODE) {
1094 FAIL(ec);
1095 }
1096 // fall through to calling applyIntPropertyValue()
1097 break;
1098 default:
1099 // p is a non-binary, non-enumerated property that we
1100 // don't support (yet).
1101 FAIL(ec);
1102 }
1103 }
1104 }
1105
1106 else {
1107 // value is empty. Interpret as General Category, Script, or
1108 // Binary property.
1109 p = UCHAR_GENERAL_CATEGORY_MASK;
1110 v = u_getPropertyValueEnum(p, pname.data());
1111 if (v == UCHAR_INVALID_CODE) {
1112 p = UCHAR_SCRIPT;
1113 v = u_getPropertyValueEnum(p, pname.data());
1114 if (v == UCHAR_INVALID_CODE) {
1115 p = u_getPropertyEnum(pname.data());
1116 if (p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) {
1117 v = 1;
1118 } else if (0 == uprv_comparePropertyNames(ANY, pname.data())) {
1119 set(MIN_VALUE, MAX_VALUE);
1120 return *this;
1121 } else if (0 == uprv_comparePropertyNames(ASCII, pname.data())) {
1122 set(0, 0x7F);
1123 return *this;
1124 } else if (0 == uprv_comparePropertyNames(ASSIGNED, pname.data())) {
1125 // [:Assigned:]=[:^Cn:]
1126 p = UCHAR_GENERAL_CATEGORY_MASK;
1127 v = U_GC_CN_MASK;
1128 invert = TRUE;
1129 } else {
1130 FAIL(ec);
1131 }
1132 }
1133 }
1134 }
1135
1136 applyIntPropertyValue(p, v, ec);
1137 if(invert) {
1138 complement();
1139 }
1140
1141 if (isBogus() && U_SUCCESS(ec)) {
1142 // We likely ran out of memory. AHHH!
1143 ec = U_MEMORY_ALLOCATION_ERROR;
1144 }
1145 return *this;
1146 }
1147
1148 //----------------------------------------------------------------
1149 // Property set patterns
1150 //----------------------------------------------------------------
1151
1152 /**
1153 * Return true if the given position, in the given pattern, appears
1154 * to be the start of a property set pattern.
1155 */
1156 UBool UnicodeSet::resemblesPropertyPattern(const UnicodeString& pattern,
1157 int32_t pos) {
1158 // Patterns are at least 5 characters long
1159 if ((pos+5) > pattern.length()) {
1160 return FALSE;
1161 }
1162
1163 // Look for an opening [:, [:^, \p, or \P
1164 return isPOSIXOpen(pattern, pos) || isPerlOpen(pattern, pos) || isNameOpen(pattern, pos);
1165 }
1166
1167 /**
1168 * Return true if the given iterator appears to point at a
1169 * property pattern. Regardless of the result, return with the
1170 * iterator unchanged.
1171 * @param chars iterator over the pattern characters. Upon return
1172 * it will be unchanged.
1173 * @param iterOpts RuleCharacterIterator options
1174 */
1175 UBool UnicodeSet::resemblesPropertyPattern(RuleCharacterIterator& chars,
1176 int32_t iterOpts) {
1177 // NOTE: literal will always be FALSE, because we don't parse escapes.
1178 UBool result = FALSE, literal;
1179 UErrorCode ec = U_ZERO_ERROR;
1180 iterOpts &= ~RuleCharacterIterator::PARSE_ESCAPES;
1181 RuleCharacterIterator::Pos pos;
1182 chars.getPos(pos);
1183 UChar32 c = chars.next(iterOpts, literal, ec);
1184 if (c == 0x5B /*'['*/ || c == 0x5C /*'\\'*/) {
1185 UChar32 d = chars.next(iterOpts & ~RuleCharacterIterator::SKIP_WHITESPACE,
1186 literal, ec);
1187 result = (c == 0x5B /*'['*/) ? (d == 0x3A /*':'*/) :
1188 (d == 0x4E /*'N'*/ || d == 0x70 /*'p'*/ || d == 0x50 /*'P'*/);
1189 }
1190 chars.setPos(pos);
1191 return result && U_SUCCESS(ec);
1192 }
1193
1194 /**
1195 * Parse the given property pattern at the given parse position.
1196 */
1197 UnicodeSet& UnicodeSet::applyPropertyPattern(const UnicodeString& pattern,
1198 ParsePosition& ppos,
1199 UErrorCode &ec) {
1200 int32_t pos = ppos.getIndex();
1201
1202 UBool posix = FALSE; // true for [:pat:], false for \p{pat} \P{pat} \N{pat}
1203 UBool isName = FALSE; // true for \N{pat}, o/w false
1204 UBool invert = FALSE;
1205
1206 if (U_FAILURE(ec)) return *this;
1207
1208 // Minimum length is 5 characters, e.g. \p{L}
1209 if ((pos+5) > pattern.length()) {
1210 FAIL(ec);
1211 }
1212
1213 // On entry, ppos should point to one of the following locations:
1214 // Look for an opening [:, [:^, \p, or \P
1215 if (isPOSIXOpen(pattern, pos)) {
1216 posix = TRUE;
1217 pos += 2;
1218 pos = ICU_Utility::skipWhitespace(pattern, pos);
1219 if (pos < pattern.length() && pattern.charAt(pos) == COMPLEMENT) {
1220 ++pos;
1221 invert = TRUE;
1222 }
1223 } else if (isPerlOpen(pattern, pos) || isNameOpen(pattern, pos)) {
1224 UChar c = pattern.charAt(pos+1);
1225 invert = (c == UPPER_P);
1226 isName = (c == UPPER_N);
1227 pos += 2;
1228 pos = ICU_Utility::skipWhitespace(pattern, pos);
1229 if (pos == pattern.length() || pattern.charAt(pos++) != OPEN_BRACE) {
1230 // Syntax error; "\p" or "\P" not followed by "{"
1231 FAIL(ec);
1232 }
1233 } else {
1234 // Open delimiter not seen
1235 FAIL(ec);
1236 }
1237
1238 // Look for the matching close delimiter, either :] or }
1239 int32_t close;
1240 if (posix) {
1241 close = pattern.indexOf(POSIX_CLOSE, 2, pos);
1242 } else {
1243 close = pattern.indexOf(CLOSE_BRACE, pos);
1244 }
1245 if (close < 0) {
1246 // Syntax error; close delimiter missing
1247 FAIL(ec);
1248 }
1249
1250 // Look for an '=' sign. If this is present, we will parse a
1251 // medium \p{gc=Cf} or long \p{GeneralCategory=Format}
1252 // pattern.
1253 int32_t equals = pattern.indexOf(EQUALS, pos);
1254 UnicodeString propName, valueName;
1255 if (equals >= 0 && equals < close && !isName) {
1256 // Equals seen; parse medium/long pattern
1257 pattern.extractBetween(pos, equals, propName);
1258 pattern.extractBetween(equals+1, close, valueName);
1259 }
1260
1261 else {
1262 // Handle case where no '=' is seen, and \N{}
1263 pattern.extractBetween(pos, close, propName);
1264
1265 // Handle \N{name}
1266 if (isName) {
1267 // This is a little inefficient since it means we have to
1268 // parse NAME_PROP back to UCHAR_NAME even though we already
1269 // know it's UCHAR_NAME. If we refactor the API to
1270 // support args of (UProperty, char*) then we can remove
1271 // NAME_PROP and make this a little more efficient.
1272 valueName = propName;
1273 propName = UnicodeString(NAME_PROP, NAME_PROP_LENGTH, US_INV);
1274 }
1275 }
1276
1277 applyPropertyAlias(propName, valueName, ec);
1278
1279 if (U_SUCCESS(ec)) {
1280 if (invert) {
1281 complement();
1282 }
1283
1284 // Move to the limit position after the close delimiter if the
1285 // parse succeeded.
1286 ppos.setIndex(close + (posix ? 2 : 1));
1287 }
1288
1289 return *this;
1290 }
1291
1292 /**
1293 * Parse a property pattern.
1294 * @param chars iterator over the pattern characters. Upon return
1295 * it will be advanced to the first character after the parsed
1296 * pattern, or the end of the iteration if all characters are
1297 * parsed.
1298 * @param rebuiltPat the pattern that was parsed, rebuilt or
1299 * copied from the input pattern, as appropriate.
1300 */
1301 void UnicodeSet::applyPropertyPattern(RuleCharacterIterator& chars,
1302 UnicodeString& rebuiltPat,
1303 UErrorCode& ec) {
1304 if (U_FAILURE(ec)) return;
1305 UnicodeString pattern;
1306 chars.lookahead(pattern);
1307 ParsePosition pos(0);
1308 applyPropertyPattern(pattern, pos, ec);
1309 if (U_FAILURE(ec)) return;
1310 if (pos.getIndex() == 0) {
1311 // syntaxError(chars, "Invalid property pattern");
1312 ec = U_MALFORMED_SET;
1313 return;
1314 }
1315 chars.jumpahead(pos.getIndex());
1316 rebuiltPat.append(pattern, 0, pos.getIndex());
1317 }
1318
1319 U_NAMESPACE_END