022b624df55b09812c331a861fbc04ba4f4163d8
[gedcom-parse.git] / gedcom / gedcom_date.y
1 /* Parser for Gedcom dates.
2    Copyright (C) 2001 The Genes Development Team
3    This file is part of the Gedcom parser library.
4    Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
5
6    The Gedcom parser library is free software; you can redistribute it
7    and/or modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The Gedcom parser library is distributed in the hope that it will be
12    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the Gedcom parser library; if not, write to the
18    Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 /* $Id$ */
22 /* $Name$ */
23
24 %{
25 #include <stdlib.h>
26 #include "date.h"
27 #include "compat.h"
28
29 int _get_day_num(const char* input);
30 int _get_year_num(Year_type ytype, const char* input1, const char* input2);
31   
32 %}
33
34 %union {
35   char *string;
36   struct date_value date_val;
37   struct date date;
38 }
39
40 %token <string> ESC_DATE_GREG
41 %token <string> ESC_DATE_JULN
42 %token <string> ESC_DATE_HEBR
43 %token <string> ESC_DATE_FREN
44 %token <string> MOD_FROM
45 %token <string> MOD_TO
46 %token <string> MOD_BEF
47 %token <string> MOD_AFT
48 %token <string> MOD_BET
49 %token <string> MOD_AND
50 %token <string> MOD_ABT
51 %token <string> MOD_CAL
52 %token <string> MOD_EST
53 %token <string> MOD_INT
54 %token <string> MON_JAN
55 %token <string> MON_FEB
56 %token <string> MON_MAR
57 %token <string> MON_APR
58 %token <string> MON_MAY
59 %token <string> MON_JUN
60 %token <string> MON_JUL
61 %token <string> MON_AUG
62 %token <string> MON_SEP
63 %token <string> MON_OCT
64 %token <string> MON_NOV
65 %token <string> MON_DEC
66 %token <string> MON_TSH
67 %token <string> MON_CSH
68 %token <string> MON_KSL
69 %token <string> MON_TVT
70 %token <string> MON_SHV
71 %token <string> MON_ADR
72 %token <string> MON_ADS
73 %token <string> MON_NSN
74 %token <string> MON_IYR
75 %token <string> MON_SVN
76 %token <string> MON_TMZ
77 %token <string> MON_AAV
78 %token <string> MON_ELL
79 %token <string> MON_VEND
80 %token <string> MON_BRUM
81 %token <string> MON_FRIM
82 %token <string> MON_NIVO
83 %token <string> MON_PLUV
84 %token <string> MON_VENT
85 %token <string> MON_GERM
86 %token <string> MON_FLOR
87 %token <string> MON_PRAI
88 %token <string> MON_MESS
89 %token <string> MON_THER
90 %token <string> MON_FRUC
91 %token <string> MON_COMP
92 %token <string> OPEN
93 %token <string> CLOSE
94 %token <string> TEXT
95 %token <string> NUMBER
96 %token <string> SLASH
97 %token <string> BADTOKEN
98
99 %type <date_val> date_value
100 %type <date_val> date_period
101 %type <date_val> date_range
102 %type <date_val> date_approx
103 %type <date_val> date_interpr
104 %type <string> date_phrase
105 %type <date> date
106
107 %%
108
109 date_value   : date           { make_date_value(DV_NO_MODIFIER,
110                                                 &$1, &def_date, ""); }
111              | date_period    
112              | date_range
113              | date_approx
114              | date_interpr
115              | date_phrase    { make_date_value(DV_PHRASE,
116                                                 &def_date, &def_date, $1); }
117              | /* empty */
118                {
119                  /* If empty string: return empty string in 'phrase'
120                     member as fallback */
121                  /* Note: this can only happen in compatibility mode */
122                  make_date_value(DV_PHRASE,
123                                  &def_date, &def_date, curr_line_value);
124                }
125              | error { /* On error: put entire string in 'phrase' member
126                           as fallback */
127                        make_date_value(DV_PHRASE,
128                                        &def_date, &def_date, curr_line_value);
129                      }
130              ;
131
132 date         : ESC_DATE_GREG date_greg { copy_date(&$$, &date_s);
133                                          $$.cal = CAL_GREGORIAN; }
134              | ESC_DATE_JULN date_juln { copy_date(&$$, &date_s);
135                                          $$.cal = CAL_JULIAN;  }
136              | ESC_DATE_HEBR date_hebr { copy_date(&$$, &date_s);
137                                          $$.cal = CAL_HEBREW;  }
138              | ESC_DATE_FREN date_fren { copy_date(&$$, &date_s);
139                                          $$.cal = CAL_FRENCH_REV;  }
140              | date_greg               { copy_date(&$$, &date_s);
141                                          $$.cal = CAL_GREGORIAN;  }
142              ;
143
144 date_period  : MOD_FROM date   { make_date_value(DV_FROM,
145                                                  &$2, &def_date, ""); }
146              | MOD_TO date     { make_date_value(DV_TO,
147                                                  &$2, &def_date, ""); }
148              | MOD_FROM date   { copy_date(&$<date>$, &$2); }
149                MOD_TO date
150                       { make_date_value(DV_FROM_TO, &$<date>3, &$5, ""); }
151              ;
152
153 date_range   : MOD_BEF date    { make_date_value(DV_BEFORE,
154                                                  &$2, &def_date, ""); }
155              | MOD_AFT date    { make_date_value(DV_AFTER,
156                                                  &$2, &def_date, ""); }
157              | MOD_BET date    { copy_date(&$<date>$, &$2); }
158                MOD_AND date
159                       { make_date_value(DV_BETWEEN, &$<date>3, &$5, ""); }
160              ;
161
162 date_approx  : MOD_ABT date    { make_date_value(DV_ABOUT,
163                                                  &$2, &def_date, ""); }
164              | MOD_CAL date    { make_date_value(DV_CALCULATED,
165                                                  &$2, &def_date, ""); }
166              | MOD_EST date    { make_date_value(DV_ESTIMATED,
167                                                  &$2, &def_date, ""); }
168              ;
169
170 date_interpr : MOD_INT date date_phrase
171                  { make_date_value(DV_INTERPRETED, &$2, &def_date, $3); }
172              ;
173
174 date_phrase  : OPEN TEXT CLOSE { $$ = $2; }
175              ;
176
177 date_greg    : day month_greg year_greg
178              | month_greg year_greg
179              | year_greg
180              ;
181
182 date_juln    : day month_greg year
183              | month_greg year
184              | year
185              ;
186
187 date_hebr    : day month_hebr year
188              | month_hebr year
189              | year
190              ;
191
192 date_fren    : day month_fren year
193              | month_fren year
194              | year
195              ;
196
197 day          : NUMBER
198                {
199                  int d = _get_day_num($1);
200                  if (d != -1) {
201                    strcpy(date_s.day_str, $1);
202                    date_s.day = d;
203                  }
204                }
205              ;
206
207 month_greg   : MON_JAN { strcpy(date_s.month_str, $1);
208                          date_s.month = 1; }
209              | MON_FEB { strcpy(date_s.month_str, $1);
210                          date_s.month = 2; }
211              | MON_MAR { strcpy(date_s.month_str, $1);
212                          date_s.month = 3; }
213              | MON_APR { strcpy(date_s.month_str, $1);
214                          date_s.month = 4; }
215              | MON_MAY { strcpy(date_s.month_str, $1);
216                          date_s.month = 5; }
217              | MON_JUN { strcpy(date_s.month_str, $1);
218                          date_s.month = 6; }
219              | MON_JUL { strcpy(date_s.month_str, $1);
220                          date_s.month = 7; }
221              | MON_AUG { strcpy(date_s.month_str, $1);
222                          date_s.month = 8; }
223              | MON_SEP { strcpy(date_s.month_str, $1);
224                          date_s.month = 9; }
225              | MON_OCT { strcpy(date_s.month_str, $1);
226                          date_s.month = 10; }
227              | MON_NOV { strcpy(date_s.month_str, $1);
228                          date_s.month = 11; }
229              | MON_DEC { strcpy(date_s.month_str, $1);
230                          date_s.month = 12; }
231              ;
232
233 month_hebr   : MON_TSH { strcpy(date_s.month_str, $1);
234                          date_s.month = 1; }
235              | MON_CSH { strcpy(date_s.month_str, $1);
236                          date_s.month = 2; }
237              | MON_KSL { strcpy(date_s.month_str, $1);
238                          date_s.month = 3; }
239              | MON_TVT { strcpy(date_s.month_str, $1);
240                          date_s.month = 4; }
241              | MON_SHV { strcpy(date_s.month_str, $1);
242                          date_s.month = 5; }
243              | MON_ADR { strcpy(date_s.month_str, $1);
244                          date_s.month = 6; }
245              | MON_ADS { strcpy(date_s.month_str, $1);
246                          date_s.month = 7; }
247              | MON_NSN { strcpy(date_s.month_str, $1);
248                          date_s.month = 8; }
249              | MON_IYR { strcpy(date_s.month_str, $1);
250                          date_s.month = 9; }
251              | MON_SVN { strcpy(date_s.month_str, $1);
252                          date_s.month = 10; }
253              | MON_TMZ { strcpy(date_s.month_str, $1);
254                          date_s.month = 11; }
255              | MON_AAV { strcpy(date_s.month_str, $1);
256                          date_s.month = 12; }
257              | MON_ELL { strcpy(date_s.month_str, $1);
258                          date_s.month = 13; }
259              ;
260
261 month_fren   : MON_VEND { strcpy(date_s.month_str, $1);
262                          date_s.month = 1; }
263              | MON_BRUM { strcpy(date_s.month_str, $1);
264                          date_s.month = 2; }
265              | MON_FRIM { strcpy(date_s.month_str, $1);
266                          date_s.month = 3; }
267              | MON_NIVO { strcpy(date_s.month_str, $1);
268                          date_s.month = 4; }
269              | MON_PLUV { strcpy(date_s.month_str, $1);
270                          date_s.month = 5; }
271              | MON_VENT { strcpy(date_s.month_str, $1);
272                          date_s.month = 6; }
273              | MON_GERM { strcpy(date_s.month_str, $1);
274                          date_s.month = 7; }
275              | MON_FLOR { strcpy(date_s.month_str, $1);
276                          date_s.month = 8; }
277              | MON_PRAI { strcpy(date_s.month_str, $1);
278                          date_s.month = 9; }
279              | MON_MESS { strcpy(date_s.month_str, $1);
280                          date_s.month = 10; }
281              | MON_THER { strcpy(date_s.month_str, $1);
282                          date_s.month = 11; }
283              | MON_FRUC { strcpy(date_s.month_str, $1);
284                          date_s.month = 12; }
285              | MON_COMP { strcpy(date_s.month_str, $1);
286                          date_s.month = 13; }
287              ;
288
289 year         : NUMBER
290                  { int y = _get_year_num(YEAR_SINGLE, $1, NULL);
291                    if (y != -1) {
292                      strcpy(date_s.year_str, $1);
293                      date_s.year = y;
294                      date_s.year_type = YEAR_SINGLE;
295                    }
296                  }
297              ;
298
299 year_greg    : NUMBER
300                  { int y = _get_year_num(YEAR_SINGLE, $1, NULL);
301                    if (y != -1) {
302                      strcpy(date_s.year_str, $1);
303                      date_s.year = y;
304                      date_s.year_type = YEAR_SINGLE;
305                    }
306                  }
307              | NUMBER SLASH NUMBER
308                  { int y = _get_year_num(YEAR_DOUBLE, $1, $3);
309                    if (y != -1) {
310                      sprintf(date_s.year_str, "%d/%d", y-1, y%100);
311                      date_s.year = y;
312                      date_s.year_type = YEAR_DOUBLE;
313                    }
314                  }
315              ;
316
317 %%
318
319 int _get_day_num(const char* input)
320 {
321   if (strlen(input) <= MAX_DAY_LEN)
322     return atoi(input);
323   else {
324     gedcom_date_error(_("Too many characters in day '%s'"), input);
325     return -1;
326   }
327 }
328
329 int get_day_num(const char* input)
330 {
331   int token = get_date_token(input);
332   if (token == NUMBER)
333     return _get_day_num(input);
334   else {
335     gedcom_date_error(_("Not a valid day number: '%s'"), input);
336     return -1;
337   }
338 }
339
340 int begin_month[] =
341 { /* CAL_GREGORIAN */   MON_JAN,
342   /* CAL_JULIAN */      MON_JAN,
343   /* CAL_HEBREW */      MON_TSH,
344   /* CAL_FRENCH_REV */  MON_VEND
345 };
346
347 int end_month[] =
348 { /* CAL_GREGORIAN */   MON_DEC,
349   /* CAL_JULIAN */      MON_DEC,
350   /* CAL_HEBREW */      MON_ELL,
351   /* CAL_FRENCH_REV */  MON_COMP
352 };
353
354 int get_month_num(Calendar_type cal, const char* input)
355 {
356   int token = get_date_token(input);
357   if (token >= begin_month[cal] && token <= end_month[cal])
358     return token - begin_month[cal] + 1;
359   else {
360     gedcom_date_error(_("Not a valid month for the given calendar: '%s'"),
361                       input);
362     return -1;
363   }
364 }
365
366 int _get_year_num(Year_type ytype, const char* input1, const char* input2)
367 {
368   if (ytype == YEAR_SINGLE) {
369     if (strlen(input1) <= MAX_YEAR_LEN) {
370       return atoi(input1);
371     }
372     else {
373       gedcom_date_error(_("Too many characters in year '%s'"), input1);
374       return -1;
375     }
376   }
377   else {
378     if (strlen(input2) != 2) {
379       if (compat_mode(C_DOUBLE_DATES_4) && strlen(input2) == 4) {
380         input2 += 2;
381       }
382       else {
383         gedcom_date_error(_("Year after slash should be two digits: '%s/%s'"),
384                           input1, input2);
385         return -1;
386       }
387     }
388     if (strlen(input1) <= MAX_YEAR_LEN - 3) {
389       int year1 = atoi(input1) + 1;
390       int year2 = atoi(input2);
391       if (year1 % 100 != year2) {
392         gedcom_date_error(_("Year after slash should be following year: '%s/%s'"),
393                           input1, input2);
394         return -1;
395       }
396       else 
397         return year1;
398     }
399     else {
400       gedcom_date_error(_("Too many characters in year '%s/%s'"),
401                         input1, input2);
402       return -1;
403     }
404   }
405 }
406
407 int get_year_num(const char* input, Year_type* ytype)
408 {
409   char *year1, *year2 = NULL;
410   int numtok = get_year_tokens(input, &year1, &year2);
411   if (numtok) {
412     *ytype = (numtok == 1 ? YEAR_SINGLE : YEAR_DOUBLE);
413     return _get_year_num (*ytype, year1, year2);
414   }
415   else {
416     gedcom_date_error(_("Not a valid year: '%s'"), input); 
417     return -1;
418   }
419 }