eSpeak NG is an open source speech synthesizer that supports more than hundred languages and accents.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

compiledict.cpp 40KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943
  1. /***************************************************************************
  2. * Copyright (C) 2005 to 2013 by Jonathan Duddington *
  3. * email: [email protected] *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 3 of the License, or *
  8. * (at your option) any later version. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License *
  16. * along with this program; if not, write see: *
  17. * <http://www.gnu.org/licenses/>. *
  18. ***************************************************************************/
  19. #include "StdAfx.h"
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <wctype.h>
  25. #include "speak_lib.h"
  26. #include "speech.h"
  27. #include "phoneme.h"
  28. #include "synthesize.h"
  29. #include "translate.h"
  30. extern void Write4Bytes(FILE *f, int value);
  31. int HashDictionary(const char *string);
  32. static FILE *f_log = NULL;
  33. extern char *dir_dictionary;
  34. extern char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes
  35. static int linenum;
  36. static int error_count;
  37. static int text_mode = 0;
  38. static int debug_flag = 0;
  39. static int error_need_dictionary = 0;
  40. static int hash_counts[N_HASH_DICT];
  41. static char *hash_chains[N_HASH_DICT];
  42. static char letterGroupsDefined[N_LETTER_GROUPS];
  43. MNEM_TAB mnem_rules[] = {
  44. {"unpr", 0x01},
  45. {"w_alt1", 0x11},
  46. {"w_alt2", 0x12},
  47. {"w_alt3", 0x13},
  48. // {"w_alt4", 0x14},
  49. {"w_alt", 0x11}, // note: put longer names before their sub-strings
  50. {"p_alt1", 0x21},
  51. {"p_alt2", 0x22},
  52. {"p_alt3", 0x23},
  53. // {"p_alt4", 0x24},
  54. {"p_alt", 0x21},
  55. {NULL, -1} };
  56. MNEM_TAB mnem_flags[] = {
  57. // these in the first group put a value in bits0-3 of dictionary_flags
  58. {"$1", 0x41}, // stress on 1st syllable
  59. {"$2", 0x42}, // stress on 2nd syllable
  60. {"$3", 0x43},
  61. {"$4", 0x44},
  62. {"$5", 0x45},
  63. {"$6", 0x46},
  64. {"$7", 0x47},
  65. {"$u", 0x48}, // reduce to unstressed
  66. {"$u1", 0x49},
  67. {"$u2", 0x4a},
  68. {"$u3", 0x4b},
  69. {"$u+", 0x4c}, // reduce to unstressed, but stress at end of clause
  70. {"$u1+", 0x4d},
  71. {"$u2+", 0x4e},
  72. {"$u3+", 0x4f},
  73. // these set the corresponding numbered bit if dictionary_flags
  74. {"$pause", 8}, /* ensure pause before this word */
  75. {"$only", 9}, /* only match on this word without suffix */
  76. {"$onlys", 10}, /* only match with none, or with 's' suffix */
  77. {"$strend", 11}, /* full stress if at end of clause */
  78. {"$strend2", 12}, /* full stress if at end of clause, or only followed by unstressed */
  79. {"$unstressend",13}, /* reduce stress at end of clause */
  80. {"$atend", 14}, /* use this pronunciation if at end of clause */
  81. {"$atstart", 15}, // use this pronunciation at start of clause
  82. {"$abbrev", 17}, /* use this pronuciation rather than split into letters */
  83. {"$stem", 18}, // must have a suffix
  84. // language specific
  85. {"$double", 19}, // IT double the initial consonant of next word
  86. {"$alt", 20}, // use alternative pronunciation
  87. {"$alt1", 20}, // synonym for $alt
  88. {"$alt2", 21},
  89. {"$alt3", 22},
  90. {"$combine", 23}, // Combine with the next word
  91. {"$dot", 24}, // ignore '.' after this word (abbreviation)
  92. {"$hasdot", 25}, // use this pronunciation if there is a dot after the word
  93. {"$max3", 27}, // limit to 3 repetitions
  94. {"$brk", 28}, // a shorter $pause
  95. {"$text", 29}, // word translates to replcement text, not phonemes
  96. // flags in dictionary word 2
  97. {"$verbf", 0x20}, /* verb follows */
  98. {"$verbsf", 0x21}, /* verb follows, allow -s suffix */
  99. {"$nounf", 0x22}, /* noun follows */
  100. {"$pastf", 0x23}, /* past tense follows */
  101. {"$verb", 0x24}, /* use this pronunciation when its a verb */
  102. {"$noun", 0x25}, /* use this pronunciation when its a noun */
  103. {"$past", 0x26}, /* use this pronunciation when its past tense */
  104. {"$verbextend",0x28}, /* extend influence of 'verb follows' */
  105. {"$capital", 0x29}, /* use this pronunciation if initial letter is upper case */
  106. {"$allcaps", 0x2a}, /* use this pronunciation if initial letter is upper case */
  107. {"$accent", 0x2b}, // character name is base-character name + accent name
  108. {"$sentence",0x2d}, // only if this clause is a sentence (i.e. terminator is {. ? !} not {, ; :}
  109. // doesn't set dictionary_flags
  110. {"$?", 100}, // conditional rule, followed by byte giving the condition number
  111. {"$textmode", 200},
  112. {"$phonememode", 201},
  113. {NULL, -1}
  114. };
  115. #define LEN_GROUP_NAME 12
  116. typedef struct {
  117. char name[LEN_GROUP_NAME+1];
  118. unsigned int start;
  119. unsigned int length;
  120. int group3_ix;
  121. } RGROUP;
  122. int isspace2(unsigned int c)
  123. {//=========================
  124. // can't use isspace() because on Windows, isspace(0xe1) gives TRUE !
  125. int c2;
  126. if(((c2 = (c & 0xff)) == 0) || (c > ' '))
  127. return(0);
  128. return(1);
  129. }
  130. static FILE *fopen_log(const char *fname,const char *access)
  131. {//==================================================
  132. // performs fopen, but produces error message to f_log if it fails
  133. FILE *f;
  134. if((f = fopen(fname,access)) == NULL)
  135. {
  136. if(f_log != NULL)
  137. fprintf(f_log,"Can't access (%s) file '%s'\n",access,fname);
  138. }
  139. return(f);
  140. }
  141. const char *LookupMnemName(MNEM_TAB *table, const int value)
  142. //==========================================================
  143. /* Lookup a mnemonic string in a table, return its name */
  144. {
  145. while(table->mnem != NULL)
  146. {
  147. if(table->value==value)
  148. return(table->mnem);
  149. table++;
  150. }
  151. return(""); /* not found */
  152. } /* end of LookupMnemValue */
  153. char *print_dictionary_flags(unsigned int *flags)
  154. {//==============================================
  155. static char buf[20];
  156. sprintf(buf,"%s 0x%x/%x",LookupMnemName(mnem_flags,(flags[0] & 0xf)+0x40), flags[0], flags[1]);
  157. return(buf);
  158. }
  159. char *DecodeRule(const char *group_chars, int group_length, char *rule, int control)
  160. {//=================================================================================
  161. /* Convert compiled match template to ascii */
  162. unsigned char rb;
  163. unsigned char c;
  164. char *p;
  165. int ix;
  166. int match_type;
  167. int finished=0;
  168. int value;
  169. int linenum=0;
  170. int flags;
  171. int suffix_char;
  172. int condition_num=0;
  173. int at_start = 0;
  174. const char *name;
  175. char buf[60];
  176. char buf_pre[60];
  177. char suffix[20];
  178. static char output[60];
  179. static char symbols[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
  180. '&','%','+','#','S','D','Z','A','L','!',' ','@','?','J','N','K','V','?','T','X','?','W'};
  181. static char symbols_lg[] = {'A','B','C','H','F','G','Y'};
  182. match_type = 0;
  183. buf_pre[0] = 0;
  184. for(ix=0; ix<group_length; ix++)
  185. {
  186. buf[ix] = group_chars[ix];
  187. }
  188. buf[ix] = 0;
  189. p = &buf[strlen(buf)];
  190. while(!finished)
  191. {
  192. rb = *rule++;
  193. if(rb <= RULE_LINENUM)
  194. {
  195. switch(rb)
  196. {
  197. case 0:
  198. case RULE_PHONEMES:
  199. finished=1;
  200. break;
  201. case RULE_PRE_ATSTART:
  202. at_start = 1; // drop through to next case
  203. case RULE_PRE:
  204. match_type = RULE_PRE;
  205. *p = 0;
  206. p = buf_pre;
  207. break;
  208. case RULE_POST:
  209. match_type = RULE_POST;
  210. *p = 0;
  211. strcat(buf," (");
  212. p = &buf[strlen(buf)];
  213. break;
  214. case RULE_PH_COMMON:
  215. break;
  216. case RULE_CONDITION:
  217. /* conditional rule, next byte gives condition number */
  218. condition_num = *rule++;
  219. break;
  220. case RULE_LINENUM:
  221. value = (rule[1] & 0xff) - 1;
  222. linenum = (rule[0] & 0xff) - 1 + (value * 255);
  223. rule+=2;
  224. break;
  225. }
  226. continue;
  227. }
  228. if(rb == RULE_DOLLAR)
  229. {
  230. value = *rule++ & 0xff;
  231. if((value != 0x01) || (control & FLAG_UNPRON_TEST))
  232. {
  233. p[0] = '$';
  234. name = LookupMnemName(mnem_rules, value);
  235. strcpy(&p[1],name);
  236. p += (strlen(name)+1);
  237. }
  238. c = ' ';
  239. }
  240. else
  241. if(rb == RULE_ENDING)
  242. {
  243. static const char *flag_chars = "eipvdfq tba ";
  244. flags = ((rule[0] & 0x7f)<< 8) + (rule[1] & 0x7f);
  245. suffix_char = 'S';
  246. if(flags & (SUFX_P >> 8))
  247. suffix_char = 'P';
  248. sprintf(suffix,"%c%d",suffix_char,rule[2] & 0x7f);
  249. rule += 3;
  250. for(ix=0;ix<9;ix++)
  251. {
  252. if(flags & 1)
  253. sprintf(&suffix[strlen(suffix)],"%c",flag_chars[ix]);
  254. flags = (flags >> 1);
  255. }
  256. strcpy(p,suffix);
  257. p += strlen(suffix);
  258. c = ' ';
  259. }
  260. else
  261. if(rb == RULE_LETTERGP)
  262. {
  263. c = symbols_lg[*rule++ - 'A'];
  264. }
  265. else
  266. if(rb == RULE_LETTERGP2)
  267. {
  268. value = *rule++ - 'A';
  269. p[0] = 'L';
  270. p[1] = (value / 10) + '0';
  271. c = (value % 10) + '0';
  272. if(match_type == RULE_PRE)
  273. {
  274. p[0] = c;
  275. c = 'L';
  276. }
  277. p+=2;
  278. }
  279. else
  280. if(rb <= RULE_LAST_RULE)
  281. c = symbols[rb];
  282. else
  283. if(rb == RULE_SPACE)
  284. c = '_';
  285. else
  286. c = rb;
  287. *p++ = c;
  288. }
  289. *p = 0;
  290. p = output;
  291. if(linenum > 0)
  292. {
  293. sprintf(p,"%5d:\t",linenum);
  294. p += 7;
  295. }
  296. if(condition_num > 0)
  297. {
  298. sprintf(p,"?%d ",condition_num);
  299. p = &p[strlen(p)];
  300. }
  301. if(((ix = strlen(buf_pre)) > 0) || at_start)
  302. {
  303. if(at_start)
  304. *p++ = '_';
  305. while(--ix >= 0)
  306. *p++ = buf_pre[ix];
  307. *p++ = ')';
  308. *p++ = ' ';
  309. }
  310. *p = 0;
  311. strcat(p,buf);
  312. ix = strlen(output);
  313. while(ix < 8)
  314. output[ix++]=' ';
  315. output[ix]=0;
  316. return(output);
  317. } /* end of DecodeRule */
  318. static int compile_line(char *linebuf, char *dict_line, int *hash)
  319. {//===============================================================
  320. // Compile a line in the language_list file
  321. unsigned char c;
  322. char *p;
  323. char *word;
  324. char *phonetic;
  325. unsigned int ix;
  326. int step;
  327. unsigned int n_flag_codes = 0;
  328. int flagnum;
  329. int flag_offset;
  330. int length;
  331. int multiple_words = 0;
  332. int multiple_numeric_hyphen = 0;
  333. char *multiple_string = NULL;
  334. char *multiple_string_end = NULL;
  335. int len_word;
  336. int len_phonetic;
  337. int text_not_phonemes; // this word specifies replacement text, not phonemes
  338. unsigned int wc;
  339. int all_upper_case;
  340. char *mnemptr;
  341. unsigned char flag_codes[100];
  342. char encoded_ph[200];
  343. unsigned char bad_phoneme[4];
  344. static char nullstring[] = {0};
  345. text_not_phonemes = 0;
  346. phonetic = word = nullstring;
  347. p = linebuf;
  348. // while(isspace2(*p)) p++;
  349. #ifdef deleted
  350. if(*p == '$')
  351. {
  352. if(memcmp(p,"$textmode",9) == 0)
  353. {
  354. text_mode = 1;
  355. return(0);
  356. }
  357. if(memcmp(p,"$phonememode",12) == 0)
  358. {
  359. text_mode = 0;
  360. return(0);
  361. }
  362. }
  363. #endif
  364. step = 0;
  365. c = 0;
  366. while(c != '\n')
  367. {
  368. c = *p;
  369. if((c == '?') && (step==0))
  370. {
  371. // conditional rule, allow only if the numbered condition is set for the voice
  372. flag_offset = 100;
  373. p++;
  374. if(*p == '!')
  375. {
  376. // allow only if the numbered condition is NOT set
  377. flag_offset = 132;
  378. p++;
  379. }
  380. ix = 0;
  381. if(isdigit(*p))
  382. {
  383. ix += (*p-'0');
  384. p++;
  385. }
  386. if(isdigit(*p))
  387. {
  388. ix = ix*10 + (*p-'0');
  389. p++;
  390. }
  391. flag_codes[n_flag_codes++] = ix + flag_offset;
  392. c = *p;
  393. }
  394. if((c == '$') && isalnum(p[1]))
  395. {
  396. /* read keyword parameter */
  397. mnemptr = p;
  398. while(!isspace2(c = *p)) p++;
  399. *p = 0;
  400. flagnum = LookupMnem(mnem_flags,mnemptr);
  401. if(flagnum > 0)
  402. {
  403. if(flagnum == 200)
  404. {
  405. text_mode = 1;
  406. }
  407. else
  408. if(flagnum == 201)
  409. {
  410. text_mode = 0;
  411. }
  412. else
  413. if(flagnum == BITNUM_FLAG_TEXTMODE)
  414. {
  415. text_not_phonemes = 1;
  416. }
  417. else
  418. {
  419. flag_codes[n_flag_codes++] = flagnum;
  420. }
  421. }
  422. else
  423. {
  424. fprintf(f_log,"%5d: Unknown keyword: %s\n",linenum,mnemptr);
  425. error_count++;
  426. }
  427. }
  428. if((c == '/') && (p[1] == '/') && (multiple_words==0))
  429. {
  430. c = '\n'; /* "//" treat comment as end of line */
  431. }
  432. switch(step)
  433. {
  434. case 0:
  435. if(c == '(')
  436. {
  437. multiple_words = 1;
  438. word = p+1;
  439. step = 1;
  440. }
  441. else
  442. if(!isspace2(c))
  443. {
  444. word = p;
  445. step = 1;
  446. }
  447. break;
  448. case 1:
  449. if((c == '-') && multiple_words)
  450. {
  451. if(isdigit(word[0]))
  452. {
  453. multiple_numeric_hyphen = 1;
  454. }
  455. // else // ???
  456. {
  457. flag_codes[n_flag_codes++] = BITNUM_FLAG_HYPHENATED;
  458. }
  459. c = ' ';
  460. }
  461. if(isspace2(c))
  462. {
  463. p[0] = 0; /* terminate english word */
  464. if(multiple_words)
  465. {
  466. multiple_string = multiple_string_end = p+1;
  467. step = 2;
  468. }
  469. else
  470. {
  471. step = 3;
  472. }
  473. }
  474. else
  475. if(c == ')')
  476. {
  477. if(multiple_words)
  478. {
  479. p[0] = 0;
  480. multiple_words = 0;
  481. step = 3;
  482. }
  483. else
  484. if(word[0] != '_')
  485. {
  486. fprintf(f_log, "%5d: Missing '('\n", linenum);
  487. error_count++;
  488. step = 3;
  489. }
  490. }
  491. break;
  492. case 2:
  493. if(isspace2(c))
  494. {
  495. multiple_words++;
  496. }
  497. else
  498. if(c == ')')
  499. {
  500. p[0] = ' '; // terminate extra string
  501. multiple_string_end = p+1;
  502. step = 3;
  503. }
  504. break;
  505. case 3:
  506. if(!isspace2(c))
  507. {
  508. phonetic = p;
  509. step = 4;
  510. }
  511. break;
  512. case 4:
  513. if(isspace2(c))
  514. {
  515. p[0] = 0; /* terminate phonetic */
  516. step = 5;
  517. }
  518. break;
  519. case 5:
  520. break;
  521. }
  522. p++;
  523. }
  524. if(word[0] == 0)
  525. {
  526. return(0); /* blank line */
  527. }
  528. if(text_mode)
  529. text_not_phonemes = 1;
  530. if(text_not_phonemes)
  531. {
  532. if(word[0] == '_')
  533. {
  534. // This is a special word, used by eSpeak. Translate this into phonemes now
  535. strcat(phonetic, " "); // need a space to indicate word-boundary
  536. // PROBLEM vowel reductions are not applied to the translated phonemes
  537. // condition rules are not applied
  538. TranslateWord(translator,phonetic,0,NULL);
  539. text_not_phonemes = 0;
  540. strncpy0(encoded_ph, word_phonemes, N_WORD_BYTES-4);
  541. if((word_phonemes[0] == 0) && (error_need_dictionary < 3))
  542. {
  543. // the dictionary was not loaded, we need a second attempt
  544. error_need_dictionary++;
  545. fprintf(f_log,"%5d: Need to compile dictionary again\n",linenum);
  546. }
  547. {
  548. //char decoded_phonemes[128];
  549. //DecodePhonemes(word_phonemes,decoded_phonemes);
  550. //printf("Translator %x %s [%s] [%s]\n",translator->translator_name,word,phonetic,decoded_phonemes);
  551. }
  552. }
  553. else
  554. {
  555. // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word
  556. strncpy0(encoded_ph,phonetic,N_WORD_BYTES-4);
  557. }
  558. }
  559. else
  560. {
  561. EncodePhonemes(phonetic,encoded_ph,bad_phoneme);
  562. if(strchr(encoded_ph,phonSWITCH) != 0)
  563. {
  564. flag_codes[n_flag_codes++] = BITNUM_FLAG_ONLY_S; // don't match on suffixes (except 's') when switching languages
  565. }
  566. // check for errors in the phonemes codes
  567. if(bad_phoneme[0] != 0)
  568. {
  569. // unrecognised phoneme, report error
  570. fprintf(f_log,"%5d: Bad phoneme [%c] (0x%x) in: %s %s\n",linenum,bad_phoneme[0],bad_phoneme[0],word,phonetic);
  571. error_count++;
  572. }
  573. }
  574. if(text_not_phonemes != translator->langopts.textmode)
  575. {
  576. flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE;
  577. }
  578. if(sscanf(word,"U+%x",&wc) == 1)
  579. {
  580. // Character code
  581. ix = utf8_out(wc, word);
  582. word[ix] = 0;
  583. }
  584. else
  585. if(word[0] != '_')
  586. {
  587. // convert to lower case, and note if the word is all-capitals
  588. int c2;
  589. all_upper_case = 1;
  590. p = word;
  591. for(p=word;;)
  592. {
  593. // this assumes that the lower case char is the same length as the upper case char
  594. // OK, except for Turkish "I", but use towlower() rather than towlower2()
  595. ix = utf8_in(&c2,p);
  596. if(c2 == 0)
  597. break;
  598. if(iswupper(c2))
  599. {
  600. utf8_out(towlower(c2),p);
  601. }
  602. else
  603. {
  604. all_upper_case = 0;
  605. }
  606. p += ix;
  607. }
  608. if(all_upper_case)
  609. {
  610. flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS;
  611. }
  612. }
  613. len_word = strlen(word);
  614. if(translator->transpose_min > 0)
  615. {
  616. len_word = TransposeAlphabet(translator, word);
  617. }
  618. *hash = HashDictionary(word);
  619. len_phonetic = strlen(encoded_ph);
  620. dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed
  621. len_word &= 0x3f;
  622. memcpy(&dict_line[2],word,len_word);
  623. if(len_phonetic == 0)
  624. {
  625. // no phonemes specified. set bit 7
  626. dict_line[1] |= 0x80;
  627. length = len_word + 2;
  628. }
  629. else
  630. {
  631. length = len_word + len_phonetic + 3;
  632. strcpy(&dict_line[(len_word)+2],encoded_ph);
  633. }
  634. for(ix=0; ix<n_flag_codes; ix++)
  635. {
  636. dict_line[ix+length] = flag_codes[ix];
  637. }
  638. length += n_flag_codes;
  639. if((multiple_string != NULL) && (multiple_words > 0))
  640. {
  641. if(multiple_words > 10)
  642. {
  643. fprintf(f_log,"%5d: Two many parts in a multi-word entry: %d\n",linenum,multiple_words);
  644. error_count++;
  645. }
  646. else
  647. {
  648. dict_line[length++] = 80 + multiple_words;
  649. ix = multiple_string_end - multiple_string;
  650. if(multiple_numeric_hyphen)
  651. {
  652. dict_line[length++] = ' '; // ???
  653. }
  654. memcpy(&dict_line[length],multiple_string,ix);
  655. length += ix;
  656. }
  657. }
  658. dict_line[0] = length;
  659. return(length);
  660. } /* end of compile_line */
  661. static void compile_dictlist_start(void)
  662. {//=====================================
  663. // initialise dictionary list
  664. int ix;
  665. char *p;
  666. char *p2;
  667. for(ix=0; ix<N_HASH_DICT; ix++)
  668. {
  669. p = hash_chains[ix];
  670. while(p != NULL)
  671. {
  672. memcpy(&p2,p,sizeof(char *));
  673. free(p);
  674. p = p2;
  675. }
  676. hash_chains[ix] = NULL;
  677. hash_counts[ix]=0;
  678. }
  679. }
  680. static void compile_dictlist_end(FILE *f_out)
  681. {//==========================================
  682. // Write out the compiled dictionary list
  683. int hash;
  684. int length;
  685. char *p;
  686. if(f_log != NULL)
  687. {
  688. #ifdef OUTPUT_FORMAT
  689. for(hash=0; hash<N_HASH_DICT; hash++)
  690. {
  691. fprintf(f_log,"%8d",hash_counts[hash]);
  692. if((hash & 7) == 7)
  693. fputc('\n',f_log);
  694. }
  695. fflush(f_log);
  696. #endif
  697. }
  698. for(hash=0; hash<N_HASH_DICT; hash++)
  699. {
  700. p = hash_chains[hash];
  701. hash_counts[hash] = (int)ftell(f_out);
  702. while(p != NULL)
  703. {
  704. length = *(p+sizeof(char *));
  705. fwrite(p+sizeof(char *),length,1,f_out);
  706. memcpy(&p,p,sizeof(char *));
  707. }
  708. fputc(0,f_out);
  709. }
  710. }
  711. static int compile_dictlist_file(const char *path, const char* filename)
  712. {//=====================================================================
  713. int length;
  714. int hash;
  715. char *p;
  716. int count=0;
  717. FILE *f_in;
  718. char buf[200];
  719. char fname[sizeof(path_home)+45];
  720. char dict_line[128];
  721. text_mode = 0;
  722. // try with and without '.txt' extension
  723. sprintf(fname,"%s%s.txt",path,filename);
  724. if((f_in = fopen(fname,"r")) == NULL)
  725. {
  726. sprintf(fname,"%s%s",path,filename);
  727. if((f_in = fopen(fname,"r")) == NULL)
  728. return(-1);
  729. }
  730. fprintf(f_log,"Compiling: '%s'\n",fname);
  731. linenum=0;
  732. while(fgets(buf,sizeof(buf),f_in) != NULL)
  733. {
  734. linenum++;
  735. length = compile_line(buf,dict_line,&hash);
  736. if(length == 0) continue; /* blank line */
  737. hash_counts[hash]++;
  738. p = (char *)malloc(length+sizeof(char *));
  739. if(p == NULL)
  740. {
  741. if(f_log != NULL)
  742. {
  743. fprintf(f_log,"Can't allocate memory\n");
  744. error_count++;
  745. }
  746. break;
  747. }
  748. memcpy(p,&hash_chains[hash],sizeof(char *));
  749. hash_chains[hash] = p;
  750. memcpy(p+sizeof(char *),dict_line,length);
  751. count++;
  752. }
  753. fprintf(f_log,"\t%d entries\n",count);
  754. fclose(f_in);
  755. return(0);
  756. } /* end of compile_dictlist_file */
  757. static char rule_cond[80];
  758. static char rule_pre[80];
  759. static char rule_post[80];
  760. static char rule_match[80];
  761. static char rule_phonemes[80];
  762. static char group_name[LEN_GROUP_NAME+1];
  763. static int group3_ix;
  764. #define N_RULES 2000 // max rules for each group
  765. int isHexDigit(int c)
  766. {
  767. if((c >= '0') && (c <= '9'))
  768. return(c - '0');
  769. if((c >= 'a') && (c <= 'f'))
  770. return(c - 'a' + 10);
  771. if((c >= 'A') && (c <= 'F'))
  772. return(c - 'A' + 10);
  773. return(-1);
  774. }
  775. static void copy_rule_string(char *string, int &state)
  776. {//===================================================
  777. // state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes
  778. static char *outbuf[5] = {rule_cond, rule_pre, rule_match, rule_post, rule_phonemes};
  779. static int next_state[5] = {2,2,4,4,4};
  780. char *output;
  781. char *p;
  782. int ix;
  783. int len;
  784. char c;
  785. int c2, c3;
  786. int sxflags;
  787. int value;
  788. int literal;
  789. int hexdigit_input = 0;
  790. MNEM_TAB *mr;
  791. if(string[0] == 0) return;
  792. output = outbuf[state];
  793. if(state==4)
  794. {
  795. // append to any previous phoneme string, i.e. allow spaces in the phoneme string
  796. len = strlen(rule_phonemes);
  797. if(len > 0)
  798. rule_phonemes[len++] = ' ';
  799. output = &rule_phonemes[len];
  800. }
  801. sxflags = 0x808000; // to ensure non-zero bytes
  802. for(p=string,ix=0;;)
  803. {
  804. literal = 0;
  805. c = *p++;
  806. if((c == '0') && (p[0] == 'x') && (isHexDigit(p[1]) >= 0) && (isHexDigit(p[2]) >= 0))
  807. {
  808. hexdigit_input = 1;
  809. c = p[1];
  810. p+= 2;
  811. }
  812. if(c == '\\')
  813. {
  814. c = *p++; // treat next character literally
  815. //#ifdef deleted
  816. if((c >= '0') && (c <= '3') && (p[0] >= '0') && (p[0] <= '7') && (p[1] >= '0') && (p[1] <= '7'))
  817. {
  818. // character code given by 3 digit octal value;
  819. c = (c-'0')*64 + (p[0]-'0')*8 + (p[1]-'0');
  820. p += 2;
  821. }
  822. //endif
  823. literal = 1;
  824. }
  825. if(hexdigit_input)
  826. {
  827. if(((c2 = isHexDigit(c)) >= 0) && ((c3 = isHexDigit(p[0])) >= 0))
  828. {
  829. c = c2 * 16 + c3;
  830. literal = 1;
  831. p++;
  832. }
  833. else
  834. {
  835. hexdigit_input = 0;
  836. }
  837. }
  838. if((state==1) || (state==3))
  839. {
  840. // replace special characters (note: 'E' is reserved for a replaced silent 'e')
  841. if(literal == 0)
  842. {
  843. static const char lettergp_letters[9] = {LETTERGP_A,LETTERGP_B,LETTERGP_C,0,0,LETTERGP_F,LETTERGP_G,LETTERGP_H,LETTERGP_Y};
  844. switch(c)
  845. {
  846. case '_':
  847. c = RULE_SPACE;
  848. break;
  849. case 'Y':
  850. c = 'I'; // drop through to next case
  851. case 'A': // vowel
  852. case 'B':
  853. case 'C':
  854. case 'H':
  855. case 'F':
  856. case 'G':
  857. if(state == 1)
  858. {
  859. // pre-rule, put the number before the RULE_LETTERGP;
  860. output[ix++] = lettergp_letters[c-'A'] + 'A';
  861. c = RULE_LETTERGP;
  862. }
  863. else
  864. {
  865. output[ix++] = RULE_LETTERGP;
  866. c = lettergp_letters[c-'A'] + 'A';
  867. }
  868. break;
  869. case 'D':
  870. c = RULE_DIGIT;
  871. break;
  872. case 'K':
  873. c = RULE_NOTVOWEL;
  874. break;
  875. case 'N':
  876. c = RULE_NO_SUFFIX;
  877. break;
  878. case 'V':
  879. c = RULE_IFVERB;
  880. break;
  881. case 'Z':
  882. c = RULE_NONALPHA;
  883. break;
  884. case '+':
  885. c = RULE_INC_SCORE;
  886. break;
  887. case '@':
  888. c = RULE_SYLLABLE;
  889. break;
  890. case '&':
  891. c = RULE_STRESSED;
  892. break;
  893. case '%':
  894. c = RULE_DOUBLE;
  895. break;
  896. case '#':
  897. c = RULE_DEL_FWD;
  898. break;
  899. case '!':
  900. c = RULE_CAPITAL;
  901. break;
  902. case 'T':
  903. output[ix++] = RULE_DOLLAR;
  904. c = 0x11;
  905. break;
  906. case 'W':
  907. c = RULE_SPELLING;
  908. break;
  909. case 'X':
  910. c = RULE_NOVOWELS;
  911. break;
  912. case 'J':
  913. c = RULE_SKIPCHARS;
  914. break;
  915. case 'L':
  916. // expect two digits
  917. c = *p++ - '0';
  918. value = *p++ - '0';
  919. c = c * 10 + value;
  920. if((value < 0) || (value > 9))
  921. {
  922. c = 0;
  923. fprintf(f_log,"%5d: Expected 2 digits after 'L'\n",linenum);
  924. error_count++;
  925. }
  926. else
  927. if((c <= 0) || (c >= N_LETTER_GROUPS) || (letterGroupsDefined[(int)c] == 0))
  928. {
  929. fprintf(f_log,"%5d: Letter group L%.2d not defined\n",linenum,c);
  930. error_count++;
  931. }
  932. c += 'A';
  933. if(state == 1)
  934. {
  935. // pre-rule, put the group number before the RULE_LETTERGP command
  936. output[ix++] = c;
  937. c = RULE_LETTERGP2;
  938. }
  939. else
  940. {
  941. output[ix++] = RULE_LETTERGP2;
  942. }
  943. break;
  944. case '$':
  945. output[ix++] = RULE_DOLLAR;
  946. c = 0;
  947. mr = mnem_rules;
  948. while(mr->mnem != NULL)
  949. {
  950. len = strlen(mr->mnem);
  951. if(memcmp(p, mr->mnem, len) == 0)
  952. {
  953. c = mr->value;
  954. p += len;
  955. break;
  956. }
  957. mr++;
  958. }
  959. if(c == 0)
  960. {
  961. fprintf(f_log,"%5d: $ command not recognized\n",linenum);
  962. error_count++;
  963. }
  964. break;
  965. case 'P':
  966. sxflags |= SUFX_P; // Prefix, now drop through to Suffix
  967. case 'S':
  968. output[ix++] = RULE_ENDING;
  969. value = 0;
  970. while(!isspace2(c = *p++) && (c != 0))
  971. {
  972. switch(c)
  973. {
  974. case 'e':
  975. sxflags |= SUFX_E;
  976. break;
  977. case 'i':
  978. sxflags |= SUFX_I;
  979. break;
  980. case 'p': // obsolete, replaced by 'P' above
  981. sxflags |= SUFX_P;
  982. break;
  983. case 'v':
  984. sxflags |= SUFX_V;
  985. break;
  986. case 'd':
  987. sxflags |= SUFX_D;
  988. break;
  989. case 'f':
  990. sxflags |= SUFX_F;
  991. break;
  992. case 'q':
  993. sxflags |= SUFX_Q;
  994. break;
  995. case 't':
  996. sxflags |= SUFX_T;
  997. break;
  998. case 'b':
  999. sxflags |= SUFX_B;
  1000. break;
  1001. case 'a':
  1002. sxflags |= SUFX_A;
  1003. break;
  1004. case 'm':
  1005. sxflags |= SUFX_M;
  1006. break;
  1007. default:
  1008. if(isdigit(c))
  1009. value = (value*10) + (c - '0');
  1010. break;
  1011. }
  1012. }
  1013. p--;
  1014. output[ix++] = sxflags >> 16;
  1015. output[ix++] = sxflags >> 8;
  1016. c = value | 0x80;
  1017. break;
  1018. }
  1019. }
  1020. }
  1021. output[ix++] = c;
  1022. if(c == 0) break;
  1023. }
  1024. state = next_state[state];
  1025. } // end of copy_rule_string
  1026. static char *compile_rule(char *input)
  1027. {//===================================
  1028. int ix;
  1029. unsigned char c;
  1030. int wc;
  1031. char *p;
  1032. char *prule;
  1033. int len;
  1034. int len_name;
  1035. int start;
  1036. int state=2;
  1037. int finish=0;
  1038. char buf[80];
  1039. char output[150];
  1040. unsigned char bad_phoneme[4];
  1041. buf[0]=0;
  1042. rule_cond[0]=0;
  1043. rule_pre[0]=0;
  1044. rule_post[0]=0;
  1045. rule_match[0]=0;
  1046. rule_phonemes[0]=0;
  1047. p = buf;
  1048. for(ix=0; finish==0; ix++)
  1049. {
  1050. c = input[ix];
  1051. switch(c = input[ix])
  1052. {
  1053. case ')': // end of prefix section
  1054. *p = 0;
  1055. state = 1;
  1056. copy_rule_string(buf,state);
  1057. p = buf;
  1058. break;
  1059. case '(': // start of suffix section
  1060. *p = 0;
  1061. state = 2;
  1062. copy_rule_string(buf,state);
  1063. state = 3;
  1064. p = buf;
  1065. if(input[ix+1] == ' ')
  1066. {
  1067. fprintf(f_log,"%5d: Syntax error. Space after (\n",linenum);
  1068. error_count++;
  1069. }
  1070. break;
  1071. case '\n': // end of line
  1072. case '\r':
  1073. case 0: // end of line
  1074. *p = 0;
  1075. copy_rule_string(buf,state);
  1076. finish=1;
  1077. break;
  1078. case '\t': // end of section section
  1079. case ' ':
  1080. *p = 0;
  1081. copy_rule_string(buf,state);
  1082. p = buf;
  1083. break;
  1084. case '?':
  1085. if(state==2)
  1086. state=0;
  1087. else
  1088. *p++ = c;
  1089. break;
  1090. default:
  1091. *p++ = c;
  1092. break;
  1093. }
  1094. }
  1095. if(strcmp(rule_match,"$group")==0)
  1096. strcpy(rule_match,group_name);
  1097. if(rule_match[0]==0)
  1098. {
  1099. if(rule_post[0] != 0)
  1100. {
  1101. fprintf(f_log,"%5d: Syntax error\n",linenum);
  1102. error_count++;
  1103. }
  1104. return(NULL);
  1105. }
  1106. EncodePhonemes(rule_phonemes,buf,bad_phoneme);
  1107. if(bad_phoneme[0] != 0)
  1108. {
  1109. fprintf(f_log,"%5d: Bad phoneme [%c] in %s\n",linenum,bad_phoneme[0],input);
  1110. error_count++;
  1111. }
  1112. strcpy(output,buf);
  1113. len = strlen(buf)+1;
  1114. len_name = strlen(group_name);
  1115. if((len_name > 0) && (memcmp(rule_match,group_name,len_name) != 0))
  1116. {
  1117. utf8_in(&wc,rule_match);
  1118. if((group_name[0] == '9') && IsDigit(wc))
  1119. {
  1120. // numeric group, rule_match starts with a digit, so OK
  1121. }
  1122. else
  1123. {
  1124. fprintf(f_log,"%5d: Wrong initial letters '%s' for group '%s'\n",linenum,rule_match,group_name);
  1125. error_count++;
  1126. }
  1127. }
  1128. strcpy(&output[len],rule_match);
  1129. len += strlen(rule_match);
  1130. if(debug_flag)
  1131. {
  1132. output[len] = RULE_LINENUM;
  1133. output[len+1] = (linenum % 255) + 1;
  1134. output[len+2] = (linenum / 255) + 1;
  1135. len+=3;
  1136. }
  1137. if(rule_cond[0] != 0)
  1138. {
  1139. ix = -1;
  1140. if(rule_cond[0] == '!')
  1141. {
  1142. // allow the rule only if the condition number is NOT set for the voice
  1143. ix = atoi(&rule_cond[1]) + 32;
  1144. }
  1145. else
  1146. {
  1147. // allow the rule only if the condition number is set for the voice
  1148. ix = atoi(rule_cond);
  1149. }
  1150. if((ix > 0) && (ix < 255))
  1151. {
  1152. output[len++] = RULE_CONDITION;
  1153. output[len++] = ix;
  1154. }
  1155. else
  1156. {
  1157. fprintf(f_log,"%5d: bad condition number ?%d\n",linenum,ix);
  1158. error_count++;
  1159. }
  1160. }
  1161. if(rule_pre[0] != 0)
  1162. {
  1163. start = 0;
  1164. if(rule_pre[0] == RULE_SPACE)
  1165. {
  1166. // omit '_' at the beginning of the pre-string and imply it by using RULE_PRE_ATSTART
  1167. c = RULE_PRE_ATSTART;
  1168. start = 1;
  1169. }
  1170. else
  1171. {
  1172. c = RULE_PRE;
  1173. }
  1174. output[len++] = c;
  1175. // output PRE string in reverse order
  1176. for(ix = strlen(rule_pre)-1; ix>=start; ix--)
  1177. output[len++] = rule_pre[ix];
  1178. }
  1179. if(rule_post[0] != 0)
  1180. {
  1181. sprintf(&output[len],"%c%s",RULE_POST,rule_post);
  1182. len += (strlen(rule_post)+1);
  1183. }
  1184. output[len++]=0;
  1185. prule = (char *)malloc(len);
  1186. memcpy(prule,output,len);
  1187. return(prule);
  1188. } // end of compile_rule
  1189. int __cdecl string_sorter(char **a, char **b)
  1190. {//===========================================
  1191. char *pa, *pb;
  1192. int ix;
  1193. if((ix = strcmp(pa = *a,pb = *b)) != 0)
  1194. return(ix);
  1195. pa += (strlen(pa)+1);
  1196. pb += (strlen(pb)+1);
  1197. return(strcmp(pa,pb));
  1198. } /* end of string_sorter */
  1199. static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b)
  1200. {//===================================================
  1201. // Sort long names before short names
  1202. int ix;
  1203. ix = strlen(b->name) - strlen(a->name);
  1204. if(ix != 0) return(ix);
  1205. ix = strcmp(a->name,b->name);
  1206. if(ix != 0) return(ix);
  1207. return(a->start-b->start);
  1208. }
  1209. #ifdef OUTPUT_FORMAT
  1210. static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
  1211. {//=============================================================================
  1212. int rule;
  1213. int ix;
  1214. unsigned char c;
  1215. int len1;
  1216. int len2;
  1217. int spaces;
  1218. char *p;
  1219. char *pout;
  1220. int condition;
  1221. char buf[80];
  1222. char suffix[12];
  1223. static unsigned char symbols[] = {'@','&','%','+','#','$','D','Z','A','B','C','F'};
  1224. fprintf(f_out,"\n$group %s\n",name);
  1225. for(rule=0; rule<n_rules; rule++)
  1226. {
  1227. p = rules[rule];
  1228. len1 = strlen(p) + 1;
  1229. p = &p[len1];
  1230. len2 = strlen(p);
  1231. rule_match[0]=0;
  1232. rule_pre[0]=0;
  1233. rule_post[0]=0;
  1234. condition = 0;
  1235. pout = rule_match;
  1236. for(ix=0; ix<len2; ix++)
  1237. {
  1238. switch(c = p[ix])
  1239. {
  1240. case RULE_PRE:
  1241. *pout = 0;
  1242. pout = rule_pre;
  1243. break;
  1244. case RULE_POST:
  1245. *pout = 0;
  1246. pout = rule_post;
  1247. break;
  1248. case RULE_CONDITION:
  1249. condition = p[++ix];
  1250. break;
  1251. case RULE_ENDING:
  1252. sprintf(suffix,"$%d[%x]",(p[ix+2]),p[ix+1] & 0x7f);
  1253. ix += 2;
  1254. strcpy(pout,suffix);
  1255. pout += strlen(suffix);
  1256. break;
  1257. default:
  1258. if(c <= RULE_LETTER7)
  1259. c = symbols[c-RULE_SYLLABLE];
  1260. if(c == ' ')
  1261. c = '_';
  1262. *pout++ = c;
  1263. break;
  1264. }
  1265. }
  1266. *pout = 0;
  1267. spaces = 12;
  1268. if(condition > 0)
  1269. {
  1270. sprintf(buf,"?%d ",condition);
  1271. spaces -= strlen(buf);
  1272. fprintf(f_out,"%s",buf);
  1273. }
  1274. if(rule_pre[0] != 0)
  1275. {
  1276. p = buf;
  1277. for(ix=strlen(rule_pre)-1;ix>=0;ix--)
  1278. *p++ = rule_pre[ix];
  1279. sprintf(p,") ");
  1280. spaces -= strlen(buf);
  1281. for(ix=0; ix<spaces; ix++)
  1282. fputc(' ',f_out);
  1283. fprintf(f_out,"%s",buf);
  1284. spaces = 0;
  1285. }
  1286. for(ix=0; ix<spaces; ix++)
  1287. fputc(' ',f_out);
  1288. spaces = 14;
  1289. sprintf(buf," %s ",rule_match);
  1290. if(rule_post[0] != 0)
  1291. {
  1292. p = &buf[strlen(buf)];
  1293. sprintf(p,"(%s ",rule_post);
  1294. }
  1295. fprintf(f_out,"%s",buf);
  1296. spaces -= strlen(buf);
  1297. for(ix=0; ix<spaces; ix++)
  1298. fputc(' ',f_out);
  1299. DecodePhonemes(rules[rule],buf);
  1300. fprintf(f_out,"%s\n",buf); // phonemes
  1301. }
  1302. }
  1303. #endif
  1304. //#define LIST_GROUP_INFO
  1305. static void output_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
  1306. {//==============================================================================
  1307. int ix;
  1308. int len1;
  1309. int len2;
  1310. int len_name;
  1311. char *p;
  1312. char *p2, *p3;
  1313. const char *common;
  1314. short nextchar_count[256];
  1315. memset(nextchar_count,0,sizeof(nextchar_count));
  1316. len_name = strlen(name);
  1317. #ifdef OUTPUT_FORMAT
  1318. print_rule_group(f_log,n_rules,rules,name);
  1319. #endif
  1320. // sort the rules in this group by their phoneme string
  1321. common = "";
  1322. qsort((void *)rules,n_rules,sizeof(char *),(int (__cdecl *)(const void *,const void *))string_sorter);
  1323. if(strcmp(name,"9")==0)
  1324. len_name = 0; // don't remove characters from numeric match strings
  1325. for(ix=0; ix<n_rules; ix++)
  1326. {
  1327. p = rules[ix];
  1328. len1 = strlen(p) + 1; // phoneme string
  1329. p3 = &p[len1];
  1330. p2 = p3 + len_name; // remove group name from start of match string
  1331. len2 = strlen(p2);
  1332. nextchar_count[(unsigned char)(p2[0])]++; // the next byte after the group name
  1333. if((common[0] != 0) && (strcmp(p,common)==0))
  1334. {
  1335. fwrite(p2,len2,1,f_out);
  1336. fputc(0,f_out); // no phoneme string, it's the same as previous rule
  1337. }
  1338. else
  1339. {
  1340. if((ix < n_rules-1) && (strcmp(p,rules[ix+1])==0))
  1341. {
  1342. common = rules[ix]; // phoneme string is same as next, set as common
  1343. fputc(RULE_PH_COMMON,f_out);
  1344. }
  1345. fwrite(p2,len2,1,f_out);
  1346. fputc(RULE_PHONEMES,f_out);
  1347. fwrite(p,len1,1,f_out);
  1348. }
  1349. }
  1350. #ifdef LIST_GROUP_INFO
  1351. for(ix=32; ix<256; ix++)
  1352. {
  1353. if(nextchar_count[ix] > 30)
  1354. printf("Group %s %c %d\n",name,ix,nextchar_count[ix]);
  1355. }
  1356. #endif
  1357. } // end of output_rule_group
  1358. static int compile_lettergroup(char *input, FILE *f_out)
  1359. {//=====================================================
  1360. char *p;
  1361. char *p_start;
  1362. int group;
  1363. int ix;
  1364. int n_items;
  1365. int length;
  1366. int max_length = 0;
  1367. #define N_LETTERGP_ITEMS 200
  1368. char *items[N_LETTERGP_ITEMS];
  1369. char item_length[N_LETTERGP_ITEMS];
  1370. p = input;
  1371. if(!isdigit(p[0]) || !isdigit(p[1]))
  1372. {
  1373. fprintf(f_log,"%5d: Expected 2 digits after '.L'\n",linenum);
  1374. error_count++;
  1375. return(1);
  1376. }
  1377. group = atoi(&p[0]);
  1378. if(group >= N_LETTER_GROUPS)
  1379. {
  1380. fprintf(f_log,"%5d: lettergroup out of range (01-%.2d)\n",linenum,N_LETTER_GROUPS-1);
  1381. error_count++;
  1382. return(1);
  1383. }
  1384. while(!isspace2(*p)) p++;
  1385. fputc(RULE_GROUP_START,f_out);
  1386. fputc(RULE_LETTERGP2,f_out);
  1387. fputc(group + 'A', f_out);
  1388. if(letterGroupsDefined[group] != 0)
  1389. {
  1390. fprintf(f_log,"%5d: lettergroup L%.2d is already defined\n",linenum,group);
  1391. error_count++;
  1392. }
  1393. letterGroupsDefined[group] = 1;
  1394. n_items = 0;
  1395. while(n_items < N_LETTERGP_ITEMS)
  1396. {
  1397. while(isspace2(*p)) p++;
  1398. if(*p == 0)
  1399. break;
  1400. items[n_items] = p_start = p;
  1401. while((*p & 0xff) > ' ')
  1402. {
  1403. if (*p == '_') *p = ' '; // allow '_' for word break
  1404. p++;
  1405. }
  1406. *p++ = 0;
  1407. length = p - p_start;
  1408. if(length > max_length)
  1409. max_length = length;
  1410. item_length[n_items++] = length;
  1411. }
  1412. // write out the items, longest first
  1413. while(max_length > 1)
  1414. {
  1415. for(ix=0; ix < n_items; ix++)
  1416. {
  1417. if(item_length[ix] == max_length)
  1418. {
  1419. fwrite(items[ix],1,max_length,f_out);
  1420. }
  1421. }
  1422. max_length--;
  1423. }
  1424. fputc(RULE_GROUP_END,f_out);
  1425. return(0);
  1426. }
  1427. static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
  1428. {//====================================================================
  1429. char *prule;
  1430. unsigned char *p;
  1431. int ix;
  1432. int c;
  1433. int gp;
  1434. FILE *f_temp;
  1435. int n_rules=0;
  1436. int count=0;
  1437. int different;
  1438. int wc;
  1439. const char *prev_rgroup_name;
  1440. unsigned int char_code;
  1441. int compile_mode=0;
  1442. char *buf;
  1443. char buf1[500];
  1444. char *rules[N_RULES];
  1445. int n_rgroups = 0;
  1446. int n_groups3 = 0;
  1447. RGROUP rgroup[N_RULE_GROUP2];
  1448. linenum = 0;
  1449. group_name[0] = 0;
  1450. if((f_temp = fopen_log(fname_temp,"wb")) == NULL)
  1451. return(1);
  1452. for(;;)
  1453. {
  1454. linenum++;
  1455. buf = fgets(buf1,sizeof(buf1),f_in);
  1456. if(buf != NULL)
  1457. {
  1458. if((p = (unsigned char *)strstr(buf,"//")) != NULL)
  1459. *p = 0;
  1460. if(buf[0] == '\r') buf++; // ignore extra \r in \r\n
  1461. }
  1462. if((buf == NULL) || (buf[0] == '.'))
  1463. {
  1464. // next .group or end of file, write out the previous group
  1465. if(n_rules > 0)
  1466. {
  1467. strcpy(rgroup[n_rgroups].name,group_name);
  1468. rgroup[n_rgroups].group3_ix = group3_ix;
  1469. rgroup[n_rgroups].start = ftell(f_temp);
  1470. output_rule_group(f_temp,n_rules,rules,group_name);
  1471. rgroup[n_rgroups].length = ftell(f_temp) - rgroup[n_rgroups].start;
  1472. n_rgroups++;
  1473. count += n_rules;
  1474. }
  1475. n_rules = 0;
  1476. if(compile_mode == 2)
  1477. {
  1478. // end of the character replacements section
  1479. fwrite(&n_rules,1,4,f_out); // write a zero word to terminate the replacemenmt list
  1480. compile_mode = 0;
  1481. }
  1482. if(buf == NULL) break; // end of file
  1483. if(memcmp(buf,".L",2)==0)
  1484. {
  1485. compile_lettergroup(&buf[2], f_out);
  1486. continue;
  1487. }
  1488. if(memcmp(buf,".replace",8)==0)
  1489. {
  1490. compile_mode = 2;
  1491. fputc(RULE_GROUP_START,f_out);
  1492. fputc(RULE_REPLACEMENTS,f_out);
  1493. // advance to next word boundary
  1494. while((ftell(f_out) & 3) != 0)
  1495. fputc(0,f_out);
  1496. }
  1497. if(memcmp(buf,".group",6)==0)
  1498. {
  1499. compile_mode = 1;
  1500. p = (unsigned char *)&buf[6];
  1501. while((p[0]==' ') || (p[0]=='\t')) p++; // Note: Windows isspace(0xe1) gives TRUE !
  1502. ix = 0;
  1503. while((*p > ' ') && (ix < LEN_GROUP_NAME))
  1504. group_name[ix++] = *p++;
  1505. group_name[ix]=0;
  1506. group3_ix = 0;
  1507. if(sscanf(group_name,"0x%x",&char_code)==1)
  1508. {
  1509. // group character is given as a character code (max 16 bits)
  1510. p = (unsigned char *)group_name;
  1511. if(char_code > 0x100)
  1512. {
  1513. *p++ = (char_code >> 8);
  1514. }
  1515. *p++ = char_code;
  1516. *p = 0;
  1517. }
  1518. else
  1519. {
  1520. if(translator->letter_bits_offset > 0)
  1521. {
  1522. utf8_in(&wc, group_name);
  1523. if(((ix = (wc - translator->letter_bits_offset)) >= 0) && (ix < 128))
  1524. {
  1525. group3_ix = ix+1; // not zero
  1526. }
  1527. }
  1528. }
  1529. if((group3_ix == 0) && (strlen(group_name) > 2))
  1530. {
  1531. if(utf8_in(&c,group_name) < 2)
  1532. {
  1533. fprintf(f_log,"%5d: Group name longer than 2 bytes (UTF8)",linenum);
  1534. error_count++;
  1535. }
  1536. group_name[2] = 0;
  1537. }
  1538. }
  1539. continue;
  1540. }
  1541. switch(compile_mode)
  1542. {
  1543. case 1: // .group
  1544. prule = compile_rule(buf);
  1545. if((prule != NULL) && (n_rules < N_RULES))
  1546. {
  1547. rules[n_rules++] = prule;
  1548. }
  1549. break;
  1550. case 2: // .replace
  1551. {
  1552. int replace1;
  1553. int replace2;
  1554. char *p;
  1555. p = buf;
  1556. replace1 = 0;
  1557. replace2 = 0;
  1558. while(isspace2(*p)) p++;
  1559. ix = 0;
  1560. while((unsigned char)(*p) > 0x20) // not space or zero-byte
  1561. {
  1562. p += utf8_in(&c,p);
  1563. replace1 += (c << ix);
  1564. ix += 16;
  1565. }
  1566. while(isspace2(*p)) p++;
  1567. ix = 0;
  1568. while((unsigned char)(*p) > 0x20)
  1569. {
  1570. p += utf8_in(&c,p);
  1571. replace2 += (c << ix);
  1572. ix += 16;
  1573. }
  1574. if(replace1 != 0)
  1575. {
  1576. Write4Bytes(f_out,replace1); // write as little-endian
  1577. Write4Bytes(f_out,replace2); // if big-endian, reverse the bytes in LoadDictionary()
  1578. }
  1579. }
  1580. break;
  1581. }
  1582. }
  1583. fclose(f_temp);
  1584. qsort((void *)rgroup,n_rgroups,sizeof(rgroup[0]),(int (__cdecl *)(const void *,const void *))rgroup_sorter);
  1585. if((f_temp = fopen(fname_temp,"rb"))==NULL)
  1586. return(2);
  1587. prev_rgroup_name = "\n";
  1588. for(gp = 0; gp < n_rgroups; gp++)
  1589. {
  1590. fseek(f_temp,rgroup[gp].start,SEEK_SET);
  1591. if((different = strcmp(rgroup[gp].name, prev_rgroup_name)) != 0)
  1592. {
  1593. // not the same as the previous group
  1594. if(gp > 0)
  1595. fputc(RULE_GROUP_END,f_out);
  1596. fputc(RULE_GROUP_START,f_out);
  1597. if(rgroup[gp].group3_ix != 0)
  1598. {
  1599. n_groups3++;
  1600. fputc(1,f_out);
  1601. fputc(rgroup[gp].group3_ix, f_out);
  1602. }
  1603. else
  1604. {
  1605. fprintf(f_out, "%s", prev_rgroup_name = rgroup[gp].name);
  1606. }
  1607. fputc(0,f_out);
  1608. }
  1609. for(ix=rgroup[gp].length; ix>0; ix--)
  1610. {
  1611. c = fgetc(f_temp);
  1612. fputc(c,f_out);
  1613. }
  1614. if(different)
  1615. {
  1616. }
  1617. }
  1618. fputc(RULE_GROUP_END,f_out);
  1619. fputc(0,f_out);
  1620. fclose(f_temp);
  1621. remove(fname_temp);
  1622. fprintf(f_log,"\t%d rules, %d groups (%d)\n\n",count,n_rgroups,n_groups3);
  1623. return(0);
  1624. } // end of compile_dictrules
  1625. int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname_err, int flags)
  1626. {//=====================================================================================================
  1627. // fname: space to write the filename in case of error
  1628. // flags: bit 0: include source line number information, for debug purposes.
  1629. FILE *f_in;
  1630. FILE *f_out;
  1631. int offset_rules=0;
  1632. int value;
  1633. char fname_in[sizeof(path_home)+45];
  1634. char fname_out[sizeof(path_home)+15];
  1635. char fname_temp[sizeof(path_home)+15];
  1636. char path[sizeof(path_home)+40]; // path_dsource+20
  1637. error_count = 0;
  1638. error_need_dictionary = 0;
  1639. memset(letterGroupsDefined,0,sizeof(letterGroupsDefined));
  1640. debug_flag = flags & 1;
  1641. if(dsource == NULL)
  1642. dsource = "";
  1643. f_log = log;
  1644. //f_log = fopen("log2.txt","w");
  1645. if(f_log == NULL)
  1646. f_log = stderr;
  1647. // try with and without '.txt' extension
  1648. sprintf(path,"%s%s_",dsource,dict_name);
  1649. sprintf(fname_in,"%srules.txt",path);
  1650. if((f_in = fopen(fname_in,"r")) == NULL)
  1651. {
  1652. sprintf(fname_in,"%srules",path);
  1653. if((f_in = fopen_log(fname_in,"r")) == NULL)
  1654. {
  1655. if(fname_err)
  1656. strcpy(fname_err,fname_in);
  1657. return(-1);
  1658. }
  1659. }
  1660. sprintf(fname_out,"%s%c%s_dict",path_home,PATHSEP,dict_name);
  1661. if((f_out = fopen_log(fname_out,"wb+")) == NULL)
  1662. {
  1663. if(fname_err)
  1664. strcpy(fname_err,fname_in);
  1665. return(-1);
  1666. }
  1667. sprintf(fname_temp,"%s%ctemp",path_home,PATHSEP);
  1668. value = N_HASH_DICT;
  1669. Write4Bytes(f_out,value);
  1670. Write4Bytes(f_out,offset_rules);
  1671. compile_dictlist_start();
  1672. fprintf(f_log,"Using phonemetable: '%s'\n",phoneme_tab_list[phoneme_tab_number].name);
  1673. compile_dictlist_file(path,"roots");
  1674. if(translator->langopts.listx)
  1675. {
  1676. compile_dictlist_file(path,"list");
  1677. compile_dictlist_file(path,"listx");
  1678. }
  1679. else
  1680. {
  1681. compile_dictlist_file(path,"listx");
  1682. compile_dictlist_file(path,"list");
  1683. }
  1684. compile_dictlist_file(path,"extra");
  1685. compile_dictlist_end(f_out);
  1686. offset_rules = ftell(f_out);
  1687. fprintf(f_log,"Compiling: '%s'\n",fname_in);
  1688. compile_dictrules(f_in,f_out,fname_temp);
  1689. fclose(f_in);
  1690. fseek(f_out,4,SEEK_SET);
  1691. Write4Bytes(f_out,offset_rules);
  1692. fclose(f_out);
  1693. LoadDictionary(translator, dict_name, 0);
  1694. return(error_count);
  1695. } // end of compile_dictionary