mirror of
https://github.com/rhasspy/espeak-ng.git
synced 2026-11-13 17:23:44 +00:00
Revert the new (broken) replacement rule logic.
The replacement tests for bs, hr, and sr are no longer marked as broken as they work using the old code. The mk tests keep the broken annotation, as they don't work in the old code either. This reverts commit801a8d197c. This reverts commit64d5701e5e. This reverts commit3b51ebf617. This reverts commit1fd235d2c0. This reverts commit9f0667de86.
This commit is contained in:
@@ -1448,53 +1448,30 @@ static espeak_ng_STATUS compile_dictrules(FILE *f_in, FILE *f_out, char *fname_t
|
||||
break;
|
||||
case 2: // .replace
|
||||
{
|
||||
int from[N_WORD_BYTES + 1] = { 0 }; // Size of N_WORD_BYTES + null byte
|
||||
int to[N_WORD_BYTES + 1] = { 0 };
|
||||
int replace1;
|
||||
int replace2;
|
||||
char *p;
|
||||
|
||||
p = buf;
|
||||
|
||||
while (isspace2(*p)) // skip spaces in xx_rules file
|
||||
p++;
|
||||
|
||||
replace1 = 0;
|
||||
replace2 = 0;
|
||||
while (isspace2(*p)) p++;
|
||||
ix = 0;
|
||||
while ((unsigned char) (*p) > ' ') { // prepare 'from' characters
|
||||
p += utf8_in(&from[ix], p);
|
||||
ix++;
|
||||
while ((unsigned char)(*p) > 0x20) { // not space or zero-byte
|
||||
p += utf8_in(&c, p);
|
||||
replace1 += (c << ix);
|
||||
ix += 16;
|
||||
}
|
||||
while (isspace2(*p)) // skip spaces again
|
||||
p++;
|
||||
while (isspace2(*p)) p++;
|
||||
ix = 0;
|
||||
while ((unsigned char) (*p) > ' ') { // prepare 'to' characters
|
||||
p += utf8_in(&to[ix], p);
|
||||
ix++;
|
||||
while ((unsigned char)(*p) > 0x20) {
|
||||
p += utf8_in(&c, p);
|
||||
replace2 += (c << ix);
|
||||
ix += 16;
|
||||
}
|
||||
// save into file
|
||||
if (from[0] != 0) {
|
||||
ix = 0;
|
||||
while (from[ix] != 0) {
|
||||
Write4Bytes(f_out, from[ix]);
|
||||
ix++;
|
||||
if (ix > N_WORD_BYTES / 4) {
|
||||
fprintf(stderr,
|
||||
"Too long .replace 'from' part (limit is: %d characters)\n",
|
||||
N_WORD_BYTES / 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Write4Bytes(f_out, ' '); // end of 'from'
|
||||
ix = 0;
|
||||
while (to[ix] != 0) {
|
||||
Write4Bytes(f_out, to[ix]);
|
||||
ix++;
|
||||
if (ix > N_WORD_BYTES / 4) {
|
||||
fprintf(stderr,
|
||||
"Too long .replace 'to' part (limit is: %d characters)\n",
|
||||
N_WORD_BYTES / 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Write4Bytes(f_out, ' '); // end of 'to'
|
||||
if (replace1 != 0) {
|
||||
Write4Bytes(f_out, replace1); // write as little-endian
|
||||
Write4Bytes(f_out, replace2); // if big-endian, reverse the bytes in LoadDictionary()
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -152,7 +152,7 @@ static void InitGroups(Translator *tr)
|
||||
pw = (unsigned int *)(((intptr_t)p+4) & ~3); // advance to next word boundary
|
||||
tr->langopts.replace_chars = pw;
|
||||
while (pw[0] != 0)
|
||||
pw ++; // find the end of the replacement list
|
||||
pw += 2; // find the end of the replacement list, each entry is 2 words.
|
||||
p = (char *)(pw+1);
|
||||
|
||||
#ifdef ARCH_BIG
|
||||
|
||||
+66
-145
@@ -1790,158 +1790,81 @@ static int EmbeddedCommand(unsigned int *source_index_out)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool GetNextTranslateChars(const int **from, const int **to, bool next_step)
|
||||
// handle .replace rule in xx_rules file
|
||||
static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in, int *insert, int *wordflags)
|
||||
{
|
||||
// Set pointers to next 'from' and 'to' part for replacement
|
||||
// next_step indicates second and succeeding replacement group
|
||||
// Return true if got data, false, if reached end
|
||||
if (next_step) { // first 'from' is pointed already
|
||||
int spaces = 0;
|
||||
while (**from != 0 && spaces < 2) { // Get next 'from' position skipping 'to' part
|
||||
(*from)++;
|
||||
if (**from == ' ')
|
||||
spaces++;
|
||||
}
|
||||
while (**from == ' ') // skip remaining spaces, if exists
|
||||
(*from)++;
|
||||
int ix;
|
||||
unsigned int word;
|
||||
unsigned int new_c, c2 = ' ', c_lower;
|
||||
int upper_case = 0;
|
||||
static bool ignore_next = false;
|
||||
const unsigned int *replace_chars;
|
||||
|
||||
if (ignore_next) {
|
||||
ignore_next = false;
|
||||
return 8;
|
||||
}
|
||||
if (**from == 0)
|
||||
return false;
|
||||
if (c == 0) return 0;
|
||||
|
||||
*to = *from; // start seeking 'to' from 'from' position
|
||||
while (**to != 0 && **to != ' ') // get next 'to' position
|
||||
(*to)++;
|
||||
if ((replace_chars = tr->langopts.replace_chars) == NULL)
|
||||
return c;
|
||||
|
||||
while (**to == ' ') // skip remaining spaces, if exists
|
||||
(*to)++;
|
||||
// there is a list of character codes to be substituted with alternative codes
|
||||
|
||||
return **to != 0;
|
||||
}
|
||||
|
||||
static void ReplaceMatchingChars(char **matchstart, const int *from, const int *to, int upper_case)
|
||||
{
|
||||
// Replace characters from 'from' to 'to' group in source clause, starting
|
||||
// with place indicated in matchstart.
|
||||
// Rewrites remaining part of source, if length of 'from' and 'to' groups differs.
|
||||
|
||||
char tmpsource[N_TR_SOURCE + 40]; // Prepare working copy of source
|
||||
memcpy(&tmpsource, *matchstart, N_TR_SOURCE + 40);
|
||||
|
||||
// prepare from part
|
||||
char charfrom[N_WORD_BYTES + 1] = { 0 };
|
||||
int usedfrom = 0;
|
||||
int const *pintfrom = from;
|
||||
char *pcharfrom = charfrom;
|
||||
while (*pintfrom != ' ') {
|
||||
usedfrom += utf8_out(*pintfrom, pcharfrom + usedfrom);
|
||||
pintfrom++;
|
||||
if (iswupper(c_lower = c)) {
|
||||
c_lower = towlower2(c, tr);
|
||||
upper_case = 1;
|
||||
}
|
||||
|
||||
// prepare to part
|
||||
char charto[N_WORD_BYTES + 1] = { 0 };
|
||||
int usedto = 0;
|
||||
int const *pintto = to;
|
||||
char *pcharto = charto;
|
||||
while (*pintto != 0 && *pintto != ' ') {
|
||||
usedto += utf8_out(*pintto, pcharto + usedto);
|
||||
pintto++;
|
||||
}
|
||||
|
||||
// Set up replacement in temporary buffer
|
||||
char *psource = *matchstart;
|
||||
char *ptmpsource = tmpsource;
|
||||
pcharto = charto;
|
||||
|
||||
// Do replacement writing 'to' part in buffer
|
||||
while (*pcharto != 0 ) {
|
||||
if(upper_case) { // TODO: should check for capital letter from source (maybe using word_flags)
|
||||
*ptmpsource = toupper(*pcharto);
|
||||
upper_case = 0; // capitalize only first character if necessary (will work in most cases)
|
||||
}
|
||||
else
|
||||
*ptmpsource = *pcharto;
|
||||
pcharto++;
|
||||
ptmpsource++;
|
||||
}
|
||||
|
||||
// If 'to' part is different than 'from' part, fix remaining part of the buffer.
|
||||
// (Read after 'from' in source, write after 'to' in buffer.
|
||||
// Don't touch last 40 bytes of source, which could contain control data.
|
||||
if (usedfrom != usedto) {
|
||||
psource = *matchstart + usedfrom;
|
||||
while (*psource != 0 && psource < &source[N_TR_SOURCE]
|
||||
&& ptmpsource < &tmpsource[N_TR_SOURCE]) {
|
||||
*ptmpsource = *psource;
|
||||
ptmpsource++;
|
||||
psource++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write temporary buffer back to source
|
||||
psource = *matchstart;
|
||||
ptmpsource = tmpsource;
|
||||
while (*ptmpsource != 0 && ptmpsource < &tmpsource[N_TR_SOURCE]
|
||||
&& psource < &source[N_TR_SOURCE]) {
|
||||
*psource = *ptmpsource;
|
||||
ptmpsource++;
|
||||
psource++;
|
||||
}
|
||||
|
||||
// if requested, print trace
|
||||
if (option_phonemes & espeakPHONEMES_TRACE)
|
||||
fprintf(f_trans, "Replace %s > %s\n", charfrom, charto);
|
||||
}
|
||||
|
||||
static void TranslateChars(Translator *tr, char *source)
|
||||
{
|
||||
// Replace characters using Translator.replace_chars in passed source buffer
|
||||
char *startmatch;
|
||||
const int *from;
|
||||
const int *to;
|
||||
bool upper_case = false;
|
||||
|
||||
if ((from = (const int *)tr->langopts.replace_chars) == NULL)
|
||||
return;
|
||||
|
||||
char *curchar = source; // pointer to the source clause
|
||||
int curint; // current UTF-16 character
|
||||
int used; // bytes used in buffer
|
||||
const int *curfrom; // pointer to current 'from'
|
||||
bool next_step = false; // signal to jumping to next 'from' part in dictionary
|
||||
while (GetNextTranslateChars(&from, &to, next_step)) {
|
||||
next_step = true;
|
||||
curfrom = from;
|
||||
curchar = source;
|
||||
startmatch = 0;
|
||||
curint = 0;
|
||||
do {
|
||||
used = utf8_in(&curint, curchar);
|
||||
if (curint < ' ') { // skip control characters
|
||||
curchar += used;
|
||||
continue;
|
||||
new_c = 0;
|
||||
for (ix = 0; (word = replace_chars[ix]) != 0; ix += 2) {
|
||||
if (c_lower == (word & 0xffff)) {
|
||||
if ((word >> 16) == 0) {
|
||||
new_c = replace_chars[ix+1];
|
||||
break;
|
||||
}
|
||||
if ((upper_case = iswupper(curint)) == true) // remember, if uppercase
|
||||
curint = ucd_tolower(curint);
|
||||
|
||||
if (curint == *curfrom) { // if matches
|
||||
if (startmatch == 0)
|
||||
startmatch = curchar;
|
||||
curfrom++; // move check to next character
|
||||
if (*curfrom == ' ') { // if full match, replace
|
||||
ReplaceMatchingChars(&startmatch, from, to, upper_case);
|
||||
startmatch = 0; // reset match to start of 'from'
|
||||
curfrom = from;
|
||||
}
|
||||
} else { // reset match to start of 'from'
|
||||
startmatch = 0;
|
||||
curfrom = from;
|
||||
if ((word >> 16) == (unsigned int)towlower2(next_in, tr)) {
|
||||
new_c = replace_chars[ix+1];
|
||||
ignore_next = true;
|
||||
break;
|
||||
}
|
||||
curchar += used;
|
||||
} while (curint > 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (new_c == 0)
|
||||
return c; // no substitution
|
||||
|
||||
if (new_c & 0xffe00000) {
|
||||
// there is a second character to be inserted
|
||||
// don't convert the case of the second character unless the next letter is also upper case
|
||||
c2 = new_c >> 16;
|
||||
if (upper_case && iswupper(next_in))
|
||||
c2 = ucd_toupper(c2);
|
||||
*insert = c2;
|
||||
new_c &= 0xffff;
|
||||
}
|
||||
|
||||
if (upper_case)
|
||||
new_c = ucd_toupper(new_c);
|
||||
|
||||
*wordflags |= FLAG_CHAR_REPLACED;
|
||||
if (option_phonemes & espeakPHONEMES_TRACE) {
|
||||
char msg[21] = {'R','e','p','l','a','c','e',':',' '};
|
||||
char *index = msg;
|
||||
index += 9;
|
||||
index += utf8_out(c, index);
|
||||
*index++ = ' ';
|
||||
*index++ = '>';
|
||||
*index++ = ' ';
|
||||
index += utf8_out(new_c, index);
|
||||
index += utf8_out(c2, index);
|
||||
*index = 0;
|
||||
fprintf(f_trans, "%s\n", msg);
|
||||
}
|
||||
return new_c;
|
||||
}
|
||||
|
||||
static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert)
|
||||
static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert, int *wordflags)
|
||||
{
|
||||
// To allow language specific examination and replacement of characters
|
||||
|
||||
@@ -2007,7 +1930,8 @@ static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c,
|
||||
}
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
// handle .replace rule in xx_rules file
|
||||
return SubstituteChar(tr, c, next_in, insert, wordflags);
|
||||
}
|
||||
|
||||
static const char *UCase_ga[] = { "bp", "bhf", "dt", "gc", "hA", "mb", "nd", "ng", "ts", "tA", "nA", NULL };
|
||||
@@ -2180,9 +2104,6 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change)
|
||||
}
|
||||
words[0].length = k;
|
||||
|
||||
// handle .replace rule of xx_rules file
|
||||
TranslateChars(tr, source);
|
||||
|
||||
while (!finished && (ix < (int)sizeof(sbuf) - 1) && (n_ph_list2 < N_PHONEME_LIST-4)) {
|
||||
prev_out2 = prev_out;
|
||||
utf8_in2(&prev_out, &sbuf[ix-1], 1);
|
||||
@@ -2285,7 +2206,7 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change)
|
||||
word_flags |= FLAG_COMMA_AFTER;
|
||||
}
|
||||
// language specific character translations
|
||||
c = TranslateChar(tr, &source[source_index], prev_in, c, next_in, &char_inserted);
|
||||
c = TranslateChar(tr, &source[source_index], prev_in, c, next_in, &char_inserted, &word_flags);
|
||||
if (c == 8)
|
||||
continue; // ignore this character
|
||||
|
||||
|
||||
Reference in New Issue
Block a user