summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-11-18 04:11:14 -0500
committerJohn Wiegley <johnw@newartisans.com>2009-11-18 04:11:14 -0500
commit7fe369eb492f737f570d0ccf4aaf5db68f900279 (patch)
treeec499b8dd3c7c9286e9fa71d3e7a8e2a94c21d83
parentfe9af7ace7a9b25a3eefe481b6b7a38ad2f758ed (diff)
downloadfork-ledger-7fe369eb492f737f570d0ccf4aaf5db68f900279.tar.gz
fork-ledger-7fe369eb492f737f570d0ccf4aaf5db68f900279.tar.bz2
fork-ledger-7fe369eb492f737f570d0ccf4aaf5db68f900279.zip
The new period parser is implemented, but untested
-rw-r--r--src/precmd.cc53
-rw-r--r--src/times.cc959
-rw-r--r--src/times.h500
3 files changed, 883 insertions, 629 deletions
diff --git a/src/precmd.cc b/src/precmd.cc
index c4d7cbaa..0e024a39 100644
--- a/src/precmd.cc
+++ b/src/precmd.cc
@@ -165,57 +165,12 @@ value_t period_command(call_scope_t& args)
report_t& report(find_scope<report_t>(args));
std::ostream& out(report.output_stream);
+ show_period_tokens(out, arg);
+ out << std::endl;
+
date_interval_t interval(arg);
+ interval.dump(out);
- out << _("global details => ") << std::endl << std::endl;
-
- if (interval.start)
- out << _(" start: ") << format_date(*interval.start) << std::endl;
- else
- out << _(" start: TODAY: ") << format_date(CURRENT_DATE()) << std::endl;
- if (interval.finish)
- out << _(" finish: ") << format_date(*interval.finish) << std::endl;
-
- if (interval.skip_duration)
- out << _(" skip: ") << *interval.skip_duration << std::endl;
- if (interval.factor)
- out << _(" factor: ") << interval.factor << std::endl;
- if (interval.duration)
- out << _("duration: ") << *interval.duration << std::endl;
-
- if (interval.find_period(interval.start ?
- *interval.start : CURRENT_DATE())) {
- out << std::endl
- << _("after finding first period => ") << std::endl
- << std::endl;
-
- if (interval.start)
- out << _(" start: ") << format_date(*interval.start) << std::endl;
- if (interval.finish)
- out << _(" finish: ") << format_date(*interval.finish) << std::endl;
-
- if (interval.skip_duration)
- out << _(" skip: ") << *interval.skip_duration << std::endl;
- if (interval.factor)
- out << _(" factor: ") << interval.factor << std::endl;
- if (interval.duration)
- out << _("duration: ") << *interval.duration << std::endl;
-
- out << std::endl;
-
- for (int i = 0; i < 20 && interval; i++, ++interval) {
- out << std::right;
- out.width(2);
-
- out << i << "): " << format_date(*interval.start);
- if (interval.end_of_duration)
- out << " -- " << format_date(*interval.inclusive_end());
- out << std::endl;
-
- if (! interval.skip_duration)
- break;
- }
- }
return NULL_VALUE;
}
diff --git a/src/times.cc b/src/times.cc
index 076cef3d..97fc3ab8 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -361,6 +361,8 @@ std::ostream& operator<<(std::ostream& out,
out << duration.length << " week(s)";
else if (duration.quantum == date_duration_t::MONTHS)
out << duration.length << " month(s)";
+ else if (duration.quantum == date_duration_t::QUARTERS)
+ out << duration.length << " quarter(s)";
else {
assert(duration.quantum == date_duration_t::YEARS);
out << duration.length << " year(s)";
@@ -368,6 +370,537 @@ std::ostream& operator<<(std::ostream& out,
return out;
}
+class date_parser_t
+{
+ friend void show_period_tokens(std::ostream& out, const string& arg);
+
+ class lexer_t
+ {
+ friend class date_parser_t;
+
+ string::const_iterator begin;
+ string::const_iterator end;
+
+ public:
+ struct token_t
+ {
+ enum kind_t {
+ UNKNOWN,
+
+ TOK_DATE,
+ TOK_INT,
+ TOK_SLASH,
+ TOK_DASH,
+ TOK_DOT,
+
+ TOK_A_YEAR,
+ TOK_A_MONTH,
+ TOK_A_WDAY,
+
+ TOK_SINCE,
+ TOK_UNTIL,
+ TOK_IN,
+ TOK_THIS,
+ TOK_NEXT,
+ TOK_LAST,
+ TOK_EVERY,
+
+ TOK_TODAY,
+ TOK_TOMORROW,
+ TOK_YESTERDAY,
+
+ TOK_YEAR,
+ TOK_QUARTER,
+ TOK_MONTH,
+ TOK_WEEK,
+ TOK_DAY,
+
+ TOK_YEARLY,
+ TOK_QUARTERLY,
+ TOK_BIMONTHLY,
+ TOK_MONTHLY,
+ TOK_BIWEEKLY,
+ TOK_WEEKLY,
+ TOK_DAILY,
+
+ TOK_YEARS,
+ TOK_QUARTERS,
+ TOK_MONTHS,
+ TOK_WEEKS,
+ TOK_DAYS,
+
+ END_REACHED
+
+ } kind;
+
+ typedef variant<unsigned short,
+ date_specifier_t::year_type,
+ date_time::months_of_year,
+ date_time::weekdays,
+ date_specifier_t> content_t;
+
+ optional<content_t> value;
+
+ explicit token_t(kind_t _kind = UNKNOWN,
+ const optional<content_t>& _value = none)
+ : kind(_kind), value(_value) {
+ TRACE_CTOR(date_parser_t::lexer_t::token_t, "");
+ }
+ token_t(const token_t& tok)
+ : kind(tok.kind), value(tok.value) {
+ TRACE_CTOR(date_parser_t::lexer_t::token_t, "copy");
+ }
+ ~token_t() throw() {
+ TRACE_DTOR(date_parser_t::lexer_t::token_t);
+ }
+
+ token_t& operator=(const token_t& tok) {
+ if (this != &tok) {
+ kind = tok.kind;
+ value = tok.value;
+ }
+ return *this;
+ }
+
+ operator bool() const {
+ return kind != END_REACHED;
+ }
+
+ string to_string() const {
+ switch (kind) {
+ case UNKNOWN: return "UNKNOWN";
+ case TOK_DATE: return "TOK_DATE";
+ case TOK_INT: return "TOK_INT";
+ case TOK_SLASH: return "TOK_SLASH";
+ case TOK_DASH: return "TOK_DASH";
+ case TOK_DOT: return "TOK_DOT";
+ case TOK_A_YEAR: return "TOK_A_YEAR";
+ case TOK_A_MONTH: return "TOK_A_MONTH";
+ case TOK_A_WDAY: return "TOK_A_WDAY";
+ case TOK_SINCE: return "TOK_SINCE";
+ case TOK_UNTIL: return "TOK_UNTIL";
+ case TOK_IN: return "TOK_IN";
+ case TOK_THIS: return "TOK_THIS";
+ case TOK_NEXT: return "TOK_NEXT";
+ case TOK_LAST: return "TOK_LAST";
+ case TOK_EVERY: return "TOK_EVERY";
+ case TOK_TODAY: return "TOK_EVERY";
+ case TOK_TOMORROW: return "TOK_TOMORROW";
+ case TOK_YESTERDAY: return "TOK_YESTERDAY";
+ case TOK_YEAR: return "TOK_YEAR";
+ case TOK_QUARTER: return "TOK_QUARTER";
+ case TOK_MONTH: return "TOK_MONTH";
+ case TOK_WEEK: return "TOK_WEEK";
+ case TOK_DAY: return "TOK_DAY";
+ case TOK_YEARLY: return "TOK_YEARLY";
+ case TOK_QUARTERLY: return "TOK_QUARTERLY";
+ case TOK_BIMONTHLY: return "TOK_BIMONTHLY";
+ case TOK_MONTHLY: return "TOK_MONTHLY";
+ case TOK_BIWEEKLY: return "TOK_BIWEEKLY";
+ case TOK_WEEKLY: return "TOK_WEEKLY";
+ case TOK_DAILY: return "TOK_DAILY";
+ case TOK_YEARS: return "TOK_YEARS";
+ case TOK_QUARTERS: return "TOK_QUARTERS";
+ case TOK_MONTHS: return "TOK_MONTHS";
+ case TOK_WEEKS: return "TOK_WEEKS";
+ case TOK_DAYS: return "TOK_DAYS";
+ case END_REACHED: return "END_REACHED";
+ }
+ assert(false);
+ return empty_string;
+ }
+
+ void unexpected();
+ static void expected(char wanted, char c = '\0');
+ };
+
+ token_t token_cache;
+
+ lexer_t(string::const_iterator _begin,
+ string::const_iterator _end)
+ : begin(_begin), end(_end)
+ {
+ TRACE_CTOR(date_parser_t::lexer_t, "");
+ }
+ lexer_t(const lexer_t& lexer)
+ : begin(lexer.begin), end(lexer.end),
+ token_cache(lexer.token_cache)
+ {
+ TRACE_CTOR(date_parser_t::lexer_t, "copy");
+ }
+ ~lexer_t() throw() {
+ TRACE_DTOR(date_parser_t::lexer_t);
+ }
+
+ token_t next_token();
+ void push_token(token_t tok) {
+ assert(token_cache.kind == token_t::UNKNOWN);
+ token_cache = tok;
+ }
+ token_t peek_token() {
+ if (token_cache.kind == token_t::UNKNOWN)
+ token_cache = next_token();
+ return token_cache;
+ }
+ };
+
+ string arg;
+ lexer_t lexer;
+
+public:
+ date_parser_t(const string& _arg)
+ : arg(_arg), lexer(arg.begin(), arg.end()) {
+ TRACE_CTOR(date_parser_t, "");
+ }
+ date_parser_t(const date_parser_t& parser)
+ : arg(parser.arg), lexer(parser.lexer) {
+ TRACE_CTOR(date_parser_t, "copy");
+ }
+ ~date_parser_t() throw() {
+ TRACE_DTOR(date_parser_t);
+ }
+
+ date_interval_t parse();
+
+private:
+ void determine_when(lexer_t::token_t& tok, date_specifier_t& specifier);
+};
+
+void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok,
+ date_specifier_t& specifier)
+{
+ switch (tok.kind) {
+ case lexer_t::token_t::TOK_DATE:
+ specifier = boost::get<date_specifier_t>(*tok.value);
+ break;
+
+ case lexer_t::token_t::TOK_INT:
+ specifier.day =
+ date_specifier_t::day_type(boost::get<unsigned short>(*tok.value));
+ break;
+ case lexer_t::token_t::TOK_A_YEAR:
+ specifier.year = boost::get<date_specifier_t::year_type>(*tok.value);
+ break;
+ case lexer_t::token_t::TOK_A_MONTH:
+ specifier.month =
+ date_specifier_t::month_type
+ (boost::get<date_time::months_of_year>(*tok.value));
+ break;
+ case lexer_t::token_t::TOK_A_WDAY:
+ specifier.wday =
+ date_specifier_t::day_of_week_type
+ (boost::get<date_time::weekdays>(*tok.value));
+ break;
+
+ default:
+ tok.unexpected();
+ break;
+ }
+}
+
+date_interval_t date_parser_t::parse()
+{
+ optional<date_specifier_t> since_specifier;
+ optional<date_specifier_t> until_specifier;
+ optional<date_specifier_t> inclusion_specifier;
+
+ date_interval_t period;
+ date_t today = CURRENT_DATE();
+ bool end_inclusive = false;
+
+ for (lexer_t::token_t tok = lexer.next_token();
+ tok.kind != lexer_t::token_t::END_REACHED;
+ tok = lexer.next_token()) {
+ switch (tok.kind) {
+#if 0
+ case lexer_t::token_t::TOK_INT:
+ // jww (2009-11-18): NYI
+ assert(! "Need to allow for expressions like \"4 months ago\"");
+ tok.unexpected();
+ break;
+#endif
+
+ case lexer_t::token_t::TOK_DATE:
+ if (! inclusion_specifier)
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+ break;
+
+ case lexer_t::token_t::TOK_INT:
+ if (! inclusion_specifier)
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+ break;
+
+ case lexer_t::token_t::TOK_A_YEAR:
+ if (! inclusion_specifier)
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+ break;
+
+ case lexer_t::token_t::TOK_A_MONTH:
+ if (! inclusion_specifier)
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+ break;
+
+ case lexer_t::token_t::TOK_A_WDAY:
+ if (! inclusion_specifier)
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+ break;
+
+ case lexer_t::token_t::TOK_DASH:
+ if (inclusion_specifier) {
+ since_specifier = inclusion_specifier;
+ until_specifier = date_specifier_t();
+ inclusion_specifier = none;
+
+ tok = lexer.next_token();
+ determine_when(tok, *until_specifier);
+
+ // The dash operator is special: it has an _inclusive_ end.
+ end_inclusive = true;
+ } else {
+ tok.unexpected();
+ }
+ break;
+
+ case lexer_t::token_t::TOK_SINCE:
+ if (since_specifier) {
+ tok.unexpected();
+ } else {
+ since_specifier = date_specifier_t();
+ tok = lexer.next_token();
+ determine_when(tok, *since_specifier);
+ }
+ break;
+
+ case lexer_t::token_t::TOK_UNTIL:
+ if (until_specifier) {
+ tok.unexpected();
+ } else {
+ until_specifier = date_specifier_t();
+ tok = lexer.next_token();
+ determine_when(tok, *until_specifier);
+ }
+ break;
+
+ case lexer_t::token_t::TOK_IN:
+ if (inclusion_specifier) {
+ tok.unexpected();
+ } else {
+ inclusion_specifier = date_specifier_t();
+ tok = lexer.next_token();
+ determine_when(tok, *inclusion_specifier);
+ }
+ break;
+
+ case lexer_t::token_t::TOK_THIS:
+ case lexer_t::token_t::TOK_NEXT:
+ case lexer_t::token_t::TOK_LAST: {
+ int8_t adjust = 0;
+ if (tok.kind == lexer_t::token_t::TOK_NEXT)
+ adjust = 1;
+ else if (tok.kind == lexer_t::token_t::TOK_LAST)
+ adjust = -1;
+
+ tok = lexer.next_token();
+ switch (tok.kind) {
+ case lexer_t::token_t::TOK_INT:
+ // jww (2009-11-18): Allow things like "last 5 weeks"
+ assert(! "Need to allow for expressions like \"last 5 weeks\"");
+ tok.unexpected();
+ break;
+
+ case lexer_t::token_t::TOK_A_MONTH: {
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+
+ date_t temp(today.year(), *inclusion_specifier->month, 1);
+ temp += gregorian::years(adjust);
+ inclusion_specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
+ temp.month());
+ break;
+ }
+
+ case lexer_t::token_t::TOK_A_WDAY: {
+ inclusion_specifier = date_specifier_t();
+ determine_when(tok, *inclusion_specifier);
+
+ date_t temp =
+ date_duration_t::find_nearest(today, date_duration_t::WEEKS);
+ while (temp.day_of_week() != inclusion_specifier->wday)
+ temp += gregorian::days(1);
+ temp += gregorian::days(7 * adjust);
+ inclusion_specifier = date_specifier_t(temp);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_YEAR: {
+ date_t temp(today);
+ temp += gregorian::years(adjust);
+ inclusion_specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()));
+ break;
+ }
+
+ case lexer_t::token_t::TOK_QUARTER: {
+ date_t temp =
+ date_duration_t::find_nearest(today, date_duration_t::QUARTERS);
+ temp += gregorian::months(3 * adjust);
+ inclusion_specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
+ temp.month());
+ period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_MONTH: {
+ date_t temp(today);
+ temp += gregorian::months(adjust);
+ inclusion_specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
+ temp.month());
+ break;
+ }
+
+ case lexer_t::token_t::TOK_WEEK: {
+ date_t temp =
+ date_duration_t::find_nearest(today, date_duration_t::WEEKS);
+ temp += gregorian::days(7 * adjust);
+ inclusion_specifier = date_specifier_t(today);
+ period.duration = date_duration_t(date_duration_t::WEEKS, 1);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_DAY: {
+ date_t temp(today);
+ temp += gregorian::days(adjust);
+ inclusion_specifier = date_specifier_t(temp);
+ break;
+ }
+
+ default:
+ tok.unexpected();
+ break;
+ }
+ break;
+ }
+
+ case lexer_t::token_t::TOK_TODAY:
+ inclusion_specifier = date_specifier_t(today);
+ break;
+ case lexer_t::token_t::TOK_TOMORROW:
+ inclusion_specifier = date_specifier_t(today + gregorian::days(1));
+ break;
+ case lexer_t::token_t::TOK_YESTERDAY:
+ inclusion_specifier = date_specifier_t(today - gregorian::days(1));
+ break;
+
+ case lexer_t::token_t::TOK_EVERY:
+ tok = lexer.next_token();
+ if (tok == lexer_t::token_t::TOK_INT) {
+ int quantity = boost::get<unsigned short>(*tok.value);
+ tok = lexer.next_token();
+ switch (tok.kind) {
+ case lexer_t::token_t::TOK_YEARS:
+ period.skip_duration = date_duration_t(date_duration_t::YEARS, quantity);
+ break;
+ case lexer_t::token_t::TOK_QUARTERS:
+ period.skip_duration = date_duration_t(date_duration_t::QUARTERS, quantity);
+ break;
+ case lexer_t::token_t::TOK_MONTHS:
+ period.skip_duration = date_duration_t(date_duration_t::MONTHS, quantity);
+ break;
+ case lexer_t::token_t::TOK_WEEKS:
+ period.skip_duration = date_duration_t(date_duration_t::WEEKS, quantity);
+ break;
+ case lexer_t::token_t::TOK_DAYS:
+ period.skip_duration = date_duration_t(date_duration_t::DAYS, quantity);
+ break;
+ default:
+ tok.unexpected();
+ break;
+ }
+ } else {
+ switch (tok.kind) {
+ case lexer_t::token_t::TOK_YEAR:
+ period.skip_duration = date_duration_t(date_duration_t::YEARS, 1);
+ break;
+ case lexer_t::token_t::TOK_QUARTER:
+ period.skip_duration = date_duration_t(date_duration_t::QUARTERS, 1);
+ break;
+ case lexer_t::token_t::TOK_MONTH:
+ period.skip_duration = date_duration_t(date_duration_t::MONTHS, 1);
+ break;
+ case lexer_t::token_t::TOK_WEEK:
+ period.skip_duration = date_duration_t(date_duration_t::WEEKS, 1);
+ break;
+ case lexer_t::token_t::TOK_DAY:
+ period.skip_duration = date_duration_t(date_duration_t::DAYS, 1);
+ break;
+ default:
+ tok.unexpected();
+ break;
+ }
+ }
+ break;
+
+ case lexer_t::token_t::TOK_YEARLY:
+ period.skip_duration = date_duration_t(date_duration_t::YEARS, 1);
+ break;
+ case lexer_t::token_t::TOK_QUARTERLY:
+ period.skip_duration = date_duration_t(date_duration_t::QUARTERS, 1);
+ break;
+ case lexer_t::token_t::TOK_BIMONTHLY:
+ period.skip_duration = date_duration_t(date_duration_t::MONTHS, 2);
+ break;
+ case lexer_t::token_t::TOK_MONTHLY:
+ period.skip_duration = date_duration_t(date_duration_t::MONTHS, 1);
+ break;
+ case lexer_t::token_t::TOK_BIWEEKLY:
+ period.skip_duration = date_duration_t(date_duration_t::WEEKS, 2);
+ break;
+ case lexer_t::token_t::TOK_WEEKLY:
+ period.skip_duration = date_duration_t(date_duration_t::WEEKS, 1);
+ break;
+ case lexer_t::token_t::TOK_DAILY:
+ period.skip_duration = date_duration_t(date_duration_t::DAYS, 1);
+ break;
+
+ default:
+ tok.unexpected();
+ break;
+ }
+ }
+
+ if (! period.duration && inclusion_specifier)
+ period.duration = inclusion_specifier->implied_duration();
+
+ if (since_specifier || until_specifier) {
+ date_range_t range(since_specifier, until_specifier);
+ range.end_inclusive = end_inclusive;
+
+ period.range = date_specifier_or_range_t(range);
+ }
+ else if (inclusion_specifier) {
+ period.range = date_specifier_or_range_t(*inclusion_specifier);
+ }
+ else {
+ /* otherwise, it's something like "monthly", with no date reference */
+ }
+
+ return period;
+}
+
+void date_interval_t::parse(const string& str)
+{
+ date_parser_t parser(str);
+ *this = parser.parse();
+}
+
void date_interval_t::resolve_end()
{
if (start && ! end_of_duration) {
@@ -395,6 +928,40 @@ void date_interval_t::resolve_end()
}
}
+date_t date_duration_t::find_nearest(const date_t& date, skip_quantum_t skip)
+{
+ date_t result;
+
+ switch (skip) {
+ case date_duration_t::YEARS:
+ result = date_t(date.year(), gregorian::Jan, 1);
+ break;
+ case date_duration_t::QUARTERS:
+ result = date_t(date.year(), date.month(), 1);
+ while (result.month() != gregorian::Jan &&
+ result.month() != gregorian::Apr &&
+ result.month() != gregorian::Jul &&
+ result.month() != gregorian::Oct)
+ result -= gregorian::months(1);
+ break;
+ case date_duration_t::MONTHS:
+ result = date_t(date.year(), date.month(), 1);
+ break;
+ case date_duration_t::WEEKS:
+ result = date;
+ while (result.day_of_week() != start_of_week)
+ result -= gregorian::days(1);
+ break;
+ case date_duration_t::DAYS:
+ result = date;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return result;
+}
+
void date_interval_t::stabilize(const optional<date_t>& date)
{
#if defined(DEBUG_ON)
@@ -430,20 +997,15 @@ void date_interval_t::stabilize(const optional<date_t>& date)
date_t when = start ? *start : *date;
if (duration->quantum == date_duration_t::MONTHS ||
+ duration->quantum == date_duration_t::QUARTERS ||
duration->quantum == date_duration_t::YEARS) {
- DEBUG("times.interval", "stabilize: monthly or yearly duration");
-
- start = date_t(when.year(), gregorian::Jan, 1);
+ DEBUG("times.interval",
+ "stabilize: monthly, quarterly or yearly duration");
+ start = date_duration_t::find_nearest(when, duration->quantum);
} else {
DEBUG("times.interval", "stabilize: daily or weekly duration");
-
- start = date_t(when - gregorian::days(400));
-
- if (duration->quantum == date_duration_t::WEEKS) {
- // Move it to a Sunday
- while (start->day_of_week() != start_of_week)
- *start += gregorian::days(1);
- }
+ start = date_duration_t::find_nearest(when - gregorian::days(400),
+ duration->quantum);
}
DEBUG("times.interval",
@@ -475,6 +1037,14 @@ void date_interval_t::stabilize(const optional<date_t>& date)
DEBUG("times.interval", "stabilize: finish reset to initial finish");
}
}
+ else if (range) {
+ start = range->begin();
+ finish = range->end();
+
+ if (start && finish)
+ duration = date_duration_t(date_duration_t::DAYS,
+ static_cast<int>((*finish - *start).days()));
+ }
aligned = true;
}
@@ -546,7 +1116,7 @@ bool date_interval_t::find_period(const date_t& date)
return true;
}
- scan = skip_duration->add(scan);
+ scan = skip_duration->add(scan);
end_of_scan = duration->add(scan);
}
@@ -581,214 +1151,57 @@ date_interval_t& date_interval_t::operator++()
return *this;
}
-namespace {
- void parse_inclusion_specifier(const string& word,
- date_t * begin,
- date_t * end)
- {
- date_traits_t traits;
- date_t when = parse_date_mask(word.c_str(), none, &traits);
-
- if (when.is_not_a_date())
- throw_(date_error, _("Could not parse date mask: %1") << word);
-
- if (begin) {
- *begin = when;
-
- if (end) {
- if (traits.has_day)
- *end = *begin + gregorian::days(1);
- else if (traits.has_month)
- *end = *begin + gregorian::months(1);
- else
- *end = *begin + gregorian::years(1);
- }
- }
- else if (end) {
- *end = when;
- }
- }
-
- inline void read_lower_word(std::istream& in, string& word) {
- in >> word;
- for (string::size_type i = 0, l = word.length(); i < l; i++)
- word[i] = static_cast<char>(std::tolower(word[i]));
- }
-
- void parse_date_words(std::istream& in,
- string& word,
- date_interval_t& interval,
- bool look_for_start = true,
- bool look_for_finish = true)
- {
- string type;
-
- if (word == _("this") || word == _("last") || word == _("next")) {
- type = word;
- if (! in.eof())
- read_lower_word(in, word);
- else
- word = _("month");
- } else {
- type = _("this");
- }
-
- date_t start = CURRENT_DATE();
- date_t finish;
- bool parse_specifier = false;
-
- optional<date_duration_t> duration;
-
- assert(look_for_start || look_for_finish);
-
- if (word == _("year")) {
- duration = date_duration_t(date_duration_t::YEARS, 1);
- start = gregorian::date(start.year(), 1, 1);
- }
- else if (word == _("month")) {
- duration = date_duration_t(date_duration_t::MONTHS, 1);
- start = gregorian::date(start.year(), start.month(), 1);
- }
- else if (word == _("today") || word == _("day")) {
- duration = date_duration_t(date_duration_t::DAYS, 1);
- }
- else {
- parse_specifier = true;
- }
-
- if (parse_specifier)
- parse_inclusion_specifier(word, &start, &finish);
- else
- finish = duration->add(start);
-
- if (type == _("last") && duration) {
- start = duration->subtract(start);
- finish = duration->subtract(finish);
- }
- else if (type == _("next") && duration) {
- start = duration->add(start);
- finish = duration->add(finish);
- }
-
- if (look_for_start && is_valid(start)) interval.start = start;
- if (look_for_finish && is_valid(finish)) interval.finish = finish;
- }
-}
-
-void date_interval_t::parse(std::istream& in)
+void date_interval_t::dump(std::ostream& out)
{
- string word;
-
- optional<date_time::months_of_year> mon;
- optional<date_time::weekdays> wday;
- optional<date_t::year_type> year;
-
- while (! in.eof()) {
- read_lower_word(in, word);
- if (word == _("every")) {
- read_lower_word(in, word);
- if (std::isdigit(word[0])) {
- int quantity = lexical_cast<int>(word);
- read_lower_word(in, word);
- if (word == _("days"))
- duration = date_duration_t(date_duration_t::DAYS, quantity);
- else if (word == _("weeks"))
- duration = date_duration_t(date_duration_t::WEEKS, quantity);
- else if (word == _("months"))
- duration = date_duration_t(date_duration_t::MONTHS, quantity);
- else if (word == _("quarters"))
- duration = date_duration_t(date_duration_t::MONTHS, 3 * quantity);
- else if (word == _("years"))
- duration = date_duration_t(date_duration_t::YEARS, quantity);
- }
- else if (word == _("day"))
- duration = date_duration_t(date_duration_t::DAYS, 1);
- else if (word == _("week"))
- duration = date_duration_t(date_duration_t::WEEKS, 1);
- else if (word == _("month"))
- duration = date_duration_t(date_duration_t::MONTHS, 1);
- else if (word == _("quarter"))
- duration = date_duration_t(date_duration_t::MONTHS, 3);
- else if (word == _("year"))
- duration = date_duration_t(date_duration_t::YEARS, 1);
- }
- else if (word == _("daily"))
- duration = date_duration_t(date_duration_t::DAYS, 1);
- else if (word == _("weekly"))
- duration = date_duration_t(date_duration_t::WEEKS, 1);
- else if (word == _("biweekly"))
- duration = date_duration_t(date_duration_t::WEEKS, 2);
- else if (word == _("monthly"))
- duration = date_duration_t(date_duration_t::MONTHS, 1);
- else if (word == _("bimonthly"))
- duration = date_duration_t(date_duration_t::MONTHS, 2);
- else if (word == _("quarterly"))
- duration = date_duration_t(date_duration_t::MONTHS, 3);
- else if (word == _("yearly"))
- duration = date_duration_t(date_duration_t::YEARS, 1);
- else if (word == _("this") || word == _("last") || word == _("next") ||
- word == _("today")) {
- parse_date_words(in, word, *this);
- }
- else if (word == _("in")) {
- read_lower_word(in, word);
- parse_date_words(in, word, *this);
- }
- else if (word == _("from") || word == _("since")) {
- read_lower_word(in, word);
- parse_date_words(in, word, *this, true, false);
- }
- else if (word == _("to") || word == _("until")) {
- read_lower_word(in, word);
- parse_date_words(in, word, *this, false, true);
- }
- else if (optional<date_time::months_of_year>
- m = string_to_month_of_year(word)) {
- mon = m;
- }
- else if (optional<date_time::weekdays>
- d = string_to_day_of_week(word)) {
- wday = d;
- }
- else if (all(word, is_digit())) {
- year = lexical_cast<unsigned short>(word);
- }
- else {
- // otherwise, it should be an explicit date
- date_t s, f;
- parse_inclusion_specifier(word, &s, &f);
- start = s;
- finish = f;
- }
- }
-
- if (year || mon || wday) {
- if (! start)
- start = CURRENT_DATE();
-
- if (wday) {
- while (start->day_of_week() != *wday)
- *start -= gregorian::days(1);
-
- if (! finish)
- finish = *start + gregorian::days(1);
- } else {
- bool overwrite_finish = false;
-
- if (year) {
- start = date_t(*year, 1, 1);
- if (! finish) {
- finish = *start + gregorian::years(1);
- overwrite_finish = true;
- }
- }
+ out << _("--- Before stabilization ---") << std::endl;
+
+ if (range)
+ out << _(" range: ") << range->to_string() << std::endl;
+ if (start)
+ out << _(" start: ") << format_date(*start) << std::endl;
+ if (finish)
+ out << _(" finish: ") << format_date(*finish) << std::endl;
+
+ if (skip_duration)
+ out << _(" skip: ") << skip_duration->to_string() << std::endl;
+ if (factor)
+ out << _(" factor: ") << factor << std::endl;
+ if (duration)
+ out << _("duration: ") << duration->to_string() << std::endl;
+
+ stabilize(begin());
+
+ out << std::endl
+ << _("--- After stabilization ---") << std::endl;
+
+ if (range)
+ out << _(" range: ") << range->to_string() << std::endl;
+ if (start)
+ out << _(" start: ") << format_date(*start) << std::endl;
+ if (finish)
+ out << _(" finish: ") << format_date(*finish) << std::endl;
+
+ if (skip_duration)
+ out << _(" skip: ") << skip_duration->to_string() << std::endl;
+ if (factor)
+ out << _(" factor: ") << factor << std::endl;
+ if (duration)
+ out << _("duration: ") << duration->to_string() << std::endl;
+
+ out << std::endl
+ << _("--- Sample dates in range (max. 20) ---") << std::endl;
+
+ for (int i = 0; i < 20 && *this; i++, ++*this) {
+ out << std::right;
+ out.width(2);
+
+ out << (i + 1) << ": " << format_date(*start);
+ if (skip_duration)
+ out << " -- " << format_date(*inclusive_skip_end());
+ out << std::endl;
- if (mon) {
- start = date_t(start->year(), *mon, 1);
- if (! finish || overwrite_finish)
- finish = *start + gregorian::months(1);
- }
- }
+ if (! skip_duration)
+ break;
}
}
@@ -846,22 +1259,22 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
if (! term.empty()) {
if (std::isdigit(term[0])) {
- return token_t(term.length() == 4 ?
- token_t::TOK_A_YEAR : token_t::TOK_INT,
- token_t::content_t(lexical_cast<int>(term)));
+ if (term.length() == 4)
+ return token_t(token_t::TOK_A_YEAR,
+ token_t::content_t
+ (lexical_cast<date_specifier_t::year_type>(term)));
+ else
+ return token_t(token_t::TOK_INT,
+ token_t::content_t(lexical_cast<unsigned short>(term)));
}
else if (std::isalpha(term[0])) {
if (optional<date_time::months_of_year> month =
string_to_month_of_year(term)) {
- date_specifier_t specifier;
- specifier.month = static_cast<date_t::month_type>(*month);
- return token_t(token_t::TOK_A_MONTH, token_t::content_t(specifier));
+ return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month));
}
else if (optional<date_time::weekdays> wday =
string_to_day_of_week(term)) {
- date_specifier_t specifier;
- specifier.wday = static_cast<date_t::day_of_week_type>(*wday);
- return token_t(token_t::TOK_A_WDAY, token_t::content_t(specifier));
+ return token_t(token_t::TOK_A_WDAY, token_t::content_t(*wday));
}
else if (term == _("from") || term == _("since"))
return token_t(token_t::TOK_SINCE);
@@ -875,6 +1288,14 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
return token_t(token_t::TOK_NEXT);
else if (term == _("last"))
return token_t(token_t::TOK_LAST);
+ else if (term == _("every"))
+ return token_t(token_t::TOK_EVERY);
+ else if (term == _("today"))
+ return token_t(token_t::TOK_TODAY);
+ else if (term == _("tomorrow"))
+ return token_t(token_t::TOK_TOMORROW);
+ else if (term == _("yesterday"))
+ return token_t(token_t::TOK_YESTERDAY);
else if (term == _("year"))
return token_t(token_t::TOK_YEAR);
else if (term == _("quarter"))
@@ -950,13 +1371,6 @@ void date_parser_t::lexer_t::token_t::expected(char wanted, char c)
}
}
-date_interval_t date_parser_t::parse_date_expr()
-{
- date_interval_t interval;
-
- return interval;
-}
-
namespace {
typedef std::map<std::string, datetime_io_t *> datetime_io_map;
typedef std::map<std::string, date_io_t *> date_io_map;
@@ -1084,85 +1498,14 @@ void show_period_tokens(std::ostream& out, const string& arg)
{
date_parser_t::lexer_t lexer(arg.begin(), arg.end());
+ out << _("--- Period expression tokens ---") << std::endl;
+
date_parser_t::lexer_t::token_t token;
do {
token = lexer.next_token();
- out << _("token: ") << token.to_string() << std::endl;
+ out << token.to_string() << std::endl;
}
while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED);
}
-void analyze_period(std::ostream& out, const string& arg)
-{
- date_parser_t date_parser(arg);
-
- date_interval_t interval = date_parser.parse();
-
- out << _("global details => ") << std::endl << std::endl;
-
- if (interval.start)
- out << _(" start: ") << format_date(*interval.start) << std::endl;
- else
- out << _(" start: TODAY: ") << format_date(CURRENT_DATE()) << std::endl;
- if (interval.finish)
- out << _(" finish: ") << format_date(*interval.finish) << std::endl;
-
- if (interval.skip_duration)
- out << _(" skip: ") << *interval.skip_duration << std::endl;
- if (interval.factor)
- out << _(" factor: ") << interval.factor << std::endl;
- if (interval.duration)
- out << _("duration: ") << *interval.duration << std::endl;
-
- if (interval.find_period(interval.start ?
- *interval.start : CURRENT_DATE())) {
- out << std::endl
- << _("after finding first period => ") << std::endl
- << std::endl;
-
- if (interval.start)
- out << _(" start: ") << format_date(*interval.start) << std::endl;
- if (interval.finish)
- out << _(" finish: ") << format_date(*interval.finish) << std::endl;
-
- if (interval.skip_duration)
- out << _(" skip: ") << *interval.skip_duration << std::endl;
- if (interval.factor)
- out << _(" factor: ") << interval.factor << std::endl;
- if (interval.duration)
- out << _("duration: ") << *interval.duration << std::endl;
-
- out << std::endl;
-
- for (int i = 0; i < 20 && interval; i++, ++interval) {
- out << std::right;
- out.width(2);
-
- out << i << "): " << format_date(*interval.start);
- if (interval.end_of_duration)
- out << " -- " << format_date(*interval.inclusive_end());
- out << std::endl;
-
- if (! interval.skip_duration)
- break;
- }
- }
-}
-
} // namespace ledger
-
-#if defined(TIMES_HARNESS)
-
-int main(int argc, char *argv[])
-{
- if (argc > 1) {
- ledger::times_initialize();
- ledger::analyze_period(std::cout, argv[1]);
- ledger::times_shutdown();
- } else {
- std::cerr << "Usage: times <PERIOD>" << std::endl;
- }
- return 0;
-}
-
-#endif // TIMES_HARNESS
diff --git a/src/times.h b/src/times.h
index 94ae4752..d378dd81 100644
--- a/src/times.h
+++ b/src/times.h
@@ -146,12 +146,18 @@ struct date_traits_t
date_traits_t(bool _has_year = false,
bool _has_month = false,
bool _has_day = false)
- : has_year(_has_year), has_month(_has_month), has_day(_has_day) {}
-
+ : has_year(_has_year), has_month(_has_month), has_day(_has_day) {
+ TRACE_CTOR(date_traits_t, "bool, bool, bool");
+ }
date_traits_t(const date_traits_t& traits)
: has_year(traits.has_year),
has_month(traits.has_month),
- has_day(traits.has_day) {}
+ has_day(traits.has_day) {
+ TRACE_CTOR(date_traits_t, "copy");
+ }
+ ~date_traits_t() throw() {
+ TRACE_DTOR(date_traits_t);
+ }
date_traits_t& operator=(const date_traits_t& traits) {
has_year = traits.has_year;
@@ -181,6 +187,100 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
+struct date_duration_t
+{
+ enum skip_quantum_t {
+ DAYS, WEEKS, MONTHS, QUARTERS, YEARS
+ } quantum;
+ int length;
+
+ date_duration_t() : quantum(DAYS), length(0) {
+ TRACE_CTOR(date_duration_t, "");
+ }
+ date_duration_t(skip_quantum_t _quantum, int _length)
+ : quantum(_quantum), length(_length) {
+ TRACE_CTOR(date_duration_t, "skip_quantum_t, int");
+ }
+ date_duration_t(const date_duration_t& dur)
+ : quantum(dur.quantum), length(dur.length) {
+ TRACE_CTOR(date_duration_t, "copy");
+ }
+ ~date_duration_t() throw() {
+ TRACE_DTOR(date_duration_t);
+ }
+
+ date_t add(const date_t& date) const {
+ switch (quantum) {
+ case DAYS:
+ return date + gregorian::days(length);
+ case WEEKS:
+ return date + gregorian::weeks(length);
+ case MONTHS:
+ return date + gregorian::months(length);
+ case QUARTERS:
+ return date + gregorian::months(length * 3);
+ case YEARS:
+ return date + gregorian::years(length);
+ default:
+ assert(false); return date_t();
+ }
+ }
+
+ date_t subtract(const date_t& date) const {
+ switch (quantum) {
+ case DAYS:
+ return date - gregorian::days(length);
+ case WEEKS:
+ return date - gregorian::weeks(length);
+ case MONTHS:
+ return date - gregorian::months(length);
+ case QUARTERS:
+ return date - gregorian::months(length * 3);
+ case YEARS:
+ return date - gregorian::years(length);
+ default:
+ assert(false); return date_t();
+ }
+ }
+
+ string to_string() const {
+ std::ostringstream out;
+
+ out << length << ' ';
+
+ switch (quantum) {
+ case DAYS: out << "day"; break;
+ case WEEKS: out << "week"; break;
+ case MONTHS: out << "month"; break;
+ case QUARTERS: out << "quarter"; break;
+ case YEARS: out << "year"; break;
+ default:
+ assert(false);
+ break;
+ }
+
+ if (length > 1)
+ out << 's';
+
+ return out.str();
+ }
+
+ static date_t find_nearest(const date_t& date, skip_quantum_t skip);
+
+#if defined(HAVE_BOOST_SERIALIZATION)
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */) {
+ ar & quantum;
+ ar & length;
+ }
+#endif // HAVE_BOOST_SERIALIZATION
+};
+
class date_specifier_t
{
friend class date_parser_t;
@@ -200,15 +300,32 @@ class date_specifier_t
optional<day_of_week_type> wday;
public:
- date_specifier_t() {}
- date_specifier_t(const date_t& date, const date_traits_t& traits) {
- if (traits.has_year)
+ date_specifier_t(const optional<year_type>& _year = none,
+ const optional<month_type>& _month = none,
+ const optional<day_type>& _day = none,
+ const optional<day_of_week_type>& _wday = none)
+ : year(_year), month(_month), day(_day), wday(_wday) {
+ TRACE_CTOR(date_specifier_t,
+ "year_type, month_type, day_type, day_of_week_type");
+ }
+ date_specifier_t(const date_t& date,
+ const optional<date_traits_t>& traits = none) {
+ TRACE_CTOR(date_specifier_t, "date_t, date_traits_t");
+ if (! traits || traits->has_year)
year = date.year();
- if (traits.has_month)
+ if (! traits || traits->has_month)
month = date.month();
- if (traits.has_day)
+ if (! traits || traits->has_day)
day = date.day();
}
+ date_specifier_t(const date_specifier_t& other)
+ : year(other.year), month(other.month),
+ day(other.day), wday(other.wday) {
+ TRACE_CTOR(date_specifier_t, "copy");
+ }
+ ~date_specifier_t() throw() {
+ TRACE_DTOR(date_specifier_t);
+ }
date_t begin(const optional_year& current_year = none) const;
date_t end(const optional_year& current_year = none) const;
@@ -218,6 +335,32 @@ public:
return date >= begin(current_year) && date < end(current_year);
}
+ optional<date_duration_t> implied_duration() const {
+ if (day || wday)
+ return date_duration_t(date_duration_t::DAYS, 1);
+ else if (month)
+ return date_duration_t(date_duration_t::MONTHS, 1);
+ else if (year)
+ return date_duration_t(date_duration_t::YEARS, 1);
+ else
+ return none;
+ }
+
+ string to_string() const {
+ std::ostringstream out;
+
+ if (year)
+ out << " year " << *year;
+ if (month)
+ out << " month " << *month;
+ if (day)
+ out << " day " << *day;
+ if (wday)
+ out << " wday " << *wday;
+
+ return out.str();
+ }
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
@@ -241,7 +384,24 @@ class date_range_t
optional<date_specifier_t> range_begin;
optional<date_specifier_t> range_end;
+ bool end_inclusive;
+
public:
+ date_range_t(const optional<date_specifier_t>& _range_begin = none,
+ const optional<date_specifier_t>& _range_end = none)
+ : range_begin(_range_begin), range_end(_range_end),
+ end_inclusive(false) {
+ TRACE_CTOR(date_range_t, "date_specifier_t, date_specifier_t");
+ }
+ date_range_t(const date_range_t& other)
+ : range_begin(other.range_begin), range_end(other.range_end),
+ end_inclusive(other.end_inclusive) {
+ TRACE_CTOR(date_range_t, "date_range_t");
+ }
+ ~date_range_t() throw() {
+ TRACE_DTOR(date_range_t);
+ }
+
optional<date_t> begin(const optional_year& current_year = none) const {
if (range_begin)
return range_begin->begin(current_year);
@@ -249,10 +409,14 @@ public:
return none;
}
optional<date_t> end(const optional_year& current_year = none) const {
- if (range_end)
- return range_end->end(current_year);
- else
+ if (range_end) {
+ if (end_inclusive)
+ return range_end->end(current_year);
+ else
+ return range_end->begin(current_year);
+ } else {
return none;
+ }
}
bool is_within(const date_t& date,
@@ -264,70 +428,15 @@ public:
return after_begin && before_end;
}
-#if defined(HAVE_BOOST_SERIALIZATION)
-private:
- /** Serialization. */
-
- friend class boost::serialization::access;
-
- template<class Archive>
- void serialize(Archive& ar, const unsigned int /* version */) {
- ar & range_begin;
- ar & range_end;
- }
-#endif // HAVE_BOOST_SERIALIZATION
-};
-
-struct date_duration_t
-{
- enum skip_quantum_t {
- DAYS, WEEKS, MONTHS, YEARS
- } quantum;
- int length;
+ string to_string() const {
+ std::ostringstream out;
- date_duration_t() : quantum(DAYS), length(0) {
- TRACE_CTOR(date_duration_t, "");
- }
- date_duration_t(skip_quantum_t _quantum, int _length)
- : quantum(_quantum), length(_length) {
- TRACE_CTOR(date_duration_t, "skip_quantum_t, int");
- }
- date_duration_t(const date_duration_t& dur)
- : quantum(dur.quantum), length(dur.length) {
- TRACE_CTOR(date_duration_t, "copy");
- }
- ~date_duration_t() throw() {
- TRACE_DTOR(date_duration_t);
- }
-
- date_t add(const date_t& date) const {
- switch (quantum) {
- case DAYS:
- return date + gregorian::days(length);
- case WEEKS:
- return date + gregorian::weeks(length);
- case MONTHS:
- return date + gregorian::months(length);
- case YEARS:
- return date + gregorian::years(length);
- default:
- assert(false); return date_t();
- }
- }
-
- date_t subtract(const date_t& date) const {
- switch (quantum) {
- case DAYS:
- return date - gregorian::days(length);
- case WEEKS:
- return date - gregorian::weeks(length);
- case MONTHS:
- return date - gregorian::months(length);
- case YEARS:
- return date - gregorian::years(length);
- default:
- assert(false); return date_t();
- }
+ if (range_begin)
+ out << "from" << range_begin->to_string();
+ if (range_end)
+ out << " to" << range_end->to_string();
+
+ return out.str();
}
#if defined(HAVE_BOOST_SERIALIZATION)
@@ -338,8 +447,8 @@ private:
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
- ar & quantum;
- ar & length;
+ ar & range_begin;
+ ar & range_end;
}
#endif // HAVE_BOOST_SERIALIZATION
};
@@ -351,6 +460,25 @@ class date_specifier_or_range_t
value_type specifier_or_range;
public:
+ date_specifier_or_range_t() {
+ TRACE_CTOR(date_specifier_or_range_t, "");
+ }
+ date_specifier_or_range_t(const date_specifier_or_range_t& other)
+ : specifier_or_range(other.specifier_or_range) {
+ TRACE_CTOR(date_specifier_or_range_t, "copy");
+ }
+ date_specifier_or_range_t(const date_specifier_t& specifier)
+ : specifier_or_range(specifier) {
+ TRACE_CTOR(date_specifier_or_range_t, "date_specifier_t");
+ }
+ date_specifier_or_range_t(const date_range_t& range)
+ : specifier_or_range(range) {
+ TRACE_CTOR(date_specifier_or_range_t, "date_range_t");
+ }
+ ~date_specifier_or_range_t() throw() {
+ TRACE_DTOR(date_specifier_or_range_t);
+ }
+
optional<date_t> begin(const optional_year& current_year = none) const {
if (specifier_or_range.type() == typeid(date_specifier_t))
return boost::get<date_specifier_t>(specifier_or_range).begin(current_year);
@@ -368,6 +496,18 @@ public:
return none;
}
+
+ string to_string() const {
+ std::ostringstream out;
+
+ if (specifier_or_range.type() == typeid(date_specifier_t))
+ out << "in" << boost::get<date_specifier_t>(specifier_or_range).to_string();
+ else if (specifier_or_range.type() == typeid(date_range_t))
+ out << boost::get<date_range_t>(specifier_or_range).to_string();
+
+ return out.str();
+ }
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
@@ -439,17 +579,12 @@ public:
return finish ? finish : (range ? range->end(current_year) : none);
}
- void parse(std::istream& in);
-
- void parse(const string& str) {
- std::istringstream in(str);
- parse(in);
- }
+ void parse(const string& str);
- void resolve_end();
- void stabilize(const optional<date_t>& date = none);
+ void resolve_end();
+ void stabilize(const optional<date_t>& date = none);
- bool is_valid() const {
+ bool is_valid() const {
return start;
}
@@ -458,6 +593,12 @@ public:
containing date, or false if no such period can be found. */
bool find_period(const date_t& date);
+ optional<date_t> inclusive_skip_end() const {
+ if (skip_duration)
+ return skip_duration->add(*start) - gregorian::days(1);
+ else
+ return none;
+ }
optional<date_t> inclusive_end() const {
if (end_of_duration)
return *end_of_duration - gregorian::days(1);
@@ -467,6 +608,8 @@ public:
date_interval_t& operator++();
+ void dump(std::ostream& out);
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
@@ -488,197 +631,10 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
-class date_parser_t
-{
- friend void show_period_tokens(std::ostream& out, const string& arg);
-
- class lexer_t
- {
- friend class date_parser_t;
-
- string::const_iterator begin;
- string::const_iterator end;
-
- public:
- struct token_t
- {
- enum kind_t {
- UNKNOWN,
-
- TOK_DATE,
- TOK_INT,
- TOK_SLASH,
- TOK_DASH,
- TOK_DOT,
-
- TOK_A_YEAR,
- TOK_A_MONTH,
- TOK_A_DAY,
- TOK_A_WDAY,
-
- TOK_SINCE,
- TOK_UNTIL,
- TOK_IN,
- TOK_THIS,
- TOK_NEXT,
- TOK_LAST,
-
- TOK_YEAR,
- TOK_QUARTER,
- TOK_MONTH,
- TOK_WEEK,
- TOK_DAY,
-
- TOK_YEARLY,
- TOK_QUARTERLY,
- TOK_BIMONTHLY,
- TOK_MONTHLY,
- TOK_BIWEEKLY,
- TOK_WEEKLY,
- TOK_DAILY,
-
- TOK_YEARS,
- TOK_QUARTERS,
- TOK_MONTHS,
- TOK_WEEKS,
- TOK_DAYS,
-
- END_REACHED
-
- } kind;
-
- typedef variant<string, int, date_specifier_t> content_t;
-
- optional<content_t> value;
-
- explicit token_t(kind_t _kind = UNKNOWN,
- const optional<content_t>& _value = none)
- : kind(_kind), value(_value) {
- TRACE_CTOR(date_parser_t::lexer_t::token_t, "");
- }
- token_t(const token_t& tok)
- : kind(tok.kind), value(tok.value) {
- TRACE_CTOR(date_parser_t::lexer_t::token_t, "copy");
- }
- ~token_t() throw() {
- TRACE_DTOR(date_parser_t::lexer_t::token_t);
- }
-
- token_t& operator=(const token_t& tok) {
- if (this != &tok) {
- kind = tok.kind;
- value = tok.value;
- }
- return *this;
- }
-
- operator bool() const {
- return kind != END_REACHED;
- }
-
- string to_string() const {
- switch (kind) {
- case UNKNOWN: return "UNKNOWN";
- case TOK_DATE: return "TOK_DATE";
- case TOK_INT: return "TOK_INT";
- case TOK_SLASH: return "TOK_SLASH";
- case TOK_DASH: return "TOK_DASH";
- case TOK_DOT: return "TOK_DOT";
- case TOK_A_YEAR: return "TOK_A_YEAR";
- case TOK_A_MONTH: return "TOK_A_MONTH";
- case TOK_A_DAY: return "TOK_A_DAY";
- case TOK_A_WDAY: return "TOK_A_WDAY";
- case TOK_SINCE: return "TOK_SINCE";
- case TOK_UNTIL: return "TOK_UNTIL";
- case TOK_IN: return "TOK_IN";
- case TOK_THIS: return "TOK_THIS";
- case TOK_NEXT: return "TOK_NEXT";
- case TOK_LAST: return "TOK_LAST";
- case TOK_YEAR: return "TOK_YEAR";
- case TOK_QUARTER: return "TOK_QUARTER";
- case TOK_MONTH: return "TOK_MONTH";
- case TOK_WEEK: return "TOK_WEEK";
- case TOK_DAY: return "TOK_DAY";
- case TOK_YEARLY: return "TOK_YEARLY";
- case TOK_QUARTERLY: return "TOK_QUARTERLY";
- case TOK_BIMONTHLY: return "TOK_BIMONTHLY";
- case TOK_MONTHLY: return "TOK_MONTHLY";
- case TOK_BIWEEKLY: return "TOK_BIWEEKLY";
- case TOK_WEEKLY: return "TOK_WEEKLY";
- case TOK_DAILY: return "TOK_DAILY";
- case TOK_YEARS: return "TOK_YEARS";
- case TOK_QUARTERS: return "TOK_QUARTERS";
- case TOK_MONTHS: return "TOK_MONTHS";
- case TOK_WEEKS: return "TOK_WEEKS";
- case TOK_DAYS: return "TOK_DAYS";
- case END_REACHED: return "END_REACHED";
- }
- assert(false);
- return empty_string;
- }
-
- void unexpected();
- static void expected(char wanted, char c = '\0');
- };
-
- token_t token_cache;
-
- lexer_t(string::const_iterator _begin,
- string::const_iterator _end)
- : begin(_begin), end(_end)
- {
- TRACE_CTOR(date_parser_t::lexer_t, "");
- }
- lexer_t(const lexer_t& lexer)
- : begin(lexer.begin), end(lexer.end),
- token_cache(lexer.token_cache)
- {
- TRACE_CTOR(date_parser_t::lexer_t, "copy");
- }
- ~lexer_t() throw() {
- TRACE_DTOR(date_parser_t::lexer_t);
- }
-
- token_t next_token();
- void push_token(token_t tok) {
- assert(token_cache.kind == token_t::UNKNOWN);
- token_cache = tok;
- }
- token_t peek_token() {
- if (token_cache.kind == token_t::UNKNOWN)
- token_cache = next_token();
- return token_cache;
- }
- };
-
- string arg;
- lexer_t lexer;
-
- date_interval_t parse_date_expr();
-
-public:
- date_parser_t(const string& _arg)
- : arg(_arg), lexer(arg.begin(), arg.end()) {
- TRACE_CTOR(date_parser_t, "");
- }
- date_parser_t(const date_parser_t& parser)
- : arg(parser.arg), lexer(parser.lexer) {
- TRACE_CTOR(date_parser_t, "copy");
- }
- ~date_parser_t() throw() {
- TRACE_DTOR(date_parser_t);
- }
-
- date_interval_t parse() {
- return date_interval_t();
- }
-};
-
void times_initialize();
void times_shutdown();
void show_period_tokens(std::ostream& out, const string& arg);
-void analyze_period(std::ostream& out, const string& arg);
std::ostream& operator<<(std::ostream& out, const date_duration_t& duration);