From c28d828d8e24fe637a74674bedc9bc0cbdabca1c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 17 Nov 2009 21:30:07 -0500 Subject: Renamed date_interval_t::end to finish --- src/report.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/report.cc') diff --git a/src/report.cc b/src/report.cc index 267a4a3d..efe162e2 100644 --- a/src/report.cc +++ b/src/report.cc @@ -125,9 +125,9 @@ void report_t::normalize_options(const string& verb) "date>=[" + to_iso_extended_string(*interval.start) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } - if (! HANDLED(end_) && interval.end) { + if (! HANDLED(end_) && interval.finish) { string predicate = - "date<[" + to_iso_extended_string(*interval.end) + "]"; + "date<[" + to_iso_extended_string(*interval.finish) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } -- cgit v1.2.3 From e4b3f0bb3a74b799f0f67d8b2f1efeedad5e2021 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 18 Nov 2009 05:45:48 -0500 Subject: The new period parser is passing all tests --- src/archive.cc | 2 +- src/filters.cc | 14 ++- src/precmd.cc | 2 +- src/report.cc | 13 ++- src/report.h | 31 ++--- src/times.cc | 275 ++++++++++++++++++++++++++++----------------- src/times.h | 18 +-- src/token.cc | 5 +- test/regress/7F3650FD.test | 69 ++++++++++-- test/regress/BBFA1759.test | 14 ++- 10 files changed, 283 insertions(+), 160 deletions(-) (limited to 'src/report.cc') diff --git a/src/archive.cc b/src/archive.cc index d36712ca..7306f8d3 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -43,7 +43,7 @@ #include "xact.h" #define LEDGER_MAGIC 0x4c454447 -#define ARCHIVE_VERSION 0x03000005 +#define ARCHIVE_VERSION 0x03000006 //BOOST_IS_ABSTRACT(ledger::scope_t) BOOST_CLASS_EXPORT(ledger::scope_t) diff --git a/src/filters.cc b/src/filters.cc index 5a92421b..47e7f66d 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -515,12 +515,14 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, optional range_start = interval ? interval->start : none; optional range_finish = interval ? interval->inclusive_end() : none; - foreach (post_t * post, component_posts) { - date_t date = post->date(); - if (! range_start || date < *range_start) - range_start = date; - if (! range_finish || date > *range_finish) - range_finish = date; + if (! range_start || ! range_finish) { + foreach (post_t * post, component_posts) { + date_t date = post->date(); + if (! range_start || date < *range_start) + range_start = date; + if (! range_finish || date > *range_finish) + range_finish = date; + } } component_posts.clear(); diff --git a/src/precmd.cc b/src/precmd.cc index 0e024a39..516b90dd 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -169,7 +169,7 @@ value_t period_command(call_scope_t& args) out << std::endl; date_interval_t interval(arg); - interval.dump(out); + interval.dump(out, report.session.current_year); return NULL_VALUE; } diff --git a/src/report.cc b/src/report.cc index efe162e2..78ed05c0 100644 --- a/src/report.cc +++ b/src/report.cc @@ -120,14 +120,15 @@ void report_t::normalize_options(const string& verb) date_interval_t interval(HANDLER(period_).str()); - if (! HANDLED(begin_) && interval.start) { - string predicate = - "date>=[" + to_iso_extended_string(*interval.start) + "]"; + optional begin = interval.begin(session.current_year); + optional end = interval.end(session.current_year); + + if (! HANDLED(begin_) && begin) { + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } - if (! HANDLED(end_) && interval.finish) { - string predicate = - "date<[" + to_iso_extended_string(*interval.finish) + "]"; + if (! HANDLED(end_) && end) { + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; HANDLER(limit_).on(string("?normalize"), predicate); } diff --git a/src/report.h b/src/report.h index 93f6e9e0..0b1baff1 100644 --- a/src/report.h +++ b/src/report.h @@ -50,6 +50,7 @@ #include "option.h" #include "commodity.h" #include "annotate.h" +#include "session.h" #include "format.h" namespace ledger { @@ -352,7 +353,7 @@ public: set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION(report_t, amount_data); + OPTION(report_t, amount_data); // -j OPTION(report_t, anon); OPTION_(report_t, average, DO() { // -A @@ -377,14 +378,14 @@ public: }); OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args[1].to_string()); - if (! interval.start) + date_interval_t interval(args[1].to_string()); + optional begin = interval.begin(parent->session.current_year); + if (! begin) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") << args[1].to_string()); - string predicate = - "date>=[" + to_iso_extended_string(*interval.start) + "]"; + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; parent->HANDLER(limit_).on(string("--begin"), predicate); }); @@ -524,17 +525,19 @@ public: OPTION(report_t, empty); // -E OPTION_(report_t, end_, DO_(args) { // -e - date_interval_t interval(args[1].to_string()); - if (! interval.start) + date_interval_t interval(args[1].to_string()); + // Use begin() here so that if the user says --end=2008, we end on + // 2008/01/01 instead of 2009/01/01 (which is what end() would return). + optional end = interval.begin(parent->session.current_year); + if (! end) throw_(std::invalid_argument, _("Could not determine end of period '%1'") << args[1].to_string()); - string predicate = - "date<[" + to_iso_extended_string(*interval.start) + "]"; + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; parent->HANDLER(limit_).on(string("--end"), predicate); - parent->terminus = datetime_t(*interval.start); + parent->terminus = datetime_t(*end); }); OPTION(report_t, equity); @@ -622,11 +625,13 @@ public: OPTION_(report_t, now_, DO_(args) { date_interval_t interval(args[1].to_string()); - if (! interval.start) + optional begin = interval.begin(parent->session.current_year); + if (! begin) throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") << args[1].to_string()); - ledger::epoch = datetime_t(*interval.start); + ledger::epoch = parent->terminus = datetime_t(*begin); + parent->session.current_year = ledger::epoch->date().year(); }); OPTION__ @@ -844,7 +849,7 @@ public: set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION(report_t, total_data); + OPTION(report_t, total_data); // -J OPTION_(report_t, truncate_, DO_(args) { string style(args[1].to_string()); diff --git a/src/times.cc b/src/times.cc index 97fc3ab8..646f2a84 100644 --- a/src/times.cc +++ b/src/times.cc @@ -434,6 +434,7 @@ class date_parser_t } kind; typedef variant value; explicit token_t(kind_t _kind = UNKNOWN, - const optional& _value = none) + const optional& _value = + content_t(empty_string)) : kind(_kind), value(_value) { TRACE_CTOR(date_parser_t::lexer_t::token_t, ""); } @@ -467,47 +469,110 @@ class date_parser_t } string to_string() const { + std::ostringstream out; + + switch (kind) { + case UNKNOWN: + out << boost::get(*value); + break; + case TOK_DATE: + return boost::get(*value).to_string(); + case TOK_INT: + out << boost::get(*value); + break; + case TOK_SLASH: return "/"; + case TOK_DASH: return "-"; + case TOK_DOT: return "."; + case TOK_A_YEAR: + out << boost::get(*value); + break; + case TOK_A_MONTH: + out << date_specifier_t::month_type + (boost::get(*value)); + break; + case TOK_A_WDAY: + out << date_specifier_t::day_of_week_type + (boost::get(*value)); + break; + case TOK_SINCE: return "since"; + case TOK_UNTIL: return "until"; + case TOK_IN: return "in"; + case TOK_THIS: return "this"; + case TOK_NEXT: return "next"; + case TOK_LAST: return "last"; + case TOK_EVERY: return "every"; + case TOK_TODAY: return "today"; + case TOK_TOMORROW: return "tomorrow"; + case TOK_YESTERDAY: return "yesterday"; + case TOK_YEAR: return "year"; + case TOK_QUARTER: return "quarter"; + case TOK_MONTH: return "month"; + case TOK_WEEK: return "week"; + case TOK_DAY: return "day"; + case TOK_YEARLY: return "yearly"; + case TOK_QUARTERLY: return "quarterly"; + case TOK_BIMONTHLY: return "bimonthly"; + case TOK_MONTHLY: return "monthly"; + case TOK_BIWEEKLY: return "biweekly"; + case TOK_WEEKLY: return "weekly"; + case TOK_DAILY: return "daily"; + case TOK_YEARS: return "years"; + case TOK_QUARTERS: return "quarters"; + case TOK_MONTHS: return "months"; + case TOK_WEEKS: return "weeks"; + case TOK_DAYS: return "days"; + case END_REACHED: return ""; + default: + assert(false); + return empty_string; + } + + return out.str(); + } + + void dump(std::ostream& out) 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"; + case UNKNOWN: out << "UNKNOWN"; break; + case TOK_DATE: out << "TOK_DATE"; break; + case TOK_INT: out << "TOK_INT"; break; + case TOK_SLASH: out << "TOK_SLASH"; break; + case TOK_DASH: out << "TOK_DASH"; break; + case TOK_DOT: out << "TOK_DOT"; break; + case TOK_A_YEAR: out << "TOK_A_YEAR"; break; + case TOK_A_MONTH: out << "TOK_A_MONTH"; break; + case TOK_A_WDAY: out << "TOK_A_WDAY"; break; + case TOK_SINCE: out << "TOK_SINCE"; break; + case TOK_UNTIL: out << "TOK_UNTIL"; break; + case TOK_IN: out << "TOK_IN"; break; + case TOK_THIS: out << "TOK_THIS"; break; + case TOK_NEXT: out << "TOK_NEXT"; break; + case TOK_LAST: out << "TOK_LAST"; break; + case TOK_EVERY: out << "TOK_EVERY"; break; + case TOK_TODAY: out << "TOK_TODAY"; break; + case TOK_TOMORROW: out << "TOK_TOMORROW"; break; + case TOK_YESTERDAY: out << "TOK_YESTERDAY"; break; + case TOK_YEAR: out << "TOK_YEAR"; break; + case TOK_QUARTER: out << "TOK_QUARTER"; break; + case TOK_MONTH: out << "TOK_MONTH"; break; + case TOK_WEEK: out << "TOK_WEEK"; break; + case TOK_DAY: out << "TOK_DAY"; break; + case TOK_YEARLY: out << "TOK_YEARLY"; break; + case TOK_QUARTERLY: out << "TOK_QUARTERLY"; break; + case TOK_BIMONTHLY: out << "TOK_BIMONTHLY"; break; + case TOK_MONTHLY: out << "TOK_MONTHLY"; break; + case TOK_BIWEEKLY: out << "TOK_BIWEEKLY"; break; + case TOK_WEEKLY: out << "TOK_WEEKLY"; break; + case TOK_DAILY: out << "TOK_DAILY"; break; + case TOK_YEARS: out << "TOK_YEARS"; break; + case TOK_QUARTERS: out << "TOK_QUARTERS"; break; + case TOK_MONTHS: out << "TOK_MONTHS"; break; + case TOK_WEEKS: out << "TOK_WEEKS"; break; + case TOK_DAYS: out << "TOK_DAYS"; break; + case END_REACHED: out << "END_REACHED"; break; + default: + assert(false); + break; } - assert(false); - return empty_string; } void unexpected(); @@ -753,7 +818,9 @@ date_interval_t date_parser_t::parse() inclusion_specifier = date_specifier_t(static_cast(temp.year()), temp.month()); +#if 0 period.duration = date_duration_t(date_duration_t::QUARTERS, 1); +#endif break; } @@ -771,7 +838,9 @@ date_interval_t date_parser_t::parse() date_duration_t::find_nearest(today, date_duration_t::WEEKS); temp += gregorian::days(7 * adjust); inclusion_specifier = date_specifier_t(today); +#if 0 period.duration = date_duration_t(date_duration_t::WEEKS, 1); +#endif break; } @@ -806,19 +875,19 @@ date_interval_t date_parser_t::parse() 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); + period.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); + period.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); + period.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); + period.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); + period.duration = date_duration_t(date_duration_t::DAYS, quantity); break; default: tok.unexpected(); @@ -827,19 +896,19 @@ date_interval_t date_parser_t::parse() } else { switch (tok.kind) { case lexer_t::token_t::TOK_YEAR: - period.skip_duration = date_duration_t(date_duration_t::YEARS, 1); + period.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); + period.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); + period.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); + period.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); + period.duration = date_duration_t(date_duration_t::DAYS, 1); break; default: tok.unexpected(); @@ -849,25 +918,25 @@ date_interval_t date_parser_t::parse() break; case lexer_t::token_t::TOK_YEARLY: - period.skip_duration = date_duration_t(date_duration_t::YEARS, 1); + period.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); + period.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); + period.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); + period.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); + period.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); + period.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); + period.duration = date_duration_t(date_duration_t::DAYS, 1); break; default: @@ -876,8 +945,10 @@ date_interval_t date_parser_t::parse() } } +#if 0 if (! period.duration && inclusion_specifier) period.duration = inclusion_specifier->implied_duration(); +#endif if (since_specifier || until_specifier) { date_range_t range(since_specifier, until_specifier); @@ -915,16 +986,9 @@ void date_interval_t::resolve_end() "stabilize: end_of_duration reset to end: " << *end_of_duration); } - if (! skip_duration) { - skip_duration = duration; - DEBUG("times.interval", - "stabilize: skip_duration set to: " << *skip_duration); - } - if (start && ! next) { - next = skip_duration->add(*start); - DEBUG("times.interval", - "stabilize: next set to: " << *next); + next = end_of_duration; + DEBUG("times.interval", "stabilize: next set to: " << *next); } } @@ -988,10 +1052,10 @@ void date_interval_t::stabilize(const optional& date) #if defined(DEBUG_ON) if (initial_start) DEBUG("times.interval", - "stabilize: initial_start = " << *initial_start); + "stabilize: initial_start = " << *initial_start); if (initial_finish) DEBUG("times.interval", - "stabilize: initial_finish = " << *initial_finish); + "stabilize: initial_finish = " << *initial_finish); #endif date_t when = start ? *start : *date; @@ -1024,9 +1088,10 @@ void date_interval_t::stabilize(const optional& date) } } - DEBUG("times.interval", "stabilize: final start date = " << *start); + DEBUG("times.interval", "stabilize: proposed start date = " << *start); if (initial_start && (! start || *start < *initial_start)) { + // Using the discovered start, find the end of the period resolve_end(); start = initial_start; @@ -1036,14 +1101,20 @@ void date_interval_t::stabilize(const optional& date) finish = initial_finish; DEBUG("times.interval", "stabilize: finish reset to initial finish"); } + + if (start) + DEBUG("times.interval", "stabilize: final start = " << *start); + if (finish) + DEBUG("times.interval", "stabilize: final finish = " << *finish); } else if (range) { - start = range->begin(); - finish = range->end(); - - if (start && finish) - duration = date_duration_t(date_duration_t::DAYS, - static_cast((*finish - *start).days())); + if (date) { + start = range->begin(date->year()); + finish = range->end(date->year()); + } else { + start = range->begin(); + finish = range->end(); + } } aligned = true; } @@ -1116,7 +1187,7 @@ bool date_interval_t::find_period(const date_t& date) return true; } - scan = skip_duration->add(scan); + scan = duration->add(scan); end_of_scan = duration->add(scan); } @@ -1130,7 +1201,7 @@ date_interval_t& date_interval_t::operator++() stabilize(); - if (! skip_duration || ! duration) + if (! duration) throw_(date_error, _("Cannot increment a date interval without a duration")); @@ -1140,10 +1211,8 @@ date_interval_t& date_interval_t::operator++() start = none; } else { start = *next; - end_of_duration = duration->add(*start); } - next = none; resolve_end(); @@ -1151,7 +1220,7 @@ date_interval_t& date_interval_t::operator++() return *this; } -void date_interval_t::dump(std::ostream& out) +void date_interval_t::dump(std::ostream& out, optional_year current_year) { out << _("--- Before stabilization ---") << std::endl; @@ -1162,14 +1231,10 @@ void date_interval_t::dump(std::ostream& out) 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()); + stabilize(begin(current_year)); out << std::endl << _("--- After stabilization ---") << std::endl; @@ -1181,27 +1246,30 @@ void date_interval_t::dump(std::ostream& out) 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) { + date_t last_date; + + for (int i = 0; i < 20 && *this; ++i, ++*this) { out << std::right; out.width(2); + if (! last_date.is_not_a_date() && last_date == *start) + break; + out << (i + 1) << ": " << format_date(*start); - if (skip_duration) - out << " -- " << format_date(*inclusive_skip_end()); + if (duration) + out << " -- " << format_date(*inclusive_end()); out << std::endl; - if (! skip_duration) + if (! duration) break; + + last_date = *start; } } @@ -1268,6 +1336,8 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() token_t::content_t(lexical_cast(term))); } else if (std::isalpha(term[0])) { + to_lower(term); + if (optional month = string_to_month_of_year(term)) { return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month)); @@ -1339,20 +1409,20 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() token_t::expected('\0', *begin); } - return token_t(token_t::UNKNOWN); + return token_t(token_t::UNKNOWN, token_t::content_t(term)); } void date_parser_t::lexer_t::token_t::unexpected() { - kind_t prev_kind = kind; - - kind = UNKNOWN; - - switch (prev_kind) { + switch (kind) { case END_REACHED: + kind = UNKNOWN; throw_(date_error, _("Unexpected end of expression")); - default: - throw_(date_error, _("Unexpected token '%1'") << to_string()); + default: { + string desc = to_string(); + kind = UNKNOWN; + throw_(date_error, _("Unexpected date period token '%1'") << desc); + } } } @@ -1503,7 +1573,8 @@ void show_period_tokens(std::ostream& out, const string& arg) date_parser_t::lexer_t::token_t token; do { token = lexer.next_token(); - out << token.to_string() << std::endl; + token.dump(out); + out << ": " << token.to_string() << std::endl; } while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED); } diff --git a/src/times.h b/src/times.h index d378dd81..1c9d812e 100644 --- a/src/times.h +++ b/src/times.h @@ -534,16 +534,14 @@ public: optional start; // the real start, after adjustment optional finish; // the real end, likewise bool aligned; - optional skip_duration; - std::size_t factor; optional next; optional duration; optional end_of_duration; - explicit date_interval_t() : aligned(false), factor(1) { + explicit date_interval_t() : aligned(false) { TRACE_CTOR(date_interval_t, ""); } - date_interval_t(const string& str) : aligned(false), factor(1) { + date_interval_t(const string& str) : aligned(false) { TRACE_CTOR(date_interval_t, "const string&"); parse(str); } @@ -552,8 +550,6 @@ public: start(other.start), finish(other.finish), aligned(other.aligned), - skip_duration(other.skip_duration), - factor(other.factor), next(other.next), duration(other.duration), end_of_duration(other.end_of_duration) { @@ -593,12 +589,6 @@ public: containing date, or false if no such period can be found. */ bool find_period(const date_t& date); - optional inclusive_skip_end() const { - if (skip_duration) - return skip_duration->add(*start) - gregorian::days(1); - else - return none; - } optional inclusive_end() const { if (end_of_duration) return *end_of_duration - gregorian::days(1); @@ -608,7 +598,7 @@ public: date_interval_t& operator++(); - void dump(std::ostream& out); + void dump(std::ostream& out, optional_year current_year = none); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -622,8 +612,6 @@ private: ar & start; ar & finish; ar & aligned; - ar & skip_duration; - ar & factor; ar & next; ar & duration; ar & end_of_duration; diff --git a/src/token.cc b/src/token.cc index 3df072a7..81c54a82 100644 --- a/src/token.cc +++ b/src/token.cc @@ -206,11 +206,12 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) length++; date_interval_t timespan(buf); - if (! timespan) + optional begin = timespan.begin(); + if (! begin) throw_(parse_error, _("Date specifier does not refer to a starting date")); kind = VALUE; - value = *timespan.start; + value = *begin; break; } diff --git a/test/regress/7F3650FD.test b/test/regress/7F3650FD.test index dce5233f..f7154eb8 100644 --- a/test/regress/7F3650FD.test +++ b/test/regress/7F3650FD.test @@ -1,50 +1,95 @@ period --now=2010/11/01 12/01 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: month Dec day 1 +END_REACHED: - start: 09-Dec-01 - finish: 09-Dec-02 - factor: 1 +--- Before stabilization --- + range: in month Dec day 1 + +--- After stabilization --- + range: in month Dec day 1 + start: 10-Dec-01 + finish: 10-Dec-02 + +--- Sample dates in range (max. 20) --- + 1: 10-Dec-01 >>>2 === 0 period --now=2010/11/01 10/01 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: month Oct day 1 +END_REACHED: + +--- Before stabilization --- + range: in month Oct day 1 +--- After stabilization --- + range: in month Oct day 1 start: 10-Oct-01 finish: 10-Oct-02 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 10-Oct-01 >>>2 === 0 period --now=2010/11/01 2009/10 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: year 2009 month Oct +END_REACHED: + +--- Before stabilization --- + range: in year 2009 month Oct +--- After stabilization --- + range: in year 2009 month Oct start: 09-Oct-01 finish: 09-Nov-01 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 09-Oct-01 >>>2 === 0 period --now=2010/11/01 2009/10/01 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_DATE: year 2009 month Oct day 1 +END_REACHED: + +--- Before stabilization --- + range: in year 2009 month Oct day 1 +--- After stabilization --- + range: in year 2009 month Oct day 1 start: 09-Oct-01 finish: 09-Oct-02 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 09-Oct-01 >>>2 === 0 period --now=2010/11/01 2009 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_A_YEAR: 2009 +END_REACHED: +--- Before stabilization --- + range: in year 2009 + +--- After stabilization --- + range: in year 2009 start: 09-Jan-01 finish: 10-Jan-01 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 09-Jan-01 >>>2 === 0 diff --git a/test/regress/BBFA1759.test b/test/regress/BBFA1759.test index b109f868..cd5990fc 100644 --- a/test/regress/BBFA1759.test +++ b/test/regress/BBFA1759.test @@ -1,10 +1,20 @@ period june 2008 <<< >>>1 -global details => +--- Period expression tokens --- +TOK_A_MONTH: Jun +TOK_A_YEAR: 2008 +END_REACHED: +--- Before stabilization --- + range: in year 2008 month Jun + +--- After stabilization --- + range: in year 2008 month Jun start: 08-Jun-01 finish: 08-Jul-01 - factor: 1 + +--- Sample dates in range (max. 20) --- + 1: 08-Jun-01 >>>2 === 0 -- cgit v1.2.3 From e27ba3e1ff77537ade8287f1d66cf6fe626c8962 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 18 Nov 2009 23:15:22 -0500 Subject: It's OK for a report query to be empty It's always possible the user only specified a display predicate. --- src/precmd.cc | 24 ++++++++++-------------- src/report.cc | 24 ++++++++++-------------- 2 files changed, 20 insertions(+), 28 deletions(-) (limited to 'src/report.cc') diff --git a/src/precmd.cc b/src/precmd.cc index 516b90dd..31249016 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -184,14 +184,12 @@ value_t args_command(call_scope_t& args) out << std::endl << std::endl; query_t query(args.value(), report.what_to_keep()); - if (! query) - throw_(std::runtime_error, - _("Invalid query predicate: %1") << join_args(args)); + if (query) { + call_scope_t sub_args(static_cast(args)); + sub_args.push_back(string_value(query.text())); - call_scope_t sub_args(static_cast(args)); - sub_args.push_back(string_value(query.text())); - - parse_command(sub_args); + parse_command(sub_args); + } if (query.tokens_remaining()) { out << std::endl << _("====== Display predicate ======") @@ -199,14 +197,12 @@ value_t args_command(call_scope_t& args) query.parse_again(); - if (! query) - throw_(std::runtime_error, - _("Invalid display predicate: %1") << join_args(args)); + if (query) { + call_scope_t disp_sub_args(static_cast(args)); + disp_sub_args.push_back(string_value(query.text())); - call_scope_t disp_sub_args(static_cast(args)); - disp_sub_args.push_back(string_value(query.text())); - - parse_command(disp_sub_args); + parse_command(disp_sub_args); + } } return NULL_VALUE; diff --git a/src/report.cc b/src/report.cc index 78ed05c0..ed3238bc 100644 --- a/src/report.cc +++ b/src/report.cc @@ -222,25 +222,21 @@ void report_t::normalize_options(const string& verb) void report_t::parse_query_args(const value_t& args, const string& whence) { query_t query(args, what_to_keep()); - if (! query) - throw_(std::runtime_error, - _("Invalid query predicate: %1") << query.text()); + if (query) { + HANDLER(limit_).on(whence, query.text()); - HANDLER(limit_).on(whence, query.text()); - - DEBUG("report.predicate", - "Predicate = " << HANDLER(limit_).str()); + DEBUG("report.predicate", + "Predicate = " << HANDLER(limit_).str()); + } if (query.tokens_remaining()) { query.parse_again(); - if (! query) - throw_(std::runtime_error, - _("Invalid display predicate: %1") << query.text()); + if (query) { + HANDLER(display_).on(whence, query.text()); - HANDLER(display_).on(whence, query.text()); - - DEBUG("report.predicate", - "Display predicate = " << HANDLER(display_).str()); + DEBUG("report.predicate", + "Display predicate = " << HANDLER(display_).str()); + } } } -- cgit v1.2.3 From 56d6df6123d84abe408e16fcecd7a7600eb33055 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 18 Nov 2009 23:15:49 -0500 Subject: Decompile all amount expressions for accounts reports This happens after running through all the post handlers, before running any of the account handlers. --- src/report.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/report.cc') diff --git a/src/report.cc b/src/report.cc index ed3238bc..92525565 100644 --- a/src/report.cc +++ b/src/report.cc @@ -278,6 +278,12 @@ void report_t::accounts_report(acct_handler_ptr handler) chain_post_handlers(*this, post_handler_ptr(new ignore_posts), true); pass_down_posts(chain, walker); + HANDLER(amount_).expr.mark_uncompiled(); + HANDLER(total_).expr.mark_uncompiled(); + HANDLER(display_amount_).expr.mark_uncompiled(); + HANDLER(display_total_).expr.mark_uncompiled(); + HANDLER(revalued_total_).expr.mark_uncompiled(); + scoped_ptr iter; if (! HANDLED(sort_)) { iter.reset(new basic_accounts_iterator(*session.journal->master)); -- cgit v1.2.3 From 63fee4c83775f79364199ea547dbc7e068b0abc8 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 19 Nov 2009 02:00:10 -0500 Subject: Added an --unrealized option, for use with bal -V When this option is on, then in balance report which show market values, any gains or losses in value will be balanced into a pair of accounts called Equity:Unrealized Gains and Equity:Unrealized Losses. --- src/chain.cc | 19 ++++++++----- src/filters.cc | 58 ++++++++++++++++++++++++++++++++++----- src/filters.h | 8 +++++- src/report.cc | 1 + src/report.h | 3 ++ test/baseline/opt-unrealized.test | 20 ++++++++++++++ 6 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 test/baseline/opt-unrealized.test (limited to 'src/report.cc') diff --git a/src/chain.cc b/src/chain.cc index 9ab26bd6..113a71d8 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -77,18 +77,23 @@ post_handler_ptr chain_post_handlers(report_t& report, report.what_to_keep()); handler.reset(new filter_posts(handler, display_predicate, report)); } - - // changed_value_posts adds virtual posts to the list to account for - // changes in market value of commodities, which otherwise would affect - // the running total unpredictably. - if (report.HANDLED(revalued)) - handler.reset(new changed_value_posts(handler, report)); } + // changed_value_posts adds virtual posts to the list to account for changes + // in market value of commodities, which otherwise would affect the running + // total unpredictably. + if (report.HANDLED(revalued) && (! for_accounts_report || + report.HANDLED(unrealized))) + handler.reset(new changed_value_posts(handler, report, + for_accounts_report, + report.HANDLED(unrealized))); + // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the // running total. - handler.reset(new calc_posts(handler, expr, ! for_accounts_report)); + handler.reset(new calc_posts(handler, expr, (! for_accounts_report || + (report.HANDLED(revalued) && + report.HANDLED(unrealized))))); // filter_posts will only pass through posts matching the // `secondary_predicate'. diff --git a/src/filters.cc b/src/filters.cc index 4ac3de4c..39097c58 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -249,6 +249,7 @@ namespace { const date_t& date = date_t(), const value_t& total = value_t(), const bool direct_amount = false, + const bool mark_visited = false, const optional& functor = none) { post_t& post = temps.create_post(*xact, account); @@ -308,6 +309,11 @@ namespace { DEBUG("filter.changed_value.rounding", "post.amount = " << post.amount); (*handler)(post); + + if (mark_visited) { + post.xdata().add_flags(POST_EXT_VISITED); + post.account->xdata().add_flags(ACCOUNT_EXT_VISITED); + } } } @@ -406,8 +412,12 @@ void related_posts::flush() } changed_value_posts::changed_value_posts(post_handler_ptr handler, - report_t& _report) - : item_handler(handler), report(_report), last_post(NULL), + report_t& _report, + bool _for_accounts_report, + bool _show_unrealized) + : item_handler(handler), report(_report), + for_accounts_report(_for_accounts_report), + show_unrealized(_show_unrealized), last_post(NULL), revalued_account(temps.create_account(_(""))), rounding_account(temps.create_account(_(""))) { @@ -419,6 +429,14 @@ changed_value_posts::changed_value_posts(post_handler_ptr handler, report.HANDLER(display_total_).expr); display_total_expr = report.HANDLER(display_total_).expr; changed_values_only = report.HANDLED(revalued_only); + + gains_equity_account = + report.session.journal->master->find_account(_("Equity:Unrealized Gains")); + gains_equity_account->add_flags(ACCOUNT_GENERATED); + + losses_equity_account = + report.session.journal->master->find_account(_("Equity:Unrealized Losses")); + losses_equity_account->add_flags(ACCOUNT_GENERATED); } void changed_value_posts::flush() @@ -460,10 +478,35 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) xact.payee = _("Commodities revalued"); xact._date = is_valid(date) ? date : post.date(); - handle_value(diff, &revalued_account, &xact, temps, handler, - *xact._date, repriced_total, false, - optional - (bind(&changed_value_posts::output_rounding, this, _1))); + if (! for_accounts_report) { + handle_value + (/* value= */ diff, + /* account= */ &revalued_account, + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ *xact._date, + /* total= */ repriced_total, + /* direct_amount= */ false, + /* mark_visited= */ false, + /* functor= */ (optional + (bind(&changed_value_posts::output_rounding, + this, _1)))); + } + else if (show_unrealized) { + handle_value + (/* value= */ - diff, + /* account= */ (diff < 0L ? + losses_equity_account : + gains_equity_account), + /* xact= */ &xact, + /* temps= */ temps, + /* handler= */ handler, + /* date= */ *xact._date, + /* total= */ value_t(), + /* direct_amount= */ false, + /* mark_visited= */ true); + } } } } @@ -513,7 +556,8 @@ void changed_value_posts::operator()(post_t& post) if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); - output_rounding(post); + if (! for_accounts_report) + output_rounding(post); item_handler::operator()(post); diff --git a/src/filters.h b/src/filters.h index f9412407..92148dbe 100644 --- a/src/filters.h +++ b/src/filters.h @@ -378,18 +378,24 @@ class changed_value_posts : public item_handler expr_t display_total_expr; report_t& report; bool changed_values_only; + bool for_accounts_report; + bool show_unrealized; post_t * last_post; value_t last_total; value_t last_display_total; temporaries_t temps; account_t& revalued_account; account_t& rounding_account; + account_t * gains_equity_account; + account_t * losses_equity_account; changed_value_posts(); public: changed_value_posts(post_handler_ptr handler, - report_t& _report); + report_t& _report, + bool _for_accounts_report, + bool _show_unrealized); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); diff --git a/src/report.cc b/src/report.cc index 92525565..7da44f8c 100644 --- a/src/report.cc +++ b/src/report.cc @@ -887,6 +887,7 @@ option_t * report_t::lookup_option(const char * p) case 'u': OPT(unbudgeted); else OPT(uncleared); + else OPT(unrealized); else OPT(unround); else OPT(unsorted); break; diff --git a/src/report.h b/src/report.h index 0b1baff1..354e31b6 100644 --- a/src/report.h +++ b/src/report.h @@ -301,6 +301,7 @@ public: HANDLER(truncate_).report(out); HANDLER(unbudgeted).report(out); HANDLER(uncleared).report(out); + HANDLER(unrealized).report(out); HANDLER(unround).report(out); HANDLER(unsorted).report(out); HANDLER(weekly).report(out); @@ -873,6 +874,8 @@ public: parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending"); }); + OPTION(report_t, unrealized); + OPTION_(report_t, unround, DO() { parent->HANDLER(display_amount_) .set_expr(string("--unround"), "unrounded(amount_expr)"); diff --git a/test/baseline/opt-unrealized.test b/test/baseline/opt-unrealized.test new file mode 100644 index 00000000..7d5d20fb --- /dev/null +++ b/test/baseline/opt-unrealized.test @@ -0,0 +1,20 @@ +bal -V --unrealized +<<< +2008/10/01 Sample + Assets:Brokerage 10 AAPL + Assets:Checking $-200.00 + +P 2008/10/20 12:00:00 AAPL $30.00 + +; 2008/10/20 +; Assets:Brokerage $100 +; Equity:Unrealized Gains +>>>1 + $100.00 Assets + $300.00 Brokerage + $-200.00 Checking + $-100.00 Equity:Unrealized Gains +-------------------- + 0 +>>>2 +=== 0 -- cgit v1.2.3