ledger/parsetime.yy
2008-04-13 03:38:28 -04:00

338 lines
6.5 KiB
Text

%{
#define YYSTYPE struct ledger::intorchar
#include "times.h"
#include "FlexLexer.h"
static struct std::tm * timeval;
namespace {
boost::posix_time::ptime moment;
struct time_to_leave : std::exception {};
yyFlexLexer * lexer;
inline void yyerror(const char *str) {
throw new ledger::datetime_error(str);
}
inline int yylex(void) {
return lexer->yylex();
}
int month_to_int(char * name)
{
switch (std::toupper(name[0])) {
case 'J':
if (std::tolower(name[1]) == 'a')
return 1;
else if (std::tolower(name[2]) == 'n')
return 6;
else
return 7;
case 'F':
return 2;
case 'M':
if (std::tolower(name[2]) == 'r')
return 3;
else
return 5;
case 'A':
if (std::tolower(name[1]) == 'p')
return 4;
else
return 8;
case 'S':
return 9;
case 'O':
return 10;
case 'N':
return 11;
case 'D':
return 12;
default:
std::cerr << "What?? (" << name << ")" << std::endl;
assert(0);
return -1;
}
}
void set_mdy(const ledger::intorchar& month,
const ledger::intorchar& day,
const ledger::intorchar& year = ledger::intorchar(),
bool shortyear = false)
{
if (ledger::day_before_month) {
timeval->tm_mon = (day.ival == -1 ?
month_to_int(day.sval) : day.ival) - 1;
timeval->tm_mday = month.ival;
} else {
timeval->tm_mon = (month.ival == -1 ?
month_to_int(month.sval) : month.ival) - 1;
timeval->tm_mday = day.ival;
}
if (year.ival != -1)
timeval->tm_year = (shortyear ?
(year.ival < 70 ? year.ival + 100 : year.ival) :
year.ival - 1900);
}
void set_hms(const ledger::intorchar& ampm,
const ledger::intorchar& hour,
const ledger::intorchar& min = ledger::intorchar(),
const ledger::intorchar& sec = ledger::intorchar())
{
if (ampm.sval && std::tolower(ampm.sval[0]) == 'a' && hour.ival == 12)
timeval->tm_hour = 0;
else if (ampm.sval && std::tolower(ampm.sval[0]) == 'p' && hour.ival == 12)
timeval->tm_hour = 12;
else if (hour.ival < 0 || (! ampm.sval && hour.ival > 23) ||
(ampm.sval && hour.ival > 12))
throw ledger::datetime_error("Hour out of range");
else
timeval->tm_hour += hour.ival;
if (min.ival < -1 || min.ival > 59)
throw ledger::datetime_error("Minute out of range");
if (sec.ival < -1 || sec.ival > 59)
throw ledger::datetime_error("Seconds out of range");
timeval->tm_min = min.ival == -1 ? 0 : min.ival;
timeval->tm_sec = sec.ival == -1 ? 0 : sec.ival;
}
}
%}
%token TOK_FOURNUM
%token TOK_TWONUM
%token TOK_ONENUM
%token TOK_MONTH
%token TOK_AMPM
%token TOK_SPACE
%left '/' '-' '.' 'T'
%%
input: date
{
throw time_to_leave();
};
date: absdate opttime
{
if (timeval->tm_gmtoff != -1) {
boost::posix_time::ptime::time_duration_type offset;
offset = boost::posix_time::seconds(timeval->tm_gmtoff);
moment = boost::posix_time::from_time_t(timegm(timeval)) - offset;
} else {
moment = boost::posix_time::ptime_from_tm(*timeval);
}
};
absdate:
year '/' morday '/' morday {
set_mdy($3, $5, $1);
}
|
year '-' morday '-' morday {
set_mdy($3, $5, $1);
}
|
year '.' morday '.' morday {
set_mdy($3, $5, $1);
}
|
morday '/' morday '/' year {
set_mdy($1, $3, $5);
}
|
morday '-' morday '-' year {
set_mdy($1, $3, $5);
}
|
morday '.' morday '.' year {
set_mdy($1, $3, $5);
}
|
morday '.' morday {
set_mdy($1, $3);
}
|
morday '/' morday {
set_mdy($1, $3);
}
|
morday '-' morday {
set_mdy($1, $3);
}
|
morday '/' morday '/' TOK_TWONUM {
set_mdy($1, $3, $5, true);
}
|
morday '-' morday '-' TOK_TWONUM {
set_mdy($1, $3, $5, true);
}
|
morday '.' morday '.' TOK_TWONUM {
set_mdy($1, $3, $5, true);
}
|
isodate
|
year TOK_SPACE TOK_MONTH TOK_SPACE morday {
set_mdy($3, $5, $1);
}
|
morday TOK_SPACE TOK_MONTH TOK_SPACE year {
set_mdy($3, $1, $5);
}
|
TOK_MONTH TOK_SPACE morday {
set_mdy($1, $3);
}
|
morday TOK_SPACE TOK_MONTH {
set_mdy($3, $1);
}
|
year '-' TOK_MONTH '-' morday {
set_mdy($3, $5, $1);
}
|
morday '-' TOK_MONTH '-' year {
set_mdy($3, $1, $5);
}
|
TOK_MONTH '-' morday {
set_mdy($1, $3);
}
|
morday '-' TOK_MONTH {
set_mdy($3, $1);
}
|
TOK_MONTH TOK_SPACE morday ',' TOK_SPACE year {
set_mdy($1, $3, $6);
}
;
opttime: /* epsilon */ | TOK_SPACE time ;
time:
onetwo optspace TOK_AMPM {
if (std::tolower($3.sval[0]) == 'p')
timeval->tm_hour = 12;
else
timeval->tm_hour = 0;
set_hms($3, $1);
}
|
onetwo ':' TOK_TWONUM optampm {
set_hms($4, $1, $3);
}
|
onetwo ':' TOK_TWONUM ':' TOK_TWONUM optampm {
set_hms($6, $1, $3, $5);
}
;
onetwo: TOK_ONENUM { $$ = $1; } | TOK_TWONUM { $$ = $1; } ;
optspace: /* epsilon */ | TOK_SPACE ;
optampm: /* epsilon */ |
optspace TOK_AMPM {
if (std::tolower($2.sval[0]) == 'p')
timeval->tm_hour = 12;
else
timeval->tm_hour = 0;
$$ = $2;
};
isodate:
year TOK_FOURNUM optisotime
{
timeval->tm_year = $1.ival - 1900;
timeval->tm_mon = $2.ival / 100 - 1;
timeval->tm_mday = $3.ival % 100;
};
optisotime: /* epsilon */ |
'T' TOK_FOURNUM TOK_TWONUM optisozone
{
timeval->tm_hour = $2.ival / 100;
timeval->tm_min = $2.ival % 100;
timeval->tm_sec = $3.ival;
};
optisozone: /* epsilon */ |
'-' TOK_FOURNUM {
timeval->tm_gmtoff = - (($2.ival / 100) * 3600 + ($2.ival % 100) * 60);
}
| '+' TOK_FOURNUM {
timeval->tm_gmtoff = (($2.ival / 100) * 3600 + ($2.ival % 100) * 60);
};
year: TOK_FOURNUM { $$ = $1; };
morday:
TOK_TWONUM { $$ = $1; }
| TOK_ONENUM { $$ = $1; };
%%
int yywrap()
{
return 1;
}
boost::posix_time::ptime parse_abs_datetime(std::istream& input)
{
lexer = new yyFlexLexer(&input);
struct std::tm temp;
std::memset(&temp, 0, sizeof(struct std::tm));
temp.tm_year = 2002 - 1900;
temp.tm_gmtoff = -1;
timeval = &temp;
// jww (2007-04-19): Catch any boost errors thrown from here and
// push them onto the new error stack scheme.
try {
if (yyparse() == 0)
return moment;
}
catch (const time_to_leave&) {
return moment;
}
catch (ledger::datetime_error *) {
throw;
}
catch (...) {
throw new ledger::datetime_error("Failed to parse date/time");
}
throw new ledger::datetime_error("Failed to parse date/time");
}
#ifdef MAIN
namespace ledger {
bool day_before_month = false;
}
int main()
{
yydebug = 1;
std::cout << parse_abs_datetime(std::cin) << std::endl;
return 0;
}
#endif // MAIN