Copied from old documentation. Removed all Gedcom_val details.
[gedcom-parse.git] / gedcom / gedcom_date.y
index 282b8641ddaf69e2d8b9b4fc6050ef5c57f86a01..79af990babef29e10739ac7c5fdfaec3744f3e53 100644 (file)
 %{
 #include <stdlib.h>
 #include "date.h"
+#include "compat.h"
+
+int _get_day_num(const char* input);
+int _get_year_num(Year_type ytype, const char* input1, const char* input2);
+void error_missing_year();
+void error_missing_month(); 
+  
 %}
 
 %union {
 %%
 
 date_value   : date           { make_date_value(DV_NO_MODIFIER,
-                                               $1, def_date, ""); }
+                                               &$1, &def_date, ""); }
              | date_period    
              | date_range
              | date_approx
              | date_interpr
              | date_phrase    { make_date_value(DV_PHRASE,
-                                               def_date, def_date, $1); }
+                                               &def_date, &def_date, $1); }
+             | /* empty */
+               {
+                /* If empty string: return empty string in 'phrase'
+                    member as fallback */
+                /* Note: this can only happen in compatibility mode */
+                make_date_value(DV_PHRASE,
+                                &def_date, &def_date, curr_line_value);
+              }
              | error { /* On error: put entire string in 'phrase' member
                          as fallback */
+                      gedcom_date_error(_("Putting date '%s' in 'phrase' member"), curr_line_value);
                       make_date_value(DV_PHRASE,
-                                      def_date, def_date, curr_line_value); }
+                                      &def_date, &def_date, curr_line_value);
+                      YYABORT;
+                    }
              ;
 
-date         : ESC_DATE_GREG date_greg { copy_date(&$$, date_s);
+date         : ESC_DATE_GREG date_greg { copy_date(&$$, &date_s);
                                          $$.cal = CAL_GREGORIAN; }
-             | ESC_DATE_JULN date_juln { copy_date(&$$, date_s);
+             | ESC_DATE_JULN date_juln { copy_date(&$$, &date_s);
                                          $$.cal = CAL_JULIAN;  }
-             | ESC_DATE_HEBR date_hebr { copy_date(&$$, date_s);
+             | ESC_DATE_HEBR date_hebr { copy_date(&$$, &date_s);
                                          $$.cal = CAL_HEBREW;  }
-             | ESC_DATE_FREN date_fren { copy_date(&$$, date_s);
+             | ESC_DATE_FREN date_fren { copy_date(&$$, &date_s);
                                          $$.cal = CAL_FRENCH_REV;  }
-             | date_greg               { copy_date(&$$, date_s);
+             | date_greg               { copy_date(&$$, &date_s);
                                          $$.cal = CAL_GREGORIAN;  }
              ;
 
 date_period  : MOD_FROM date   { make_date_value(DV_FROM,
-                                                $2, def_date, ""); }
+                                                &$2, &def_date, ""); }
              | MOD_TO date     { make_date_value(DV_TO,
-                                                $2, def_date, ""); }
-             | MOD_FROM date   { copy_date(&$<date>$, $2); }
+                                                &$2, &def_date, ""); }
+             | MOD_FROM date   { copy_date(&$<date>$, &$2); }
               MOD_TO date
-                      { make_date_value(DV_FROM_TO, $<date>3, $5, ""); }
+                      { make_date_value(DV_FROM_TO, &$<date>3, &$5, ""); }
              ;
 
 date_range   : MOD_BEF date    { make_date_value(DV_BEFORE,
-                                                $2, def_date, ""); }
+                                                &$2, &def_date, ""); }
              | MOD_AFT date    { make_date_value(DV_AFTER,
-                                                $2, def_date, ""); }
-             | MOD_BET date    { copy_date(&$<date>$, $2); }
+                                                &$2, &def_date, ""); }
+             | MOD_BET date    { copy_date(&$<date>$, &$2); }
               MOD_AND date
-                      { make_date_value(DV_BETWEEN, $<date>3, $5, ""); }
+                      { make_date_value(DV_BETWEEN, &$<date>3, &$5, ""); }
              ;
 
 date_approx  : MOD_ABT date    { make_date_value(DV_ABOUT,
-                                                $2, def_date, ""); }
+                                                &$2, &def_date, ""); }
              | MOD_CAL date    { make_date_value(DV_CALCULATED,
-                                                $2, def_date, ""); }
+                                                &$2, &def_date, ""); }
              | MOD_EST date    { make_date_value(DV_ESTIMATED,
-                                                $2, def_date, ""); }
+                                                &$2, &def_date, ""); }
              ;
 
 date_interpr : MOD_INT date date_phrase
-                 { make_date_value(DV_INTERPRETED, $2, def_date, $3); }
+                 { make_date_value(DV_INTERPRETED, &$2, &def_date, $3); }
              ;
 
 date_phrase  : OPEN TEXT CLOSE { $$ = $2; }
@@ -163,33 +181,43 @@ date_phrase  : OPEN TEXT CLOSE { $$ = $2; }
 date_greg    : day month_greg year_greg
              | month_greg year_greg
              | year_greg
+             | day month_greg { error_missing_year(); YYERROR; }
+             | month_greg     { error_missing_year(); YYERROR; }
+             | day year_greg  { error_missing_month(); YYERROR; }
              ;
 
 date_juln    : day month_greg year
              | month_greg year
              | year
+             | day month_greg { error_missing_year(); YYERROR; }
+             | month_greg     { error_missing_year(); YYERROR; }
+             | day year_greg  { error_missing_month(); YYERROR; }
              ;
 
 date_hebr    : day month_hebr year
              | month_hebr year
              | year
+             | day month_hebr { error_missing_year(); YYERROR; }
+             | month_hebr     { error_missing_year(); YYERROR; }
+             | day year       { error_missing_month(); YYERROR; }
              ;
 
 date_fren    : day month_fren year
              | month_fren year
              | year
+             | day month_hebr { error_missing_year(); YYERROR; }
+             | month_hebr     { error_missing_year(); YYERROR; }
+             | day year       { error_missing_month(); YYERROR; }
              ;
 
 day          : NUMBER
                {
-                if (strlen($1) <= MAX_DAY_LEN) {
+                int d = _get_day_num($1);
+                if (d != -1) {
                   strcpy(date_s.day_str, $1);
-                  date_s.day = atoi($1);
-                }
-                else {
-                  gedcom_date_error(_("Too many characters in day '%s'"),
-                                    $1); 
+                  date_s.day = d;
                 }
+                else YYERROR;
               }
              ;
 
@@ -276,41 +304,149 @@ month_fren   : MON_VEND { strcpy(date_s.month_str, $1);
              ;
 
 year         : NUMBER
-                 { if (strlen($1) <= MAX_YEAR_LEN) {
+                 { int y = _get_year_num(YEAR_SINGLE, $1, NULL);
+                  if (y != -1) {
                     strcpy(date_s.year_str, $1);
-                    date_s.year = atoi($1);
+                    date_s.year = y;
                     date_s.year_type = YEAR_SINGLE;
                   }
-                  else {
-                    gedcom_date_error(_("Too many characters in year '%s'"),
-                                    $1); 
-                  }
+                  else YYERROR;
                 }
              ;
 
 year_greg    : NUMBER
-                 { if (strlen($1) <= MAX_YEAR_LEN) {
+                 { int y = _get_year_num(YEAR_SINGLE, $1, NULL);
+                  if (y != -1) {
                     strcpy(date_s.year_str, $1);
-                    date_s.year = atoi($1);
+                    date_s.year = y;
                     date_s.year_type = YEAR_SINGLE;
                   }
-                  else {
-                    gedcom_date_error(_("Too many characters in year '%s'"),
-                                    $1); 
-                  }
+                  else YYERROR;
                 }
              | NUMBER SLASH NUMBER
-                 { if (strlen($1) + strlen($3) + 1 <= MAX_YEAR_LEN) {
-                    sprintf(date_s.year_str, "%s/%s", $1, $3);
-                    date_s.year = atoi($1) + 1;
-                    date_s.year_type = YEAR_DOUBLE;
+                 { if (compat_double_date_check($3)) {
+                    safe_buf_append(&compat_buffer, "BET %s AND %s",
+                                    $1, $3);
                   }
                   else {
-                    gedcom_date_error(_("Too many characters in year '%s/%s'"),
-                                    $1, $3); 
+                    int y = _get_year_num(YEAR_DOUBLE, $1, $3);
+                    if (y != -1) {
+                      sprintf(date_s.year_str, "%d/%02d", y-1, y%100);
+                      date_s.year = y;
+                      date_s.year_type = YEAR_DOUBLE;
+                    }
+                    else YYERROR;
                   }
-                
                 }
              ;
 
 %%
+
+void error_missing_year()
+{
+  gedcom_date_error(_("Year is missing: '%s'"),
+                   curr_line_value);
+}
+
+void error_missing_month()
+{
+  gedcom_date_error(_("Month is missing: '%s'"),
+                   curr_line_value);
+}
+
+int _get_day_num(const char* input)
+{
+  if (strlen(input) <= MAX_DAY_LEN)
+    return atoi(input);
+  else {
+    gedcom_date_error(_("Too many characters in day '%s'"), input);
+    return -1;
+  }
+}
+
+int get_day_num(const char* input)
+{
+  int token = get_date_token(input);
+  if (token == NUMBER)
+    return _get_day_num(input);
+  else {
+    gedcom_date_error(_("Not a valid day number: '%s'"), input);
+    return -1;
+  }
+}
+
+int begin_month[] =
+{ /* CAL_GREGORIAN */   MON_JAN,
+  /* CAL_JULIAN */      MON_JAN,
+  /* CAL_HEBREW */      MON_TSH,
+  /* CAL_FRENCH_REV */  MON_VEND
+};
+
+int end_month[] =
+{ /* CAL_GREGORIAN */   MON_DEC,
+  /* CAL_JULIAN */      MON_DEC,
+  /* CAL_HEBREW */      MON_ELL,
+  /* CAL_FRENCH_REV */  MON_COMP
+};
+
+int get_month_num(Calendar_type cal, const char* input)
+{
+  int token = get_date_token(input);
+  if (token >= begin_month[cal] && token <= end_month[cal])
+    return token - begin_month[cal] + 1;
+  else {
+    gedcom_date_error(_("Not a valid month for the given calendar: '%s'"),
+                     input);
+    return -1;
+  }
+}
+
+int _get_year_num(Year_type ytype, const char* input1, const char* input2)
+{
+  if (ytype == YEAR_SINGLE) {
+    if (strlen(input1) <= MAX_YEAR_LEN) {
+      return atoi(input1);
+    }
+    else {
+      gedcom_date_error(_("Too many characters in year '%s'"), input1);
+      return -1;
+    }
+  }
+  else {
+    if (strlen(input2) != 2) {
+      gedcom_date_error(_("Year after slash should be two digits: '%s/%s'"),
+                       input1, input2);
+      return -1;
+    }
+    if (strlen(input1) <= MAX_YEAR_LEN - 3) {
+      int year1 = atoi(input1) + 1;
+      int year2 = atoi(input2);
+      if (year1 % 100 != year2) {
+       gedcom_date_error(_("Year after slash should be following year: '%s/%s'"),
+                         input1, input2);
+       return -1;
+      }
+      else 
+       return year1;
+    }
+    else {
+      gedcom_date_error(_("Too many characters in year '%s/%s'"),
+                       input1, input2);
+      return -1;
+    }
+  }
+}
+
+int get_year_num(const char* input, Year_type* ytype)
+{
+  char *year1, *year2 = NULL;
+  int numtok = get_year_tokens(input, &year1, &year2);
+  if (numtok) {
+    *ytype = (numtok == 1 ? YEAR_SINGLE : YEAR_DOUBLE);
+    return _get_year_num (*ytype, year1, year2);
+  }
+  else {
+    gedcom_date_error(_("Not a valid year: '%s'"), input); 
+    return -1;
+  }
+}