Bayonne 3 - API
 All Classes Namespaces Files Functions Variables Typedefs Macros
runtime/phrasebook.cpp
Go to the documentation of this file.
1 // Copyright (C) 2008-2011 David Sugar, Tycho Softworks.
2 //
3 // This file is part of GNU Bayonne.
4 //
5 // GNU Bayonne 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 // GNU Bayonne 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 GNU Bayonne. If not, see <http://www.gnu.org/licenses/>.
17 
18 #include <config.h>
19 #include <ucommon/ucommon.h>
20 #include <ucommon/export.h>
21 #include <bayonne.h>
22 #include <ctype.h>
23 
24 using namespace BAYONNE_NAMESPACE;
25 using namespace UCOMMON_NAMESPACE;
26 
27 static LinkedObject *primary = NULL;
28 static LinkedObject *secondary = NULL;
29 
30 Phrasebook::Phrasebook(bool pri) :
31 LinkedObject()
32 {
33  if(pri)
34  enlist(&primary);
35  else
36  enlist(&secondary);
37 }
38 
39 static class __LOCAL _default : public Phrasebook
40 {
41 public:
42  _default();
43 
44  bool id(const char *lang);
45 
46 } _default_rule;
47 
48 static class __LOCAL _en_US : public Phrasebook
49 {
50 public:
51  _en_US();
52 
53  bool id(const char *lang);
54 
55  void time(const char *text, rule_t *state);
56 
57  void date(const char *text, rule_t *state);
58 
59 } _en_US_rule;
60 
61 Phrasebook *Phrasebook::find(const char *lang)
62 {
63  linked_pointer<Phrasebook> rp;
64 
65  if(!lang) {
66 #ifdef _MSWINDOWS_
67 #else
68  lang = getenv("LANG");
69 #endif
70  }
71  if(!lang)
72  return &_default_rule;
73 
74  rp = secondary;
75  while(is(rp)) {
76  if(rp->id(lang))
77  return *rp;
78  rp.next();
79  }
80 
81  rp = primary;
82  while(is(rp)) {
83  if(rp->id(lang))
84  return *rp;
85  rp.next();
86  }
87 
88  return NULL;
89 }
90 
91 _default::_default() :
92 Phrasebook(true)
93 {
94 }
95 
96 _en_US::_en_US() :
97 Phrasebook(false)
98 {
99 }
100 
101 bool _en_US::id(const char *lang)
102 {
103  if(case_eq(lang, "en_US"))
104  return true;
105 
106  if(case_eq(lang, "en_US.", 6))
107  return true;
108 
109  return false;
110 }
111 
112 void _en_US::time(const char *text, rule_t *state)
113 {
114  const char *ap = "a";
115 
116  Time now((char *)text);
117  if(now[Time::hour] >= 12) {
118  ap = "p";
119  unsigned hour = now[Time::hour];
120  if(hour % 12)
121  _lownumber(now[Time::hour] % 12, state);
122  else
123  _lownumber(12, state);
124  }
125  else if(now[Time::hour])
126  _lownumber(now[Time::hour], state);
127  else
128  _lownumber(12, state);
129 
130  if(now[Time::minute]) {
131  if(now[Time::minute] < 10)
132  _add("o", state);
133  _lownumber(now[Time::minute], state);
134  }
135 
136  _add(ap, state);
137  _add("m", state);
138 }
139 
140 void _en_US::date(const char *text, rule_t *state)
141 {
142  static const char *_month[] = {"", "january", "febuary", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
143 
144  Date now((char *)text);
145  _add(_month[now[Date::month]], state);
146 
147  char buf[8];
148  snprintf(buf, sizeof(buf), "%d", now[Date::day]);
149  order(buf, state);
150 }
151 
152 bool _default::id(const char *lang)
153 {
154  if(case_eq(lang, "en_", 3))
155  return true;
156 
157  if(case_eq(lang, "C"))
158  return true;
159 
160  if(case_eq(lang, "en"))
161  return true;
162 
163  return false;
164 }
165 
166 void Phrasebook::_dup(const char *text, rule_t *state)
167 {
168  size_t len = strlen(text) + 1;
169  if(!state->bp)
170  state->bp = ((char *)(&state->list[state->max])) - 1;
171 
172  state->bp -= len;
173  if(state->bp && ((char *)(&state->list[state->pos + 2]) >= state->bp)) {
174  state->list[0] = NULL;
175  state->pos = state->max;
176  }
177  else {
178  String::set(state->bp, len, text);
179  state->list[state->pos++] = state->bp;
180  state->list[state->pos] = NULL;
181  }
182 }
183 
184 void Phrasebook::_add(const char *text, rule_t *state)
185 {
186  if(state->bp && ((char *)(&state->list[state->pos + 2]) >= state->bp)) {
187  state->list[0] = NULL;
188  state->pos = state->max;
189  }
190  else if(state->pos >= state->max - 1)
191  state->list[0] = NULL;
192  else {
193  state->list[state->pos++] = text;
194  state->list[state->pos] = NULL;
195  }
196 }
197 
198 static const char *_0to19[] =
199  {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
200  "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
201 
202 static const char *_tens[] =
203  {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
204 
205 void Phrasebook::_lownumber(int num, rule_t *state)
206 {
207  if(num >= 100)
208  {
209  _add(_0to19[num / 100], state);
210  _add("hundred", state);
211  num %= 100;
212  if(!num)
213  return;
214  }
215  if(num < 20)
216  {
217  _add(_0to19[num], state);
218  return;
219  }
220  _add(_tens[num / 10], state);
221  if(num % 10)
222  _add(_0to19[num % 10], state);
223 }
224 
225 void Phrasebook::nonzero(const char *text, rule_t *state)
226 {
227  char *bp = state->bp;
228  unsigned pos = state->pos;
229 
230  number(text, state);
231  if(state->zeroflag) {
232  state->bp = bp;
233  state->pos = pos;
234  state->list[pos] = NULL;
235  }
236 }
237 
238 void Phrasebook::zero(const char *text, rule_t *state)
239 {
240  if(state->zeroflag)
241  literal(text, state);
242 }
243 
244 void Phrasebook::single(const char *text, rule_t *state)
245 {
246  if(state->last == 1)
247  literal(text, state);
248 }
249 
250 void Phrasebook::plural(const char *text, rule_t *state)
251 {
252  if(state->last > 1)
253  literal(text, state);
254 }
255 
256 void Phrasebook::number(const char *text, rule_t *state)
257 {
258  unsigned long num;
259 
260  state->zeroflag = true;
261  state->last = atol(text);
262 
263  if(!text || !*text)
264  return;
265 
266  if(*text == '-')
267  {
268  _add("negative", state);
269  ++text;
270  }
271  num = atol(text);
272  if(num > 999999999)
273  {
274  state->zeroflag = false;
275  _lownumber(num / 1000000000, state);
276  _add("billion", state);
277  num %= 1000000000;
278  }
279  if(num > 999999)
280  {
281  state->zeroflag = false;
282  _lownumber(num / 1000000, state);
283  _add("million", state);
284  num %= 1000000;
285  }
286  if(num > 999)
287  {
288  state->zeroflag = false;
289  _lownumber(num / 1000, state);
290  _add("thousand", state);
291  num %= 1000;
292  }
293  if(num || state->zeroflag)
294  _lownumber(num, state);
295 
296  if(num)
297  state->zeroflag = false;
298 
299  text = strchr(text, '.');
300  if(!text)
301  return;
302  _add("point", state);
303  while(*(++text)) {
304  if(*text >= '0' && *text <= '9')
305  _add(_0to19[(*text - '0')], state);
306  }
307  state->zeroflag = false;
308 }
309 
310 void Phrasebook::order(const char *text, rule_t *state)
311 {
312  static const char *low[] = { "th",
313  "1st", "2nd", "3rd", "4th", "5th",
314  "6th", "7th", "8th", "9th", "10th",
315  "11th", "12th", "13th", "14th", "15th",
316  "16th", "17th", "18th", "19th"};
317 
318  static const char *hi[] = {"", "", "20th", "30th", "40th", "50th", "60th", "70th", "80th", "90th"};
319 
320  unsigned num = atoi(text);
321  state->last = atol(text);
322 
323  if(num > 100)
324  {
325  if(num % 100) {
326  _add(_0to19[num / 100], state);
327  _add("hundred", state);
328  }
329  else {
330  _add(_0to19[num / 100], state);
331  _add("hundred", state);
332  _add("th", state);
333  }
334  num %= 100;
335  }
336  if(num > 19)
337  {
338  if(num % 10)
339  _add(_tens[num / 10], state);
340  else
341  _add(hi[num / 10], state);
342  num %= 10;
343  }
344  if(num)
345  _add(low[num], state);
346 }
347 
348 void Phrasebook::spell(const char *text, rule_t *state)
349 {
350  ucs4_t code;
351  char buf[16];
352 
353  if(!state->bp)
354  state->bp = (char *)(&state->list[state->max]);
355 
356  while(text && *text) {
357  if(isalnum(*text)) {
358  buf[0] = tolower(*(text++));
359  buf[1] = 0;
360  }
361  else {
362  code = utf8::codepoint(text);
363  if(!code || code == (ucs4_t)EOF)
364  break;
365  text += utf8::size(text);
366  snprintf(buf, sizeof(buf), "cp%lu", (long unsigned)code);
367  }
368  _dup(buf, state);
369  }
370 }
371 
372 void Phrasebook::literal(const char *text, rule_t *state)
373 {
374  const char *cp = text;
375  bool dot = false;
376  bool num = true;
377 
378  if(!text || !*text)
379  return;
380 
381  if(isdigit(text[0]) && isdigit(text[1]) && text[2] == ':') {
382  time(text, state);
383  return;
384  }
385 
386  if(isdigit(text[0]) && isdigit(text[1]) && isdigit(text[2]) && isdigit(text[3]) && text[4] == '-') {
387  fulldate(text, state);
388  return;
389  }
390 
391 
392  if(*cp == '-')
393  ++cp;
394  while(*cp) {
395  if(*cp == '.' && !dot) {
396  dot = true;
397  continue;
398  }
399  if(!isdigit(*cp)) {
400  num = false;
401  break;
402  }
403  ++cp;
404  }
405  if(num)
406  number(text, state);
407  else
408  _add(text, state);
409 }
410 
411 void Phrasebook::weekday(const char *text, rule_t *state)
412 {
413  static const char *_dow[] = {"sunday", "monday", "tuesday", "wednasday", "thursday", "friday", "saturday"};
414 
415  Date now((char *)text);
416  _add(_dow[now[Date::dow]], state);
417 }
418 
419 void Phrasebook::fulldate(const char *text, rule_t *state)
420 {
421  date(text, state);
422  year(text, state);
423 }
424 
425 void Phrasebook::date(const char *text, rule_t *state)
426 {
427  static const char *_month[] = {"", "january", "febuary", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
428 
429  Date now((char *)text);
430  char buf[8];
431  snprintf(buf, sizeof(buf), "%d", now[Date::day]);
432  order(buf, state);
433  _add(_month[now[Date::month]], state);
434 }
435 
436 void Phrasebook::year(const char *text, rule_t *state)
437 {
438  Date now((char *)text);
439  unsigned year = now[Date::year];
440  unsigned hi = year / 100;
441  unsigned lo = year % 100;
442 
443  if((hi % 10) == 0) {
444  _lownumber(hi / 10, state);
445  _add("thousand", state);
446  if(!lo)
447  return;
448  _lownumber(lo, state);
449  return;
450  }
451 
452  if(!lo) {
453  _lownumber(hi, state);
454  _add("hundred", state);
455  return;
456  }
457 
458  _lownumber(hi, state);
459  if(lo < 10)
460  _add("o", state);
461  _lownumber(lo, state);
462 }
463 
464 void Phrasebook::time(const char *text, rule_t *state)
465 {
466  Time now((char *)text);
467  _lownumber(now[Time::hour], state);
468  if(now[Time::minute] < 1)
469  _add("o", state);
470  if(now[Time::minute] < 10)
471  _add("o", state);
472  if(now[Time::minute])
473  _lownumber(now[Time::minute], state);
474 }
475 
476 void Phrasebook::init(rule_t *state, size_t size)
477 {
478  memset(state, 0, size);
479  state->max = (size - sizeof(rule_t)) / sizeof(char *);
480  state->size = size;
481 }
482 
483 void Phrasebook::reset(rule_t *state)
484 {
485  if(state->size)
486  init(state, state->size);
487 }
488 
489 const char *Phrasebook::path(void)
490 {
491  return "/en/";
492 }
#define BAYONNE_NAMESPACE
Definition: bayonne.h:25
GNU Bayonne library namespace.