#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "darray.h"
#include "tts.h"
static const char *ASCII[] =
{
"null", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"space", "exclamation mark", "double quote", "hash",
"dollar", "percent", "ampersand", "quote",
"open parenthesis", "close parenthesis", "asterisk", "plus",
"comma", "minus", "full stop", "slash",
"zero", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine", "colon", "semi colon",
"less than", "equals", "greater than", "question mark",
#ifndef ALPHA_IN_DICT
"at", "ay", "bee", "see",
"dee", "e", "eff", "gee",
"aych", "i", "jay", "kay",
"ell", "em", "en", "ohe",
"pee", "kju", "are", "es",
"tee", "you", "vee", "double you",
"eks", "why", "zed", "open bracket",
#else
"at", "A", "B", "C",
"D", "E", "F", "G",
"H", "I", "J", "K",
"L", "M", "N", "O",
"P", "Q", "R", "S",
"T", "U", "V", "W",
"X", "Y", "Z", "open bracket",
#endif
"back slash", "close bracket", "circumflex", "underscore",
#ifndef ALPHA_IN_DICT
"back quote", "ay", "bee", "see",
"dee", "e", "eff", "gee",
"aych", "i", "jay", "kay",
"ell", "em", "en", "ohe",
"pee", "kju", "are", "es",
"tee", "you", "vee", "double you",
"eks", "why", "zed", "open brace",
#else
"back quote", "A", "B", "C",
"D", "E", "F", "G",
"H", "I", "J", "K",
"L", "M", "N", "O",
"P", "Q", "R", "S",
"T", "U", "V", "W",
"X", "Y", "Z", "open brace",
#endif
"vertical bar", "close brace", "tilde", "delete",
NULL
};
static const char Anything[] = "";
static const char Nothing[] = " ";
static const char Silent[] = "";
#define LEFT_PART 0
#define MATCH_PART 1
#define RIGHT_PART 2
#define OUT_PART 3
typedef const char *Rule[4];
static Rule punct_rules[] =
{
{Anything, " ", Anything, " "},
{Anything, "-", Anything, ""},
{".", "'S", Anything, "z"},
{"#:.E", "'S", Anything, "z"},
{"#", "'S", Anything, "z"},
{Anything, "'", Anything, ""},
{Anything, ",", Anything, " "},
{Anything, ".", Anything, " "},
{Anything, "?", Anything, " "},
{Anything, "!", Anything, " "},
{Anything, 0, Anything, Silent},
};
static Rule A_rules[] =
{
{Anything, "A", Nothing, "@"},
{Nothing, "ARE", Nothing, "0r"},
{Nothing, "AR", "O", "@r"},
{Anything, "AR", "#", "er"},
{"^", "AS", "#", "eIs"},
{Anything, "A", "WA", "@"},
{Anything, "AW", Anything, "O"},
{" :", "ANY", Anything, "eni"},
{Anything, "A", "^+#", "eI"},
{"#:", "ALLY", Anything, "@li"},
{Nothing, "AL", "#", "@l"},
{Anything, "AGAIN", Anything, "@gen"},
{"#:", "AG", "E", "IdZ"},
{Anything, "A", "^+:#", "&"},
{" :", "A", "^+ ", "eI"},
{Anything, "A", "^%", "eI"},
{Nothing, "ARR", Anything, "@r"},
{Anything, "ARR", Anything, "&r"},
{" :", "AR", Nothing, "0r"},
{Anything, "AR", Nothing, "3"},
{Anything, "AR", Anything, "0r"},
{Anything, "AIR", Anything, "er"},
{Anything, "AI", Anything, "eI"},
{Anything, "AY", Anything, "eI"},
{Anything, "AU", Anything, "O"},
{"#:", "AL", Nothing, "@l"},
{"#:", "ALS", Nothing, "@lz"},
{Anything, "ALK", Anything, "Ok"},
{Anything, "AL", "^", "Ol"},
{" :", "ABLE", Anything, "eIb@l"},
{Anything, "ABLE", Anything, "@b@l"},
{Anything, "ANG", "+", "eIndZ"},
{"^", "A", "^#", "eI"},
{Anything, "A", Anything, "&"},
{Anything, 0, Anything, Silent},
};
static Rule B_rules[] =
{
{Nothing, "BE", "^#", "bI"},
{Anything, "BEING", Anything, "biIN"},
{Nothing, "BOTH", Nothing, "b@UT"},
{Nothing, "BUS", "#", "bIz"},
{Anything, "BUIL", Anything, "bIl"},
{Anything, "B", Anything, "b"},
{Anything, 0, Anything, Silent},
};
static Rule C_rules[] =
{
{Nothing, "CH", "^", "k"},
{"^E", "CH", Anything, "k"},
{Anything, "CH", Anything, "tS"},
{" S", "CI", "#", "saI"},
{Anything, "CI", "A", "S"},
{Anything, "CI", "O", "S"},
{Anything, "CI", "EN", "S"},
{Anything, "C", "+", "s"},
{Anything, "CK", Anything, "k"},
{Anything, "COM", "%", "kVm"},
{Anything, "C", Anything, "k"},
{Anything, 0, Anything, Silent},
};
static Rule D_rules[] =
{
{"#:", "DED", Nothing, "dId"},
{".E", "D", Nothing, "d"},
{"#:^E", "D", Nothing, "t"},
{Nothing, "DE", "^#", "dI"},
{Nothing, "DO", Nothing, "mDU"},
{Nothing, "DOES", Anything, "dVz"},
{Nothing, "DOING", Anything, "duIN"},
{Nothing, "DOW", Anything, "daU"},
{Anything, "DU", "A", "dZu"},
{Anything, "D", Anything, "d"},
{Anything, 0, Anything, Silent},
};
static Rule E_rules[] =
{
{"#:", "E", Nothing, ""},
{"':^", "E", Nothing, ""},
{" :", "E", Nothing, "i"},
{"#", "ED", Nothing, "d"},
{"#:", "E", "D ", ""},
{Anything, "EV", "ER", "ev"},
{Anything, "E", "^%", "i"},
{Anything, "ERI", "#", "iri"},
{Anything, "ERI", Anything, "erI"},
{"#:", "ER", "#", "3"},
{Anything, "ER", "#", "er"},
{Anything, "ER", Anything, "3"},
{Nothing, "EVEN", Anything, "iven"},
{"#:", "E", "W", ""},
{"T", "EW", Anything, "u"},
{"S", "EW", Anything, "u"},
{"R", "EW", Anything, "u"},
{"D", "EW", Anything, "u"},
{"L", "EW", Anything, "u"},
{"Z", "EW", Anything, "u"},
{"N", "EW", Anything, "u"},
{"J", "EW", Anything, "u"},
{"TH", "EW", Anything, "u"},
{"CH", "EW", Anything, "u"},
{"SH", "EW", Anything, "u"},
{Anything, "EW", Anything, "ju"},
{Anything, "E", "O", "i"},
{"#:S", "ES", Nothing, "Iz"},
{"#:C", "ES", Nothing, "Iz"},
{"#:G", "ES", Nothing, "Iz"},
{"#:Z", "ES", Nothing, "Iz"},
{"#:X", "ES", Nothing, "Iz"},
{"#:J", "ES", Nothing, "Iz"},
{"#:CH", "ES", Nothing, "Iz"},
{"#:SH", "ES", Nothing, "Iz"},
{"#:", "E", "S ", ""},
{"#:", "ELY", Nothing, "li"},
{"#:", "EMENT", Anything, "ment"},
{Anything, "EFUL", Anything, "fUl"},
{Anything, "EE", Anything, "i"},
{Anything, "EARN", Anything, "3n"},
{Nothing, "EAR", "^", "3"},
{Anything, "EAD", Anything, "ed"},
{"#:", "EA", Nothing, "i@"},
{Anything, "EA", "SU", "e"},
{Anything, "EA", Anything, "i"},
{Anything, "EIGH", Anything, "eI"},
{Anything, "EI", Anything, "i"},
{Nothing, "EYE", Anything, "aI"},
{Anything, "EY", Anything, "i"},
{Anything, "EU", Anything, "ju"},
{Anything, "E", Anything, "e"},
{Anything, 0, Anything, Silent},
};
static Rule F_rules[] =
{
{Anything, "FUL", Anything, "fUl"},
{Anything, "F", Anything, "f"},
{Anything, 0, Anything, Silent},
};
static Rule G_rules[] =
{
{Anything, "GIV", Anything, "gIv"},
{Nothing, "G", "I^", "g"},
{Anything, "GE", "T", "ge"},
{"SU", "GGES", Anything, "gdZes"},
{Anything, "GG", Anything, "g"},
{" B#", "G", Anything, "g"},
{Anything, "G", "+", "dZ"},
{Anything, "GREAT", Anything, "greIt"},
{"#", "GH", Anything, ""},
{Anything, "G", Anything, "g"},
{Anything, 0, Anything, Silent},
};
static Rule H_rules[] =
{
{Nothing, "HAV", Anything, "h&v"},
{Nothing, "HERE", Anything, "hir"},
{Nothing, "HOUR", Anything, "aU3"},
{Anything, "HOW", Anything, "haU"},
{Anything, "H", "#", "h"},
{Anything, "H", Anything, ""},
{Anything, 0, Anything, Silent},
};
static Rule I_rules[] =
{
{Nothing, "IAIN", Nothing, "I@n"},
{Nothing, "ING", Nothing, "IN"},
{Nothing, "IN", Anything, "In"},
{Nothing, "I", Nothing, "aI"},
{Anything, "IN", "D", "aIn"},
{Anything, "IER", Anything, "i3"},
{"#:R", "IED", Anything, "id"},
{Anything, "IED", Nothing, "aId"},
{Anything, "IEN", Anything, "ien"},
{Anything, "IE", "T", "aIe"},
{" :", "I", "%", "aI"},
{Anything, "I", "%", "i"},
{Anything, "IE", Anything, "i"},
{Anything, "I", "^+:#", "I"},
{Anything, "IR", "#", "aIr"},
{Anything, "IZ", "%", "aIz"},
{Anything, "IS", "%", "aIz"},
{Anything, "I", "D%", "aI"},
{"+^", "I", "^+", "I"},
{Anything, "I", "T%", "aI"},
{"#:^", "I", "^+", "I"},
{Anything, "I", "^+", "aI"},
{Anything, "IR", Anything, "3"},
{Anything, "IGH", Anything, "aI"},
{Anything, "ILD", Anything, "aIld"},
{Anything, "IGN", Nothing, "aIn"},
{Anything, "IGN", "^", "aIn"},
{Anything, "IGN", "%", "aIn"},
{Anything, "IQUE", Anything, "ik"},
{"^", "I", "^#", "aI"},
{Anything, "I", Anything, "I"},
{Anything, 0, Anything, Silent},
};
static Rule J_rules[] =
{
{Anything, "J", Anything, "dZ"},
{Anything, 0, Anything, Silent},
};
static Rule K_rules[] =
{
{Nothing, "K", "N", ""},
{Anything, "K", Anything, "k"},
{Anything, 0, Anything, Silent},
};
static Rule L_rules[] =
{
{Anything, "LO", "C#", "l@U"},
{"L", "L", Anything, ""},
{"#:^", "L", "%", "@l"},
{Anything, "LEAD", Anything, "lid"},
{Anything, "L", Anything, "l"},
{Anything, 0, Anything, Silent},
};
static Rule M_rules[] =
{
{Anything, "MOV", Anything, "muv"},
{"#", "MM", "#", "m"},
{Anything, "M", Anything, "m"},
{Anything, 0, Anything, Silent},
};
static Rule N_rules[] =
{
{"E", "NG", "+", "ndZ"},
{Anything, "NG", "R", "Ng"},
{Anything, "NG", "#", "Ng"},
{Anything, "NGL", "%", "Ng@l"},
{Anything, "NG", Anything, "N"},
{Anything, "NK", Anything, "Nk"},
{Nothing, "NOW", Nothing, "naU"},
{"#", "NG", Nothing, "Ng"},
{Anything, "N", Anything, "n"},
{Anything, 0, Anything, Silent},
};
static Rule O_rules[] =
{
{Anything, "OF", Nothing, "@v"},
{Anything, "OROUGH", Anything, "3@U"},
{"#:", "OR", Nothing, "3"},
{"#:", "ORS", Nothing, "3z"},
{Anything, "OR", Anything, "Or"},
{Nothing, "ONE", Anything, "wVn"},
{Anything, "OW", Anything, "@U"},
{Nothing, "OVER", Anything, "@Uv3"},
{Anything, "OV", Anything, "Vv"},
{Anything, "O", "^%", "@U"},
{Anything, "O", "^EN", "@U"},
{Anything, "O", "^I#", "@U"},
{Anything, "OL", "D", "@Ul"},
{Anything, "OUGHT", Anything, "Ot"},
{Anything, "OUGH", Anything, "Vf"},
{Nothing, "OU", Anything, "aU"},
{"H", "OU", "S#", "aU"},
{Anything, "OUS", Anything, "@s"},
{Anything, "OUR", Anything, "Or"},
{Anything, "OULD", Anything, "Ud"},
{"^", "OU", "^L", "V"},
{Anything, "OUP", Anything, "up"},
{Anything, "OU", Anything, "aU"},
{Anything, "OY", Anything, "oI"},
{Anything, "OING", Anything, "@UIN"},
{Anything, "OI", Anything, "oI"},
{Anything, "OOR", Anything, "Or"},
{Anything, "OOK", Anything, "Uk"},
{Anything, "OOD", Anything, "Ud"},
{Anything, "OO", Anything, "u"},
{Anything, "O", "E", "@U"},
{Anything, "O", Nothing, "@U"},
{Anything, "OA", Anything, "@U"},
{Nothing, "ONLY", Anything, "@Unli"},
{Nothing, "ONCE", Anything, "wVns"},
{Anything, "ON'T", Anything, "@Unt"},
{"C", "O", "N", "0"},
{Anything, "O", "NG", "O"},
{" :^", "O", "N", "V"},
{"I", "ON", Anything, "@n"},
{"#:", "ON", Nothing, "@n"},
{"#^", "ON", Anything, "@n"},
{Anything, "O", "ST ", "@U"},
{Anything, "OF", "^", "Of"},
{Anything, "OTHER", Anything, "VD3"},
{Anything, "OSS", Nothing, "Os"},
{"#:^", "OM", Anything, "Vm"},
{Anything, "O", Anything, "0"},
{Anything, 0, Anything, Silent},
};
static Rule P_rules[] =
{
{Anything, "PH", Anything, "f"},
{Anything, "PEOP", Anything, "pip"},
{Anything, "POW", Anything, "paU"},
{Anything, "PUT", Nothing, "pUt"},
{Anything, "P", Anything, "p"},
{Anything, 0, Anything, Silent},
};
static Rule Q_rules[] =
{
{Anything, "QUAR", Anything, "kwOr"},
{Anything, "QU", Anything, "kw"},
{Anything, "Q", Anything, "k"},
{Anything, 0, Anything, Silent},
};
static Rule R_rules[] =
{
{Nothing, "RE", "^#", "ri"},
{Anything, "R", Anything, "r"},
{Anything, 0, Anything, Silent},
};
static Rule S_rules[] =
{
{Anything, "SH", Anything, "S"},
{"#", "SION", Anything, "Z@n"},
{Anything, "SOME", Anything, "sVm"},
{"#", "SUR", "#", "Z3"},
{Anything, "SUR", "#", "S3"},
{"#", "SU", "#", "Zu"},
{"#", "SSU", "#", "Su"},
{"#", "SED", Nothing, "zd"},
{"#", "S", "#", "z"},
{Anything, "SAID", Anything, "sed"},
{"^", "SION", Anything, "S@n"},
{Anything, "S", "S", ""},
{".", "S", Nothing, "z"},
{"#:.E", "S", Nothing, "z"},
{"#:^##", "S", Nothing, "z"},
{"#:^#", "S", Nothing, "s"},
{"U", "S", Nothing, "s"},
{" :#", "S", Nothing, "z"},
{Nothing, "SCH", Anything, "sk"},
{Anything, "S", "C+", ""},
{"#", "SM", Anything, "zm"},
{"#", "SN", "'", "z@n"},
{Anything, "S", Anything, "s"},
{Anything, 0, Anything, Silent},
};
static Rule T_rules[] =
{
{Nothing, "THE", Nothing, "D@"},
{Anything, "TO", Nothing, "tu"},
{Anything, "THAT", Nothing, "D&t"},
{Nothing, "THIS", Nothing, "DIs"},
{Nothing, "THEY", Anything, "DeI"},
{Nothing, "THERE", Anything, "Der"},
{Anything, "THER", Anything, "D3"},
{Anything, "THEIR", Anything, "Der"},
{Nothing, "THAN", Nothing, "D&n"},
{Nothing, "THEM", Nothing, "Dem"},
{Anything, "THESE", Nothing, "Diz"},
{Nothing, "THEN", Anything, "Den"},
{Anything, "THROUGH", Anything, "Tru"},
{Anything, "THOSE", Anything, "D@Uz"},
{Anything, "THOUGH", Nothing, "D@U"},
{Nothing, "THUS", Anything, "DVs"},
{Anything, "TH", Anything, "T"},
{"#:", "TED", Nothing, "tId"},
{"S", "TI", "#N", "tS"},
{Anything, "TI", "O", "S"},
{Anything, "TI", "A", "S"},
{Anything, "TIEN", Anything, "S@n"},
{Anything, "TUR", "#", "tS3"},
{Anything, "TU", "A", "tSu"},
{Nothing, "TWO", Anything, "tu"},
{Anything, "T", Anything, "t"},
{Anything, 0, Anything, Silent},
};
static Rule U_rules[] =
{
{Nothing, "UN", "I", "jun"},
{Nothing, "UN", Anything, "Vn"},
{Nothing, "UPON", Anything, "@pOn"},
{"T", "UR", "#", "Ur"},
{"S", "UR", "#", "Ur"},
{"R", "UR", "#", "Ur"},
{"D", "UR", "#", "Ur"},
{"L", "UR", "#", "Ur"},
{"Z", "UR", "#", "Ur"},
{"N", "UR", "#", "Ur"},
{"J", "UR", "#", "Ur"},
{"TH", "UR", "#", "Ur"},
{"CH", "UR", "#", "Ur"},
{"SH", "UR", "#", "Ur"},
{Anything, "UR", "#", "jUr"},
{Anything, "UR", Anything, "3"},
{Anything, "U", "^ ", "V"},
{Anything, "U", "^^", "V"},
{Anything, "UY", Anything, "aI"},
{" G", "U", "#", ""},
{"G", "U", "%", ""},
{"G", "U", "#", "w"},
{"#N", "U", Anything, "ju"},
{"T", "U", Anything, "u"},
{"S", "U", Anything, "u"},
{"R", "U", Anything, "u"},
{"D", "U", Anything, "u"},
{"L", "U", Anything, "u"},
{"Z", "U", Anything, "u"},
{"N", "U", Anything, "u"},
{"J", "U", Anything, "u"},
{"TH", "U", Anything, "u"},
{"CH", "U", Anything, "u"},
{"SH", "U", Anything, "u"},
{Anything, "U", Anything, "ju"},
{Anything, 0, Anything, Silent},
};
static Rule V_rules[] =
{
{Anything, "VIEW", Anything, "vju"},
{Anything, "V", Anything, "v"},
{Anything, 0, Anything, Silent},
};
static Rule W_rules[] =
{
{Nothing, "WERE", Anything, "w3"},
{Anything, "WA", "S", "w0"},
{Anything, "WA", "T", "w0"},
{Anything, "WHERE", Anything, "hwer"},
{Anything, "WHAT", Anything, "hw0t"},
{Anything, "WHOL", Anything, "h@Ul"},
{Anything, "WHO", Anything, "hu"},
{Anything, "WH", Anything, "hw"},
{Anything, "WAR", Anything, "wOr"},
{Anything, "WOR", "^", "w3"},
{Anything, "WR", Anything, "r"},
{Anything, "W", Anything, "w"},
{Anything, 0, Anything, Silent},
};
static Rule X_rules[] =
{
{Anything, "X", Anything, "ks"},
{Anything, 0, Anything, Silent},
};
static Rule Y_rules[] =
{
{Anything, "YOUNG", Anything, "jVN"},
{Nothing, "YOU", Anything, "ju"},
{Nothing, "YES", Anything, "jes"},
{Nothing, "Y", Anything, "j"},
{"#:^", "Y", Nothing, "i"},
{"#:^", "Y", "I", "i"},
{" :", "Y", Nothing, "aI"},
{" :", "Y", "#", "aI"},
{" :", "Y", "^+:#", "I"},
{" :", "Y", "^#", "aI"},
{Anything, "Y", Anything, "I"},
{Anything, 0, Anything, Silent},
};
static Rule Z_rules[] =
{
{Anything, "Z", Anything, "z"},
{Anything, 0, Anything, Silent},
};
static Rule *Rules[] =
{
punct_rules,
A_rules, B_rules, C_rules, D_rules, E_rules, F_rules, G_rules,
H_rules, I_rules, J_rules, K_rules, L_rules, M_rules, N_rules,
O_rules, P_rules, Q_rules, R_rules, S_rules, T_rules, U_rules,
V_rules, W_rules, X_rules, Y_rules, Z_rules
};
static const char *Cardinals[] =
{
"zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
};
static const char *Twenties[] =
{
"twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety"
};
static const char *Ordinals[] =
{
"zeroth", "first", "second", "third", "fourth",
"fifth", "sixth", "seventh","eighth", "ninth",
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth",
"fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
};
static const char *Ord_twenties[] =
{
"twentieth", "thirtieth", "fortieth", "fiftieth",
"sixtieth", "seventieth", "eightieth", "ninetieth"
};
static int xlate_cardinal(int value, darray *phone)
{
int nph = 0;
if (value < 0)
{
nph += xlate_string("minus", phone);
value = (-value);
if (value < 0)
{
nph += xlate_string("a lot", phone);
return nph;
}
}
if (value >= 1000000000L)
{
nph += xlate_cardinal(value / 1000000000L, phone);
nph += xlate_string("billion", phone);
value = value % 1000000000;
if (value == 0)
return nph;
if (value < 100)
nph += xlate_string("and", phone);
}
if (value >= 1000000L)
{
nph += xlate_cardinal(value / 1000000L, phone);
nph += xlate_string("million", phone);
value = value % 1000000L;
if (value == 0)
return nph;
if (value < 100)
nph += xlate_string("and", phone);
}
if ((value >= 1000L && value <= 1099L) || value >= 2000L)
{
nph += xlate_cardinal(value / 1000L, phone);
nph += xlate_string("thousand", phone);
value = value % 1000L;
if (value == 0)
return nph;
if (value < 100)
nph += xlate_string("and", phone);
}
if (value >= 100L)
{
nph += xlate_string(Cardinals[value / 100], phone);
nph += xlate_string("hundred", phone);
value = value % 100;
if (value == 0)
return nph;
}
if (value >= 20)
{
nph += xlate_string(Twenties[(value - 20) / 10], phone);
value = value % 10;
if (value == 0)
return nph;
}
nph += xlate_string(Cardinals[value], phone);
return nph;
}
#if 0#endif
static int isvowel(int chr)
{
return (chr == 'A' || chr == 'E' || chr == 'I' ||
chr == 'O' || chr == 'U');
}
static int isconsonant(int chr)
{
return (isupper(chr) && !isvowel(chr));
}
static int leftmatch(
const char *pattern,
const char *context)
{
const char *pat;
const char *text;
int count;
if (*pattern == '\0')
{
return 1;
}
count = (int)strlen(pattern);
pat = pattern + (count - 1);
text = context;
for (; count > 0; pat--, count--)
{
if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
{
if (*pat != *text)
{
return 0;
}
else
{
text--;
continue;
}
}
switch (*pat)
{
case '#':
if (!isvowel(*text))
return 0;
text--;
while (isvowel(*text))
text--;
break;
case ':':
while (isconsonant(*text))
text--;
break;
case '^':
if (!isconsonant(*text))
return 0;
text--;
break;
case '.':
if (*text != 'B' && *text != 'D' && *text != 'V'
&& *text != 'G' && *text != 'J' && *text != 'L'
&& *text != 'M' && *text != 'N' && *text != 'R'
&& *text != 'W' && *text != 'Z')
return 0;
text--;
break;
case '+':
if (*text != 'E' && *text != 'I' && *text != 'Y')
return 0;
text--;
break;
case '%':
default:
fprintf(stderr, "Bad char in left rule: '%c'\n", *pat);
return 0;
}
}
return 1;
}
static int rightmatch(
const char *pattern,
const char *context)
{
const char *pat;
const char *text;
if (*pattern == '\0')
return 1;
pat = pattern;
text = context;
for (pat = pattern; *pat != '\0'; pat++)
{
if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
{
if (*pat != *text)
{
return 0;
}
else
{
text++;
continue;
}
}
switch (*pat)
{
case '#':
if (!isvowel(*text))
return 0;
text++;
while (isvowel(*text))
text++;
break;
case ':':
while (isconsonant(*text))
text++;
break;
case '^':
if (!isconsonant(*text))
return 0;
text++;
break;
case '.':
if (*text != 'B' && *text != 'D' && *text != 'V'
&& *text != 'G' && *text != 'J' && *text != 'L'
&& *text != 'M' && *text != 'N' && *text != 'R'
&& *text != 'W' && *text != 'Z')
return 0;
text++;
break;
case '+':
if (*text != 'E' && *text != 'I' && *text != 'Y')
return 0;
text++;
break;
case '%':
if (*text == 'E')
{
text++;
if (*text == 'L')
{
text++;
if (*text == 'Y')
{
text++;
break;
}
else
{
text--;
break;
}
}
else
if (*text == 'R' || *text == 'S' || *text == 'D')
text++;
break;
}
else
if (*text == 'I')
{
text++;
if (*text == 'N')
{
text++;
if (*text == 'G')
{
text++;
break;
}
}
return 0;
}
else
return 0;
default:
fprintf(stderr, "Bad char in right rule:'%c'\n", *pat);
return 0;
}
}
return 1;
}
static void phone_cat(darray *arg, const char *s)
{
char ch;
while ((ch = *s++))
arg->put(ch);
}
static int find_rule(darray *arg, char *word, int index, Rule *rules)
{
for (;;)
{
Rule *rule;
const char *left,
*match,
*right,
*output;
int remainder;
rule = rules++;
match = (*rule)[1];
if (match == 0)
{
fprintf(stderr, "Error: Can't find rule for: '%c' in \"%s\"\n",
word[index], word);
return index + 1;
}
for (remainder = index; *match != '\0'; match++, remainder++)
{
if (*match != word[remainder])
break;
}
if (*match != '\0')
continue;
left = (*rule)[0];
right = (*rule)[2];
if (!leftmatch(left, &word[index - 1]))
continue;
if (!rightmatch(right, &word[remainder]))
continue;
output = (*rule)[3];
phone_cat(arg, output);
return remainder;
}
}
static void guess_word(darray *arg, char *word)
{
int index;
int type;
index = 1;
do
{
if (isupper(word[index]))
type = word[index] - 'A' + 1;
else
type = 0;
index = find_rule(arg, word, index, Rules[type]);
}
while (word[index] != '\0');
}
static int NRL(const char *s, int n, darray *phone)
{
int old = phone->getSize();
char *word = (char *) malloc(n + 3); char *d = word;
*d++ = ' ';
while (n-- > 0)
{
char ch = *s++;
if (islower(ch))
ch = (char)toupper(ch);
*d++ = ch;
}
*d++ = ' ';
*d = '\0';
guess_word(phone, word);
free(word);
return phone->getSize() - old;
}
static int spell_out(const char *word, int n, darray *phone)
{
int nph = 0;
while (n-- > 0)
{
nph += xlate_string(ASCII[*word++ & 0x7F], phone);
}
return nph;
}
static int suspect_word(const char *s, int n)
{
int i = 0;
int seen_lower = 0;
int seen_upper = 0;
int seen_vowel = 0;
int last = 0;
for (i = 0; i < n; i++)
{
char ch = *s++;
if (i && last != '-' && isupper(ch))
seen_upper = 1;
if (islower(ch))
{
seen_lower = 1;
ch = (char)toupper(ch);
}
if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y')
seen_vowel = 1;
last = ch;
}
return !seen_vowel || (seen_upper && seen_lower) || !seen_lower;
}
static int xlate_word(const char *word, int n, darray *phone)
{
int nph = 0;
if (*word != '[')
{
if (suspect_word(word, n))
return spell_out(word, n, phone);
else
{
nph += NRL(word, n, phone);
}
}
else
{
if ((++word)[(--n) - 1] == ']')
n--;
while (n-- > 0)
{
phone->put(*word++);
nph++;
}
}
phone->put(' ');
return nph + 1;
}
int xlate_string(const char *string, darray *phone)
{
int nph = 0;
const char *s = string;
char ch;
while (isspace(ch = *s))
s++;
while (*s)
{
ch = *s;
const char *word = s;
if (isalpha(ch))
{
while (isalpha(ch = *s) || ((ch == '\'' || ch == '-' || ch == '.') && isalpha(s[1])))
{
s++;
}
if (!ch || isspace(ch) || ispunct(ch) || (isdigit(ch) && !suspect_word(word, (int)(s - word))))
{
nph += xlate_word(word, (int)(s - word), phone);
}
else
{
while (*s && !isspace(*s) && !ispunct(*s))
{
ch = *s;
s++;
}
nph += spell_out(word, (int)(s - word), phone);
}
}
else
{
if (isdigit(ch) || (ch == '-' && isdigit(s[1])))
{
int sign = (ch == '-') ? -1 : 1;
int value = 0;
if (sign < 0)
{
ch = *++s;
}
while (isdigit(ch = *s))
{
value = value * 10 + ch - '0';
s++;
}
if (ch == '.' && isdigit(s[1]))
{
word = ++s;
nph += xlate_cardinal(value * sign, phone);
nph += xlate_string("point", phone);
while (isdigit(ch = *s))
{
s++;
}
nph += spell_out(word, (int)(s - word), phone);
}
else
{
nph += xlate_cardinal(value * sign, phone);
}
}
else
{
if (ch == '[' && strchr(s, ']'))
{
const char *thisword = s;
while (*s && *s++ != ']')
;
nph += xlate_word(thisword, (int)(s - thisword), phone);
}
else
{
if (ispunct(ch))
{
switch (ch)
{
case '!':
case '?':
case '.':
s++;
phone->put('.'); break;
case '"':
case ':':
case '-':
case ';':
case ',':
case '(':
case ')':
s++;
phone->put(' ');
break;
case '[':
{
const char *e = strchr(s, ']');
if (e)
{
s++;
while (s < e)
phone->put(*s++);
s = e + 1;
break;
}
}
default:
nph += spell_out(word, 1, phone);
s++;
break;
}
}
else
{
while (*s && !isspace(*s))
{
ch = *s;
s++;
}
nph += spell_out(word, (int)(s - word), phone);
}
}
}
while (isspace(ch = *s))
s++;
}
}
return nph;
}