us_expand.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /*************************************************************************/
  2. /* */
  3. /* Language Technologies Institute */
  4. /* Carnegie Mellon University */
  5. /* Copyright (c) 2001 */
  6. /* All Rights Reserved. */
  7. /* */
  8. /* Permission is hereby granted, free of charge, to use and distribute */
  9. /* this software and its documentation without restriction, including */
  10. /* without limitation the rights to use, copy, modify, merge, publish, */
  11. /* distribute, sublicense, and/or sell copies of this work, and to */
  12. /* permit persons to whom this work is furnished to do so, subject to */
  13. /* the following conditions: */
  14. /* 1. The code must retain the above copyright notice, this list of */
  15. /* conditions and the following disclaimer. */
  16. /* 2. Any modifications must be clearly marked as such. */
  17. /* 3. Original authors' names are not deleted. */
  18. /* 4. The authors' names are not used to endorse or promote products */
  19. /* derived from this software without specific prior written */
  20. /* permission. */
  21. /* */
  22. /* CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK */
  23. /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
  24. /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
  25. /* SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE */
  26. /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
  27. /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
  28. /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
  29. /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
  30. /* THIS SOFTWARE. */
  31. /* */
  32. /*************************************************************************/
  33. /* Author: Alan W Black (awb@cs.cmu.edu) */
  34. /* Date: January 2001 */
  35. /*************************************************************************/
  36. /* */
  37. /* English text expanders */
  38. /* */
  39. /* numbers, digits, ids (years), money */
  40. /* */
  41. /*************************************************************************/
  42. #include <ctype.h>
  43. #include "us_text.h"
  44. static const char * const digit2num[] = {
  45. "zero",
  46. "one",
  47. "two",
  48. "three",
  49. "four",
  50. "five",
  51. "six",
  52. "seven",
  53. "eight",
  54. "nine" };
  55. static const char * const digit2teen[] = {
  56. "ten", /* shouldn't get called */
  57. "eleven",
  58. "twelve",
  59. "thirteen",
  60. "fourteen",
  61. "fifteen",
  62. "sixteen",
  63. "seventeen",
  64. "eighteen",
  65. "nineteen" };
  66. static const char * const digit2enty[] = {
  67. "zero", /* shouldn't get called */
  68. "ten",
  69. "twenty",
  70. "thirty",
  71. "forty",
  72. "fifty",
  73. "sixty",
  74. "seventy",
  75. "eighty",
  76. "ninety" };
  77. static const char * const ord2num[] = {
  78. "zeroth",
  79. "first",
  80. "second",
  81. "third",
  82. "fourth",
  83. "fifth",
  84. "sixth",
  85. "seventh",
  86. "eighth",
  87. "ninth" };
  88. static const char * const ord2teen[] = {
  89. "tenth", /* shouldn't get called */
  90. "eleventh",
  91. "twelfth",
  92. "thirteenth",
  93. "fourteenth",
  94. "fifteenth",
  95. "sixteenth",
  96. "seventeenth",
  97. "eighteenth",
  98. "nineteenth" };
  99. static const char * const ord2enty[] = {
  100. "zeroth", /* shouldn't get called */
  101. "tenth",
  102. "twentieth",
  103. "thirtieth",
  104. "fortieth",
  105. "fiftieth",
  106. "sixtieth",
  107. "seventieth",
  108. "eightieth",
  109. "ninetieth" };
  110. cst_val *en_exp_number(const char *numstring)
  111. {
  112. /* Expand given token to list of words pronouncing it as a number */
  113. int num_digits = cst_strlen(numstring);
  114. char part[4];
  115. cst_val *p;
  116. int i;
  117. if (num_digits == 0)
  118. return NULL;
  119. else if (num_digits == 1)
  120. return en_exp_digits(numstring);
  121. else if (num_digits == 2)
  122. {
  123. if (numstring[0] == '0')
  124. {
  125. if (numstring[1] == '0')
  126. return 0;
  127. else
  128. return cons_val(string_val(digit2num[numstring[1]-'0']),0);
  129. }
  130. else if (numstring[1] == '0')
  131. return cons_val(string_val(digit2enty[numstring[0]-'0']),0);
  132. else if (numstring[0] == '1')
  133. return cons_val(string_val(digit2teen[numstring[1]-'0']),0);
  134. else
  135. return cons_val(string_val(digit2enty[numstring[0]-'0']),
  136. en_exp_digits(numstring+1));
  137. }
  138. else if (num_digits == 3)
  139. {
  140. if (numstring[0] == '0')
  141. return en_exp_number(numstring+1);
  142. else
  143. return cons_val(string_val(digit2num[numstring[0]-'0']),
  144. cons_val(string_val("hundred"),
  145. en_exp_number(numstring+1)));
  146. }
  147. else if (num_digits < 7)
  148. {
  149. for (i=0; i < num_digits-3; i++)
  150. part[i] = numstring[i];
  151. part[i]='\0';
  152. p = en_exp_number(part);
  153. if (p == 0) /* no thousands */
  154. return en_exp_number(numstring+i);
  155. else
  156. return val_append(p,cons_val(string_val("thousand"),
  157. en_exp_number(numstring+i)));
  158. }
  159. else if (num_digits < 10)
  160. {
  161. for (i=0; i < num_digits-6; i++)
  162. part[i] = numstring[i];
  163. part[i]='\0';
  164. p = en_exp_number(part);
  165. if (p == 0) /* no millions */
  166. return en_exp_number(numstring+i);
  167. else
  168. return val_append(p,cons_val(string_val("million"),
  169. en_exp_number(numstring+i)));
  170. }
  171. else if (num_digits < 13)
  172. { /* If there are pedantic brits out there, tough!, 10^9 is a billion */
  173. for (i=0; i < num_digits-9; i++)
  174. part[i] = numstring[i];
  175. part[i]='\0';
  176. p = en_exp_number(part);
  177. if (p == 0) /* no billions */
  178. return en_exp_number(numstring+i);
  179. else
  180. return val_append(p,cons_val(string_val("billion"),
  181. en_exp_number(numstring+i)));
  182. }
  183. else /* Way too many digits here, to be a number */
  184. {
  185. return en_exp_digits(numstring);
  186. }
  187. }
  188. cst_val *en_exp_ordinal(const char *rawnumstring)
  189. {
  190. /* return ordinal for digit string */
  191. cst_val *card, *o;
  192. const cst_val *t;
  193. const char *l;
  194. const char *ord;
  195. char *numstring;
  196. int i,j;
  197. numstring = cst_strdup(rawnumstring);
  198. for (j=i=0; i < cst_strlen(rawnumstring); i++)
  199. if (rawnumstring[i] != ',')
  200. {
  201. numstring[j] = rawnumstring[i];
  202. j++;
  203. }
  204. numstring[j] = '\0';
  205. card = val_reverse(en_exp_number(numstring));
  206. cst_free(numstring);
  207. l = val_string(val_car(card));
  208. ord = 0;
  209. for (i=0; i<10; i++)
  210. if (cst_streq(l,digit2num[i]))
  211. ord = ord2num[i];
  212. if (!ord)
  213. for (i=0; i<10; i++)
  214. if (cst_streq(l,digit2teen[i]))
  215. ord = ord2teen[i];
  216. if (!ord)
  217. for (i=0; i<10; i++)
  218. if (cst_streq(l,digit2enty[i]))
  219. ord = ord2enty[i];
  220. if (cst_streq(l,"hundred"))
  221. ord = "hundredth";
  222. if (cst_streq(l,"thousand"))
  223. ord = "thousandth";
  224. if (cst_streq(l,"billion"))
  225. ord = "billtionth";
  226. if (!ord) /* dunno, so don't convert anything */
  227. return card;
  228. o = cons_val(string_val(ord),0);
  229. for (t=val_cdr(card); t; t=val_cdr(t))
  230. o = cons_val(val_car(t),o);
  231. delete_val(card);
  232. return o;
  233. }
  234. cst_val *en_exp_id(const char *numstring)
  235. {
  236. /* Expand numstring as pairs as in years or ids */
  237. char aaa[3];
  238. if ((cst_strlen(numstring) == 4) &&
  239. (numstring[2] == '0') &&
  240. (numstring[3] == '0'))
  241. {
  242. if (numstring[1] == '0')
  243. return en_exp_number(numstring); /* 2000, 3000 */
  244. else
  245. {
  246. aaa[0] = numstring[0];
  247. aaa[1] = numstring[1];
  248. aaa[2] = '\0';
  249. return val_append(en_exp_number(aaa),
  250. cons_val(string_val("hundred"),0));
  251. }
  252. }
  253. else if ((cst_strlen(numstring) == 3) &&
  254. (numstring[0] != '0') &&
  255. (numstring[1] == '0') &&
  256. (numstring[2] == '0'))
  257. {
  258. return cons_val(string_val(digit2num[numstring[0]-'0']),
  259. cons_val(string_val("hundred"),0));
  260. }
  261. else if ((cst_strlen(numstring) == 2) && (numstring[0] == '0')
  262. && (numstring[1] == '0'))
  263. return cons_val(string_val("zero"),
  264. cons_val(string_val("zero"),NULL));
  265. else if ((cst_strlen(numstring) == 2) && (numstring[0] == '0'))
  266. return cons_val(string_val("oh"),
  267. en_exp_digits(&numstring[1]));
  268. else if (((cst_strlen(numstring) == 4) &&
  269. (numstring[1] == '0') && (numstring[2] == '0')) ||
  270. (cst_strlen(numstring) < 3))
  271. return en_exp_number(numstring);
  272. else if (cst_strlen(numstring)%2 == 1)
  273. {
  274. return cons_val(string_val(digit2num[numstring[0]-'0']),
  275. en_exp_id(&numstring[1]));
  276. }
  277. else
  278. {
  279. aaa[0] = numstring[0];
  280. aaa[1] = numstring[1];
  281. aaa[2] = '\0';
  282. return val_append(en_exp_number(aaa),en_exp_id(&numstring[2]));
  283. }
  284. }
  285. cst_val *en_exp_real(const char *numstring)
  286. {
  287. char *aaa, *p;
  288. cst_val *r;
  289. if (numstring && (numstring[0] == '-'))
  290. r = cons_val(string_val("minus"),
  291. en_exp_real(&numstring[1]));
  292. else if (numstring && (numstring[0] == '+'))
  293. r = cons_val(string_val("plus"),
  294. en_exp_real(&numstring[1]));
  295. else if (((p=strchr(numstring,'e')) != 0) ||
  296. ((p=strchr(numstring,'E')) != 0))
  297. {
  298. aaa = cst_strdup(numstring);
  299. aaa[cst_strlen(numstring)-cst_strlen(p)] = '\0';
  300. r = val_append(en_exp_real(aaa),
  301. cons_val(string_val("e"),
  302. en_exp_real(p+1)));
  303. cst_free(aaa);
  304. }
  305. else if ((p=strchr(numstring,'.')) != 0)
  306. {
  307. aaa = cst_strdup(numstring);
  308. aaa[cst_strlen(numstring)-cst_strlen(p)] = '\0';
  309. r = val_append(en_exp_number(aaa),
  310. cons_val(string_val("point"),
  311. en_exp_digits(p+1)));
  312. cst_free(aaa);
  313. }
  314. else
  315. r = en_exp_number(numstring); /* I don't think you can get here */
  316. return r;
  317. }
  318. cst_val *en_exp_digits(const char *numstring)
  319. {
  320. /* Expand given token to list of words pronouncing it as digits */
  321. cst_val *d = 0;
  322. const char *p;
  323. for (p=numstring; *p; p++)
  324. {
  325. if ((*p >= '0') && (*p <= '9'))
  326. d = cons_val(string_val(digit2num[*p-'0']),d);
  327. else
  328. d = cons_val(string_val("umpty"),d);
  329. }
  330. return val_reverse(d);
  331. }
  332. cst_val *en_exp_letters(const char *lets)
  333. {
  334. /* returns these as list of single char symbols */
  335. char *aaa;
  336. cst_val *r;
  337. int i;
  338. aaa = cst_alloc(char,2);
  339. aaa[1] = '\0';
  340. for (r=0,i=0; lets[i] != '\0'; i++)
  341. {
  342. aaa[0] = lets[i];
  343. if (isupper((int)aaa[0]))
  344. aaa[0] = tolower((int)aaa[0]);
  345. if (strchr("0123456789",aaa[0]))
  346. r = cons_val(string_val(digit2num[aaa[0]-'0']),r);
  347. else if (cst_streq(aaa,"a"))
  348. r = cons_val(string_val("_a"),r);
  349. else
  350. r = cons_val(string_val(aaa),r);
  351. }
  352. cst_free(aaa);
  353. return val_reverse(r);
  354. }
  355. int en_exp_roman(const char *roman)
  356. {
  357. int val;
  358. const char *p;
  359. val = 0;
  360. for (p=roman; *p != 0; p++)
  361. {
  362. if (*p == 'X')
  363. val += 10;
  364. else if (*p == 'V')
  365. val += 5;
  366. else if (*p == 'I')
  367. {
  368. if (p[1] == 'V')
  369. {
  370. val += 4;
  371. p++;
  372. }
  373. else if (p[1] == 'X')
  374. {
  375. val += 9;
  376. p++;
  377. }
  378. else
  379. val += 1;
  380. }
  381. }
  382. return val;
  383. }