/* Date parser bison grammar file * Copyright (c) 2012 Google Inc. * Written by Michal Nazarewicz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/ . */ %code requires { #ifndef YYSTYPE # define YYSTYPE long #endif #ifndef YYLTYPE # define YYLTYPE struct yylocation #endif #ifdef YYLLOC_DEFAULT # undef YYLLOC_DEFAULT #endif #define YYLLOC_DEFAULT(Cur, Rhs, N) do { \ if (N) { \ (Cur).start = YYRHSLOC(Rhs, 1).start; \ (Cur).end = YYRHSLOC(Rhs, N).end; \ } else { \ (Cur) = YYRHSLOC(Rhs, 0); \ } \ } while (0) } %code{ #include "date-parser-grammar.tab.h" #include "date-parser.h" #define ASSERT(cond, loc, message) do { \ if (!(cond)) { \ parse_date_print_error(&loc, message); \ YYERROR; \ } \ } while (0) } %locations %defines %error-verbose %define api.pure %parse-param {struct date *ret} %parse-param {const char **inputp} %lex-param {const char **inputp} %token T_NUMBER "" /* Always positive. */ %token T_NUMBER_4 "####" /* Four digit number. */ %token T_AGO "ago" /* Also used for minutes, hours, days and weeks. */ %token T_SECONDS "seconds" /* Also used for quarters and years */ %token T_MONTHS "months" %token T_YESTERDAY "yesterday" %token T_AMPM "am/pm" %token T_HOUR "" %token T_MONTH "" %expect 3 /* Two shift/reduce conflicts caused by year_maybe, and onde * caused by day_maybe. */ %% /* For backwards compatibility, just a number and nothing else * is treated as timestamp */ input : number { date_set_from_stamp(ret, $1) } | date ; date : part | date part ; part : integer "seconds" ago_maybe { ASSERT(date_add_seconds(ret, $1 * $3, $2), @$, "offset ends up in date out of range") } | integer "months" ago_maybe { ASSERT(date_add_months(ret, $1 * $3, $2), @$, "offset ends up in date out of range") } | "yesterday" { ASSERT(date_set_yesterday(ret), @$, "offset ends up in date out of range") } | '@' number { date_set_from_stamp(ret, $2) } | "" { date_set_time(ret, $1, -1, -1, -1) } /* HH:MM, HH:MM:SS, HH:MM am/pm, HH:MM:SS am/pm */ | number ':' number seconds_maybe ampm_maybe { ASSERT(date_set_time(ret, $1, $3, $4, $5), @$, "invalid time") } | number "am/pm" { /* HH am/pm */ ASSERT(date_set_time(ret, $1, -1, -1, $2), @$, "invalid hour") } | "####" '/' "" '/' "" { /* YYYY/MM/DD */ ASSERT(date_set_date(ret, $1, $3, $5), @$, "invalid date") } | "####" '-' "" '-' "" { /* YYYY-MM-DD */ ASSERT(date_set_date(ret, $1, $3, $5), @$, "invalid date") } | "" '-' "" '-' "####" { /* DD-MM-YYYY */ ASSERT(date_set_date(ret, $5, $3, $1), @$, "invalid date") } /* No MM/DD/YYYY or DD/MM/YYYY because it's confusing. */ | "" "" year_maybe { /* 1 January 2012 */ ASSERT(date_set_date(ret, $3, $2, $1), @$, "invalid date") } | "" day_maybe year_maybe { /* January 1 2012 */ ASSERT(date_set_date(ret, $3, $1, $2), @$, "invalid date") } | "####" 'q' "" { /* Quarter, 2012q1 */ ASSERT(date_set_quarter(ret, $1, $3), @$, "invalid quarter"); } ; number : "" { $$ = $1 } | "####" { $$ = $1 } ; integer : number { $$ = $1 } | '-' number { $$ = -$2 } ; ago_maybe : /* empty */ { $$ = 1 } | "ago" { $$ = -1 } ; seconds_maybe : /* empty */ { $$ = -1 } | ':' "" { $$ = $2 } ; ampm_maybe : /* empty */ { $$ = -1 } | "am/pm" { $$ = $$ } /* For people who like writing "a.m." or "p.m." and since dot * is ignored by the lexer (ie. it's treated just like white * space), dot is lost. */ | 'a' 'm' { $$ = 0 } | 'p' 'm' { $$ = 0 } ; day_maybe : /* empty */ { $$ = -1 } | "" { $$ = $1 } ; year_maybe : /* empty */ { $$ = -1 } | "####" { $$ = $1 } ; %%