diff options
author | John Wiegley <johnw@newartisans.com> | 2009-11-18 04:11:14 -0500 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-11-18 04:11:14 -0500 |
commit | 7fe369eb492f737f570d0ccf4aaf5db68f900279 (patch) | |
tree | ec499b8dd3c7c9286e9fa71d3e7a8e2a94c21d83 | |
parent | fe9af7ace7a9b25a3eefe481b6b7a38ad2f758ed (diff) | |
download | fork-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.cc | 53 | ||||
-rw-r--r-- | src/times.cc | 959 | ||||
-rw-r--r-- | src/times.h | 500 |
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); |