[net.sources] Final English-to-Phoneme version!

wasser_1@viking.DEC (John A. Wasser) (04/15/85)

	                Final Version of
                ENGLISH TO PHONEME TRANSLATION
                           4/15/85

        Here it is one last time.  I have fixed all of the bugs I
        heard about and added a new feature or two (it now talks
        money as well as numbers).  I think that this version is
        good enough for most purposes.  I have proof-read the
        phoneme rules (found one bug) and made the program more
        "robust".  I added protection against the "toupper()"
        problem some people had with earlier versions.

        If you make a major addition (like better abbreviation
        handling or an exception dictionary) please send me a
        copy.  As before, this is all public domain and I make
        no copyright claims on it.  The part derived from the
        Naval Research Lab should be public anyway.  Sell it
        if you can!

                -John A. Wasser

Work address:
ARPAnet:        WASSER%VIKING.DEC@decwrl.ARPA
Usenet:         {allegra,Shasta,decvax}!decwrl!dec-rhea!dec-viking!wasser
Easynet:        VIKING::WASSER
Telephone:      (617)486-2505
USPS:           Digital Equipment Corp.
                Mail stop: LJO2/E4
                30 Porter Rd
                Littleton, MA  01460


   The files that make up this package are:

          ENGLISH.C       Translation rules.
          PHONEME.C       Translate a single word.
          PARSE.C         Split a file into words.
          SPELLWORD.C     Spell an ASCII character or word.
          SAYNUM.C        Say a cardinal or ordinal number (long int).

-------------------------------------------------------------------------------
**FILE**        ENGLISH.C        Translation rules.
-------------------------------------------------------------------------------
/*
**	English to Phoneme rules.
**
**	Derived from: 
**
**	     AUTOMATIC TRANSLATION OF ENGLISH TEXT TO PHONETICS
**	            BY MEANS OF LETTER-TO-SOUND RULES
**
**			NRL Report 7948
**
**		      January 21st, 1976
**	    Naval Research Laboratory, Washington, D.C.
**
**
**	Published by the National Technical Information Service as
**	document "AD/A021 929".
**
**
**
**	The Phoneme codes:
**
**		IY	bEEt		IH	bIt
**		EY	gAte		EH	gEt
**		AE	fAt		AA	fAther
**		AO	lAWn		OW	lOne
**		UH	fUll		UW	fOOl
**		ER	mURdER		AX	About
**		AH	bUt		AY	hIde
**		AW	hOW		OY	tOY
**	
**		p	Pack		b	Back
**		t	Time		d	Dime
**		k	Coat		g	Goat
**		f	Fault		v	Vault
**		TH	eTHer		DH	eiTHer
**		s	Sue		z	Zoo
**		SH	leaSH		ZH	leiSure
**		HH	How		m	suM
**		n	suN		NG	suNG
**		l	Laugh		w	Wear
**		y	Young		r	Rate
**		CH	CHar		j	Jar
**		WH	WHere
**
**
**	Rules are made up of four parts:
**	
**		The left context.
**		The text to match.
**		The right context.
**		The phonemes to substitute for the matched text.
**
**	Procedure:
**
**		Seperate each block of letters (apostrophes included) 
**		and add a space on each side.  For each unmatched 
**		letter in the word, look through the rules where the 
**		text to match starts with the letter in the word.  If 
**		the text to match is found and the right and left 
**		context patterns also match, output the phonemes for 
**		that rule and skip to the next unmatched letter.
**
**
**	Special Context Symbols:
**
**		#	One or more vowels
**		:	Zero or more consonants
**		^	One consonant.
**		.	One of B, D, V, G, J, L, M, N, R, W or Z (voiced 
**			consonants)
**		%	One of ER, E, ES, ED, ING, ELY (a suffix)
**			(Found in right context only)
**		+	One of E, I or Y (a "front" vowel)
**
*/


/* Context definitions */
static char Anything[] = "";	/* No context requirement */
static char Nothing[] = " ";	/* Context is beginning or end of word */

/* Phoneme definitions */
static char Pause[] = " ";	/* Short silence */
static char Silent[] = "";	/* No phonemes */

#define LEFT_PART	0
#define MATCH_PART	1
#define RIGHT_PART	2
#define OUT_PART	3

typedef char *Rule[4];	/* Rule is an array of 4 character pointers */

/*0 = Punctuation */
/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule punct_rules[] =
	{
	{Anything,	" ",		Anything,	Pause	},
	{Anything,	"-",		Anything,	Silent	},
	{".",		"'S",		Anything,	"z"	},
	{"#:.E",	"'S",		Anything,	"z"	},
	{"#",		"'S",		Anything,	"z"	},
	{Anything,	"'",		Anything,	Silent	},
	{Anything,	",",		Anything,	Pause	},
	{Anything,	".",		Anything,	Pause	},
	{Anything,	"?",		Anything,	Pause	},
	{Anything,	"!",		Anything,	Pause	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule A_rules[] =
	{
	{Anything,	"A",		Nothing,	"AX"	},
	{Nothing,	"ARE",		Nothing,	"AAr"	},
	{Nothing,	"AR",		"O",		"AXr"	},
	{Anything,	"AR",		"#",		"EHr"	},
	{"^",		"AS",		"#",		"EYs"	},
	{Anything,	"A",		"WA",		"AX"	},
	{Anything,	"AW",		Anything,	"AO"	},
	{" :",		"ANY",		Anything,	"EHnIY"	},
	{Anything,	"A",		"^+#",		"EY"	},
	{"#:",		"ALLY",		Anything,	"AXlIY"	},
	{Nothing,	"AL",		"#",		"AXl"	},
	{Anything,	"AGAIN",	Anything,	"AXgEHn"},
	{"#:",		"AG",		"E",		"IHj"	},
	{Anything,	"A",		"^+:#",		"AE"	},
	{" :",		"A",		"^+ ",		"EY"	},
	{Anything,	"A",		"^%",		"EY"	},
	{Nothing,	"ARR",		Anything,	"AXr"	},
	{Anything,	"ARR",		Anything,	"AEr"	},
	{" :",		"AR",		Nothing,	"AAr"	},
	{Anything,	"AR",		Nothing,	"ER"	},
	{Anything,	"AR",		Anything,	"AAr"	},
	{Anything,	"AIR",		Anything,	"EHr"	},
	{Anything,	"AI",		Anything,	"EY"	},
	{Anything,	"AY",		Anything,	"EY"	},
	{Anything,	"AU",		Anything,	"AO"	},
	{"#:",		"AL",		Nothing,	"AXl"	},
	{"#:",		"ALS",		Nothing,	"AXlz"	},
	{Anything,	"ALK",		Anything,	"AOk"	},
	{Anything,	"AL",		"^",		"AOl"	},
	{" :",		"ABLE",		Anything,	"EYbAXl"},
	{Anything,	"ABLE",		Anything,	"AXbAXl"},
	{Anything,	"ANG",		"+",		"EYnj"	},
	{Anything,	"A",		Anything,	"AE"	},
 	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule B_rules[] =
	{
	{Nothing,	"BE",		"^#",		"bIH"	},
	{Anything,	"BEING",	Anything,	"bIYIHNG"},
	{Nothing,	"BOTH",		Nothing,	"bOWTH"	},
	{Nothing,	"BUS",		"#",		"bIHz"	},
	{Anything,	"BUIL",		Anything,	"bIHl"	},
	{Anything,	"B",		Anything,	"b"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule C_rules[] =
	{
	{Nothing,	"CH",		"^",		"k"	},
	{"^E",		"CH",		Anything,	"k"	},
	{Anything,	"CH",		Anything,	"CH"	},
	{" S",		"CI",		"#",		"sAY"	},
	{Anything,	"CI",		"A",		"SH"	},
	{Anything,	"CI",		"O",		"SH"	},
	{Anything,	"CI",		"EN",		"SH"	},
	{Anything,	"C",		"+",		"s"	},
	{Anything,	"CK",		Anything,	"k"	},
	{Anything,	"COM",		"%",		"kAHm"	},
	{Anything,	"C",		Anything,	"k"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule D_rules[] =
	{
	{"#:",		"DED",		Nothing,	"dIHd"	},
	{".E",		"D",		Nothing,	"d"	},
	{"#:^E",	"D",		Nothing,	"t"	},
	{Nothing,	"DE",		"^#",		"dIH"	},
	{Nothing,	"DO",		Nothing,	"dUW"	},
	{Nothing,	"DOES",		Anything,	"dAHz"	},
	{Nothing,	"DOING",	Anything,	"dUWIHNG"},
	{Nothing,	"DOW",		Anything,	"dAW"	},
	{Anything,	"DU",		"A",		"jUW"	},
	{Anything,	"D",		Anything,	"d"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule E_rules[] =
	{
	{"#:",		"E",		Nothing,	Silent	},
	{"':^",		"E",		Nothing,	Silent	},
	{" :",		"E",		Nothing,	"IY"	},
	{"#",		"ED",		Nothing,	"d"	},
	{"#:",		"E",		"D ",		Silent	},
	{Anything,	"EV",		"ER",		"EHv"	},
	{Anything,	"E",		"^%",		"IY"	},
	{Anything,	"ERI",		"#",		"IYrIY"	},
	{Anything,	"ERI",		Anything,	"EHrIH"	},
	{"#:",		"ER",		"#",		"ER"	},
	{Anything,	"ER",		"#",		"EHr"	},
	{Anything,	"ER",		Anything,	"ER"	},
	{Nothing,	"EVEN",		Anything,	"IYvEHn"},
	{"#:",		"E",		"W",		Silent	},
	{"T",		"EW",		Anything,	"UW"	},
	{"S",		"EW",		Anything,	"UW"	},
	{"R",		"EW",		Anything,	"UW"	},
	{"D",		"EW",		Anything,	"UW"	},
	{"L",		"EW",		Anything,	"UW"	},
	{"Z",		"EW",		Anything,	"UW"	},
	{"N",		"EW",		Anything,	"UW"	},
	{"J",		"EW",		Anything,	"UW"	},
	{"TH",		"EW",		Anything,	"UW"	},
	{"CH",		"EW",		Anything,	"UW"	},
	{"SH",		"EW",		Anything,	"UW"	},
	{Anything,	"EW",		Anything,	"yUW"	},
	{Anything,	"E",		"O",		"IY"	},
	{"#:S",		"ES",		Nothing,	"IHz"	},
	{"#:C",		"ES",		Nothing,	"IHz"	},
	{"#:G",		"ES",		Nothing,	"IHz"	},
	{"#:Z",		"ES",		Nothing,	"IHz"	},
	{"#:X",		"ES",		Nothing,	"IHz"	},
	{"#:J",		"ES",		Nothing,	"IHz"	},
	{"#:CH",	"ES",		Nothing,	"IHz"	},
	{"#:SH",	"ES",		Nothing,	"IHz"	},
	{"#:",		"E",		"S ",		Silent	},
	{"#:",		"ELY",		Nothing,	"lIY"	},
	{"#:",		"EMENT",	Anything,	"mEHnt"	},
	{Anything,	"EFUL",		Anything,	"fUHl"	},
	{Anything,	"EE",		Anything,	"IY"	},
	{Anything,	"EARN",		Anything,	"ERn"	},
	{Nothing,	"EAR",		"^",		"ER"	},
	{Anything,	"EAD",		Anything,	"EHd"	},
	{"#:",		"EA",		Nothing,	"IYAX"	},
	{Anything,	"EA",		"SU",		"EH"	},
	{Anything,	"EA",		Anything,	"IY"	},
	{Anything,	"EIGH",		Anything,	"EY"	},
	{Anything,	"EI",		Anything,	"IY"	},
	{Nothing,	"EYE",		Anything,	"AY"	},
	{Anything,	"EY",		Anything,	"IY"	},
	{Anything,	"EU",		Anything,	"yUW"	},
	{Anything,	"E",		Anything,	"EH"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule F_rules[] =
	{
	{Anything,	"FUL",		Anything,	"fUHl"	},
	{Anything,	"F",		Anything,	"f"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule G_rules[] =
	{
	{Anything,	"GIV",		Anything,	"gIHv"	},
	{Nothing,	"G",		"I^",		"g"	},
	{Anything,	"GE",		"T",		"gEH"	},
	{"SU",		"GGES",		Anything,	"gjEHs"	},
	{Anything,	"GG",		Anything,	"g"	},
	{" B#",		"G",		Anything,	"g"	},
	{Anything,	"G",		"+",		"j"	},
	{Anything,	"GREAT",	Anything,	"grEYt"	},
	{"#",		"GH",		Anything,	Silent	},
	{Anything,	"G",		Anything,	"g"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule H_rules[] =
	{
	{Nothing,	"HAV",		Anything,	"hAEv"	},
	{Nothing,	"HERE",		Anything,	"hIYr"	},
	{Nothing,	"HOUR",		Anything,	"AWER"	},
	{Anything,	"HOW",		Anything,	"hAW"	},
	{Anything,	"H",		"#",		"h"	},
	{Anything,	"H",		Anything,	Silent	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule I_rules[] =
	{
	{Nothing,	"IN",		Anything,	"IHn"	},
	{Nothing,	"I",		Nothing,	"AY"	},
	{Anything,	"IN",		"D",		"AYn"	},
	{Anything,	"IER",		Anything,	"IYER"	},
	{"#:R",		"IED",		Anything,	"IYd"	},
	{Anything,	"IED",		Nothing,	"AYd"	},
	{Anything,	"IEN",		Anything,	"IYEHn"	},
	{Anything,	"IE",		"T",		"AYEH"	},
	{" :",		"I",		"%",		"AY"	},
	{Anything,	"I",		"%",		"IY"	},
	{Anything,	"IE",		Anything,	"IY"	},
	{Anything,	"I",		"^+:#",		"IH"	},
	{Anything,	"IR",		"#",		"AYr"	},
	{Anything,	"IZ",		"%",		"AYz"	},
	{Anything,	"IS",		"%",		"AYz"	},
	{Anything,	"I",		"D%",		"AY"	},
	{"+^",		"I",		"^+",		"IH"	},
	{Anything,	"I",		"T%",		"AY"	},
	{"#:^",		"I",		"^+",		"IH"	},
	{Anything,	"I",		"^+",		"AY"	},
	{Anything,	"IR",		Anything,	"ER"	},
	{Anything,	"IGH",		Anything,	"AY"	},
	{Anything,	"ILD",		Anything,	"AYld"	},
	{Anything,	"IGN",		Nothing,	"AYn"	},
	{Anything,	"IGN",		"^",		"AYn"	},
	{Anything,	"IGN",		"%",		"AYn"	},
	{Anything,	"IQUE",		Anything,	"IYk"	},
	{Anything,	"I",		Anything,	"IH"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule J_rules[] =
	{
	{Anything,	"J",		Anything,	"j"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule K_rules[] =
	{
	{Nothing,	"K",		"N",		Silent	},
	{Anything,	"K",		Anything,	"k"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule L_rules[] =
	{
	{Anything,	"LO",		"C#",		"lOW"	},
	{"L",		"L",		Anything,	Silent	},
	{"#:^",		"L",		"%",		"AXl"	},
	{Anything,	"LEAD",		Anything,	"lIYd"	},
	{Anything,	"L",		Anything,	"l"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule M_rules[] =
	{
	{Anything,	"MOV",		Anything,	"mUWv"	},
	{Anything,	"M",		Anything,	"m"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule N_rules[] =
	{
	{"E",		"NG",		"+",		"nj"	},
	{Anything,	"NG",		"R",		"NGg"	},
	{Anything,	"NG",		"#",		"NGg"	},
	{Anything,	"NGL",		"%",		"NGgAXl"},
	{Anything,	"NG",		Anything,	"NG"	},
	{Anything,	"NK",		Anything,	"NGk"	},
	{Nothing,	"NOW",		Nothing,	"nAW"	},
	{Anything,	"N",		Anything,	"n"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule O_rules[] =
	{
	{Anything,	"OF",		Nothing,	"AXv"	},
	{Anything,	"OROUGH",	Anything,	"EROW"	},
	{"#:",		"OR",		Nothing,	"ER"	},
	{"#:",		"ORS",		Nothing,	"ERz"	},
	{Anything,	"OR",		Anything,	"AOr"	},
	{Nothing,	"ONE",		Anything,	"wAHn"	},
	{Anything,	"OW",		Anything,	"OW"	},
	{Nothing,	"OVER",		Anything,	"OWvER"	},
	{Anything,	"OV",		Anything,	"AHv"	},
	{Anything,	"O",		"^%",		"OW"	},
	{Anything,	"O",		"^EN",		"OW"	},
	{Anything,	"O",		"^I#",		"OW"	},
	{Anything,	"OL",		"D",		"OWl"	},
	{Anything,	"OUGHT",	Anything,	"AOt"	},
	{Anything,	"OUGH",		Anything,	"AHf"	},
	{Nothing,	"OU",		Anything,	"AW"	},
	{"H",		"OU",		"S#",		"AW"	},
	{Anything,	"OUS",		Anything,	"AXs"	},
	{Anything,	"OUR",		Anything,	"AOr"	},
	{Anything,	"OULD",		Anything,	"UHd"	},
	{"^",		"OU",		"^L",		"AH"	},
	{Anything,	"OUP",		Anything,	"UWp"	},
	{Anything,	"OU",		Anything,	"AW"	},
	{Anything,	"OY",		Anything,	"OY"	},
	{Anything,	"OING",		Anything,	"OWIHNG"},
	{Anything,	"OI",		Anything,	"OY"	},
	{Anything,	"OOR",		Anything,	"AOr"	},
	{Anything,	"OOK",		Anything,	"UHk"	},
	{Anything,	"OOD",		Anything,	"UHd"	},
	{Anything,	"OO",		Anything,	"UW"	},
	{Anything,	"O",		"E",		"OW"	},
	{Anything,	"O",		Nothing,	"OW"	},
	{Anything,	"OA",		Anything,	"OW"	},
	{Nothing,	"ONLY",		Anything,	"OWnlIY"},
	{Nothing,	"ONCE",		Anything,	"wAHns"	},
	{Anything,	"ON'T",		Anything,	"OWnt"	},
	{"C",		"O",		"N",		"AA"	},
	{Anything,	"O",		"NG",		"AO"	},
	{" :^",		"O",		"N",		"AH"	},
	{"I",		"ON",		Anything,	"AXn"	},
	{"#:",		"ON",		Nothing,	"AXn"	},
	{"#^",		"ON",		Anything,	"AXn"	},
	{Anything,	"O",		"ST ",		"OW"	},
	{Anything,	"OF",		"^",		"AOf"	},
	{Anything,	"OTHER",	Anything,	"AHDHER"},
	{Anything,	"OSS",		Nothing,	"AOs"	},
	{"#:^",		"OM",		Anything,	"AHm"	},
	{Anything,	"O",		Anything,	"AA"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule P_rules[] =
	{
	{Anything,	"PH",		Anything,	"f"	},
	{Anything,	"PEOP",		Anything,	"pIYp"	},
	{Anything,	"POW",		Anything,	"pAW"	},
	{Anything,	"PUT",		Nothing,	"pUHt"	},
	{Anything,	"P",		Anything,	"p"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule Q_rules[] =
	{
	{Anything,	"QUAR",		Anything,	"kwAOr"	},
	{Anything,	"QU",		Anything,	"kw"	},
	{Anything,	"Q",		Anything,	"k"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule R_rules[] =
	{
	{Nothing,	"RE",		"^#",		"rIY"	},
	{Anything,	"R",		Anything,	"r"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule S_rules[] =
	{
	{Anything,	"SH",		Anything,	"SH"	},
	{"#",		"SION",		Anything,	"ZHAXn"	},
	{Anything,	"SOME",		Anything,	"sAHm"	},
	{"#",		"SUR",		"#",		"ZHER"	},
	{Anything,	"SUR",		"#",		"SHER"	},
	{"#",		"SU",		"#",		"ZHUW"	},
	{"#",		"SSU",		"#",		"SHUW"	},
	{"#",		"SED",		Nothing,	"zd"	},
	{"#",		"S",		"#",		"z"	},
	{Anything,	"SAID",		Anything,	"sEHd"	},
	{"^",		"SION",		Anything,	"SHAXn"	},
	{Anything,	"S",		"S",		Silent	},
	{".",		"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+",		Silent	},
	{"#",		"SM",		Anything,	"zm"	},
	{"#",		"SN",		"'",		"zAXn"	},
	{Anything,	"S",		Anything,	"s"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule T_rules[] =
	{
	{Nothing,	"THE",		Nothing,	"DHAX"	},
	{Anything,	"TO",		Nothing,	"tUW"	},
	{Anything,	"THAT",		Nothing,	"DHAEt"	},
	{Nothing,	"THIS",		Nothing,	"DHIHs"	},
	{Nothing,	"THEY",		Anything,	"DHEY"	},
	{Nothing,	"THERE",	Anything,	"DHEHr"	},
	{Anything,	"THER",		Anything,	"DHER"	},
	{Anything,	"THEIR",	Anything,	"DHEHr"	},
	{Nothing,	"THAN",		Nothing,	"DHAEn"	},
	{Nothing,	"THEM",		Nothing,	"DHEHm"	},
	{Anything,	"THESE",	Nothing,	"DHIYz"	},
	{Nothing,	"THEN",		Anything,	"DHEHn"	},
	{Anything,	"THROUGH",	Anything,	"THrUW"	},
	{Anything,	"THOSE",	Anything,	"DHOWz"	},
	{Anything,	"THOUGH",	Nothing,	"DHOW"	},
	{Nothing,	"THUS",		Anything,	"DHAHs"	},
	{Anything,	"TH",		Anything,	"TH"	},
	{"#:",		"TED",		Nothing,	"tIHd"	},
	{"S",		"TI",		"#N",		"CH"	},
	{Anything,	"TI",		"O",		"SH"	},
	{Anything,	"TI",		"A",		"SH"	},
	{Anything,	"TIEN",		Anything,	"SHAXn"	},
	{Anything,	"TUR",		"#",		"CHER"	},
	{Anything,	"TU",		"A",		"CHUW"	},
	{Nothing,	"TWO",		Anything,	"tUW"	},
	{Anything,	"T",		Anything,	"t"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule U_rules[] =
	{
	{Nothing,	"UN",		"I",		"yUWn"	},
	{Nothing,	"UN",		Anything,	"AHn"	},
	{Nothing,	"UPON",		Anything,	"AXpAOn"},
	{"T",		"UR",		"#",		"UHr"	},
	{"S",		"UR",		"#",		"UHr"	},
	{"R",		"UR",		"#",		"UHr"	},
	{"D",		"UR",		"#",		"UHr"	},
	{"L",		"UR",		"#",		"UHr"	},
	{"Z",		"UR",		"#",		"UHr"	},
	{"N",		"UR",		"#",		"UHr"	},
	{"J",		"UR",		"#",		"UHr"	},
	{"TH",		"UR",		"#",		"UHr"	},
	{"CH",		"UR",		"#",		"UHr"	},
	{"SH",		"UR",		"#",		"UHr"	},
	{Anything,	"UR",		"#",		"yUHr"	},
	{Anything,	"UR",		Anything,	"ER"	},
	{Anything,	"U",		"^ ",		"AH"	},
	{Anything,	"U",		"^^",		"AH"	},
	{Anything,	"UY",		Anything,	"AY"	},
	{" G",		"U",		"#",		Silent	},
	{"G",		"U",		"%",		Silent	},
	{"G",		"U",		"#",		"w"	},
	{"#N",		"U",		Anything,	"yUW"	},
	{"T",		"U",		Anything,	"UW"	},
	{"S",		"U",		Anything,	"UW"	},
	{"R",		"U",		Anything,	"UW"	},
	{"D",		"U",		Anything,	"UW"	},
	{"L",		"U",		Anything,	"UW"	},
	{"Z",		"U",		Anything,	"UW"	},
	{"N",		"U",		Anything,	"UW"	},
	{"J",		"U",		Anything,	"UW"	},
	{"TH",		"U",		Anything,	"UW"	},
	{"CH",		"U",		Anything,	"UW"	},
	{"SH",		"U",		Anything,	"UW"	},
	{Anything,	"U",		Anything,	"yUW"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule V_rules[] =
	{
	{Anything,	"VIEW",		Anything,	"vyUW"	},
	{Anything,	"V",		Anything,	"v"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule W_rules[] =
	{
	{Nothing,	"WERE",		Anything,	"wER"	},
	{Anything,	"WA",		"S",		"wAA"	},
	{Anything,	"WA",		"T",		"wAA"	},
	{Anything,	"WHERE",	Anything,	"WHEHr"	},
	{Anything,	"WHAT",		Anything,	"WHAAt"	},
	{Anything,	"WHOL",		Anything,	"hOWl"	},
	{Anything,	"WHO",		Anything,	"hUW"	},
	{Anything,	"WH",		Anything,	"WH"	},
	{Anything,	"WAR",		Anything,	"wAOr"	},
	{Anything,	"WOR",		"^",		"wER"	},
	{Anything,	"WR",		Anything,	"r"	},
	{Anything,	"W",		Anything,	"w"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule X_rules[] =
	{
	{Anything,	"X",		Anything,	"ks"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule Y_rules[] =
	{
	{Anything,	"YOUNG",	Anything,	"yAHNG"	},
	{Nothing,	"YOU",		Anything,	"yUW"	},
	{Nothing,	"YES",		Anything,	"yEHs"	},
	{Nothing,	"Y",		Anything,	"y"	},
	{"#:^",		"Y",		Nothing,	"IY"	},
	{"#:^",		"Y",		"I",		"IY"	},
	{" :",		"Y",		Nothing,	"AY"	},
	{" :",		"Y",		"#",		"AY"	},
	{" :",		"Y",		"^+:#",		"IH"	},
	{" :",		"Y",		"^#",		"AY"	},
	{Anything,	"Y",		Anything,	"IH"	},
	{Anything,	0,		Anything,	Silent	},
	};

/*
**	LEFT_PART	MATCH_PART	RIGHT_PART	OUT_PART
*/
static Rule Z_rules[] =
	{
	{Anything,	"Z",		Anything,	"z"	},
	{Anything,	0,		Anything,	Silent	},
	};

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
	};
-------------------------------------------------------------------------------
**FILE**        PHONEME.C        Translate a single word.
-------------------------------------------------------------------------------
#include <stdio.h>
#include <ctype.h>

#define FALSE (0)
#define TRUE (!0)

/*
**	English to Phoneme translation.
**
**	Rules are made up of four parts:
**	
**		The left context.
**		The text to match.
**		The right context.
**		The phonemes to substitute for the matched text.
**
**	Procedure:
**
**		Seperate each block of letters (apostrophes included) 
**		and add a space on each side.  For each unmatched 
**		letter in the word, look through the rules where the 
**		text to match starts with the letter in the word.  If 
**		the text to match is found and the right and left 
**		context patterns also match, output the phonemes for 
**		that rule and skip to the next unmatched letter.
**
**
**	Special Context Symbols:
**
**		#	One or more vowels
**		:	Zero or more consonants
**		^	One consonant.
**		.	One of B, D, V, G, J, L, M, N, R, W or Z (voiced 
**			consonants)
**		%	One of ER, E, ES, ED, ING, ELY (a suffix)
**			(Right context only)
**		+	One of E, I or Y (a "front" vowel)
*/

typedef char *Rule[4];	/* A rule is four character pointers */

extern Rule *Rules[];	/* An array of pointers to rules */

int isvowel(chr)
	char chr;
	{
	return (chr == 'A' || chr == 'E' || chr == 'I' || 
		chr == 'O' || chr == 'U');
	}

int isconsonant(chr)
	char chr;
	{
	return (isupper(chr) && !isvowel(chr));
	}

xlate_word(word)
	char word[];
	{
	int index;	/* Current position in word */
	int type;	/* First letter of match part */

	index = 1;	/* Skip the initial blank */
	do
		{
		if (isupper(word[index]))
			type = word[index] - 'A' + 1;
		else
			type = 0;

		index = find_rule(word, index, Rules[type]);
		}
	while (word[index] != '\0');
	}

find_rule(word, index, rules)
	char word[];
	int index;
	Rule *rules;
	{
	Rule *rule;
	char *left, *match, *right, *output;
	int remainder;

	for (;;)	/* Search for the rule */
		{
		rule = rules++;
		match = (*rule)[1];

		if (match == 0)	/* bad symbol! */
			{
			fprintf(stderr,
"Error: Can't find rule for: '%c' in \"%s\"\n", word[index], word);
			return index+1;	/* Skip it! */
			}

		for (remainder = index; *match != '\0'; match++, remainder++)
			{
			if (*match != word[remainder])
				break;
			}

		if (*match != '\0')	/* found missmatch */
			continue;
/*
printf("\nWord: \"%s\", Index:%4d, Trying: \"%s/%s/%s\" = \"%s\"\n",
    word, index, (*rule)[0], (*rule)[1], (*rule)[2], (*rule)[3]);
*/
		left = (*rule)[0];
		right = (*rule)[2];

		if (!leftmatch(left, &word[index-1]))
			continue;
/*
printf("leftmatch(\"%s\",\"...%c\") succeded!\n", left, word[index-1]);
*/
		if (!rightmatch(right, &word[remainder]))
			continue;
/*
printf("rightmatch(\"%s\",\"%s\") succeded!\n", right, &word[remainder]);
*/
		output = (*rule)[3];
/*
printf("Success: ");
*/
		outstring(output);
		return remainder;
		}
	}


leftmatch(pattern, context)
	char *pattern;	/* first char of pattern to match in text */
	char *context;	/* last char of text to be matched */
	{
	char *pat;
	char *text;
	int count;

	if (*pattern == '\0')	/* null string matches any context */
		{
		return TRUE;
		}

	/* point to last character in pattern string */
	count = strlen(pattern);
	pat = pattern + (count - 1);

	text = context;

	for (; count > 0; pat--, count--)
		{
		/* First check for simple text or space */
		if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
			if (*pat != *text)
				return FALSE;
			else
				{
				text--;
				continue;
				}

		switch (*pat)
			{
		case '#':	/* One or more vowels */
			if (!isvowel(*text))
				return FALSE;

			text--;

			while (isvowel(*text))
				text--;
			break;

		case ':':	/* Zero or more consonants */
			while (isconsonant(*text))
				text--;
			break;

		case '^':	/* One consonant */
			if (!isconsonant(*text))
				return FALSE;
			text--;
			break;

		case '.':	/* B, D, V, G, J, L, M, N, R, W, Z */
			if (*text != 'B' && *text != 'D' && *text != 'V'
			   && *text != 'G' && *text != 'J' && *text != 'L'
			   && *text != 'M' && *text != 'N' && *text != 'R'
			   && *text != 'W' && *text != 'Z')
				return FALSE;
			text--;
			break;

		case '+':	/* E, I or Y (front vowel) */
			if (*text != 'E' && *text != 'I' && *text != 'Y')
				return FALSE;
			text--;
			break;

		case '%':
		default:
			fprintf(stderr, "Bad char in left rule: '%c'\n", *pat);
			return FALSE;
			}
		}

	return TRUE;
	}


rightmatch(pattern, context)
	char *pattern;	/* first char of pattern to match in text */
	char *context;	/* last char of text to be matched */
	{
	char *pat;
	char *text;

	if (*pattern == '\0')	/* null string matches any context */
		return TRUE;

	pat = pattern;
	text = context;

	for (pat = pattern; *pat != '\0'; pat++)
		{
		/* First check for simple text or space */
		if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
			if (*pat != *text)
				return FALSE;
			else
				{
				text++;
				continue;
				}

		switch (*pat)
			{
		case '#':	/* One or more vowels */
			if (!isvowel(*text))
				return FALSE;

			text++;

			while (isvowel(*text))
				text++;
			break;

		case ':':	/* Zero or more consonants */
			while (isconsonant(*text))
				text++;
			break;

		case '^':	/* One consonant */
			if (!isconsonant(*text))
				return FALSE;
			text++;
			break;

		case '.':	/* B, D, V, G, J, L, M, N, R, W, Z */
			if (*text != 'B' && *text != 'D' && *text != 'V'
			   && *text != 'G' && *text != 'J' && *text != 'L'
			   && *text != 'M' && *text != 'N' && *text != 'R'
			   && *text != 'W' && *text != 'Z')
				return FALSE;
			text++;
			break;

		case '+':	/* E, I or Y (front vowel) */
			if (*text != 'E' && *text != 'I' && *text != 'Y')
				return FALSE;
			text++;
			break;

		case '%':	/* ER, E, ES, ED, ING, ELY (a suffix) */
			if (*text == 'E')
				{
				text++;
				if (*text == 'L')
					{
					text++;
					if (*text == 'Y')
						{
						text++;
						break;
						}
					else
						{
						text--; /* Don't gobble L */
						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 FALSE;
				}
			else
			return FALSE;

		default:
			fprintf(stderr, "Bad char in right rule:'%c'\n", *pat);
			return FALSE;
			}
		}

	return TRUE;
	}
-------------------------------------------------------------------------------
**FILE**        PARSE.C          Split a file into words.
-------------------------------------------------------------------------------
#include <stdio.h>
#include <ctype.h>

#define MAX_LENGTH 128

static FILE *In_file;
static FILE *Out_file;

static int Char, Char1, Char2, Char3;

/*
** main(argc, argv)
**	int argc;
**	char *argv[];
**
**	This is the main program.  It takes up to two file names (input
**	and output)  and translates the input file to phoneme codes
**	(see ENGLISH.C) on the output file.
*/
main(argc, argv)
	int argc;
	char *argv[];
	{
	if (argc > 3)
		{
		fputs("Usage: PHONEME [infile [outfile]]\n", stderr);
		exit();
		}

	if (argc == 1)
		{
		fputs("Enter english text:\n", stderr);
		}

	if (argc > 1)
		{
		In_file = fopen(argv[1], "r");
		if (In_file == 0)
			{
			fputs("Error: Cannot open input file.\n", stderr);
			fputs("Usage: PHONEME [infile [outfile]]\n", stderr);
			exit();
			}
		}
	else
		In_file = stdin;

	if (argc > 2)
		{
		Out_file = fopen(argv[2], "w");
		if (Out_file == 0)
			{
			fputs("Error: Cannot create output file.\n", stderr);
			fputs("Usage: PHONEME [infile [outfile]]\n", stderr);
			exit();
			}
		}
	else
		Out_file = stdout;

	xlate_file();
	}

outstring(string)
	char *string;
	{
	while (*string != '\0')
		outchar(*string++);
	}

outchar(chr)
	int chr;
	{
	fputc(chr,Out_file);
	}


int makeupper(character)
	int character;
	{
	if (islower(character))
		return toupper(character);
	else
		return character;
	}

new_char()
	{
	/*
	If the cache is full of newline, time to prime the look-ahead
	again.  If an EOF is found, fill the remainder of the queue with
	EOF's.
	*/
	if (Char == '\n'  && Char1 == '\n' && Char2 == '\n' && Char3 == '\n')
		{	/* prime the pump again */
		Char = getc(In_file);
		if (Char == EOF)
			{
			Char1 = EOF;
			Char2 = EOF;
			Char3 = EOF;
			return Char;
			}
		if (Char == '\n')
			return Char;

		Char1 = getc(In_file);
		if (Char1 == EOF)
			{
			Char2 = EOF;
			Char3 = EOF;
			return Char;
			}
		if (Char1 == '\n')
			return Char;

		Char2 = getc(In_file);
		if (Char2 == EOF)
			{
			Char3 = EOF;
			return Char;
			}
		if (Char2 == '\n')
			return Char;

		Char3 = getc(In_file);
		}
	else
		{
		/*
		Buffer not full of newline, shuffle the characters and
		either get a new one or propagate a newline or EOF.
		*/
		Char = Char1;
		Char1 = Char2;
		Char2 = Char3;
		if (Char3 != '\n' && Char3 != EOF)
			Char3 = getc(In_file);
		}
	return Char;
	}

/*
** xlate_file()
**
**	This is the input file translator.  It sets up the first character
**	and uses it to determine what kind of text follows.
*/
xlate_file()
	{
	/* Prime the queue */
	Char = '\n';
	Char1 = '\n';
	Char2 = '\n';
	Char3 = '\n';
	new_char();	/* Fill Char, Char1, Char2 and Char3 */

	while (Char != EOF)	/* All of the words in the file */
		{
		if (isdigit(Char))
			have_number();
		else
		if (isalpha(Char) || Char == '\'')
			have_letter();
		else
		if (Char == '$' && isdigit(Char1))
			have_dollars();
		else
			have_special();
		}
	}

have_dollars()
	{
	long int value;

	value = 0L;
	for (new_char() ; isdigit(Char) || Char == ',' ; new_char())
		{
		if (Char != ',')
			value = 10 * value + (Char-'0');
		}

	say_cardinal(value);	/* Say number of whole dollars */

	/* Found a character that is a non-digit and non-comma */

	/* Check for no decimal or no cents digits */
	if (Char != '.' || !isdigit(Char1))
		{
		if (value == 1L)
			outstring("dAAlER ");
		else
			outstring("dAAlAArz ");
		return;
		}

	/* We have '.' followed by a digit */

	new_char();	/* Skip the period */

	/* If it is ".dd " say as " DOLLARS AND n CENTS " */
	if (isdigit(Char1) && !isdigit(Char2))
		{
		if (value == 1L)
			outstring("dAAlER ");
		else
			outstring("dAAlAArz ");
		if (Char == '0' && Char1 == '0')
			{
			new_char();	/* Skip tens digit */
			new_char();	/* Skip units digit */
			return;
			}

		outstring("AAnd ");
		value = (Char-'0')*10 + Char1-'0';
		say_cardinal(value);

		if (value == 1L)
			outstring("sEHnt ");
		else
			outstring("sEHnts ");
		new_char();	/* Used Char (tens digit) */
		new_char();	/* Used Char1 (units digit) */
		return;
		}

	/* Otherwise say as "n POINT ddd DOLLARS " */

	outstring("pOYnt ");
	for ( ; isdigit(Char) ; new_char())
		{
		say_ascii(Char);
		}

	outstring("dAAlAArz ");

	return;
	}

have_special()
	{
	if (Char == '\n')
		outchar('\n');
	else
	if (!isspace(Char))
		say_ascii(Char);

	new_char();
	return;
	}


have_number()
	{
	long int value;
	int lastdigit;

	value = Char - '0';
	lastdigit = Char;

	for (new_char() ; isdigit(Char) ; new_char())
		{
		value = 10 * value + (Char-'0');
		lastdigit = Char;
		}

	/* Recognize ordinals based on last digit of number */
	switch (lastdigit)
		{
	case '1':	/* ST */
		if (makeupper(Char) == 'S' && makeupper(Char1) == 'T' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;

	case '2':	/* ND */
		if (makeupper(Char) == 'N' && makeupper(Char1) == 'D' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;

	case '3':	/* RD */
		if (makeupper(Char) == 'R' && makeupper(Char1) == 'D' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;

	case '0':	/* TH */
	case '4':	/* TH */
	case '5':	/* TH */
	case '6':	/* TH */
	case '7':	/* TH */
	case '8':	/* TH */
	case '9':	/* TH */
		if (makeupper(Char) == 'T' && makeupper(Char1) == 'H' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;
		}

	say_cardinal(value);

	/* Recognize decimal points */
	if (Char == '.' && isdigit(Char1))
		{
		outstring("pOYnt ");
		for (new_char() ; isdigit(Char) ; new_char())
			{
			say_ascii(Char);
			}
		}

	/* Spell out trailing abbreviations */
	if (isalpha(Char))
		{
		while (isalpha(Char))
			{
			say_ascii(Char);
			new_char();
			}
		}

	return;
	}


have_letter()
	{
	char buff[MAX_LENGTH];
	int count;

	count = 0;
	buff[count++] = ' ';	/* Required initial blank */

	buff[count++] = makeupper(Char);

	for (new_char() ; isalpha(Char) || Char == '\'' ; new_char())
		{
		buff[count++] = makeupper(Char);
		if (count > MAX_LENGTH-2)
			{
			buff[count++] = ' ';
			buff[count++] = '\0';
			xlate_word(buff);
			count = 1;
			}
		}

	buff[count++] = ' ';	/* Required terminating blank */
	buff[count++] = '\0';

	/* Check for AAANNN type abbreviations */
	if (isdigit(Char))
		{
		spell_word(buff);
		return;
		}
	else
	if (strlen(buff) == 3)	 /* one character, two spaces */
		say_ascii(buff[1]);
	else
	if (Char == '.')		/* Possible abbreviation */
		abbrev(buff);
	else
		xlate_word(buff);

	if (Char == '-' && isalpha(Char1))
		new_char();	/* Skip hyphens */

	}

/* Handle abbreviations.  Text in buff was followed by '.' */
abbrev(buff)
	char buff[];
	{
	if (strcmp(buff, " DR ") == 0)
		{
		xlate_word(" DOCTOR ");
		new_char();
		}
	else
	if (strcmp(buff, " MR ") == 0)
		{
		xlate_word(" MISTER ");
		new_char();
		}
	else
	if (strcmp(buff, " MRS ") == 0)
		{
		xlate_word(" MISSUS ");
		new_char();
		}
	else
	if (strcmp(buff, " PHD ") == 0)
		{
		spell_word(" PHD ");
		new_char();
		}
	else
		xlate_word(buff);
	}
-------------------------------------------------------------------------------
**FILE**        SPELLWORD.C      Spell an ASCII character or word.
-------------------------------------------------------------------------------
#include <stdio.h>

static char *Ascii[] =
	{
"nUWl ","stAArt AXv hEHdER ","stAArt AXv tEHkst ","EHnd AXv tEHkst ",
"EHnd AXv trAEnsmIHSHAXn",
"EHnkwAYr ","AEk ","bEHl ","bAEkspEYs ","tAEb ","lIHnIYfIYd ",
"vERtIHkAXl tAEb ","fAOrmfIYd ","kAErAYj rIYtERn ","SHIHft AWt ",
"SHIHft IHn ","dIHlIYt ","dIHvIHs kAAntrAAl wAHn ","dIHvIHs kAAntrAAl tUW ",
"dIHvIHs kAAntrAAl THrIY ","dIHvIHs kAAntrAAl fOWr ","nAEk ","sIHnk ",
"EHnd tEHkst blAAk ","kAEnsEHl ","EHnd AXv mEHsIHj ","sUWbstIHtUWt ",
"EHskEYp ","fAYEHld sIYpERAEtER ","grUWp sIYpERAEtER ","rIYkAOrd sIYpERAEtER ",
"yUWnIHt sIYpERAEtER ","spEYs ","EHksklAEmEYSHAXn mAArk ","dAHbl kwOWt ",
"nUWmbER sAYn ","dAAlER sAYn ","pERsEHnt ","AEmpERsAEnd ","kwOWt ",
"OWpEHn pEHrEHn ","klOWz pEHrEHn ","AEstEHrIHsk ","plAHs ","kAAmmAX ",
"mIHnAHs ","pIYrIYAAd ","slAESH ",

"zIHrOW ","wAHn ","tUW ","THrIY ","fOWr ",
"fAYv ","sIHks ","sEHvAXn ","EYt ","nAYn ",

"kAAlAXn ","sEHmIHkAAlAXn ","lEHs DHAEn ","EHkwAXl sAYn ","grEYtER DHAEn ",
"kwEHsCHAXn mAArk ","AEt sAYn ",

"EY ","bIY ","sIY ","dIY ","IY ","EHf ","jIY  ",
"EYtCH ","AY ","jEY ","kEY ","EHl ","EHm ","EHn ","AA ","pIY ",
"kw ","AAr ","EHz ","tIY ","AHw ","vIY ",
"dAHblyUWw ","EHks ","wAYIY ","zIY ",

"lEHft brAEkEHt ","bAEkslAESH ","rAYt brAEkEHt ","kAErEHt ",
"AHndERskAOr ","AEpAAstrAAfIH ",

"EY ","bIY ","sIY ","dIY ","IY ","EHf ","jIY  ",
"EYtCH ","AY ","jEY ","kEY ","EHl ","EHm ","EHn ","AA ","pIY ",
"kw ","AAr ","EHz ","tIY ","AHw ","vIY ",
"dAHblyUWw ","EHks ","wAYIY ","zIY ",

"lEHft brEYs ","vERtIHkAXl bAAr ","rAYt brEYs ","tAYld ","dEHl ",
	};

say_ascii(character)
	int character;
	{
	outstring(Ascii[character&0x7F]);
	}

spell_word(word)
	char *word;
	{
	for (word++ ; word[1] != '\0' ; word++)
		outstring(Ascii[(*word)&0x7F]);
	}
-------------------------------------------------------------------------------
**FILE**        SAYNUM.C         Say a cardinal or ordinal number (long int).
-------------------------------------------------------------------------------
#include <stdio.h>

/*
**              Integer to Readable ASCII Conversion Routine.
**
** Synopsis:
**
**      say_cardinal(value)
**      	long int     value;          -- The number to output
**
**	The number is translated into a string of phonemes
**
*/

static char *Cardinals[] = 
	{
	"zIHrOW ",	"wAHn ",	"tUW ",		"THrIY ",
	"fOWr ",	"fAYv ",	"sIHks ",	"sEHvAXn ",
	"EYt ",		"nAYn ",		
	"tEHn ",	"IYlEHvAXn ",	"twEHlv ",	"THERtIYn ",
	"fOWrtIYn ",	"fIHftIYn ", 	"sIHkstIYn ",	"sEHvEHntIYn ",
	"EYtIYn ",	"nAYntIYn "
	} ;

static char *Twenties[] = 
	{
	"twEHntIY ",	"THERtIY ",	"fAOrtIY ",	"fIHftIY ",
	"sIHkstIY ",	"sEHvEHntIY ",	"EYtIY ",	"nAYntIY "
	} ;

static char *Ordinals[] = 
	{
	"zIHrOWEHTH ",	"fERst ",	"sEHkAHnd ",	"THERd ",
	"fOWrTH ",	"fIHfTH ",	"sIHksTH ",	"sEHvEHnTH ",
	"EYtTH ",	"nAYnTH ",		
	"tEHnTH ",	"IYlEHvEHnTH ",	"twEHlvTH ",	"THERtIYnTH ",
	"fAOrtIYnTH ",	"fIHftIYnTH ", 	"sIHkstIYnTH ",	"sEHvEHntIYnTH ",
	"EYtIYnTH ",	"nAYntIYnTH "
	} ;

static char *Ord_twenties[] = 
	{
	"twEHntIYEHTH ","THERtIYEHTH ",	"fOWrtIYEHTH ",	"fIHftIYEHTH ",
	"sIHkstIYEHTH ","sEHvEHntIYEHTH ","EYtIYEHTH ",	"nAYntIYEHTH "
	} ;


/*
** Translate a number to phonemes.  This version is for CARDINAL numbers.
**	 Note: this is recursive.
*/
say_cardinal(value)
	long int value;
	{
	if (value < 0)
		{
		outstring("mAYnAHs ");
		value = (-value);
		if (value < 0)	/* Overflow!  -32768 */
			{
			outstring("IHnfIHnIHtIY ");
			return;
			}
		}

	if (value >= 1000000000L)	/* Billions */
		{
		say_cardinal(value/1000000000L);
		outstring("bIHlIYAXn ");
		value = value % 1000000000;
		if (value == 0)
			return;		/* Even billion */
		if (value < 100)	/* as in THREE BILLION AND FIVE */
			outstring("AEnd ");
		}

	if (value >= 1000000L)	/* Millions */
		{
		say_cardinal(value/1000000L);
		outstring("mIHlIYAXn ");
		value = value % 1000000L;
		if (value == 0)
			return;		/* Even million */
		if (value < 100)	/* as in THREE MILLION AND FIVE */
			outstring("AEnd ");
		}

	/* Thousands 1000..1099 2000..99999 */
	/* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */
	if ((value >= 1000L && value <= 1099L) || value >= 2000L)
		{
		say_cardinal(value/1000L);
		outstring("THAWzAEnd ");
		value = value % 1000L;
		if (value == 0)
			return;		/* Even thousand */
		if (value < 100)	/* as in THREE THOUSAND AND FIVE */
			outstring("AEnd ");
		}

	if (value >= 100L)
		{
		outstring(Cardinals[value/100]);
		outstring("hAHndrEHd ");
		value = value % 100;
		if (value == 0)
			return;		/* Even hundred */
		}

	if (value >= 20)
		{
		outstring(Twenties[(value-20)/ 10]);
		value = value % 10;
		if (value == 0)
			return;		/* Even ten */
		}

	outstring(Cardinals[value]);
	return;
	} 


/*
** Translate a number to phonemes.  This version is for ORDINAL numbers.
**	 Note: this is recursive.
*/
say_ordinal(value)
	long int value;
	{

	if (value < 0)
		{
		outstring("mAHnAXs ");
		value = (-value);
		if (value < 0)	/* Overflow!  -32768 */
			{
			outstring("IHnfIHnIHtIY ");
			return;
			}
		}

	if (value >= 1000000000L)	/* Billions */
		{
		say_cardinal(value/1000000000L);
		value = value % 1000000000;
		if (value == 0)
			{
			outstring("bIHlIYAXnTH ");
			return;		/* Even billion */
			}
		outstring("bIHlIYAXn ");
		if (value < 100)	/* as in THREE BILLION AND FIVE */
			outstring("AEnd ");
		}

	if (value >= 1000000L)	/* Millions */
		{
		say_cardinal(value/1000000L);
		value = value % 1000000L;
		if (value == 0)
			{
			outstring("mIHlIYAXnTH ");
			return;		/* Even million */
			}
		outstring("mIHlIYAXn ");
		if (value < 100)	/* as in THREE MILLION AND FIVE */
			outstring("AEnd ");
		}

	/* Thousands 1000..1099 2000..99999 */
	/* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */
	if ((value >= 1000L && value <= 1099L) || value >= 2000L)
		{
		say_cardinal(value/1000L);
		value = value % 1000L;
		if (value == 0)
			{
			outstring("THAWzAEndTH ");
			return;		/* Even thousand */
			}
		outstring("THAWzAEnd ");
		if (value < 100)	/* as in THREE THOUSAND AND FIVE */
			outstring("AEnd ");
		}

	if (value >= 100L)
		{
		outstring(Cardinals[value/100]);
		value = value % 100;
		if (value == 0)
			{
			outstring("hAHndrEHdTH ");
			return;		/* Even hundred */
			}
		outstring("hAHndrEHd ");
		}

	if (value >= 20)
		{
		if ((value%10) == 0)
			{
			outstring(Ord_twenties[(value-20)/ 10]);
			return;		/* Even ten */
			}
		outstring(Twenties[(value-20)/ 10]);
		value = value % 10;
		}

	outstring(Ordinals[value]);
	return;
	} 
-------------------------------------------------------------------------------
**FILE**        End of Distribution
-------------------------------------------------------------------------------

                -John A. Wasser

Work address:
ARPAnet:        WASSER%VIKING.DEC@decwrl.ARPA
Usenet:         {allegra,Shasta,decvax}!decwrl!dec-rhea!dec-viking!wasser
Easynet:        VIKING::WASSER
Telephone:      (617)486-2505
USPS:           Digital Equipment Corp.
                Mail stop: LJO2/E4
                30 Porter Rd
                Littleton, MA  01460