From 590ba76bd1440ca2acda562857bae4733619e1f5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 21 Jan 2010 04:33:56 -0500 Subject: Restore "-p this" option (aka -p 'this month') --- src/times.cc | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/times.cc b/src/times.cc index 7ea3ae32..963639f1 100644 --- a/src/times.cc +++ b/src/times.cc @@ -824,15 +824,6 @@ date_interval_t date_parser_t::parse() break; } - case lexer_t::token_t::TOK_MONTH: { - date_t temp(today); - temp += gregorian::months(adjust); - inclusion_specifier = - date_specifier_t(static_cast(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); @@ -852,10 +843,15 @@ date_interval_t date_parser_t::parse() } default: - tok.unexpected(); + case lexer_t::token_t::TOK_MONTH: { + date_t temp(today); + temp += gregorian::months(adjust); + inclusion_specifier = + date_specifier_t(static_cast(temp.year()), + temp.month()); break; } - break; + } } case lexer_t::token_t::TOK_TODAY: -- cgit v1.2.3 From 61edd3d8f4c7d5a9d8cabfb9391a7e9f20a95446 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 21 Jan 2010 04:55:11 -0500 Subject: Fixed account display bug in the balance report --- src/output.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/output.cc b/src/output.cc index 71ec6d88..bb7eff5c 100644 --- a/src/output.cc +++ b/src/output.cc @@ -152,13 +152,12 @@ format_accounts::format_accounts(report_t& _report, std::size_t format_accounts::post_account(account_t& account, const bool flat) { + if (! flat && account.parent) + post_account(*account.parent, flat); + if (account.xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) && ! account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) { - if (! flat && account.parent && - account.parent->xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) && - ! account.parent->xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) - post_account(*account.parent, flat); - + DEBUG("account.display", "Displaying account: " << account.fullname()); account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); bind_scope_t bound_scope(report, account); -- cgit v1.2.3 From d406aeb85d837bacb4e6e764794ca91a016a8361 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 21 Jan 2010 04:57:15 -0500 Subject: Guard against divide by zero error in -A reg report --- src/report.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.h b/src/report.h index 94d39215..75f3b1ff 100644 --- a/src/report.h +++ b/src/report.h @@ -362,7 +362,7 @@ public: OPTION_(report_t, average, DO() { // -A parent->HANDLER(display_total_) - .set_expr(string("--average"), "total_expr/count"); + .set_expr(string("--average"), "count>0?(total_expr/count):0"); }); OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { -- cgit v1.2.3 From 0be486c58b1cb40f898e3504b6b80b4a0462145b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 21 Jan 2010 04:59:45 -0500 Subject: Fixed assertion failure in -M -A bal report --- src/account.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index e6c7af56..f8729409 100644 --- a/src/account.cc +++ b/src/account.cc @@ -512,7 +512,7 @@ void account_t::xdata_t::details_t::update(post_t& post, if (post.has_flags(POST_VIRTUAL)) posts_virtuals_count++; - if (gather_all) + if (gather_all && post.pos) filenames.insert(post.pos->pathname); date_t date = post.date(); -- cgit v1.2.3 From b76aea7c0be2a36fb24c7c2bb3a8c0af80709609 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 2 Feb 2010 16:25:31 -0500 Subject: Added a debug statement for report.predicate --- src/report.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 2d9d7cc6..ea02d14f 100644 --- a/src/report.cc +++ b/src/report.cc @@ -293,12 +293,15 @@ void report_t::accounts_report(acct_handler_ptr handler) sort_expr, HANDLED(flat))); } - if (HANDLED(display_)) + if (HANDLED(display_)) { + DEBUG("report.predicate", + "Display predicate = " << HANDLER(display_).str()); pass_down_accounts(handler, *iter.get(), predicate_t(HANDLER(display_).str(), what_to_keep()), *this); - else + } else { pass_down_accounts(handler, *iter.get()); + } session.journal->clear_xdata(); } -- cgit v1.2.3 From a56a1db66294c06c7b9d582f694538a0cb473abf Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 2 Feb 2010 16:25:46 -0500 Subject: When creating temporary accounts, transfer flags --- src/filters.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 0084fac7..aa532ab6 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -793,12 +793,18 @@ void transfer_details::operator()(post_t& post) break; case SET_ACCOUNT: { - std::list account_names; + account_t * prev_account = temp.account; temp.account->remove_post(&temp); + + std::list account_names; split_string(substitute.to_string(), ':', account_names); temp.account = create_temp_account_from_path(account_names, temps, xact.journal->master); temp.account->add_post(&temp); + + temp.account->add_flags(prev_account->flags()); + if (prev_account->has_xdata()) + temp.account->xdata().add_flags(prev_account->xdata().flags()); break; } -- cgit v1.2.3 From 141308597784f77414032b00f239e0601f9d4d38 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 2 Feb 2010 17:07:05 -0500 Subject: Added a --pivot=TAG option This is equivalent to the following: --account='"TAG:" + tag(/TAG/)' --- doc/ledger.1 | 14 ++------------ src/chain.cc | 12 +++++++++++- src/item.cc | 38 +++++++++++++++++++++++++++++++++----- src/report.cc | 1 + src/report.h | 3 +++ 5 files changed, 50 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/doc/ledger.1 b/doc/ledger.1 index 92457c48..5a91ceb2 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd November 13, 2009 +.Dd February 2, 2010 .Dt ledger 1 .Sh NAME .Nm ledger @@ -277,20 +277,10 @@ transactions they are contained in. See the manual for more information. .It Fl \-budget .It Fl \-by-payee Pq Fl P .It Fl \-cleared Pq Fl C -.It Fl \-code-as-account -.It Fl \-code-as-payee .It Fl \-collapse Pq Fl n .It Fl \-collapse-if-zero .It Fl \-color .It Fl \-columns Ar INT -.It Fl \-commodity-as-account -(Also -.Fl \-\-comm\-as\-account -). -.It Fl \-commodity-as-payee -(Also -.Fl \-\-comm\-as\-payee -). .It Fl \-cost See .Fl \-basis . @@ -343,12 +333,12 @@ See .It Fl \-only Ar EXPR .It Fl \-output Ar FILE Pq Fl o .It Fl \-pager Ar STR -.It Fl \-payee-as-account .It Fl \-payee-width Ar INT .It Fl \-pending .It Fl \-percentage Pq Fl \% .It Fl \-period Ar PERIOD Pq Fl p .It Fl \-period-sort +.It Fl \-pivot Ar STR .It Fl \-plot-amount-format Ar FMT .It Fl \-plot-total-format Ar FMT .It Fl \-price Pq Fl I diff --git a/src/chain.cc b/src/chain.cc index 113a71d8..ecb39e0b 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -158,11 +158,21 @@ post_handler_ptr chain_post_handlers(report_t& report, report.session.journal->master, report.HANDLER(date_).str(), report)); - if (report.HANDLED(account_)) + + if (report.HANDLED(account_)) { handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT, report.session.journal->master, report.HANDLER(account_).str(), report)); + } + else if (report.HANDLED(pivot_)) { + string pivot = report.HANDLER(pivot_).str(); + pivot = string("\"") + pivot + ":\" + tag(/" + pivot + "/)"; + handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT, + report.session.journal->master, pivot, + report)); + } + if (report.HANDLED(payee_)) handler.reset(new transfer_details(handler, transfer_details::SET_PAYEE, report.session.journal->master, diff --git a/src/item.cc b/src/item.cc index f86b8ec8..0e596f33 100644 --- a/src/item.cc +++ b/src/item.cc @@ -238,11 +238,39 @@ namespace { return false; } - value_t get_tag(call_scope_t& scope) { - in_context_t env(scope, "s"); - if (optional value = env->get_tag(env.get(0))) - return string_value(*value); - return string_value(empty_string); + value_t get_tag(call_scope_t& args) { + item_t& item(find_scope(args)); + optional str; + + if (args.size() == 1) { + if (args[0].is_string()) + str = item.get_tag(args[0].as_string()); + else if (args[0].is_mask()) + str = item.get_tag(args[0].as_mask()); + else + throw_(std::runtime_error, + _("Expected string or mask for argument 1, but received %1") + << args[0].label()); + } + else if (args.size() == 2) { + if (args[0].is_mask() && args[1].is_mask()) + str = item.get_tag(args[0].to_mask(), args[1].to_mask()); + else + throw_(std::runtime_error, + _("Expected masks for arguments 1 and 2, but received %1 and %2") + << args[0].label() << args[1].label()); + } + else if (args.size() == 0) { + throw_(std::runtime_error, _("Too few arguments to function")); + } + else { + throw_(std::runtime_error, _("Too many arguments to function")); + } + + if (str) + return string_value(*str); + else + return string_value(empty_string); } value_t get_pathname(item_t& item) { diff --git a/src/report.cc b/src/report.cc index ea02d14f..fa71e584 100644 --- a/src/report.cc +++ b/src/report.cc @@ -873,6 +873,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(percent); else OPT_(period_); else OPT_ALT(sort_xacts_, period_sort_); + else OPT(pivot_); else OPT(plot_amount_format_); else OPT(plot_total_format_); else OPT(price); diff --git a/src/report.h b/src/report.h index 75f3b1ff..08819e23 100644 --- a/src/report.h +++ b/src/report.h @@ -273,6 +273,7 @@ public: HANDLER(pending).report(out); HANDLER(percent).report(out); HANDLER(period_).report(out); + HANDLER(pivot_).report(out); HANDLER(plot_amount_format_).report(out); HANDLER(plot_total_format_).report(out); HANDLER(prepend_format_).report(out); @@ -715,6 +716,8 @@ public: string_value(text.as_string() + " " + str())); }); + OPTION(report_t, pivot_); + OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) { on(none, "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); -- cgit v1.2.3 From 428a2b40191eb1c4fc7f8998e17e551dab9820a9 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 04:12:19 -0500 Subject: Improved the behavior of pivot reports --- src/filters.cc | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index aa532ab6..2926eb08 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -793,18 +793,25 @@ void transfer_details::operator()(post_t& post) break; case SET_ACCOUNT: { - account_t * prev_account = temp.account; - temp.account->remove_post(&temp); - - std::list account_names; - split_string(substitute.to_string(), ':', account_names); - temp.account = create_temp_account_from_path(account_names, temps, - xact.journal->master); - temp.account->add_post(&temp); - - temp.account->add_flags(prev_account->flags()); - if (prev_account->has_xdata()) - temp.account->xdata().add_flags(prev_account->xdata().flags()); + string account_name = substitute.to_string(); + if (! account_name.empty() && + account_name[account_name.length() - 1] != ':') { + account_t * prev_account = temp.account; + temp.account->remove_post(&temp); + + account_name += ':'; + account_name += prev_account->fullname(); + + std::list account_names; + split_string(account_name, ':', account_names); + temp.account = create_temp_account_from_path(account_names, temps, + xact.journal->master); + temp.account->add_post(&temp); + + temp.account->add_flags(prev_account->flags()); + if (prev_account->has_xdata()) + temp.account->xdata().add_flags(prev_account->xdata().flags()); + } break; } -- cgit v1.2.3 From b78e22d52b75cafd63678ee786b1653afffa49e6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 18:23:52 -0500 Subject: Added a "seq" sequence property for all items This indicates the absolute parsing order of every transaction and posting. It is 0 for generated items. --- src/item.cc | 6 ++++++ src/item.h | 6 +++++- src/textual.cc | 47 ++++++++++++++++++++++++++++------------------- 3 files changed, 39 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 0e596f33..135fa18f 100644 --- a/src/item.cc +++ b/src/item.cc @@ -296,6 +296,10 @@ namespace { return item.pos ? long(item.pos->end_line) : 0L; } + value_t get_seq(item_t& item) { + return item.pos ? long(item.pos->sequence) : 0L; + } + value_t get_depth(item_t&) { return 0L; } @@ -416,6 +420,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, case 's': if (name == "status") return WRAP_FUNCTOR(get_wrapper<&get_status>); + else if (name == "seq") + return WRAP_FUNCTOR(get_wrapper<&get_seq>); break; case 't': diff --git a/src/item.h b/src/item.h index 84385eb7..8250ab6e 100644 --- a/src/item.h +++ b/src/item.h @@ -53,8 +53,10 @@ struct position_t std::size_t beg_line; istream_pos_type end_pos; std::size_t end_line; + std::size_t sequence; - position_t() : beg_pos(0), beg_line(0), end_pos(0), end_line(0) { + position_t() + : beg_pos(0), beg_line(0), end_pos(0), end_line(0), sequence(0) { TRACE_CTOR(position_t, ""); } position_t(const position_t& pos) { @@ -72,6 +74,7 @@ struct position_t beg_line = pos.beg_line; end_pos = pos.end_pos; end_line = pos.end_line; + sequence = pos.sequence; } return *this; } @@ -89,6 +92,7 @@ private: ar & beg_line; ar & end_pos; ar & end_line; + ar & sequence; } #endif // HAVE_BOOST_SERIALIZATION }; diff --git a/src/textual.cc b/src/textual.cc index cbeb6358..697a22f8 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -77,6 +77,7 @@ namespace { istream_pos_type curr_pos; std::size_t count; std::size_t errors; + std::size_t * global_sequence; optional current_year; @@ -87,10 +88,11 @@ namespace { std::istream& _in, scope_t& _scope, journal_t& _journal, - account_t * _master = NULL, - const path * _original_file = NULL, - bool _strict = false, - instance_t * _parent = NULL); + std::size_t * _global_sequence = NULL, + account_t * _master = NULL, + const path * _original_file = NULL, + bool _strict = false, + instance_t * _parent = NULL); ~instance_t(); @@ -213,6 +215,7 @@ instance_t::instance_t(std::list& _state_stack, std::istream& _in, scope_t& _scope, journal_t& _journal, + std::size_t * _global_sequence, account_t * _master, const path * _original_file, bool _strict, @@ -223,7 +226,8 @@ instance_t::instance_t(std::list& _state_stack, #endif parent(_parent), in(_in), scope(_scope), journal(_journal), master(_master), - original_file(_original_file), strict(_strict) + original_file(_original_file), strict(_strict), + global_sequence(_global_sequence) { TRACE_CTOR(instance_t, "..."); @@ -452,6 +456,7 @@ void instance_t::clock_in_directive(char * line, position.beg_line = linenum; position.end_pos = curr_pos; position.end_line = linenum; + position.sequence = (*global_sequence)++; time_xact_t event(position, parse_datetime(datetime, current_year), p ? top_account()->find_account(p) : NULL, @@ -481,6 +486,7 @@ void instance_t::clock_out_directive(char * line, position.beg_line = linenum; position.end_pos = curr_pos; position.end_line = linenum; + position.sequence = (*global_sequence)++; time_xact_t event(position, parse_datetime(datetime, current_year), p ? top_account()->find_account(p) : NULL, @@ -554,8 +560,7 @@ void instance_t::option_directive(char * line) void instance_t::automated_xact_directive(char * line) { - istream_pos_type pos = line_beg_pos; - std::size_t lnum = linenum; + istream_pos_type pos= line_beg_pos; bool reveal_context = true; @@ -564,6 +569,11 @@ void instance_t::automated_xact_directive(char * line) std::auto_ptr ae (new auto_xact_t(query_t(string(skip_ws(line + 1)), keep_details_t(true, true, true)))); + ae->pos = position_t(); + ae->pos->pathname = pathname; + ae->pos->beg_pos = line_beg_pos; + ae->pos->beg_line = linenum; + ae->pos->sequence = (*global_sequence)++; reveal_context = false; @@ -573,10 +583,6 @@ void instance_t::automated_xact_directive(char * line) journal.auto_xacts.push_back(ae.get()); ae->journal = &journal; - ae->pos = position_t(); - ae->pos->pathname = pathname; - ae->pos->beg_pos = pos; - ae->pos->beg_line = lnum; ae->pos->end_pos = curr_pos; ae->pos->end_line = linenum; @@ -595,14 +601,18 @@ void instance_t::automated_xact_directive(char * line) void instance_t::period_xact_directive(char * line) { - istream_pos_type pos = line_beg_pos; - std::size_t lnum = linenum; + istream_pos_type pos = line_beg_pos; bool reveal_context = true; try { std::auto_ptr pe(new period_xact_t(skip_ws(line + 1))); + pe->pos = position_t(); + pe->pos->pathname = pathname; + pe->pos->beg_pos = line_beg_pos; + pe->pos->beg_line = linenum; + pe->pos->sequence = (*global_sequence)++; reveal_context = false; @@ -614,10 +624,6 @@ void instance_t::period_xact_directive(char * line) journal.extend_xact(pe.get()); journal.period_xacts.push_back(pe.get()); - pe->pos = position_t(); - pe->pos->pathname = pathname; - pe->pos->beg_pos = pos; - pe->pos->beg_line = lnum; pe->pos->end_pos = curr_pos; pe->pos->end_line = linenum; @@ -694,7 +700,7 @@ void instance_t::include_directive(char * line) #if defined(TIMELOG_SUPPORT) timelog, #endif - stream, scope, journal, master, + stream, scope, journal, global_sequence, master, &filename, strict, this); instance.parse(); @@ -880,6 +886,7 @@ post_t * instance_t::parse_post(char * line, post->pos->pathname = pathname; post->pos->beg_pos = line_beg_pos; post->pos->beg_line = linenum; + post->pos->sequence = (*global_sequence)++; char buf[MAX_LINE + 1]; std::strcpy(buf, line); @@ -1229,6 +1236,7 @@ xact_t * instance_t::parse_xact(char * line, xact->pos->pathname = pathname; xact->pos->beg_pos = line_beg_pos; xact->pos->beg_line = linenum; + xact->pos->sequence = (*global_sequence)++; bool reveal_context = true; @@ -1378,11 +1386,12 @@ std::size_t journal_t::parse(std::istream& in, time_log_t timelog(*this); #endif + std::size_t parsing_sequence = 1; instance_t parsing_instance(state_stack, #if defined(TIMELOG_SUPPORT) timelog, #endif - in, scope, *this, master, + in, scope, *this, &parsing_sequence, master, original_file, strict); parsing_instance.parse(); -- cgit v1.2.3 From 9d61d3d621606f0158a048628c866a692465d886 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 19:47:16 -0500 Subject: Added actual_date() methods for items --- src/item.cc | 5 +++++ src/item.h | 4 ++++ src/post.cc | 12 ++++++++++++ src/post.h | 1 + 4 files changed, 22 insertions(+) (limited to 'src') diff --git a/src/item.cc b/src/item.cc index 135fa18f..8d1ba34f 100644 --- a/src/item.cc +++ b/src/item.cc @@ -199,6 +199,9 @@ namespace { value_t get_date(item_t& item) { return item.date(); } + value_t get_actual_date(item_t& item) { + return item.actual_date(); + } value_t get_effective_date(item_t& item) { if (optional effective = item.effective_date()) return *effective; @@ -351,6 +354,8 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, case 'a': if (name == "actual") return WRAP_FUNCTOR(get_wrapper<&get_actual>); + else if (name == "actual_date") + return WRAP_FUNCTOR(get_wrapper<&get_actual_date>); break; case 'b': diff --git a/src/item.h b/src/item.h index 8250ab6e..f23350fc 100644 --- a/src/item.h +++ b/src/item.h @@ -173,6 +173,10 @@ public: return *effective; return *_date; } + virtual date_t actual_date() const { + assert(_date); + return *_date; + } virtual optional effective_date() const { return _date_eff; } diff --git a/src/post.cc b/src/post.cc index 34284e1b..5b9d2bb3 100644 --- a/src/post.cc +++ b/src/post.cc @@ -97,6 +97,18 @@ date_t post_t::date() const return *_date; } +date_t post_t::actual_date() const +{ + if (xdata_ && is_valid(xdata_->date)) + return xdata_->date; + + if (! _date) { + assert(xact); + return xact->date(); + } + return *_date; +} + optional post_t::effective_date() const { optional date = item_t::effective_date(); diff --git a/src/post.h b/src/post.h index a0ab5ffd..cea1aca1 100644 --- a/src/post.h +++ b/src/post.h @@ -105,6 +105,7 @@ public: const optional& value_mask = none) const; virtual date_t date() const; + virtual date_t actual_date() const; virtual optional effective_date() const; bool must_balance() const { -- cgit v1.2.3 From 6870c54644ba41fe360d43d27885ded46beac7c5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 19:47:49 -0500 Subject: Refactored some comparison helper classes --- src/balance.cc | 3 ++- src/commodity.cc | 4 ++-- src/commodity.h | 8 ++++---- src/value.cc | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/balance.cc b/src/balance.cc index 4ff51ffc..4fcc83fa 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -271,7 +271,8 @@ void balance_t::print(std::ostream& out, if (pair.second) sorted.push_back(&pair.second); - std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities()); + std::stable_sort(sorted.begin(), sorted.end(), + commodity_t::compare_by_commodity()); foreach (const amount_t * amount, sorted) { int width; diff --git a/src/commodity.cc b/src/commodity.cc index 79ed77fe..24016830 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -587,8 +587,8 @@ bool commodity_t::valid() const return true; } -bool compare_amount_commodities::operator()(const amount_t * left, - const amount_t * right) const +bool commodity_t::compare_by_commodity::operator()(const amount_t * left, + const amount_t * right) const { commodity_t& leftcomm(left->commodity()); commodity_t& rightcomm(right->commodity()); diff --git a/src/commodity.h b/src/commodity.h index 3370f3f2..d2d8af21 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -386,6 +386,10 @@ public: bool valid() const; + struct compare_by_commodity { + bool operator()(const amount_t * left, const amount_t * right) const; + }; + #if defined(HAVE_BOOST_SERIALIZATION) private: supports_flags temp_flags; @@ -419,10 +423,6 @@ inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) { return out; } -struct compare_amount_commodities { - bool operator()(const amount_t * left, const amount_t * right) const; -}; - void to_xml(std::ostream& out, const commodity_t& comm, bool commodity_details = false); diff --git a/src/value.cc b/src/value.cc index cce4c4e8..7d079caf 100644 --- a/src/value.cc +++ b/src/value.cc @@ -861,7 +861,7 @@ bool value_t::is_less_than(const value_t& val) const return as_amount() < val.as_amount(); } catch (const amount_error&) { - return compare_amount_commodities()(&as_amount(), &val.as_amount()); + return commodity_t::compare_by_commodity()(&as_amount(), &val.as_amount()); } default: break; -- cgit v1.2.3 From 313c88a6566dd3aee78069125925c867ae24e871 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 19:48:11 -0500 Subject: Added post_t::compare_by_date_and_sequence comparator --- src/post.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src') diff --git a/src/post.h b/src/post.h index cea1aca1..3a8c5541 100644 --- a/src/post.h +++ b/src/post.h @@ -199,6 +199,20 @@ public: friend class xact_t; + struct compare_by_date_and_sequence + { + bool operator()(const post_t * left, const post_t * right) const { + gregorian::date_duration duration = + left->actual_date() - right->actual_date(); + if (duration.days() == 0) { + return ((left->pos ? left->pos->sequence : 0) < + (right->pos ? right->pos->sequence : 0)); + } else { + return duration.days() < 0; + } + } + }; + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ -- cgit v1.2.3 From 40eae8bf94f65a000541449b863ea26a469627fd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 20:01:44 -0500 Subject: Reformatted a comment --- src/journal.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/journal.cc b/src/journal.cc index 6ebccd66..5aa2e7f7 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -64,9 +64,8 @@ journal_t::~journal_t() { TRACE_DTOR(journal_t); - // Don't bother unhooking each xact's posts from the - // accounts they refer to, because all accounts are about to - // be deleted. + // Don't bother unhooking each xact's posts from the accounts they refer to, + // because all accounts are about to be deleted. foreach (xact_t * xact, xacts) checked_delete(xact); -- cgit v1.2.3 From 69da18cd303b10f9badd542141ffdfd546009508 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 4 Feb 2010 23:59:17 -0500 Subject: Some minor refactoring of the textual parser --- src/post.cc | 14 +++ src/post.h | 2 + src/textual.cc | 305 ++++++++++++++++++++++++--------------------------------- 3 files changed, 145 insertions(+), 176 deletions(-) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index 5b9d2bb3..43cfe55f 100644 --- a/src/post.cc +++ b/src/post.cc @@ -417,6 +417,20 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, return item_t::lookup(kind, name); } +amount_t post_t::resolve_expr(scope_t& scope, expr_t& expr) +{ + bind_scope_t bound_scope(scope, *this); + value_t result(expr.calc(bound_scope)); + if (result.is_long()) { + return result.to_amount(); + } else { + if (! result.is_amount()) + throw_(amount_error, + _("Amount expressions must result in a simple amount")); + return result.as_amount(); + } +} + bool post_t::valid() const { if (! xact) { diff --git a/src/post.h b/src/post.h index 3a8c5541..8852e7b2 100644 --- a/src/post.h +++ b/src/post.h @@ -115,6 +115,8 @@ public: virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); + amount_t resolve_expr(scope_t& scope, expr_t& expr); + bool valid() const; struct xdata_t : public supports_flags diff --git a/src/textual.cc b/src/textual.cc index 697a22f8..8953d2b8 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -49,52 +49,26 @@ namespace ledger { namespace { - class instance_t : public noncopyable, public scope_t - { - static const std::size_t MAX_LINE = 1024; + typedef std::pair fixed_rate_t; + typedef variant state_t; + class parse_context_t : public noncopyable + { public: - typedef std::pair fixed_rate_t; - typedef variant state_t; - - std::list& state_stack; - -#if defined(TIMELOG_SUPPORT) - time_log_t& timelog; -#endif - instance_t * parent; - std::istream& in; - scope_t& scope; - journal_t& journal; - account_t * master; - const path * original_file; - accounts_map account_aliases; - bool strict; - path pathname; - char linebuf[MAX_LINE + 1]; - std::size_t linenum; - istream_pos_type line_beg_pos; - istream_pos_type curr_pos; - std::size_t count; - std::size_t errors; - std::size_t * global_sequence; - - optional current_year; - - instance_t(std::list& _state_stack, + journal_t& journal; + scope_t& scope; + std::list state_stack; #if defined(TIMELOG_SUPPORT) - time_log_t& _timelog, + time_log_t timelog; #endif - std::istream& _in, - scope_t& _scope, - journal_t& _journal, - std::size_t * _global_sequence = NULL, - account_t * _master = NULL, - const path * _original_file = NULL, - bool _strict = false, - instance_t * _parent = NULL); + bool strict; + std::size_t count; + std::size_t errors; + std::size_t sequence; - ~instance_t(); + parse_context_t(journal_t& _journal, scope_t& _scope) + : journal(_journal), scope(_scope), timelog(journal), + strict(false), count(0), errors(0), sequence(1) {} bool front_is_account() { return state_stack.front().type() == typeid(account_t *); @@ -112,6 +86,34 @@ namespace { return boost::get(state); return NULL; } + }; + + class instance_t : public noncopyable, public scope_t + { + static const std::size_t MAX_LINE = 1024; + + public: + parse_context_t& context; + instance_t * parent; + account_t * master; + accounts_map account_aliases; + const path * original_file; + path pathname; + std::istream& in; + char linebuf[MAX_LINE + 1]; + std::size_t linenum; + istream_pos_type line_beg_pos; + istream_pos_type curr_pos; + + optional current_year; + + instance_t(parse_context_t& _context, + std::istream& _in, + account_t * _master = NULL, + const path * _original_file = NULL, + instance_t * _parent = NULL); + + ~instance_t(); void parse(); std::streamsize read_line(char *& line); @@ -150,7 +152,6 @@ namespace { std::streamsize len, account_t * account, xact_t * xact, - bool honor_strict = true, bool defer_expr = false); bool parse_posts(account_t * account, @@ -165,13 +166,13 @@ namespace { const string& name); }; - void parse_amount_expr(scope_t& scope, - std::istream& in, + void parse_amount_expr(std::istream& in, + scope_t& scope, + post_t& post, amount_t& amount, - optional * amount_expr, - post_t * post, - const parse_flags_t& flags = PARSE_DEFAULT, - const bool defer_expr = false) + const parse_flags_t& flags = PARSE_DEFAULT, + const bool defer_expr = false, + optional * amount_expr = NULL) { expr_t expr(in, flags.plus_flags(PARSE_PARTIAL)); @@ -187,53 +188,27 @@ namespace { #endif if (expr) { - bind_scope_t bound_scope(scope, *post); - if (defer_expr) { - assert(amount_expr); + if (amount_expr) *amount_expr = expr; - (*amount_expr)->compile(bound_scope); - } else { - value_t result(expr.calc(bound_scope)); - if (result.is_long()) { - amount = result.to_amount(); - } else { - if (! result.is_amount()) - throw_(amount_error, - _("Amount expressions must result in a simple amount")); - amount = result.as_amount(); - } - DEBUG("textual.parse", "The posting amount is " << amount); - } + if (! defer_expr) + amount = post.resolve_expr(scope, expr); } } } -instance_t::instance_t(std::list& _state_stack, -#if defined(TIMELOG_SUPPORT) - time_log_t& _timelog, -#endif - std::istream& _in, - scope_t& _scope, - journal_t& _journal, - std::size_t * _global_sequence, - account_t * _master, - const path * _original_file, - bool _strict, - instance_t * _parent) - : state_stack(_state_stack), -#if defined(TIMELOG_SUPPORT) - timelog(_timelog), -#endif - parent(_parent), in(_in), scope(_scope), - journal(_journal), master(_master), - original_file(_original_file), strict(_strict), - global_sequence(_global_sequence) +instance_t::instance_t(parse_context_t& _context, + std::istream& _in, + account_t * _master, + const path * _original_file, + instance_t * _parent) + : context(_context), parent(_parent), master(_master), + original_file(_original_file), in(_in) { TRACE_CTOR(instance_t, "..."); if (! master) - master = journal.master; - state_stack.push_front(master); + master = context.journal.master; + context.state_stack.push_front(master); if (_original_file) pathname = *_original_file; @@ -245,8 +220,8 @@ instance_t::~instance_t() { TRACE_DTOR(instance_t); - assert(! state_stack.empty()); - state_stack.pop_front(); + assert(! context.state_stack.empty()); + context.state_stack.pop_front(); } void instance_t::parse() @@ -260,8 +235,6 @@ void instance_t::parse() return; linenum = 0; - errors = 0; - count = 0; curr_pos = in.tellg(); while (in.good() && ! in.eof()) { @@ -290,15 +263,15 @@ void instance_t::parse() if (caught_signal != NONE_CAUGHT) throw; - string context = error_context(); - if (! context.empty()) - std::cerr << context << std::endl; + string err_context = error_context(); + if (! err_context.empty()) + std::cerr << err_context << std::endl; if (! current_context.empty()) std::cerr << current_context << std::endl; std::cerr << _("Error: ") << err.what() << std::endl; - errors++; + context.errors++; } } @@ -456,14 +429,14 @@ void instance_t::clock_in_directive(char * line, position.beg_line = linenum; position.end_pos = curr_pos; position.end_line = linenum; - position.sequence = (*global_sequence)++; + position.sequence = context.sequence++; time_xact_t event(position, parse_datetime(datetime, current_year), - p ? top_account()->find_account(p) : NULL, + p ? context.top_account()->find_account(p) : NULL, n ? n : "", end ? end : ""); - timelog.clock_in(event); + context.timelog.clock_in(event); } void instance_t::clock_out_directive(char * line, @@ -486,15 +459,15 @@ void instance_t::clock_out_directive(char * line, position.beg_line = linenum; position.end_pos = curr_pos; position.end_line = linenum; - position.sequence = (*global_sequence)++; + position.sequence = context.sequence++; time_xact_t event(position, parse_datetime(datetime, current_year), - p ? top_account()->find_account(p) : NULL, + p ? context.top_account()->find_account(p) : NULL, n ? n : "", end ? end : ""); - timelog.clock_out(event); - count++; + context.timelog.clock_out(event); + context.count++; } #endif // TIMELOG_SUPPORT @@ -509,8 +482,8 @@ void instance_t::default_commodity_directive(char * line) void instance_t::default_account_directive(char * line) { - journal.bucket = top_account()->find_account(skip_ws(line + 1)); - journal.bucket->add_flags(ACCOUNT_KNOWN); + context.journal.bucket = context.top_account()->find_account(skip_ws(line + 1)); + context.journal.bucket->add_flags(ACCOUNT_KNOWN); } void instance_t::price_conversion_directive(char * line) @@ -554,7 +527,7 @@ void instance_t::option_directive(char * line) *p++ = '\0'; } - if (! process_option(pathname.string(), line + 2, scope, p, line)) + if (! process_option(pathname.string(), line + 2, context.scope, p, line)) throw_(option_error, _("Illegal option --%1") << line + 2); } @@ -573,16 +546,16 @@ void instance_t::automated_xact_directive(char * line) ae->pos->pathname = pathname; ae->pos->beg_pos = line_beg_pos; ae->pos->beg_line = linenum; - ae->pos->sequence = (*global_sequence)++; + ae->pos->sequence = context.sequence++; reveal_context = false; - if (parse_posts(top_account(), *ae.get(), true)) { + if (parse_posts(context.top_account(), *ae.get(), true)) { reveal_context = true; - journal.auto_xacts.push_back(ae.get()); + context.journal.auto_xacts.push_back(ae.get()); - ae->journal = &journal; + ae->journal = &context.journal; ae->pos->end_pos = curr_pos; ae->pos->end_line = linenum; @@ -612,17 +585,17 @@ void instance_t::period_xact_directive(char * line) pe->pos->pathname = pathname; pe->pos->beg_pos = line_beg_pos; pe->pos->beg_line = linenum; - pe->pos->sequence = (*global_sequence)++; + pe->pos->sequence = context.sequence++; reveal_context = false; - if (parse_posts(top_account(), *pe.get())) { + if (parse_posts(context.top_account(), *pe.get())) { reveal_context = true; - pe->journal = &journal; + pe->journal = &context.journal; if (pe->finalize()) { - journal.extend_xact(pe.get()); - journal.period_xacts.push_back(pe.get()); + context.journal.extend_xact(pe.get()); + context.journal.period_xacts.push_back(pe.get()); pe->pos->end_pos = curr_pos; pe->pos->end_line = linenum; @@ -648,12 +621,12 @@ void instance_t::xact_directive(char * line, std::streamsize len) { TRACE_START(xacts, 1, "Time spent handling transactions:"); - if (xact_t * xact = parse_xact(line, len, top_account())) { + if (xact_t * xact = parse_xact(line, len, context.top_account())) { std::auto_ptr manager(xact); - if (journal.add_xact(xact)) { + if (context.journal.add_xact(xact)) { manager.release(); // it's owned by the journal now - count++; + context.count++; } // It's perfectly valid for the journal to reject the xact, which it will // do if the xact has no substantive effect (for example, a checking @@ -696,22 +669,14 @@ void instance_t::include_directive(char * line) ifstream stream(filename); - instance_t instance(state_stack, -#if defined(TIMELOG_SUPPORT) - timelog, -#endif - stream, scope, journal, global_sequence, master, - &filename, strict, this); + instance_t instance(context, stream, master, &filename, this); instance.parse(); - - errors += instance.errors; - count += instance.count; } void instance_t::master_account_directive(char * line) { - if (account_t * acct = top_account()->find_account(line)) - state_stack.push_front(acct); + if (account_t * acct = context.top_account()->find_account(line)) + context.state_stack.push_front(acct); else assert(! "Failed to create account"); } @@ -720,21 +685,21 @@ void instance_t::end_directive(char * kind) { string name(kind); - if ((name.empty() || name == "account") && ! front_is_account()) + if ((name.empty() || name == "account") && ! context.front_is_account()) throw_(std::runtime_error, _("'end account' directive does not match open directive")); - else if (name == "tag" && ! front_is_string()) + else if (name == "tag" && ! context.front_is_string()) throw_(std::runtime_error, _("'end tag' directive does not match open directive")); - else if (name == "fixed" && ! front_is_fixed_rate()) + else if (name == "fixed" && ! context.front_is_fixed_rate()) throw_(std::runtime_error, _("'end fixed' directive does not match open directive")); - if (state_stack.size() <= 1) + if (context.state_stack.size() <= 1) throw_(std::runtime_error, _("'end' found, but no enclosing tag or account directive")); else - state_stack.pop_front(); + context.state_stack.pop_front(); } void instance_t::alias_directive(char * line) @@ -751,7 +716,7 @@ void instance_t::alias_directive(char * line) // name (e), add a reference to the account in the // `account_aliases' map, which is used by the post // parser to resolve alias references. - account_t * acct = top_account()->find_account(e); + account_t * acct = context.top_account()->find_account(e); std::pair result = account_aliases.insert(accounts_map::value_type(b, acct)); assert(result.second); @@ -763,8 +728,8 @@ void instance_t::fixed_directive(char * line) if (optional > price_point = commodity_pool_t::current_pool->parse_price_directive(trim_ws(line), true)) { - state_stack.push_front(fixed_rate_t(price_point->first, - price_point->second.price)); + context.state_stack.push_front(fixed_rate_t(price_point->first, + price_point->second.price)); } else { throw_(std::runtime_error, _("Error in fixed directive")); } @@ -777,13 +742,13 @@ void instance_t::tag_directive(char * line) if (tag.find(':') == string::npos) tag = string(":") + tag + ":"; - state_stack.push_front(tag); + context.state_stack.push_front(tag); } void instance_t::define_directive(char * line) { expr_t def(skip_ws(line)); - def.compile(scope); // causes definitions to be established + def.compile(context.scope); // causes definitions to be established } bool instance_t::general_directive(char * line) @@ -874,7 +839,6 @@ post_t * instance_t::parse_post(char * line, std::streamsize len, account_t * account, xact_t * xact, - bool honor_strict, bool defer_expr) { TRACE_START(post_details, 1, "Time spent parsing postings:"); @@ -886,7 +850,7 @@ post_t * instance_t::parse_post(char * line, post->pos->pathname = pathname; post->pos->beg_pos = line_beg_pos; post->pos->beg_line = linenum; - post->pos->sequence = (*global_sequence)++; + post->pos->sequence = context.sequence++; char buf[MAX_LINE + 1]; std::strcpy(buf, line); @@ -958,7 +922,7 @@ post_t * instance_t::parse_post(char * line, if (! post->account) post->account = account->find_account(name); - if (honor_strict && strict && ! post->account->has_flags(ACCOUNT_KNOWN)) { + if (context.strict && ! post->account->has_flags(ACCOUNT_KNOWN)) { if (post->_state == item_t::UNCLEARED) warning_(_("\"%1\", line %2: Unknown account '%3'") << pathname << linenum << post->account->fullname()); @@ -978,13 +942,13 @@ post_t * instance_t::parse_post(char * line, if (*next != '(') // indicates a value expression post->amount.parse(stream, PARSE_NO_REDUCE); else - parse_amount_expr(scope, stream, post->amount, &post->amount_expr, - post.get(), PARSE_NO_REDUCE | PARSE_SINGLE | - PARSE_NO_ASSIGN, defer_expr); + parse_amount_expr(stream, context.scope, *post.get(), post->amount, + PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN, + defer_expr, &post->amount_expr); if (! post->amount.is_null() && post->amount.has_commodity()) { - if (honor_strict && strict && - ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) { + if (context.strict && + ! post->amount.commodity().has_flags(COMMODITY_KNOWN)) { if (post->_state == item_t::UNCLEARED) warning_(_("\"%1\", line %2: Unknown commodity '%3'") << pathname << linenum << post->amount.commodity()); @@ -992,7 +956,7 @@ post_t * instance_t::parse_post(char * line, } if (! post->amount.has_annotation()) { - foreach (state_t& state, state_stack) { + foreach (state_t& state, context.state_stack) { if (state.type() == typeid(fixed_rate_t)) { fixed_rate_t& rate(boost::get(state)); if (*rate.first == post->amount.commodity()) { @@ -1040,9 +1004,8 @@ post_t * instance_t::parse_post(char * line, if (*p != '(') // indicates a value expression post->cost->parse(cstream, PARSE_NO_MIGRATE); else - parse_amount_expr(scope, cstream, *post->cost, NULL, post.get(), - PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN, - defer_expr); + parse_amount_expr(cstream, context.scope, *post.get(), *post->cost, + PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN); if (post->cost->sign() < 0) throw parse_error(_("A posting's cost may not be negative")); @@ -1095,9 +1058,9 @@ post_t * instance_t::parse_post(char * line, if (*p != '(') // indicates a value expression post->assigned_amount->parse(stream, PARSE_NO_MIGRATE); else - parse_amount_expr(scope, stream, *post->assigned_amount, NULL, - post.get(), PARSE_SINGLE | PARSE_NO_MIGRATE, - defer_expr); + parse_amount_expr(stream, context.scope, *post.get(), + *post->assigned_amount, + PARSE_SINGLE | PARSE_NO_MIGRATE); if (post->assigned_amount->is_null()) { if (post->amount.is_null()) @@ -1180,8 +1143,8 @@ post_t * instance_t::parse_post(char * line, post->pos->end_pos = curr_pos; post->pos->end_line = linenum; - if (! state_stack.empty()) { - foreach (const state_t& state, state_stack) + if (! context.state_stack.empty()) { + foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) post->parse_tags(boost::get(state).c_str()); } @@ -1211,9 +1174,7 @@ bool instance_t::parse_posts(account_t * account, std::streamsize len = read_line(line); assert(len > 0); - if (post_t * post = - parse_post(line, len, account, NULL, /* honor_strict= */ false, - defer_expr)) { + if (post_t * post = parse_post(line, len, account, NULL, defer_expr)) { xact.add_post(post); added = true; } @@ -1236,7 +1197,7 @@ xact_t * instance_t::parse_xact(char * line, xact->pos->pathname = pathname; xact->pos->beg_pos = line_beg_pos; xact->pos->beg_line = linenum; - xact->pos->sequence = (*global_sequence)++; + xact->pos->sequence = context.sequence++; bool reveal_context = true; @@ -1346,8 +1307,8 @@ xact_t * instance_t::parse_xact(char * line, xact->pos->end_pos = curr_pos; xact->pos->end_line = linenum; - if (! state_stack.empty()) { - foreach (const state_t& state, state_stack) + if (! context.state_stack.empty()) { + foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) xact->parse_tags(boost::get(state).c_str()); } @@ -1370,7 +1331,7 @@ xact_t * instance_t::parse_xact(char * line, expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind, const string& name) { - return scope.lookup(kind, name); + return context.scope.lookup(kind, name); } std::size_t journal_t::parse(std::istream& in, @@ -1381,19 +1342,11 @@ std::size_t journal_t::parse(std::istream& in, { TRACE_START(parsing_total, 1, "Total time spent parsing text:"); - std::list state_stack; -#if defined(TIMELOG_SUPPORT) - time_log_t timelog(*this); -#endif + parse_context_t context(*this, scope); + context.strict = strict; - std::size_t parsing_sequence = 1; - instance_t parsing_instance(state_stack, -#if defined(TIMELOG_SUPPORT) - timelog, -#endif - in, scope, *this, &parsing_sequence, master, - original_file, strict); - parsing_instance.parse(); + instance_t instance(context, in, master, original_file); + instance.parse(); TRACE_STOP(parsing_total, 1); @@ -1405,10 +1358,10 @@ std::size_t journal_t::parse(std::istream& in, TRACE_FINISH(instance_parse, 1); // report per-instance timers TRACE_FINISH(parsing_total, 1); - if (parsing_instance.errors > 0) - throw static_cast(parsing_instance.errors); + if (context.errors > 0) + throw static_cast(context.errors); - return parsing_instance.count; + return context.count; } } // namespace ledger -- cgit v1.2.3