760e4c2a65
X-SVN-Rev: 20504
1911 lines
79 KiB
C++
1911 lines
79 KiB
C++
/*
|
|
*******************************************************************************
|
|
*
|
|
* Copyright (C) 2001-2006, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
*
|
|
*******************************************************************************
|
|
* file name: ucol_tok.cpp
|
|
* encoding: US-ASCII
|
|
* tab size: 8 (not used)
|
|
* indentation:4
|
|
*
|
|
* created 02/22/2001
|
|
* created by: Vladimir Weinstein
|
|
*
|
|
* This module reads a tailoring rule string and produces a list of
|
|
* tokens that will be turned into collation elements
|
|
*
|
|
*/
|
|
|
|
#include "unicode/utypes.h"
|
|
|
|
#if !UCONFIG_NO_COLLATION
|
|
|
|
#include "unicode/ustring.h"
|
|
#include "unicode/uchar.h"
|
|
#include "unicode/uniset.h"
|
|
|
|
#include "ucol_tok.h"
|
|
#include "cmemory.h"
|
|
#include "util.h"
|
|
|
|
U_CDECL_BEGIN
|
|
static int32_t U_CALLCONV
|
|
uhash_hashTokens(const UHashTok k)
|
|
{
|
|
int32_t hash = 0;
|
|
//uint32_t key = (uint32_t)k.integer;
|
|
UColToken *key = (UColToken *)k.pointer;
|
|
if (key != 0) {
|
|
//int32_t len = (key & 0xFF000000)>>24;
|
|
int32_t len = (key->source & 0xFF000000)>>24;
|
|
int32_t inc = ((len - 32) / 32) + 1;
|
|
|
|
//const UChar *p = (key & 0x00FFFFFF) + rulesToParse;
|
|
const UChar *p = (key->source & 0x00FFFFFF) + key->rulesToParse;
|
|
const UChar *limit = p + len;
|
|
|
|
while (p<limit) {
|
|
hash = (hash * 37) + *p;
|
|
p += inc;
|
|
}
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
static UBool U_CALLCONV
|
|
uhash_compareTokens(const UHashTok key1, const UHashTok key2)
|
|
{
|
|
//uint32_t p1 = (uint32_t) key1.integer;
|
|
//uint32_t p2 = (uint32_t) key2.integer;
|
|
UColToken *p1 = (UColToken *)key1.pointer;
|
|
UColToken *p2 = (UColToken *)key2.pointer;
|
|
const UChar *s1 = (p1->source & 0x00FFFFFF) + p1->rulesToParse;
|
|
const UChar *s2 = (p2->source & 0x00FFFFFF) + p2->rulesToParse;
|
|
uint32_t s1L = ((p1->source & 0xFF000000) >> 24);
|
|
uint32_t s2L = ((p2->source & 0xFF000000) >> 24);
|
|
const UChar *end = s1+s1L-1;
|
|
|
|
if (p1 == p2) {
|
|
return TRUE;
|
|
}
|
|
if (p1->source == 0 || p2->source == 0) {
|
|
return FALSE;
|
|
}
|
|
if(s1L != s2L) {
|
|
return FALSE;
|
|
}
|
|
if(p1->source == p2->source) {
|
|
return TRUE;
|
|
}
|
|
while((s1 < end) && *s1 == *s2) {
|
|
++s1;
|
|
++s2;
|
|
}
|
|
if(*s1 == *s2) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
U_CDECL_END
|
|
|
|
static inline void U_CALLCONV
|
|
uhash_freeBlockWrapper(void *obj) {
|
|
uhash_freeBlock(obj);
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
uint32_t startCE;
|
|
uint32_t startContCE;
|
|
uint32_t limitCE;
|
|
uint32_t limitContCE;
|
|
} indirectBoundaries;
|
|
|
|
/* these values are used for finding CE values for indirect positioning. */
|
|
/* Indirect positioning is a mechanism for allowing resets on symbolic */
|
|
/* values. It only works for resets and you cannot tailor indirect names */
|
|
/* An indirect name can define either an anchor point or a range. An */
|
|
/* anchor point behaves in exactly the same way as a code point in reset */
|
|
/* would, except that it cannot be tailored. A range (we currently only */
|
|
/* know for the [top] range will explicitly set the upper bound for */
|
|
/* generated CEs, thus allowing for better control over how many CEs can */
|
|
/* be squeezed between in the range without performance penalty. */
|
|
/* In that respect, we use [top] for tailoring of locales that use CJK */
|
|
/* characters. Other indirect values are currently a pure convenience, */
|
|
/* they can be used to assure that the CEs will be always positioned in */
|
|
/* the same place relative to a point with known properties (e.g. first */
|
|
/* primary ignorable). */
|
|
static indirectBoundaries ucolIndirectBoundaries[15];
|
|
/*
|
|
static indirectBoundaries ucolIndirectBoundaries[11] = {
|
|
{ UCOL_RESET_TOP_VALUE, 0,
|
|
UCOL_NEXT_TOP_VALUE, 0 },
|
|
{ UCOL_FIRST_PRIMARY_IGNORABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_LAST_PRIMARY_IGNORABLE, UCOL_LAST_PRIMARY_IGNORABLE_CONT,
|
|
0, 0 },
|
|
{ UCOL_FIRST_SECONDARY_IGNORABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_LAST_SECONDARY_IGNORABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_FIRST_TERTIARY_IGNORABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_LAST_TERTIARY_IGNORABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_FIRST_VARIABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_LAST_VARIABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_FIRST_NON_VARIABLE, 0,
|
|
0, 0 },
|
|
{ UCOL_LAST_NON_VARIABLE, 0,
|
|
0, 0 },
|
|
};
|
|
*/
|
|
|
|
static void setIndirectBoundaries(uint32_t indexR, uint32_t *start, uint32_t *end) {
|
|
|
|
// Set values for the top - TODO: once we have values for all the indirects, we are going
|
|
// to initalize here.
|
|
ucolIndirectBoundaries[indexR].startCE = start[0];
|
|
ucolIndirectBoundaries[indexR].startContCE = start[1];
|
|
if(end) {
|
|
ucolIndirectBoundaries[indexR].limitCE = end[0];
|
|
ucolIndirectBoundaries[indexR].limitContCE = end[1];
|
|
} else {
|
|
ucolIndirectBoundaries[indexR].limitCE = 0;
|
|
ucolIndirectBoundaries[indexR].limitContCE = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static inline
|
|
void syntaxError(const UChar* rules,
|
|
int32_t pos,
|
|
int32_t rulesLen,
|
|
UParseError* parseError)
|
|
{
|
|
parseError->offset = pos;
|
|
parseError->line = 0 ; /* we are not using line numbers */
|
|
|
|
// for pre-context
|
|
int32_t start = (pos <=U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1));
|
|
int32_t stop = pos;
|
|
|
|
u_memcpy(parseError->preContext,rules+start,stop-start);
|
|
//null terminate the buffer
|
|
parseError->preContext[stop-start] = 0;
|
|
|
|
//for post-context
|
|
start = pos+1;
|
|
stop = ((pos+U_PARSE_CONTEXT_LEN)<= rulesLen )? (pos+(U_PARSE_CONTEXT_LEN-1)) :
|
|
rulesLen;
|
|
|
|
if(start < stop) {
|
|
u_memcpy(parseError->postContext,rules+start,stop-start);
|
|
//null terminate the buffer
|
|
parseError->postContext[stop-start]= 0;
|
|
} else {
|
|
parseError->postContext[0] = 0;
|
|
}
|
|
}
|
|
|
|
static
|
|
void ucol_uprv_tok_setOptionInImage(UColOptionSet *opts, UColAttribute attrib, UColAttributeValue value) {
|
|
switch(attrib) {
|
|
case UCOL_HIRAGANA_QUATERNARY_MODE:
|
|
opts->hiraganaQ = value;
|
|
break;
|
|
case UCOL_FRENCH_COLLATION:
|
|
opts->frenchCollation = value;
|
|
break;
|
|
case UCOL_ALTERNATE_HANDLING:
|
|
opts->alternateHandling = value;
|
|
break;
|
|
case UCOL_CASE_FIRST:
|
|
opts->caseFirst = value;
|
|
break;
|
|
case UCOL_CASE_LEVEL:
|
|
opts->caseLevel = value;
|
|
break;
|
|
case UCOL_NORMALIZATION_MODE:
|
|
opts->normalizationMode = value;
|
|
break;
|
|
case UCOL_STRENGTH:
|
|
opts->strength = value;
|
|
break;
|
|
case UCOL_NUMERIC_COLLATION:
|
|
opts->numericCollation = value;
|
|
break;
|
|
case UCOL_ATTRIBUTE_COUNT:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define UTOK_OPTION_COUNT 20
|
|
|
|
static UBool didInit = FALSE;
|
|
/* we can be strict, or we can be lenient */
|
|
/* I'd surely be lenient with the option arguments */
|
|
/* maybe even with options */
|
|
U_STRING_DECL(suboption_00, "non-ignorable", 13);
|
|
U_STRING_DECL(suboption_01, "shifted", 7);
|
|
|
|
U_STRING_DECL(suboption_02, "lower", 5);
|
|
U_STRING_DECL(suboption_03, "upper", 5);
|
|
U_STRING_DECL(suboption_04, "off", 3);
|
|
U_STRING_DECL(suboption_05, "on", 2);
|
|
U_STRING_DECL(suboption_06, "1", 1);
|
|
U_STRING_DECL(suboption_07, "2", 1);
|
|
U_STRING_DECL(suboption_08, "3", 1);
|
|
U_STRING_DECL(suboption_09, "4", 1);
|
|
U_STRING_DECL(suboption_10, "I", 1);
|
|
|
|
U_STRING_DECL(suboption_11, "primary", 7);
|
|
U_STRING_DECL(suboption_12, "secondary", 9);
|
|
U_STRING_DECL(suboption_13, "tertiary", 8);
|
|
U_STRING_DECL(suboption_14, "variable", 8);
|
|
U_STRING_DECL(suboption_15, "regular", 7);
|
|
U_STRING_DECL(suboption_16, "implicit", 8);
|
|
U_STRING_DECL(suboption_17, "trailing", 8);
|
|
|
|
|
|
U_STRING_DECL(option_00, "undefined", 9);
|
|
U_STRING_DECL(option_01, "rearrange", 9);
|
|
U_STRING_DECL(option_02, "alternate", 9);
|
|
U_STRING_DECL(option_03, "backwards", 9);
|
|
U_STRING_DECL(option_04, "variable top", 12);
|
|
U_STRING_DECL(option_05, "top", 3);
|
|
U_STRING_DECL(option_06, "normalization", 13);
|
|
U_STRING_DECL(option_07, "caseLevel", 9);
|
|
U_STRING_DECL(option_08, "caseFirst", 9);
|
|
U_STRING_DECL(option_09, "scriptOrder", 11);
|
|
U_STRING_DECL(option_10, "charsetname", 11);
|
|
U_STRING_DECL(option_11, "charset", 7);
|
|
U_STRING_DECL(option_12, "before", 6);
|
|
U_STRING_DECL(option_13, "hiraganaQ", 9);
|
|
U_STRING_DECL(option_14, "strength", 8);
|
|
U_STRING_DECL(option_15, "first", 5);
|
|
U_STRING_DECL(option_16, "last", 4);
|
|
U_STRING_DECL(option_17, "optimize", 8);
|
|
U_STRING_DECL(option_18, "suppressContractions", 20);
|
|
U_STRING_DECL(option_19, "numericOrdering", 15);
|
|
|
|
|
|
/*
|
|
[last variable] last variable value
|
|
[last primary ignorable] largest CE for primary ignorable
|
|
[last secondary ignorable] largest CE for secondary ignorable
|
|
[last tertiary ignorable] largest CE for tertiary ignorable
|
|
[top] guaranteed to be above all implicit CEs, for now and in the future (in 1.8)
|
|
*/
|
|
|
|
|
|
static const ucolTokSuboption alternateSub[2] = {
|
|
{suboption_00, 13, UCOL_NON_IGNORABLE},
|
|
{suboption_01, 7, UCOL_SHIFTED}
|
|
};
|
|
|
|
static const ucolTokSuboption caseFirstSub[3] = {
|
|
{suboption_02, 5, UCOL_LOWER_FIRST},
|
|
{suboption_03, 5, UCOL_UPPER_FIRST},
|
|
{suboption_04, 3, UCOL_OFF},
|
|
};
|
|
|
|
static const ucolTokSuboption onOffSub[2] = {
|
|
{suboption_04, 3, UCOL_OFF},
|
|
{suboption_05, 2, UCOL_ON}
|
|
};
|
|
|
|
static const ucolTokSuboption frenchSub[1] = {
|
|
{suboption_07, 1, UCOL_ON}
|
|
};
|
|
|
|
static const ucolTokSuboption beforeSub[3] = {
|
|
{suboption_06, 1, UCOL_PRIMARY},
|
|
{suboption_07, 1, UCOL_SECONDARY},
|
|
{suboption_08, 1, UCOL_TERTIARY}
|
|
};
|
|
|
|
static const ucolTokSuboption strengthSub[5] = {
|
|
{suboption_06, 1, UCOL_PRIMARY},
|
|
{suboption_07, 1, UCOL_SECONDARY},
|
|
{suboption_08, 1, UCOL_TERTIARY},
|
|
{suboption_09, 1, UCOL_QUATERNARY},
|
|
{suboption_10, 1, UCOL_IDENTICAL},
|
|
};
|
|
|
|
static const ucolTokSuboption firstLastSub[7] = {
|
|
{suboption_11, 7, UCOL_PRIMARY},
|
|
{suboption_12, 9, UCOL_PRIMARY},
|
|
{suboption_13, 8, UCOL_PRIMARY},
|
|
{suboption_14, 8, UCOL_PRIMARY},
|
|
{suboption_15, 7, UCOL_PRIMARY},
|
|
{suboption_16, 8, UCOL_PRIMARY},
|
|
{suboption_17, 8, UCOL_PRIMARY},
|
|
};
|
|
|
|
enum OptionNumber {
|
|
OPTION_ALTERNATE_HANDLING = 0,
|
|
OPTION_FRENCH_COLLATION,
|
|
OPTION_CASE_LEVEL,
|
|
OPTION_CASE_FIRST,
|
|
OPTION_NORMALIZATION_MODE,
|
|
OPTION_HIRAGANA_QUATERNARY,
|
|
OPTION_STRENGTH,
|
|
OPTION_NUMERIC_COLLATION,
|
|
OPTION_NORMAL_OPTIONS_LIMIT = OPTION_NUMERIC_COLLATION,
|
|
OPTION_VARIABLE_TOP,
|
|
OPTION_REARRANGE,
|
|
OPTION_BEFORE,
|
|
OPTION_TOP,
|
|
OPTION_FIRST,
|
|
OPTION_LAST,
|
|
OPTION_OPTIMIZE,
|
|
OPTION_SUPPRESS_CONTRACTIONS,
|
|
OPTION_UNDEFINED,
|
|
OPTION_SCRIPT_ORDER,
|
|
OPTION_CHARSET_NAME,
|
|
OPTION_CHARSET
|
|
} ;
|
|
|
|
static const ucolTokOption rulesOptions[UTOK_OPTION_COUNT] = {
|
|
/*00*/ {option_02, 9, alternateSub, 2, UCOL_ALTERNATE_HANDLING}, /*"alternate" */
|
|
/*01*/ {option_03, 9, frenchSub, 1, UCOL_FRENCH_COLLATION}, /*"backwards" */
|
|
/*02*/ {option_07, 9, onOffSub, 2, UCOL_CASE_LEVEL}, /*"caseLevel" */
|
|
/*03*/ {option_08, 9, caseFirstSub, 3, UCOL_CASE_FIRST}, /*"caseFirst" */
|
|
/*04*/ {option_06, 13, onOffSub, 2, UCOL_NORMALIZATION_MODE}, /*"normalization" */
|
|
/*05*/ {option_13, 9, onOffSub, 2, UCOL_HIRAGANA_QUATERNARY_MODE}, /*"hiraganaQ" */
|
|
/*06*/ {option_14, 8, strengthSub, 5, UCOL_STRENGTH}, /*"strength" */
|
|
/*07*/ {option_19, 15, onOffSub, 2, UCOL_NUMERIC_COLLATION}, /*"numericOrdering"*/
|
|
/*08*/ {option_04, 12, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"variable top" */
|
|
/*09*/ {option_01, 9, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"rearrange" */
|
|
/*10*/ {option_12, 6, beforeSub, 3, UCOL_ATTRIBUTE_COUNT}, /*"before" */
|
|
/*11*/ {option_05, 3, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"top" */
|
|
/*12*/ {option_15, 5, firstLastSub, 7, UCOL_ATTRIBUTE_COUNT}, /*"first" */
|
|
/*13*/ {option_16, 4, firstLastSub, 7, UCOL_ATTRIBUTE_COUNT}, /*"last" */
|
|
/*14*/ {option_17, 8, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"optimize" */
|
|
/*15*/ {option_18, 20, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"suppressContractions" */
|
|
/*16*/ {option_00, 9, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"undefined" */
|
|
/*17*/ {option_09, 11, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"scriptOrder" */
|
|
/*18*/ {option_10, 11, NULL, 0, UCOL_ATTRIBUTE_COUNT}, /*"charsetname" */
|
|
/*19*/ {option_11, 7, NULL, 0, UCOL_ATTRIBUTE_COUNT} /*"charset" */
|
|
};
|
|
|
|
static
|
|
int32_t u_strncmpNoCase(const UChar *s1,
|
|
const UChar *s2,
|
|
int32_t n)
|
|
{
|
|
if(n > 0) {
|
|
int32_t rc;
|
|
for(;;) {
|
|
rc = (int32_t)u_tolower(*s1) - (int32_t)u_tolower(*s2);
|
|
if(rc != 0 || *s1 == 0 || --n == 0) {
|
|
return rc;
|
|
}
|
|
++s1;
|
|
++s2;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void ucol_uprv_tok_initData() {
|
|
if(!didInit) {
|
|
U_STRING_INIT(suboption_00, "non-ignorable", 13);
|
|
U_STRING_INIT(suboption_01, "shifted", 7);
|
|
|
|
U_STRING_INIT(suboption_02, "lower", 5);
|
|
U_STRING_INIT(suboption_03, "upper", 5);
|
|
U_STRING_INIT(suboption_04, "off", 3);
|
|
U_STRING_INIT(suboption_05, "on", 2);
|
|
|
|
U_STRING_INIT(suboption_06, "1", 1);
|
|
U_STRING_INIT(suboption_07, "2", 1);
|
|
U_STRING_INIT(suboption_08, "3", 1);
|
|
U_STRING_INIT(suboption_09, "4", 1);
|
|
U_STRING_INIT(suboption_10, "I", 1);
|
|
|
|
U_STRING_INIT(suboption_11, "primary", 7);
|
|
U_STRING_INIT(suboption_12, "secondary", 9);
|
|
U_STRING_INIT(suboption_13, "tertiary", 8);
|
|
U_STRING_INIT(suboption_14, "variable", 8);
|
|
U_STRING_INIT(suboption_15, "regular", 7);
|
|
U_STRING_INIT(suboption_16, "implicit", 8);
|
|
U_STRING_INIT(suboption_17, "trailing", 8);
|
|
|
|
|
|
U_STRING_INIT(option_00, "undefined", 9);
|
|
U_STRING_INIT(option_01, "rearrange", 9);
|
|
U_STRING_INIT(option_02, "alternate", 9);
|
|
U_STRING_INIT(option_03, "backwards", 9);
|
|
U_STRING_INIT(option_04, "variable top", 12);
|
|
U_STRING_INIT(option_05, "top", 3);
|
|
U_STRING_INIT(option_06, "normalization", 13);
|
|
U_STRING_INIT(option_07, "caseLevel", 9);
|
|
U_STRING_INIT(option_08, "caseFirst", 9);
|
|
U_STRING_INIT(option_09, "scriptOrder", 11);
|
|
U_STRING_INIT(option_10, "charsetname", 11);
|
|
U_STRING_INIT(option_11, "charset", 7);
|
|
U_STRING_INIT(option_12, "before", 6);
|
|
U_STRING_INIT(option_13, "hiraganaQ", 9);
|
|
U_STRING_INIT(option_14, "strength", 8);
|
|
U_STRING_INIT(option_15, "first", 5);
|
|
U_STRING_INIT(option_16, "last", 4);
|
|
U_STRING_INIT(option_17, "optimize", 8);
|
|
U_STRING_INIT(option_18, "suppressContractions", 20);
|
|
U_STRING_INIT(option_19, "numericOrdering", 15);
|
|
didInit = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// This function reads basic options to set in the runtime collator
|
|
// used by data driven tests. Should not support build time options
|
|
U_CAPI const UChar * U_EXPORT2
|
|
ucol_tok_getNextArgument(const UChar *start, const UChar *end,
|
|
UColAttribute *attrib, UColAttributeValue *value,
|
|
UErrorCode *status)
|
|
{
|
|
uint32_t i = 0;
|
|
int32_t j=0;
|
|
UBool foundOption = FALSE;
|
|
const UChar *optionArg = NULL;
|
|
|
|
ucol_uprv_tok_initData();
|
|
|
|
while(start < end && u_isWhitespace(*start)) { /* eat whitespace */
|
|
start++;
|
|
}
|
|
if(start >= end) {
|
|
return NULL;
|
|
}
|
|
/* skip opening '[' */
|
|
if(*start == 0x005b) {
|
|
start++;
|
|
} else {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR; // no opening '['
|
|
return NULL;
|
|
}
|
|
|
|
while(i < UTOK_OPTION_COUNT) {
|
|
if(u_strncmpNoCase(start, rulesOptions[i].optionName, rulesOptions[i].optionLen) == 0) {
|
|
foundOption = TRUE;
|
|
if(end - start > rulesOptions[i].optionLen) {
|
|
optionArg = start+rulesOptions[i].optionLen+1; /* start of the options, skip space */
|
|
while(u_isWhitespace(*optionArg)) { /* eat whitespace */
|
|
optionArg++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if(!foundOption) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
if(optionArg) {
|
|
for(j = 0; j<rulesOptions[i].subSize; j++) {
|
|
if(u_strncmpNoCase(optionArg, rulesOptions[i].subopts[j].subName, rulesOptions[i].subopts[j].subLen) == 0) {
|
|
//ucol_uprv_tok_setOptionInImage(src->opts, rulesOptions[i].attr, rulesOptions[i].subopts[j].attrVal);
|
|
*attrib = rulesOptions[i].attr;
|
|
*value = rulesOptions[i].subopts[j].attrVal;
|
|
optionArg += rulesOptions[i].subopts[j].subLen;
|
|
while(u_isWhitespace(*optionArg)) { /* eat whitespace */
|
|
optionArg++;
|
|
}
|
|
if(*optionArg == 0x005d) {
|
|
optionArg++;
|
|
return optionArg;
|
|
} else {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
static
|
|
USet *ucol_uprv_tok_readAndSetUnicodeSet(const UChar *start, const UChar *end, UErrorCode *status) {
|
|
while(*start != 0x005b) { /* advance while we find the first '[' */
|
|
start++;
|
|
}
|
|
// now we need to get a balanced set of '[]'. The problem is that a set can have
|
|
// many, and *end point to the first closing '['
|
|
int32_t noOpenBraces = 1;
|
|
int32_t current = 1; // skip the opening brace
|
|
while(start+current < end && noOpenBraces != 0) {
|
|
if(start[current] == 0x005b) {
|
|
noOpenBraces++;
|
|
} else if(start[current] == 0x005D) { // closing brace
|
|
noOpenBraces--;
|
|
}
|
|
current++;
|
|
}
|
|
|
|
if(noOpenBraces != 0 || u_strchr(start+current, 0x005d /*']'*/) == NULL) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return NULL;
|
|
}
|
|
return uset_openPattern(start, current, status);
|
|
}
|
|
|
|
static
|
|
int32_t ucol_uprv_tok_readOption(const UChar *start, const UChar *end, const UChar **optionArg) {
|
|
int32_t i = 0;
|
|
ucol_uprv_tok_initData();
|
|
|
|
while(u_isWhitespace(*start)) { /* eat whitespace */
|
|
start++;
|
|
}
|
|
while(i < UTOK_OPTION_COUNT) {
|
|
if(u_strncmpNoCase(start, rulesOptions[i].optionName, rulesOptions[i].optionLen) == 0) {
|
|
if(end - start > rulesOptions[i].optionLen) {
|
|
*optionArg = start+rulesOptions[i].optionLen; /* start of the options*/
|
|
while(u_isWhitespace(**optionArg)) { /* eat whitespace */
|
|
(*optionArg)++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
if(i == UTOK_OPTION_COUNT) {
|
|
i = -1; // didn't find an option
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
// reads and conforms to various options in rules
|
|
// end is the position of the first closing ']'
|
|
// However, some of the options take an UnicodeSet definition
|
|
// which needs to duplicate the closing ']'
|
|
// for example: '[copy [\uAC00-\uD7FF]]'
|
|
// These options will move end to the second ']' and the
|
|
// caller will set the current to it.
|
|
static
|
|
uint8_t ucol_uprv_tok_readAndSetOption(UColTokenParser *src, UErrorCode *status) {
|
|
const UChar* start = src->current;
|
|
int32_t i = 0;
|
|
int32_t j=0;
|
|
const UChar *optionArg = NULL;
|
|
|
|
uint8_t result = 0;
|
|
|
|
start++; /*skip opening '['*/
|
|
i = ucol_uprv_tok_readOption(start, src->end, &optionArg);
|
|
if(optionArg) {
|
|
src->current = optionArg;
|
|
}
|
|
|
|
if(i < 0) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
} else {
|
|
int32_t noOpenBraces = 1;
|
|
switch(i) {
|
|
case OPTION_ALTERNATE_HANDLING:
|
|
case OPTION_FRENCH_COLLATION:
|
|
case OPTION_CASE_LEVEL:
|
|
case OPTION_CASE_FIRST:
|
|
case OPTION_NORMALIZATION_MODE:
|
|
case OPTION_HIRAGANA_QUATERNARY:
|
|
case OPTION_STRENGTH:
|
|
case OPTION_NUMERIC_COLLATION:
|
|
if(optionArg) {
|
|
for(j = 0; j<rulesOptions[i].subSize; j++) {
|
|
if(u_strncmpNoCase(optionArg, rulesOptions[i].subopts[j].subName, rulesOptions[i].subopts[j].subLen) == 0) {
|
|
ucol_uprv_tok_setOptionInImage(src->opts, rulesOptions[i].attr, rulesOptions[i].subopts[j].attrVal);
|
|
result = UCOL_TOK_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
if(result == 0) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
case OPTION_VARIABLE_TOP:
|
|
result = UCOL_TOK_SUCCESS | UCOL_TOK_VARIABLE_TOP;
|
|
break;
|
|
case OPTION_REARRANGE:
|
|
result = UCOL_TOK_SUCCESS;
|
|
break;
|
|
case OPTION_BEFORE:
|
|
if(optionArg) {
|
|
for(j = 0; j<rulesOptions[i].subSize; j++) {
|
|
if(u_strncmpNoCase(optionArg, rulesOptions[i].subopts[j].subName, rulesOptions[i].subopts[j].subLen) == 0) {
|
|
result = UCOL_TOK_SUCCESS | rulesOptions[i].subopts[j].attrVal + 1;
|
|
}
|
|
}
|
|
}
|
|
if(result == 0) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
case OPTION_TOP: /* we are going to have an array with structures of limit CEs */
|
|
/* index to this array will be src->parsedToken.indirectIndex*/
|
|
src->parsedToken.indirectIndex = 0;
|
|
result = UCOL_TOK_SUCCESS | UCOL_TOK_TOP;
|
|
break;
|
|
case OPTION_FIRST:
|
|
case OPTION_LAST: /* first, last */
|
|
for(j = 0; j<rulesOptions[i].subSize; j++) {
|
|
if(u_strncmpNoCase(optionArg, rulesOptions[i].subopts[j].subName, rulesOptions[i].subopts[j].subLen) == 0) {
|
|
// the calculation below assumes that OPTION_FIRST and OPTION_LAST are at i and i+1 and that the first
|
|
// element of indirect boundaries is reserved for top.
|
|
src->parsedToken.indirectIndex = (uint16_t)(i-OPTION_FIRST+1+j*2);
|
|
result = UCOL_TOK_SUCCESS | UCOL_TOK_TOP;;
|
|
}
|
|
}
|
|
if(result == 0) {
|
|
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
}
|
|
break;
|
|
case OPTION_OPTIMIZE:
|
|
case OPTION_SUPPRESS_CONTRACTIONS: // copy and remove are handled before normalization
|
|
// we need to move end here
|
|
src->current++; // skip opening brace
|
|
while(src->current < src->end && noOpenBraces != 0) {
|
|
if(*src->current == 0x005b) {
|
|
noOpenBraces++;
|
|
} else if(*src->current == 0x005D) { // closing brace
|
|
noOpenBraces--;
|
|
}
|
|
src->current++;
|
|
}
|
|
result = UCOL_TOK_SUCCESS;
|
|
break;
|
|
default:
|
|
*status = U_UNSUPPORTED_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
src->current = u_memchr(src->current, 0x005d, src->end-src->current);
|
|
return result;
|
|
}
|
|
|
|
|
|
inline void ucol_tok_addToExtraCurrent(UColTokenParser *src, const UChar *stuff, int32_t len, UErrorCode *status) {
|
|
if(src->extraCurrent+len >= src->extraEnd) {
|
|
/* reallocate */
|
|
UChar *newSrc = (UChar *)uprv_realloc(src->source, (src->extraEnd-src->source)*2*sizeof(UChar));
|
|
if(newSrc != NULL) {
|
|
src->current = newSrc + (src->current - src->source);
|
|
src->extraCurrent = newSrc + (src->extraCurrent - src->source);
|
|
src->end = newSrc + (src->end - src->source);
|
|
src->extraEnd = newSrc + (src->extraEnd-src->source)*2;
|
|
src->sourceCurrent = newSrc + (src->sourceCurrent-src->source);
|
|
src->source = newSrc;
|
|
} else {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
}
|
|
}
|
|
if(len == 1) {
|
|
*src->extraCurrent++ = *stuff;
|
|
} else {
|
|
uprv_memcpy(src->extraCurrent, stuff, len*sizeof(UChar));
|
|
src->extraCurrent += len;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
inline UBool ucol_tok_doSetTop(UColTokenParser *src, UErrorCode *status) {
|
|
/*
|
|
top = TRUE;
|
|
*/
|
|
UChar buff[5];
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
buff[0] = 0xFFFE;
|
|
buff[1] = (UChar)(ucolIndirectBoundaries[src->parsedToken.indirectIndex].startCE >> 16);
|
|
buff[2] = (UChar)(ucolIndirectBoundaries[src->parsedToken.indirectIndex].startCE & 0xFFFF);
|
|
if(ucolIndirectBoundaries[src->parsedToken.indirectIndex].startContCE == 0) {
|
|
src->parsedToken.charsLen = 3;
|
|
ucol_tok_addToExtraCurrent(src, buff, 3, status);
|
|
} else {
|
|
buff[3] = (UChar)(ucolIndirectBoundaries[src->parsedToken.indirectIndex].startContCE >> 16);
|
|
buff[4] = (UChar)(ucolIndirectBoundaries[src->parsedToken.indirectIndex].startContCE & 0xFFFF);
|
|
src->parsedToken.charsLen = 5;
|
|
ucol_tok_addToExtraCurrent(src, buff, 5, status);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static UBool isCharNewLine(UChar c){
|
|
switch(c){
|
|
case 0x000A: /* LF */
|
|
case 0x000D: /* CR */
|
|
case 0x000C: /* FF */
|
|
case 0x0085: /* NEL */
|
|
case 0x2028: /* LS */
|
|
case 0x2029: /* PS */
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
U_CAPI const UChar* U_EXPORT2
|
|
ucol_tok_parseNextToken(UColTokenParser *src,
|
|
UBool startOfRules,
|
|
UParseError *parseError,
|
|
UErrorCode *status)
|
|
{
|
|
/* parsing part */
|
|
UBool variableTop = FALSE;
|
|
UBool top = FALSE;
|
|
UBool inChars = TRUE;
|
|
UBool inQuote = FALSE;
|
|
UBool wasInQuote = FALSE;
|
|
uint8_t before = 0;
|
|
UBool isEscaped = FALSE;
|
|
// TODO: replace these variables with src->parsedToken counterparts
|
|
// no need to use them anymore since we have src->parsedToken.
|
|
// Ideally, token parser would be a nice class... Once, when I have
|
|
// more time (around 2020 probably).
|
|
uint32_t newExtensionLen = 0;
|
|
uint32_t extensionOffset = 0;
|
|
uint32_t newStrength = UCOL_TOK_UNSET;
|
|
UChar buff[10];
|
|
|
|
src->parsedToken.charsOffset = 0; src->parsedToken.charsLen = 0;
|
|
src->parsedToken.prefixOffset = 0; src->parsedToken.prefixLen = 0;
|
|
src->parsedToken.indirectIndex = 0;
|
|
|
|
while (src->current < src->end) {
|
|
UChar ch = *(src->current);
|
|
|
|
if (inQuote) {
|
|
if (ch == 0x0027/*'\''*/) {
|
|
inQuote = FALSE;
|
|
} else {
|
|
if ((src->parsedToken.charsLen == 0) || inChars) {
|
|
if(src->parsedToken.charsLen == 0) {
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
}
|
|
src->parsedToken.charsLen++;
|
|
} else {
|
|
if(newExtensionLen == 0) {
|
|
extensionOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
}
|
|
newExtensionLen++;
|
|
}
|
|
}
|
|
}else if(isEscaped){
|
|
isEscaped =FALSE;
|
|
if (newStrength == UCOL_TOK_UNSET) {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
return NULL;
|
|
// enabling rules to start with non-tokens a < b
|
|
// newStrength = UCOL_TOK_RESET;
|
|
}
|
|
if(ch != 0x0000 && src->current != src->end) {
|
|
if (inChars) {
|
|
if(src->parsedToken.charsLen == 0) {
|
|
src->parsedToken.charsOffset = (uint32_t)(src->current - src->source);
|
|
}
|
|
src->parsedToken.charsLen++;
|
|
} else {
|
|
if(newExtensionLen == 0) {
|
|
extensionOffset = (uint32_t)(src->current - src->source);
|
|
}
|
|
newExtensionLen++;
|
|
}
|
|
}
|
|
}else {
|
|
if(!uprv_isRuleWhiteSpace(ch)) {
|
|
/* Sets the strength for this entry */
|
|
switch (ch) {
|
|
case 0x003D/*'='*/ :
|
|
if (newStrength != UCOL_TOK_UNSET) {
|
|
goto EndOfLoop;
|
|
}
|
|
|
|
/* if we start with strength, we'll reset to top */
|
|
if(startOfRules == TRUE) {
|
|
src->parsedToken.indirectIndex = 5;
|
|
top = ucol_tok_doSetTop(src, status);
|
|
newStrength = UCOL_TOK_RESET;
|
|
goto EndOfLoop;
|
|
}
|
|
newStrength = UCOL_IDENTICAL;
|
|
break;
|
|
|
|
case 0x002C/*','*/:
|
|
if (newStrength != UCOL_TOK_UNSET) {
|
|
goto EndOfLoop;
|
|
}
|
|
|
|
/* if we start with strength, we'll reset to top */
|
|
if(startOfRules == TRUE) {
|
|
src->parsedToken.indirectIndex = 5;
|
|
top = ucol_tok_doSetTop(src, status);
|
|
newStrength = UCOL_TOK_RESET;
|
|
goto EndOfLoop;
|
|
}
|
|
newStrength = UCOL_TERTIARY;
|
|
break;
|
|
|
|
case 0x003B/*';'*/:
|
|
if (newStrength != UCOL_TOK_UNSET) {
|
|
goto EndOfLoop;
|
|
}
|
|
|
|
/* if we start with strength, we'll reset to top */
|
|
if(startOfRules == TRUE) {
|
|
src->parsedToken.indirectIndex = 5;
|
|
top = ucol_tok_doSetTop(src, status);
|
|
newStrength = UCOL_TOK_RESET;
|
|
goto EndOfLoop;
|
|
}
|
|
newStrength = UCOL_SECONDARY;
|
|
break;
|
|
|
|
case 0x003C/*'<'*/:
|
|
if (newStrength != UCOL_TOK_UNSET) {
|
|
goto EndOfLoop;
|
|
}
|
|
|
|
/* if we start with strength, we'll reset to top */
|
|
if(startOfRules == TRUE) {
|
|
src->parsedToken.indirectIndex = 5;
|
|
top = ucol_tok_doSetTop(src, status);
|
|
newStrength = UCOL_TOK_RESET;
|
|
goto EndOfLoop;
|
|
}
|
|
/* before this, do a scan to verify whether this is */
|
|
/* another strength */
|
|
if(*(src->current+1) == 0x003C) {
|
|
src->current++;
|
|
if(*(src->current+1) == 0x003C) {
|
|
src->current++; /* three in a row! */
|
|
newStrength = UCOL_TERTIARY;
|
|
} else { /* two in a row */
|
|
newStrength = UCOL_SECONDARY;
|
|
}
|
|
} else { /* just one */
|
|
newStrength = UCOL_PRIMARY;
|
|
}
|
|
break;
|
|
|
|
case 0x0026/*'&'*/:
|
|
if (newStrength != UCOL_TOK_UNSET) {
|
|
/**/
|
|
goto EndOfLoop;
|
|
}
|
|
|
|
newStrength = UCOL_TOK_RESET; /* PatternEntry::RESET = 0 */
|
|
break;
|
|
|
|
case 0x005b/*'['*/:
|
|
/* options - read an option, analyze it */
|
|
if(u_strchr(src->current, 0x005d /*']'*/) != NULL) {
|
|
uint8_t result = ucol_uprv_tok_readAndSetOption(src, status);
|
|
if(U_SUCCESS(*status)) {
|
|
if(result & UCOL_TOK_TOP) {
|
|
if(newStrength == UCOL_TOK_RESET) {
|
|
top = ucol_tok_doSetTop(src, status);
|
|
if(before) { // This is a combination of before and indirection like '&[before 2][first regular]<b'
|
|
src->parsedToken.charsLen+=2;
|
|
buff[0] = 0x002d;
|
|
buff[1] = before;
|
|
ucol_tok_addToExtraCurrent(src, buff, 2, status);
|
|
}
|
|
|
|
src->current++;
|
|
goto EndOfLoop;
|
|
} else {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
}
|
|
} else if(result & UCOL_TOK_VARIABLE_TOP) {
|
|
if(newStrength != UCOL_TOK_RESET && newStrength != UCOL_TOK_UNSET) {
|
|
variableTop = TRUE;
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
src->parsedToken.charsLen = 1;
|
|
buff[0] = 0xFFFF;
|
|
ucol_tok_addToExtraCurrent(src, buff, 1, status);
|
|
src->current++;
|
|
goto EndOfLoop;
|
|
} else {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
}
|
|
} else if (result & UCOL_TOK_BEFORE){
|
|
if(newStrength == UCOL_TOK_RESET) {
|
|
before = result & UCOL_TOK_BEFORE;
|
|
} else {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
|
|
}
|
|
}
|
|
} else {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
return NULL;
|
|
}
|
|
}
|
|
break;
|
|
case 0x0021/*! skip java thai modifier reordering*/:
|
|
break;
|
|
case 0x002F/*'/'*/:
|
|
wasInQuote = FALSE; /* if we were copying source characters, we want to stop now */
|
|
inChars = FALSE; /* we're now processing expansion */
|
|
break;
|
|
case 0x005C /* back slash for escaped chars */:
|
|
isEscaped = TRUE;
|
|
break;
|
|
/* found a quote, we're gonna start copying */
|
|
case 0x0027/*'\''*/:
|
|
if (newStrength == UCOL_TOK_UNSET) { /* quote is illegal until we have a strength */
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
return NULL;
|
|
// enabling rules to start with a non-token character a < b
|
|
// newStrength = UCOL_TOK_RESET;
|
|
}
|
|
|
|
inQuote = TRUE;
|
|
|
|
if(inChars) { /* we're doing characters */
|
|
if(wasInQuote == FALSE) {
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
}
|
|
if (src->parsedToken.charsLen != 0) {
|
|
ucol_tok_addToExtraCurrent(src, src->current - src->parsedToken.charsLen, src->parsedToken.charsLen, status);
|
|
}
|
|
src->parsedToken.charsLen++;
|
|
} else { /* we're doing an expansion */
|
|
if(wasInQuote == FALSE) {
|
|
extensionOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
}
|
|
if (newExtensionLen != 0) {
|
|
ucol_tok_addToExtraCurrent(src, src->current - newExtensionLen, newExtensionLen, status);
|
|
}
|
|
newExtensionLen++;
|
|
}
|
|
|
|
wasInQuote = TRUE;
|
|
|
|
ch = *(++(src->current));
|
|
if(ch == 0x0027) { /* copy the double quote */
|
|
ucol_tok_addToExtraCurrent(src, &ch, 1, status);
|
|
inQuote = FALSE;
|
|
}
|
|
break;
|
|
|
|
/* '@' is french only if the strength is not currently set */
|
|
/* if it is, it's just a regular character in collation rules */
|
|
case 0x0040/*'@'*/:
|
|
if (newStrength == UCOL_TOK_UNSET) {
|
|
src->opts->frenchCollation = UCOL_ON;
|
|
break;
|
|
}
|
|
|
|
case 0x007C /*|*/: /* this means we have actually been reading prefix part */
|
|
// we want to store read characters to the prefix part and continue reading
|
|
// the characters (proper way would be to restart reading the chars, but in
|
|
// that case we would have to complicate the token hasher, which I do not
|
|
// intend to play with. Instead, we will do prefixes when prefixes are due
|
|
// (before adding the elements).
|
|
src->parsedToken.prefixOffset = src->parsedToken.charsOffset;
|
|
src->parsedToken.prefixLen = src->parsedToken.charsLen;
|
|
|
|
if(inChars) { /* we're doing characters */
|
|
if(wasInQuote == FALSE) {
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
}
|
|
if (src->parsedToken.charsLen != 0) {
|
|
ucol_tok_addToExtraCurrent(src, src->current - src->parsedToken.charsLen, src->parsedToken.charsLen, status);
|
|
}
|
|
src->parsedToken.charsLen++;
|
|
}
|
|
|
|
wasInQuote = TRUE;
|
|
|
|
do {
|
|
ch = *(++(src->current));
|
|
// skip whitespace between '|' and the character
|
|
} while (uprv_isRuleWhiteSpace(ch));
|
|
break;
|
|
|
|
//charsOffset = 0;
|
|
//newCharsLen = 0;
|
|
//break; // We want to store the whole prefix/character sequence. If we break
|
|
// the '|' is going to get lost.
|
|
case 0x0023 /*#*/: /* this is a comment, skip everything through the end of line */
|
|
do {
|
|
ch = *(++(src->current));
|
|
} while (!isCharNewLine(ch));
|
|
|
|
break;
|
|
default:
|
|
if (newStrength == UCOL_TOK_UNSET) {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
return NULL;
|
|
}
|
|
|
|
if (ucol_tok_isSpecialChar(ch) && (inQuote == FALSE)) {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
return NULL;
|
|
}
|
|
|
|
if(ch == 0x0000 && src->current+1 == src->end) {
|
|
break;
|
|
}
|
|
|
|
if (inChars) {
|
|
if(src->parsedToken.charsLen == 0) {
|
|
src->parsedToken.charsOffset = (uint32_t)(src->current - src->source);
|
|
}
|
|
src->parsedToken.charsLen++;
|
|
} else {
|
|
if(newExtensionLen == 0) {
|
|
extensionOffset = (uint32_t)(src->current - src->source);
|
|
}
|
|
newExtensionLen++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(wasInQuote) {
|
|
if(ch != 0x27) {
|
|
if(inQuote || !uprv_isRuleWhiteSpace(ch)) {
|
|
ucol_tok_addToExtraCurrent(src, &ch, 1, status);
|
|
}
|
|
}
|
|
}
|
|
|
|
src->current++;
|
|
}
|
|
|
|
EndOfLoop:
|
|
wasInQuote = FALSE;
|
|
if (newStrength == UCOL_TOK_UNSET) {
|
|
return NULL;
|
|
}
|
|
|
|
if (src->parsedToken.charsLen == 0 && top == FALSE) {
|
|
syntaxError(src->source,(int32_t)(src->current-src->source),(int32_t)(src->end-src->source),parseError);
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
src->parsedToken.strength = newStrength;
|
|
src->parsedToken.extensionOffset = extensionOffset;
|
|
src->parsedToken.extensionLen = newExtensionLen;
|
|
src->parsedToken.flags = (UCOL_TOK_VARIABLE_TOP * (variableTop?1:0)) | (UCOL_TOK_TOP * (top?1:0)) | before;
|
|
|
|
return src->current;
|
|
}
|
|
|
|
/*
|
|
Processing Description
|
|
1 Build a ListList. Each list has a header, which contains two lists (positive
|
|
and negative), a reset token, a baseCE, nextCE, and previousCE. The lists and
|
|
reset may be null.
|
|
2 As you process, you keep a LAST pointer that points to the last token you
|
|
handled.
|
|
*/
|
|
|
|
static UColToken *ucol_tok_initAReset(UColTokenParser *src, UChar *expand, uint32_t *expandNext,
|
|
UParseError *parseError, UErrorCode *status)
|
|
{
|
|
if(src->resultLen == src->listCapacity) {
|
|
// Unfortunately, this won't work, as we store addresses of lhs in token
|
|
src->listCapacity *= 2;
|
|
src->lh = (UColTokListHeader *)uprv_realloc(src->lh, src->listCapacity*sizeof(UColTokListHeader));
|
|
if(src->lh == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return NULL;
|
|
}
|
|
}
|
|
/* do the reset thing */
|
|
UColToken *sourceToken = (UColToken *)uprv_malloc(sizeof(UColToken));
|
|
/* test for NULL */
|
|
if (sourceToken == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return NULL;
|
|
}
|
|
sourceToken->rulesToParse = src->source;
|
|
sourceToken->source = src->parsedToken.charsLen << 24 | src->parsedToken.charsOffset;
|
|
sourceToken->expansion = src->parsedToken.extensionLen << 24 | src->parsedToken.extensionOffset;
|
|
|
|
sourceToken->debugSource = *(src->source + src->parsedToken.charsOffset);
|
|
sourceToken->debugExpansion = *(src->source + src->parsedToken.extensionOffset);
|
|
|
|
// keep the flags around so that we know about before
|
|
sourceToken->flags = src->parsedToken.flags;
|
|
|
|
if(src->parsedToken.prefixOffset != 0) {
|
|
// this is a syntax error
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,src->parsedToken.charsOffset-1,src->parsedToken.charsOffset+src->parsedToken.charsLen,parseError);
|
|
return 0;
|
|
} else {
|
|
sourceToken->prefix = 0;
|
|
}
|
|
|
|
sourceToken->polarity = UCOL_TOK_POLARITY_POSITIVE; /* TODO: this should also handle reverse */
|
|
sourceToken->strength = UCOL_TOK_RESET;
|
|
sourceToken->next = NULL;
|
|
sourceToken->previous = NULL;
|
|
sourceToken->noOfCEs = 0;
|
|
sourceToken->noOfExpCEs = 0;
|
|
sourceToken->listHeader = &src->lh[src->resultLen];
|
|
|
|
src->lh[src->resultLen].first = NULL;
|
|
src->lh[src->resultLen].last = NULL;
|
|
src->lh[src->resultLen].first = NULL;
|
|
src->lh[src->resultLen].last = NULL;
|
|
|
|
src->lh[src->resultLen].reset = sourceToken;
|
|
|
|
/*
|
|
3 Consider each item: relation, source, and expansion: e.g. ...< x / y ...
|
|
First convert all expansions into normal form. Examples:
|
|
If "xy" doesn't occur earlier in the list or in the UCA, convert &xy * c *
|
|
d * ... into &x * c/y * d * ...
|
|
Note: reset values can never have expansions, although they can cause the
|
|
very next item to have one. They may be contractions, if they are found
|
|
earlier in the list.
|
|
*/
|
|
*expandNext = 0;
|
|
if(expand != NULL) {
|
|
/* check to see if there is an expansion */
|
|
if(src->parsedToken.charsLen > 1) {
|
|
uint32_t resetCharsOffset;
|
|
resetCharsOffset = (uint32_t)(expand - src->source);
|
|
sourceToken->source = ((resetCharsOffset - src->parsedToken.charsOffset ) << 24) | src->parsedToken.charsOffset;
|
|
*expandNext = ((src->parsedToken.charsLen + src->parsedToken.charsOffset - resetCharsOffset)<<24) | (resetCharsOffset);
|
|
}
|
|
}
|
|
|
|
src->resultLen++;
|
|
|
|
uhash_put(src->tailored, sourceToken, sourceToken, status);
|
|
|
|
return sourceToken;
|
|
}
|
|
|
|
static
|
|
inline UColToken *getVirginBefore(UColTokenParser *src, UColToken *sourceToken, uint8_t strength, UParseError *parseError, UErrorCode *status) {
|
|
if(U_FAILURE(*status)) {
|
|
return NULL;
|
|
}
|
|
/* this is a virgin before - we need to fish the anchor from the UCA */
|
|
collIterate s;
|
|
uint32_t baseCE = UCOL_NOT_FOUND, baseContCE = UCOL_NOT_FOUND;
|
|
uint32_t CE, SecondCE;
|
|
uint32_t invPos;
|
|
if(sourceToken != NULL) {
|
|
uprv_init_collIterate(src->UCA, src->source+((sourceToken->source)&0xFFFFFF), 1, &s);
|
|
} else {
|
|
uprv_init_collIterate(src->UCA, src->source+src->parsedToken.charsOffset /**charsOffset*/, 1, &s);
|
|
}
|
|
|
|
baseCE = ucol_getNextCE(src->UCA, &s, status) & 0xFFFFFF3F;
|
|
baseContCE = ucol_getNextCE(src->UCA, &s, status);
|
|
if(baseContCE == UCOL_NO_MORE_CES) {
|
|
baseContCE = 0;
|
|
}
|
|
|
|
|
|
UCAConstants *consts = (UCAConstants *)((uint8_t *)src->UCA->image + src->UCA->image->UCAConsts);
|
|
uint32_t ch = 0;
|
|
uint32_t expandNext = 0;
|
|
UColToken key;
|
|
|
|
if((baseCE & 0xFF000000) >= (consts->UCA_PRIMARY_IMPLICIT_MIN<<24) && (baseCE & 0xFF000000) <= (consts->UCA_PRIMARY_IMPLICIT_MAX<<24) ) { /* implicits - */
|
|
uint32_t primary = baseCE & UCOL_PRIMARYMASK | (baseContCE & UCOL_PRIMARYMASK) >> 16;
|
|
uint32_t raw = uprv_uca_getRawFromImplicit(primary);
|
|
ch = uprv_uca_getCodePointFromRaw(raw-1);
|
|
uint32_t primaryCE = uprv_uca_getImplicitFromRaw(raw-1);
|
|
CE = primaryCE & UCOL_PRIMARYMASK | 0x0505;
|
|
SecondCE = (primaryCE << 16) & UCOL_PRIMARYMASK | UCOL_CONTINUATION_MARKER;
|
|
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source);
|
|
*src->extraCurrent++ = 0xFFFE;
|
|
*src->extraCurrent++ = (UChar)ch;
|
|
src->parsedToken.charsLen++;
|
|
|
|
key.source = (src->parsedToken.charsLen/**newCharsLen*/ << 24) | src->parsedToken.charsOffset/**charsOffset*/;
|
|
key.rulesToParse = src->source;
|
|
|
|
//sourceToken = (UColToken *)uhash_iget(src->tailored, (int32_t)key);
|
|
sourceToken = (UColToken *)uhash_get(src->tailored, &key);
|
|
|
|
if(sourceToken == NULL) {
|
|
src->lh[src->resultLen].baseCE = CE & 0xFFFFFF3F;
|
|
if(isContinuation(SecondCE)) {
|
|
src->lh[src->resultLen].baseContCE = SecondCE;
|
|
} else {
|
|
src->lh[src->resultLen].baseContCE = 0;
|
|
}
|
|
src->lh[src->resultLen].nextCE = 0;
|
|
src->lh[src->resultLen].nextContCE = 0;
|
|
src->lh[src->resultLen].previousCE = 0;
|
|
src->lh[src->resultLen].previousContCE = 0;
|
|
|
|
src->lh[src->resultLen].indirect = FALSE;
|
|
|
|
sourceToken = ucol_tok_initAReset(src, 0, &expandNext, parseError, status);
|
|
}
|
|
|
|
} else {
|
|
invPos = ucol_inv_getPrevCE(src, baseCE, baseContCE, &CE, &SecondCE, strength);
|
|
|
|
// we got the previous CE. Now we need to see if the difference between
|
|
// the two CEs is really of the requested strength.
|
|
// if it's a bigger difference (we asked for secondary and got primary), we
|
|
// need to modify the CE.
|
|
if(ucol_getCEStrengthDifference(baseCE, baseContCE, CE, SecondCE) < strength) {
|
|
// adjust the strength
|
|
// now we are in the situation where our baseCE should actually be modified in
|
|
// order to get the CE in the right position.
|
|
if(strength == UCOL_SECONDARY) {
|
|
CE = baseCE - 0x0200;
|
|
} else { // strength == UCOL_TERTIARY
|
|
CE = baseCE - 0x02;
|
|
}
|
|
if(baseContCE) {
|
|
if(strength == UCOL_SECONDARY) {
|
|
SecondCE = baseContCE - 0x0200;
|
|
} else { // strength == UCOL_TERTIARY
|
|
SecondCE = baseContCE - 0x02;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// the code below relies on getting a code point from the inverse table, in order to be
|
|
// able to merge the situations like &x < 9 &[before 1]a < d. This won't work:
|
|
// 1. There are many code points that have the same CE
|
|
// 2. The CE to codepoint table (things pointed to by CETable[3*invPos+2] are broken.
|
|
// Also, in case when there is no equivalent strength before an element, we have to actually
|
|
// construct one. For example, &[before 2]a << x won't result in x << a, because the element
|
|
// before a is a primary difference.
|
|
|
|
//uint32_t *CETable = (uint32_t *)((uint8_t *)src->invUCA+src->invUCA->table);
|
|
|
|
|
|
ch = CETable[3*invPos+2];
|
|
|
|
if((ch & UCOL_INV_SIZEMASK) != 0) {
|
|
uint16_t *conts = (uint16_t *)((uint8_t *)src->invUCA+src->invUCA->conts);
|
|
uint32_t offset = (ch & UCOL_INV_OFFSETMASK);
|
|
ch = conts[offset];
|
|
}
|
|
|
|
*src->extraCurrent++ = (UChar)ch;
|
|
src->parsedToken.charsOffset = (uint32_t)(src->extraCurrent - src->source - 1);
|
|
src->parsedToken.charsLen = 1;
|
|
|
|
// We got an UCA before. However, this might have been tailored.
|
|
// example:
|
|
// &\u30ca = \u306a
|
|
// &[before 3]\u306a<<<\u306a|\u309d
|
|
|
|
|
|
// uint32_t key = (*newCharsLen << 24) | *charsOffset;
|
|
key.source = (src->parsedToken.charsLen/**newCharsLen*/ << 24) | src->parsedToken.charsOffset/**charsOffset*/;
|
|
key.rulesToParse = src->source;
|
|
|
|
//sourceToken = (UColToken *)uhash_iget(src->tailored, (int32_t)key);
|
|
sourceToken = (UColToken *)uhash_get(src->tailored, &key);
|
|
#endif
|
|
|
|
// here is how it should be. The situation such as &[before 1]a < x, should be
|
|
// resolved exactly as if we wrote &a > x.
|
|
// therefore, I don't really care if the UCA value before a has been changed.
|
|
// However, I do care if the strength between my element and the previous element
|
|
// is bigger then I wanted. So, if CE < baseCE and I wanted &[before 2], then i'll
|
|
// have to construct the base CE.
|
|
|
|
|
|
|
|
// if we found a tailored thing, we have to use the UCA value and construct
|
|
// a new reset token with constructed name
|
|
//if(sourceToken != NULL && sourceToken->strength != UCOL_TOK_RESET) {
|
|
// character to which we want to anchor is already tailored.
|
|
// We need to construct a new token which will be the anchor
|
|
// point
|
|
//*(src->extraCurrent-1) = 0xFFFE;
|
|
//*src->extraCurrent++ = (UChar)ch;
|
|
// grab before
|
|
src->parsedToken.charsOffset -= 10;
|
|
src->parsedToken.charsLen += 10;
|
|
src->lh[src->resultLen].baseCE = CE & 0xFFFFFF3F;
|
|
if(isContinuation(SecondCE)) {
|
|
src->lh[src->resultLen].baseContCE = SecondCE;
|
|
} else {
|
|
src->lh[src->resultLen].baseContCE = 0;
|
|
}
|
|
src->lh[src->resultLen].nextCE = 0;
|
|
src->lh[src->resultLen].nextContCE = 0;
|
|
src->lh[src->resultLen].previousCE = 0;
|
|
src->lh[src->resultLen].previousContCE = 0;
|
|
|
|
src->lh[src->resultLen].indirect = FALSE;
|
|
|
|
sourceToken = ucol_tok_initAReset(src, 0, &expandNext, parseError, status);
|
|
//}
|
|
}
|
|
|
|
return sourceToken;
|
|
|
|
}
|
|
|
|
uint32_t ucol_tok_assembleTokenList(UColTokenParser *src, UParseError *parseError, UErrorCode *status) {
|
|
UColToken *lastToken = NULL;
|
|
const UChar *parseEnd = NULL;
|
|
uint32_t expandNext = 0;
|
|
UBool variableTop = FALSE;
|
|
UBool top = FALSE;
|
|
uint16_t specs = 0;
|
|
UColTokListHeader *ListList = NULL;
|
|
|
|
src->parsedToken.strength = UCOL_TOK_UNSET;
|
|
|
|
ListList = src->lh;
|
|
|
|
if(U_FAILURE(*status)) {
|
|
return 0;
|
|
}
|
|
|
|
while(src->current < src->end) {
|
|
src->parsedToken.prefixOffset = 0;
|
|
|
|
parseEnd = ucol_tok_parseNextToken(src,
|
|
(UBool)(lastToken == NULL),
|
|
parseError,
|
|
status);
|
|
|
|
specs = src->parsedToken.flags;
|
|
|
|
|
|
variableTop = ((specs & UCOL_TOK_VARIABLE_TOP) != 0);
|
|
top = ((specs & UCOL_TOK_TOP) != 0);
|
|
|
|
if(U_SUCCESS(*status) && parseEnd != NULL) {
|
|
UColToken *sourceToken = NULL;
|
|
//uint32_t key = 0;
|
|
uint32_t lastStrength = UCOL_TOK_UNSET;
|
|
|
|
if(lastToken != NULL ) {
|
|
lastStrength = lastToken->strength;
|
|
}
|
|
|
|
//key = newCharsLen << 24 | charsOffset;
|
|
UColToken key;
|
|
key.source = src->parsedToken.charsLen << 24 | src->parsedToken.charsOffset;
|
|
key.rulesToParse = src->source;
|
|
|
|
/* 4 Lookup each source in the CharsToToken map, and find a sourceToken */
|
|
sourceToken = (UColToken *)uhash_get(src->tailored, &key);
|
|
|
|
if(src->parsedToken.strength != UCOL_TOK_RESET) {
|
|
if(lastToken == NULL) { /* this means that rules haven't started properly */
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,0,(int32_t)(src->end-src->source),parseError);
|
|
return 0;
|
|
}
|
|
/* 6 Otherwise (when relation != reset) */
|
|
if(sourceToken == NULL) {
|
|
/* If sourceToken is null, create new one, */
|
|
sourceToken = (UColToken *)uprv_malloc(sizeof(UColToken));
|
|
/* test for NULL */
|
|
if (sourceToken == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return 0;
|
|
}
|
|
sourceToken->rulesToParse = src->source;
|
|
sourceToken->source = src->parsedToken.charsLen << 24 | src->parsedToken.charsOffset;
|
|
|
|
sourceToken->debugSource = *(src->source + src->parsedToken.charsOffset);
|
|
|
|
sourceToken->prefix = src->parsedToken.prefixLen << 24 | src->parsedToken.prefixOffset;
|
|
sourceToken->debugPrefix = *(src->source + src->parsedToken.prefixOffset);
|
|
|
|
sourceToken->polarity = UCOL_TOK_POLARITY_POSITIVE; /* TODO: this should also handle reverse */
|
|
sourceToken->next = NULL;
|
|
sourceToken->previous = NULL;
|
|
sourceToken->noOfCEs = 0;
|
|
sourceToken->noOfExpCEs = 0;
|
|
// keep the flags around so that we know about before
|
|
sourceToken->flags = src->parsedToken.flags;
|
|
uhash_put(src->tailored, sourceToken, sourceToken, status);
|
|
} else {
|
|
/* we could have fished out a reset here */
|
|
if(sourceToken->strength != UCOL_TOK_RESET && lastToken != sourceToken) {
|
|
/* otherwise remove sourceToken from where it was. */
|
|
if(sourceToken->next != NULL) {
|
|
if(sourceToken->next->strength > sourceToken->strength) {
|
|
sourceToken->next->strength = sourceToken->strength;
|
|
}
|
|
sourceToken->next->previous = sourceToken->previous;
|
|
} else {
|
|
sourceToken->listHeader->last = sourceToken->previous;
|
|
}
|
|
|
|
if(sourceToken->previous != NULL) {
|
|
sourceToken->previous->next = sourceToken->next;
|
|
} else {
|
|
sourceToken->listHeader->first = sourceToken->next;
|
|
}
|
|
sourceToken->next = NULL;
|
|
sourceToken->previous = NULL;
|
|
}
|
|
}
|
|
|
|
sourceToken->strength = src->parsedToken.strength;
|
|
sourceToken->listHeader = lastToken->listHeader;
|
|
|
|
/*
|
|
1. Find the strongest strength in each list, and set strongestP and strongestN
|
|
accordingly in the headers.
|
|
*/
|
|
if(lastStrength == UCOL_TOK_RESET
|
|
|| sourceToken->listHeader->first == 0) {
|
|
/* If LAST is a reset
|
|
insert sourceToken in the list. */
|
|
if(sourceToken->listHeader->first == 0) {
|
|
sourceToken->listHeader->first = sourceToken;
|
|
sourceToken->listHeader->last = sourceToken;
|
|
} else { /* we need to find a place for us */
|
|
/* and we'll get in front of the same strength */
|
|
if(sourceToken->listHeader->first->strength <= sourceToken->strength) {
|
|
sourceToken->next = sourceToken->listHeader->first;
|
|
sourceToken->next->previous = sourceToken;
|
|
sourceToken->listHeader->first = sourceToken;
|
|
sourceToken->previous = NULL;
|
|
} else {
|
|
lastToken = sourceToken->listHeader->first;
|
|
while(lastToken->next != NULL && lastToken->next->strength > sourceToken->strength) {
|
|
lastToken = lastToken->next;
|
|
}
|
|
if(lastToken->next != NULL) {
|
|
lastToken->next->previous = sourceToken;
|
|
} else {
|
|
sourceToken->listHeader->last = sourceToken;
|
|
}
|
|
sourceToken->previous = lastToken;
|
|
sourceToken->next = lastToken->next;
|
|
lastToken->next = sourceToken;
|
|
}
|
|
}
|
|
} else {
|
|
/* Otherwise (when LAST is not a reset)
|
|
if polarity (LAST) == polarity(relation), insert sourceToken after LAST,
|
|
otherwise insert before.
|
|
when inserting after or before, search to the next position with the same
|
|
strength in that direction. (This is called postpone insertion). */
|
|
if(sourceToken != lastToken) {
|
|
if(lastToken->polarity == sourceToken->polarity) {
|
|
while(lastToken->next != NULL && lastToken->next->strength > sourceToken->strength) {
|
|
lastToken = lastToken->next;
|
|
}
|
|
sourceToken->previous = lastToken;
|
|
if(lastToken->next != NULL) {
|
|
lastToken->next->previous = sourceToken;
|
|
} else {
|
|
sourceToken->listHeader->last = sourceToken;
|
|
}
|
|
|
|
sourceToken->next = lastToken->next;
|
|
lastToken->next = sourceToken;
|
|
} else {
|
|
while(lastToken->previous != NULL && lastToken->previous->strength > sourceToken->strength) {
|
|
lastToken = lastToken->previous;
|
|
}
|
|
sourceToken->next = lastToken;
|
|
if(lastToken->previous != NULL) {
|
|
lastToken->previous->next = sourceToken;
|
|
} else {
|
|
sourceToken->listHeader->first = sourceToken;
|
|
}
|
|
sourceToken->previous = lastToken->previous;
|
|
lastToken->previous = sourceToken;
|
|
}
|
|
} else { /* repeated one thing twice in rules, stay with the stronger strength */
|
|
if(lastStrength < sourceToken->strength) {
|
|
sourceToken->strength = lastStrength;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if the token was a variable top, we're gonna put it in */
|
|
if(variableTop == TRUE && src->varTop == NULL) {
|
|
variableTop = FALSE;
|
|
src->varTop = sourceToken;
|
|
}
|
|
|
|
// Treat the expansions.
|
|
// There are two types of expansions: explicit (x / y) and reset based propagating expansions
|
|
// (&abc * d * e <=> &ab * d / c * e / c)
|
|
// if both of them are in effect for a token, they are combined.
|
|
|
|
sourceToken->expansion = src->parsedToken.extensionLen << 24 | src->parsedToken.extensionOffset;
|
|
|
|
if(expandNext != 0) {
|
|
if(sourceToken->strength == UCOL_PRIMARY) { /* primary strength kills off the implicit expansion */
|
|
expandNext = 0;
|
|
} else if(sourceToken->expansion == 0) { /* if there is no expansion, implicit is just added to the token */
|
|
sourceToken->expansion = expandNext;
|
|
} else { /* there is both explicit and implicit expansion. We need to make a combination */
|
|
uprv_memcpy(src->extraCurrent, src->source + (expandNext & 0xFFFFFF), (expandNext >> 24)*sizeof(UChar));
|
|
uprv_memcpy(src->extraCurrent+(expandNext >> 24), src->source + src->parsedToken.extensionOffset, src->parsedToken.extensionLen*sizeof(UChar));
|
|
sourceToken->expansion = (uint32_t)(((expandNext >> 24) + src->parsedToken.extensionLen)<<24 | (uint32_t)(src->extraCurrent - src->source));
|
|
src->extraCurrent += (expandNext >> 24) + src->parsedToken.extensionLen;
|
|
}
|
|
}
|
|
|
|
// This is just for debugging purposes
|
|
if(sourceToken->expansion != 0) {
|
|
sourceToken->debugExpansion = *(src->source + src->parsedToken.extensionOffset);
|
|
} else {
|
|
sourceToken->debugExpansion = 0;
|
|
}
|
|
// if the previous token was a reset before, the strength of this
|
|
// token must match the strength of before. Otherwise we have an
|
|
// undefined situation.
|
|
// In other words, we currently have a cludge which we use to
|
|
// represent &a >> x. This is written as &[before 2]a << x.
|
|
if((lastToken->flags & UCOL_TOK_BEFORE) != 0) {
|
|
uint8_t beforeStrength = (lastToken->flags & UCOL_TOK_BEFORE) - 1;
|
|
if(beforeStrength != sourceToken->strength) {
|
|
*status = U_INVALID_FORMAT_ERROR;
|
|
syntaxError(src->source,0,(int32_t)(src->end-src->source),parseError);
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
if(lastToken != NULL && lastStrength == UCOL_TOK_RESET) {
|
|
/* if the previous token was also a reset, */
|
|
/*this means that we have two consecutive resets */
|
|
/* and we want to remove the previous one if empty*/
|
|
if(src->resultLen > 0 && ListList[src->resultLen-1].first == NULL) {
|
|
src->resultLen--;
|
|
}
|
|
}
|
|
|
|
if(sourceToken == NULL) { /* this is a reset, but it might still be somewhere in the tailoring, in shorter form */
|
|
uint32_t searchCharsLen = src->parsedToken.charsLen;
|
|
while(searchCharsLen > 1 && sourceToken == NULL) {
|
|
searchCharsLen--;
|
|
//key = searchCharsLen << 24 | charsOffset;
|
|
UColToken key;
|
|
key.source = searchCharsLen << 24 | src->parsedToken.charsOffset;
|
|
key.rulesToParse = src->source;
|
|
sourceToken = (UColToken *)uhash_get(src->tailored, &key);
|
|
}
|
|
if(sourceToken != NULL) {
|
|
expandNext = (src->parsedToken.charsLen - searchCharsLen) << 24 | (src->parsedToken.charsOffset + searchCharsLen);
|
|
}
|
|
}
|
|
|
|
if((specs & UCOL_TOK_BEFORE) != 0) { /* we're doing before */
|
|
if(top == FALSE) { /* there is no indirection */
|
|
uint8_t strength = (specs & UCOL_TOK_BEFORE) - 1;
|
|
if(sourceToken != NULL && sourceToken->strength != UCOL_TOK_RESET) {
|
|
/* this is a before that is already ordered in the UCA - so we need to get the previous with good strength */
|
|
while(sourceToken->strength > strength && sourceToken->previous != NULL) {
|
|
sourceToken = sourceToken->previous;
|
|
}
|
|
/* here, either we hit the strength or NULL */
|
|
if(sourceToken->strength == strength) {
|
|
if(sourceToken->previous != NULL) {
|
|
sourceToken = sourceToken->previous;
|
|
} else { /* start of list */
|
|
sourceToken = sourceToken->listHeader->reset;
|
|
}
|
|
} else { /* we hit NULL */
|
|
/* we should be doing the else part */
|
|
sourceToken = sourceToken->listHeader->reset;
|
|
sourceToken = getVirginBefore(src, sourceToken, strength, parseError, status);
|
|
}
|
|
} else {
|
|
sourceToken = getVirginBefore(src, sourceToken, strength, parseError, status);
|
|
}
|
|
} else { /* this is both before and indirection */
|
|
top = FALSE;
|
|
ListList[src->resultLen].previousCE = 0;
|
|
ListList[src->resultLen].previousContCE = 0;
|
|
ListList[src->resultLen].indirect = TRUE;
|
|
/* we need to do slightly more work. we need to get the baseCE using the */
|
|
/* inverse UCA & getPrevious. The next bound is not set, and will be decided */
|
|
/* in ucol_bld */
|
|
uint8_t strength = (specs & UCOL_TOK_BEFORE) - 1;
|
|
uint32_t baseCE = ucolIndirectBoundaries[src->parsedToken.indirectIndex].startCE;
|
|
uint32_t baseContCE = ucolIndirectBoundaries[src->parsedToken.indirectIndex].startContCE;//&0xFFFFFF3F;
|
|
uint32_t CE = UCOL_NOT_FOUND, SecondCE = UCOL_NOT_FOUND;
|
|
|
|
UCAConstants *consts = (UCAConstants *)((uint8_t *)src->UCA->image + src->UCA->image->UCAConsts);
|
|
if((baseCE & 0xFF000000) >= (consts->UCA_PRIMARY_IMPLICIT_MIN<<24) && (baseCE & 0xFF000000) <= (consts->UCA_PRIMARY_IMPLICIT_MAX<<24) ) { /* implicits - */
|
|
uint32_t primary = baseCE & UCOL_PRIMARYMASK | (baseContCE & UCOL_PRIMARYMASK) >> 16;
|
|
uint32_t raw = uprv_uca_getRawFromImplicit(primary);
|
|
uint32_t primaryCE = uprv_uca_getImplicitFromRaw(raw-1);
|
|
CE = primaryCE & UCOL_PRIMARYMASK | 0x0505;
|
|
SecondCE = (primaryCE << 16) & UCOL_PRIMARYMASK | UCOL_CONTINUATION_MARKER;
|
|
} else {
|
|
/*int32_t invPos = ucol_inv_getPrevCE(baseCE, baseContCE, &CE, &SecondCE, strength);*/
|
|
ucol_inv_getPrevCE(src, baseCE, baseContCE, &CE, &SecondCE, strength);
|
|
}
|
|
|
|
ListList[src->resultLen].baseCE = CE;
|
|
ListList[src->resultLen].baseContCE = SecondCE;
|
|
ListList[src->resultLen].nextCE = 0;
|
|
ListList[src->resultLen].nextContCE = 0;
|
|
|
|
sourceToken = ucol_tok_initAReset(src, 0, &expandNext, parseError, status);
|
|
}
|
|
}
|
|
|
|
|
|
/* 5 If the relation is a reset:
|
|
If sourceToken is null
|
|
Create new list, create new sourceToken, make the baseCE from source, put
|
|
the sourceToken in ListHeader of the new list */
|
|
if(sourceToken == NULL) {
|
|
/*
|
|
3 Consider each item: relation, source, and expansion: e.g. ...< x / y ...
|
|
First convert all expansions into normal form. Examples:
|
|
If "xy" doesn't occur earlier in the list or in the UCA, convert &xy * c *
|
|
d * ... into &x * c/y * d * ...
|
|
Note: reset values can never have expansions, although they can cause the
|
|
very next item to have one. They may be contractions, if they are found
|
|
earlier in the list.
|
|
*/
|
|
if(top == FALSE) {
|
|
collIterate s;
|
|
uint32_t CE = UCOL_NOT_FOUND, SecondCE = UCOL_NOT_FOUND;
|
|
|
|
uprv_init_collIterate(src->UCA, src->source+src->parsedToken.charsOffset, src->parsedToken.charsLen, &s);
|
|
|
|
CE = ucol_getNextCE(src->UCA, &s, status);
|
|
UChar *expand = s.pos;
|
|
SecondCE = ucol_getNextCE(src->UCA, &s, status);
|
|
|
|
ListList[src->resultLen].baseCE = CE & 0xFFFFFF3F;
|
|
if(isContinuation(SecondCE)) {
|
|
ListList[src->resultLen].baseContCE = SecondCE;
|
|
} else {
|
|
ListList[src->resultLen].baseContCE = 0;
|
|
}
|
|
ListList[src->resultLen].nextCE = 0;
|
|
ListList[src->resultLen].nextContCE = 0;
|
|
ListList[src->resultLen].previousCE = 0;
|
|
ListList[src->resultLen].previousContCE = 0;
|
|
ListList[src->resultLen].indirect = FALSE;
|
|
sourceToken = ucol_tok_initAReset(src, expand, &expandNext, parseError, status);
|
|
} else { /* top == TRUE */
|
|
/* just use the supplied values */
|
|
top = FALSE;
|
|
ListList[src->resultLen].previousCE = 0;
|
|
ListList[src->resultLen].previousContCE = 0;
|
|
ListList[src->resultLen].indirect = TRUE;
|
|
ListList[src->resultLen].baseCE = ucolIndirectBoundaries[src->parsedToken.indirectIndex].startCE;
|
|
ListList[src->resultLen].baseContCE = ucolIndirectBoundaries[src->parsedToken.indirectIndex].startContCE;
|
|
ListList[src->resultLen].nextCE = ucolIndirectBoundaries[src->parsedToken.indirectIndex].limitCE;
|
|
ListList[src->resultLen].nextContCE = ucolIndirectBoundaries[src->parsedToken.indirectIndex].limitContCE;
|
|
|
|
sourceToken = ucol_tok_initAReset(src, 0, &expandNext, parseError, status);
|
|
|
|
}
|
|
} else { /* reset to something already in rules */
|
|
top = FALSE;
|
|
}
|
|
}
|
|
/* 7 After all this, set LAST to point to sourceToken, and goto step 3. */
|
|
lastToken = sourceToken;
|
|
} else {
|
|
if(U_FAILURE(*status)) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(src->resultLen > 0 && ListList[src->resultLen-1].first == NULL) {
|
|
src->resultLen--;
|
|
}
|
|
return src->resultLen;
|
|
}
|
|
|
|
void ucol_tok_initTokenList(UColTokenParser *src, const UChar *rules, const uint32_t rulesLength, const UCollator *UCA, UErrorCode *status) {
|
|
U_NAMESPACE_USE
|
|
|
|
uint32_t nSize = 0;
|
|
uint32_t estimatedSize = (2*rulesLength+UCOL_TOK_EXTRA_RULE_SPACE_SIZE);
|
|
if(U_FAILURE(*status)) {
|
|
return;
|
|
}
|
|
|
|
// set everything to zero, so that we can clean up gracefully
|
|
uprv_memset(src, 0, sizeof(UColTokenParser));
|
|
|
|
// first we need to find options that don't like to be normalized,
|
|
// like copy and remove...
|
|
//const UChar *openBrace = rules;
|
|
int32_t optionNumber = -1;
|
|
const UChar *setStart;
|
|
uint32_t i = 0;
|
|
while(i < rulesLength) {
|
|
if(rules[i] == 0x005B) {
|
|
// while((openBrace = u_strchr(openBrace, 0x005B)) != NULL) { // find open braces
|
|
//optionNumber = ucol_uprv_tok_readOption(openBrace+1, rules+rulesLength, &setStart);
|
|
optionNumber = ucol_uprv_tok_readOption(rules+i+1, rules+rulesLength, &setStart);
|
|
if(optionNumber == OPTION_OPTIMIZE) { /* copy - parts of UCA to tailoring */
|
|
USet *newSet = ucol_uprv_tok_readAndSetUnicodeSet(setStart, rules+rulesLength, status);
|
|
if(U_SUCCESS(*status)) {
|
|
if(src->copySet == NULL) {
|
|
src->copySet = newSet;
|
|
} else {
|
|
((UnicodeSet *)src->copySet)->addAll(*((UnicodeSet *)newSet));
|
|
uset_close(newSet);
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
} else if(optionNumber == OPTION_SUPPRESS_CONTRACTIONS) {
|
|
USet *newSet = ucol_uprv_tok_readAndSetUnicodeSet(setStart, rules+rulesLength, status);
|
|
if(U_SUCCESS(*status)) {
|
|
if(src->removeSet == NULL) {
|
|
src->removeSet = newSet;
|
|
} else {
|
|
((UnicodeSet *)src->removeSet)->addAll(*((UnicodeSet *)newSet));
|
|
uset_close(newSet);
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//openBrace++;
|
|
i++;
|
|
}
|
|
|
|
src->source = (UChar *)uprv_malloc(estimatedSize*sizeof(UChar));
|
|
/* test for NULL */
|
|
if (src->source == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
uprv_memset(src->source, 0, estimatedSize*sizeof(UChar));
|
|
nSize = unorm_normalize(rules, rulesLength, UNORM_NFD, 0, src->source, estimatedSize, status);
|
|
if(nSize > estimatedSize || *status == U_BUFFER_OVERFLOW_ERROR) {
|
|
*status = U_ZERO_ERROR;
|
|
src->source = (UChar *)uprv_realloc(src->source, (nSize+UCOL_TOK_EXTRA_RULE_SPACE_SIZE)*sizeof(UChar));
|
|
/* test for NULL */
|
|
if (src->source == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
nSize = unorm_normalize(rules, rulesLength, UNORM_NFD, 0, src->source, nSize+UCOL_TOK_EXTRA_RULE_SPACE_SIZE, status);
|
|
}
|
|
src->current = src->source;
|
|
src->end = src->source+nSize;
|
|
src->sourceCurrent = src->source;
|
|
src->extraCurrent = src->end+1; // Preserve terminating zero in the rule string so that option scanning works correctly
|
|
src->extraEnd = src->source+estimatedSize; //src->end+UCOL_TOK_EXTRA_RULE_SPACE_SIZE;
|
|
src->varTop = NULL;
|
|
src->UCA = UCA;
|
|
src->invUCA = ucol_initInverseUCA(status);
|
|
src->parsedToken.charsLen = 0;
|
|
src->parsedToken.charsOffset = 0;
|
|
src->parsedToken.extensionLen = 0;
|
|
src->parsedToken.extensionOffset = 0;
|
|
src->parsedToken.prefixLen = 0;
|
|
src->parsedToken.prefixOffset = 0;
|
|
src->parsedToken.flags = 0;
|
|
src->parsedToken.strength = UCOL_TOK_UNSET;
|
|
|
|
|
|
if(U_FAILURE(*status)) {
|
|
return;
|
|
}
|
|
src->tailored = uhash_open(uhash_hashTokens, uhash_compareTokens, NULL, status);
|
|
if(U_FAILURE(*status)) {
|
|
return;
|
|
}
|
|
uhash_setValueDeleter(src->tailored, uhash_freeBlock);
|
|
|
|
src->opts = (UColOptionSet *)uprv_malloc(sizeof(UColOptionSet));
|
|
/* test for NULL */
|
|
if (src->opts == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
|
|
uprv_memcpy(src->opts, UCA->options, sizeof(UColOptionSet));
|
|
|
|
// rulesToParse = src->source;
|
|
src->lh = 0;
|
|
src->listCapacity = 1024;
|
|
src->lh = (UColTokListHeader *)uprv_malloc(src->listCapacity*sizeof(UColTokListHeader));
|
|
//Test for NULL
|
|
if (src->lh == NULL) {
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
uprv_memset(src->lh, 0, src->listCapacity*sizeof(UColTokListHeader));
|
|
src->resultLen = 0;
|
|
|
|
UCAConstants *consts = (UCAConstants *)((uint8_t *)src->UCA->image + src->UCA->image->UCAConsts);
|
|
|
|
// UCOL_RESET_TOP_VALUE
|
|
setIndirectBoundaries(0, consts->UCA_LAST_NON_VARIABLE, consts->UCA_FIRST_IMPLICIT);
|
|
// UCOL_FIRST_PRIMARY_IGNORABLE
|
|
setIndirectBoundaries(1, consts->UCA_FIRST_PRIMARY_IGNORABLE, 0);
|
|
// UCOL_LAST_PRIMARY_IGNORABLE
|
|
setIndirectBoundaries(2, consts->UCA_LAST_PRIMARY_IGNORABLE, 0);
|
|
// UCOL_FIRST_SECONDARY_IGNORABLE
|
|
setIndirectBoundaries(3, consts->UCA_FIRST_SECONDARY_IGNORABLE, 0);
|
|
// UCOL_LAST_SECONDARY_IGNORABLE
|
|
setIndirectBoundaries(4, consts->UCA_LAST_SECONDARY_IGNORABLE, 0);
|
|
// UCOL_FIRST_TERTIARY_IGNORABLE
|
|
setIndirectBoundaries(5, consts->UCA_FIRST_TERTIARY_IGNORABLE, 0);
|
|
// UCOL_LAST_TERTIARY_IGNORABLE
|
|
setIndirectBoundaries(6, consts->UCA_LAST_TERTIARY_IGNORABLE, 0);
|
|
// UCOL_FIRST_VARIABLE
|
|
setIndirectBoundaries(7, consts->UCA_FIRST_VARIABLE, 0);
|
|
// UCOL_LAST_VARIABLE
|
|
setIndirectBoundaries(8, consts->UCA_LAST_VARIABLE, 0);
|
|
// UCOL_FIRST_NON_VARIABLE
|
|
setIndirectBoundaries(9, consts->UCA_FIRST_NON_VARIABLE, 0);
|
|
// UCOL_LAST_NON_VARIABLE
|
|
setIndirectBoundaries(10, consts->UCA_LAST_NON_VARIABLE, consts->UCA_FIRST_IMPLICIT);
|
|
// UCOL_FIRST_IMPLICIT
|
|
setIndirectBoundaries(11, consts->UCA_FIRST_IMPLICIT, 0);
|
|
// UCOL_LAST_IMPLICIT
|
|
setIndirectBoundaries(12, consts->UCA_LAST_IMPLICIT, consts->UCA_FIRST_TRAILING);
|
|
// UCOL_FIRST_TRAILING
|
|
setIndirectBoundaries(13, consts->UCA_FIRST_TRAILING, 0);
|
|
// UCOL_LAST_TRAILING
|
|
setIndirectBoundaries(14, consts->UCA_LAST_TRAILING, 0);
|
|
ucolIndirectBoundaries[14].limitCE = (consts->UCA_PRIMARY_SPECIAL_MIN<<24);
|
|
}
|
|
|
|
|
|
void ucol_tok_closeTokenList(UColTokenParser *src) {
|
|
if(src->copySet != NULL) {
|
|
uset_close(src->copySet);
|
|
}
|
|
if(src->removeSet != NULL) {
|
|
uset_close(src->removeSet);
|
|
}
|
|
if(src->tailored != NULL) {
|
|
uhash_close(src->tailored);
|
|
}
|
|
if(src->lh != NULL) {
|
|
uprv_free(src->lh);
|
|
}
|
|
if(src->source != NULL) {
|
|
uprv_free(src->source);
|
|
}
|
|
if(src->opts != NULL) {
|
|
uprv_free(src->opts);
|
|
}
|
|
}
|
|
|
|
#endif /* #if !UCONFIG_NO_COLLATION */
|
|
|