From 47bfe58ab39aa8d7ed4dc804409f1f78f485a5a6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 18 May 2010 17:37:27 -0400 Subject: Added account_id and xact_id valexpr vars for posts account_id is the "whicheth" number for that posting within its account. The xact_id is within its transaction. --- src/post.cc | 36 ++++++++++++++++++++++++++++++++++++ src/post.h | 3 +++ 2 files changed, 39 insertions(+) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index 183fb901..fb3281a7 100644 --- a/src/post.cc +++ b/src/post.cc @@ -142,6 +142,10 @@ namespace { return value_t(static_cast(post.xact)); } + value_t get_xact_id(post_t& post) { + return static_cast(post.xact_id()); + } + value_t get_code(post_t& post) { if (post.xact->code) return string_value(*post.xact->code); @@ -275,6 +279,10 @@ namespace { return string_value(name); } + value_t get_account_id(post_t& post) { + return static_cast(post.account_id()); + } + value_t get_account_base(post_t& post) { return string_value(post.reported_account()->name); } @@ -351,6 +359,8 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_account); else if (name == "account_base") return WRAP_FUNCTOR(get_wrapper<&get_account_base>); + else if (name == "account_id") + return WRAP_FUNCTOR(get_wrapper<&get_account_id>); else if (name == "any") return WRAP_FUNCTOR(&fn_any); else if (name == "all") @@ -444,6 +454,8 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, case 'x': if (name == "xact") return WRAP_FUNCTOR(get_wrapper<&get_xact>); + else if (name == "xact_id") + return WRAP_FUNCTOR(get_wrapper<&get_xact_id>); break; case 'N': @@ -479,6 +491,30 @@ amount_t post_t::resolve_expr(scope_t& scope, expr_t& expr) } } +std::size_t post_t::xact_id() const +{ + std::size_t id = 1; + foreach (post_t * p, xact->posts) { + if (p == this) + return id; + id++; + } + assert(! "Failed to find posting within its transaction"); + return 0; +} + +std::size_t post_t::account_id() const +{ + std::size_t id = 1; + foreach (post_t * p, account->posts) { + if (p == this) + return id; + id++; + } + assert(! "Failed to find posting within its transaction"); + return 0; +} + bool post_t::valid() const { if (! xact) { diff --git a/src/post.h b/src/post.h index 272cd9d8..226d6289 100644 --- a/src/post.h +++ b/src/post.h @@ -118,6 +118,9 @@ public: amount_t resolve_expr(scope_t& scope, expr_t& expr); + std::size_t xact_id() const; + std::size_t account_id() const; + bool valid() const; struct xdata_t : public supports_flags -- cgit v1.2.3 From 57abfd7ef8b4500b7a0c14d136d397ecf974163b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 13:08:53 -0400 Subject: Temporary accounts were referenced after deletion Fixes D53C98E5-506D-4CE5-91A3-7666FD33B65B --- src/account.cc | 18 +++++++++++++++++- src/temps.cc | 6 +++--- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index e02d21d7..46f37091 100644 --- a/src/account.cc +++ b/src/account.cc @@ -43,8 +43,16 @@ account_t::~account_t() TRACE_DTOR(account_t); foreach (accounts_map::value_type& pair, accounts) - if (! pair.second->has_flags(ACCOUNT_TEMP)) + if (! pair.second->has_flags(ACCOUNT_TEMP) || + has_flags(ACCOUNT_TEMP)) checked_delete(pair.second); + + foreach (post_t * post, posts) { + if (post->account) { + assert(post->account == this); + post->account = NULL; + } + } } account_t * account_t::find_account(const string& name, @@ -79,6 +87,14 @@ account_t * account_t::find_account(const string& name, return NULL; account = new account_t(this, first); + + // An account created within a temporary or generated account is itself + // temporary or generated, so that the whole tree has the same status. + if (has_flags(ACCOUNT_TEMP)) + account->add_flags(ACCOUNT_TEMP); + if (has_flags(ACCOUNT_GENERATED)) + account->add_flags(ACCOUNT_GENERATED); + std::pair result = accounts.insert(accounts_map::value_type(first, account)); assert(result.second); diff --git a/src/temps.cc b/src/temps.cc index 68b9ffa0..dcaa9101 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -91,9 +91,9 @@ post_t& temporaries_t::copy_post(post_t& origin, xact_t& xact, post_temps->push_back(origin); post_t& temp(post_temps->back()); + temp.add_flags(ITEM_TEMP); if (account) temp.account = account; - temp.add_flags(ITEM_TEMP); temp.account->add_post(&temp); xact.add_post(&temp); @@ -109,8 +109,8 @@ post_t& temporaries_t::create_post(xact_t& xact, account_t * account) post_temps->push_back(post_t(account)); post_t& temp(post_temps->back()); - temp.account = account; temp.add_flags(ITEM_TEMP); + temp.account = account; temp.account->add_post(&temp); xact.add_post(&temp); @@ -127,10 +127,10 @@ account_t& temporaries_t::create_account(const string& name, acct_temps->push_back(account_t(parent, name)); account_t& temp(acct_temps->back()); + temp.add_flags(ACCOUNT_TEMP); if (parent) parent->add_account(&temp); - temp.add_flags(ACCOUNT_TEMP); return temp; } -- cgit v1.2.3 From 8d4de7783666e901538a0cbaa716400ef95e408d Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 13:18:25 -0400 Subject: emacs command was referencing uninitialized values Fixes 7B54CF80-45A4-4D50-A8D3-63272D60FA1B --- src/emacs.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/emacs.cc b/src/emacs.cc index d47f04ad..3c8bb256 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -40,18 +40,21 @@ namespace ledger { void format_emacs_posts::write_xact(xact_t& xact) { - out << "\"" << xact.pos->pathname << "\" " - << xact.pos->beg_line << " "; + if (xact.pos) + out << "\"" << xact.pos->pathname << "\" " + << xact.pos->beg_line << " "; + else + out << "\"\" " << -1 << " "; tm when = gregorian::to_tm(xact.date()); std::time_t date = std::mktime(&when); out << "(" << (date / 65536) << " " << (date % 65536) << " 0) "; - if (! xact.code) - out << "nil "; - else + if (xact.code) out << "\"" << *xact.code << "\" "; + else + out << "nil "; if (xact.payee.empty()) out << "nil"; @@ -77,7 +80,11 @@ void format_emacs_posts::operator()(post_t& post) out << "\n"; } - out << " (" << post.pos->beg_line << " "; + if (post.pos) + out << " (" << post.pos->beg_line << " "; + else + out << " (" << -1 << " "; + out << "\"" << post.reported_account()->fullname() << "\" \"" << post.amount << "\""; -- cgit v1.2.3 From 68056c194895f9943a8b8cbdccf52fa369322612 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 13:23:38 -0400 Subject: Guard against NULL value passed to "end" directive Fixes 89233B6D-CB21-4162-98E3-BE38B9336070 --- src/textual.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index dfca7943..d953da26 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -713,7 +713,7 @@ void instance_t::master_account_directive(char * line) void instance_t::end_directive(char * kind) { - string name(kind); + string name(kind ? kind : ""); if ((name.empty() || name == "account") && ! context.front_is_account()) throw_(std::runtime_error, -- cgit v1.2.3 From a596727d3d2cef76e7347f3695d5d04067b24d19 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 14:49:46 -0400 Subject: Interpolate uses of $account in automated postings Fixes 5CB52887-408E-48F0-8798-3C640D0295B3 --- src/xact.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 344f66ea..3c2505af 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -716,8 +716,14 @@ void auto_xact_t::extend_xact(xact_base_t& xact) account_t * account = post->account; string fullname = account->fullname(); assert(! fullname.empty()); - if (fullname == "$account" || fullname == "@account") - account = initial_post->account; + + if (contains(fullname, "$account")) { + fullname = regex_replace(fullname, regex("\\$account\\>"), + initial_post->account->fullname()); + while (account->parent) + account = account->parent; + account = account->find_account(fullname); + } // Copy over details so that the resulting post is a mirror of // the automated xact's one. -- cgit v1.2.3 From de3803d0277353520116f05c7b2357196a8cfe48 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 15:40:38 -0400 Subject: Added new commands: acounts, payees, commodities These three reports simply dump an unordered list (with the exception of payees) shows all accounts, payees, and commodities represented in a given report. This can be used to easily generate per-entity report, for example: ledger payees | \ while read payee; do \ echo ; echo $payee ; \ ledger reg payee "$payee" ; \ done --- src/output.cc | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/output.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/report.cc | 16 ++++++++++++++- 3 files changed, 144 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/output.cc b/src/output.cc index 30775310..e3aa9f4a 100644 --- a/src/output.cc +++ b/src/output.cc @@ -232,4 +232,70 @@ void format_accounts::operator()(account_t& account) posted_accounts.push_back(&account); } +void report_accounts::flush() +{ + std::ostream& out(report.output_stream); + + foreach (accounts_pair& entry, accounts) + out << *entry.first << '\n'; +} + +void report_accounts::operator()(post_t& post) +{ + std::map::iterator i = accounts.find(post.account); + if (i == accounts.end()) + accounts.insert(accounts_pair(post.account, true)); +} + +void report_payees::flush() +{ + std::ostream& out(report.output_stream); + + foreach (payees_pair& entry, payees) + out << entry.first << '\n'; +} + +void report_payees::operator()(post_t& post) +{ + std::map::iterator i = payees.find(post.xact->payee); + if (i == payees.end()) + payees.insert(payees_pair(post.xact->payee, true)); +} + +void report_commodities::flush() +{ + std::ostream& out(report.output_stream); + + foreach (commodities_pair& entry, commodities) + out << *entry.first << '\n'; +} + +void report_commodities::operator()(post_t& post) +{ + amount_t temp(post.amount.strip_annotations(report.what_to_keep())); + commodity_t& comm(temp.commodity()); + + std::map::iterator i = commodities.find(&comm); + if (i == commodities.end()) + commodities.insert(commodities_pair(&comm, true)); + + if (comm.has_annotation()) { + annotated_commodity_t& ann_comm(as_annotated_commodity(comm)); + if (ann_comm.details.price) { + std::map::iterator i = + commodities.find(&ann_comm.details.price->commodity()); + if (i == commodities.end()) + commodities.insert + (commodities_pair(&ann_comm.details.price->commodity(), true)); + } + } + + if (post.cost) { + amount_t temp_cost(post.cost->strip_annotations(report.what_to_keep())); + i = commodities.find(&temp_cost.commodity()); + if (i == commodities.end()) + commodities.insert(commodities_pair(&temp_cost.commodity(), true)); + } +} + } // namespace ledger diff --git a/src/output.h b/src/output.h index 7618e567..3e70d9fe 100644 --- a/src/output.h +++ b/src/output.h @@ -103,6 +103,69 @@ public: virtual void operator()(account_t& account); }; +class report_accounts : public item_handler +{ +protected: + report_t& report; + + std::map accounts; + + typedef std::map::value_type accounts_pair; + +public: + report_accounts(report_t& _report) : report(_report) { + TRACE_CTOR(report_accounts, "report&"); + } + virtual ~report_accounts() { + TRACE_DTOR(report_accounts); + } + + virtual void flush(); + virtual void operator()(post_t& post); +}; + +class report_payees : public item_handler +{ +protected: + report_t& report; + + std::map payees; + + typedef std::map::value_type payees_pair; + +public: + report_payees(report_t& _report) : report(_report) { + TRACE_CTOR(report_payees, "report&"); + } + virtual ~report_payees() { + TRACE_DTOR(report_payees); + } + + virtual void flush(); + virtual void operator()(post_t& post); +}; + +class report_commodities : public item_handler +{ +protected: + report_t& report; + + std::map commodities; + + typedef std::map::value_type commodities_pair; + +public: + report_commodities(report_t& _report) : report(_report) { + TRACE_CTOR(report_commodities, "report&"); + } + virtual ~report_commodities() { + TRACE_DTOR(report_commodities); + } + + virtual void flush(); + virtual void operator()(post_t& post); +}; + } // namespace ledger #endif // _OUTPUT_H diff --git a/src/report.cc b/src/report.cc index 1180c019..509be8b1 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1222,6 +1222,12 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case symbol_t::COMMAND: switch (*p) { + case 'a': + if (is_eq(p, "accounts")) + return WRAP_FUNCTOR(reporter<>(new report_accounts(*this), *this, + "#accounts")); + break; + case 'b': if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { return expr_t::op_t::wrap_functor @@ -1262,8 +1268,13 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, maybe_format(HANDLER(prepend_format_))), *this, "#cleared")); } - else if (is_eq(p, "convert")) + else if (is_eq(p, "convert")) { return WRAP_FUNCTOR(convert_command); + } + else if (is_eq(p, "commodities")) { + return WRAP_FUNCTOR(reporter<>(new report_commodities(*this), *this, + "#commodities")); + } break; case 'e': @@ -1296,6 +1307,9 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, (new format_posts(*this, report_format(HANDLER(pricedb_format_)), maybe_format(HANDLER(prepend_format_))), *this, "#pricedb")); + else if (is_eq(p, "payees")) + return WRAP_FUNCTOR(reporter<>(new report_payees(*this), *this, + "#payees")); break; case 'r': -- cgit v1.2.3 From 02e78255166b17bff08a408166cc56ff9269c925 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 17:04:38 -0400 Subject: Option --count sums payees, account, commodities --- src/output.cc | 43 +++++++++++++++++++++++++++++++------------ src/output.h | 12 ++++++------ src/report.cc | 1 + src/report.h | 1 + 4 files changed, 39 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/output.cc b/src/output.cc index e3aa9f4a..ec1faba6 100644 --- a/src/output.cc +++ b/src/output.cc @@ -236,38 +236,51 @@ void report_accounts::flush() { std::ostream& out(report.output_stream); - foreach (accounts_pair& entry, accounts) + foreach (accounts_pair& entry, accounts) { + if (report.HANDLED(count)) + out << entry.second << ' '; out << *entry.first << '\n'; + } } void report_accounts::operator()(post_t& post) { - std::map::iterator i = accounts.find(post.account); + std::map::iterator i = accounts.find(post.account); if (i == accounts.end()) - accounts.insert(accounts_pair(post.account, true)); + accounts.insert(accounts_pair(post.account, 1)); + else + (*i).second++; } void report_payees::flush() { std::ostream& out(report.output_stream); - foreach (payees_pair& entry, payees) + foreach (payees_pair& entry, payees) { + if (report.HANDLED(count)) + out << entry.second << ' '; out << entry.first << '\n'; + } } void report_payees::operator()(post_t& post) { - std::map::iterator i = payees.find(post.xact->payee); + std::map::iterator i = payees.find(post.xact->payee); if (i == payees.end()) - payees.insert(payees_pair(post.xact->payee, true)); + payees.insert(payees_pair(post.xact->payee, 1)); + else + (*i).second++; } void report_commodities::flush() { std::ostream& out(report.output_stream); - foreach (commodities_pair& entry, commodities) + foreach (commodities_pair& entry, commodities) { + if (report.HANDLED(count)) + out << entry.second << ' '; out << *entry.first << '\n'; + } } void report_commodities::operator()(post_t& post) @@ -275,18 +288,22 @@ void report_commodities::operator()(post_t& post) amount_t temp(post.amount.strip_annotations(report.what_to_keep())); commodity_t& comm(temp.commodity()); - std::map::iterator i = commodities.find(&comm); + std::map::iterator i = commodities.find(&comm); if (i == commodities.end()) - commodities.insert(commodities_pair(&comm, true)); + commodities.insert(commodities_pair(&comm, 1)); + else + (*i).second++; if (comm.has_annotation()) { annotated_commodity_t& ann_comm(as_annotated_commodity(comm)); if (ann_comm.details.price) { - std::map::iterator i = + std::map::iterator i = commodities.find(&ann_comm.details.price->commodity()); if (i == commodities.end()) commodities.insert - (commodities_pair(&ann_comm.details.price->commodity(), true)); + (commodities_pair(&ann_comm.details.price->commodity(), 1)); + else + (*i).second++; } } @@ -294,7 +311,9 @@ void report_commodities::operator()(post_t& post) amount_t temp_cost(post.cost->strip_annotations(report.what_to_keep())); i = commodities.find(&temp_cost.commodity()); if (i == commodities.end()) - commodities.insert(commodities_pair(&temp_cost.commodity(), true)); + commodities.insert(commodities_pair(&temp_cost.commodity(), 1)); + else + (*i).second++; } } diff --git a/src/output.h b/src/output.h index 3e70d9fe..3a7b68df 100644 --- a/src/output.h +++ b/src/output.h @@ -108,9 +108,9 @@ class report_accounts : public item_handler protected: report_t& report; - std::map accounts; + std::map accounts; - typedef std::map::value_type accounts_pair; + typedef std::map::value_type accounts_pair; public: report_accounts(report_t& _report) : report(_report) { @@ -129,9 +129,9 @@ class report_payees : public item_handler protected: report_t& report; - std::map payees; + std::map payees; - typedef std::map::value_type payees_pair; + typedef std::map::value_type payees_pair; public: report_payees(report_t& _report) : report(_report) { @@ -150,9 +150,9 @@ class report_commodities : public item_handler protected: report_t& report; - std::map commodities; + std::map commodities; - typedef std::map::value_type commodities_pair; + typedef std::map::value_type commodities_pair; public: report_commodities(report_t& _report) : report(_report) { diff --git a/src/report.cc b/src/report.cc index 509be8b1..d2db87b0 100644 --- a/src/report.cc +++ b/src/report.cc @@ -853,6 +853,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(columns_); else OPT_ALT(basis, cost); else OPT_(current); + else OPT(count); break; case 'd': OPT(daily); diff --git a/src/report.h b/src/report.h index 6b10dbcc..783f0026 100644 --- a/src/report.h +++ b/src/report.h @@ -454,6 +454,7 @@ public: }); OPTION(report_t, columns_); + OPTION(report_t, count); OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) { on(none, -- cgit v1.2.3 From 33aa0cc3a6ad9485198c0e5abe694822811483b4 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 17:08:16 -0400 Subject: Changed the report generated by the csv command Fields are now: Date,Code,Payee,Account,Commodity,Total,State,Note Instead of outputting amounts potentially as $1,000.00 (which was an error anyway), the output is now: $,1000.00. This makes the commodity available in a separate field, and removes display of thousands markers. Also, european formatting is always off. --- src/post.cc | 14 ++++++++++++-- src/report.h | 5 +++-- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index fb3281a7..18c566c4 100644 --- a/src/post.cc +++ b/src/post.cc @@ -187,11 +187,21 @@ namespace { } value_t get_commodity(post_t& post) { - return string_value(post.amount.commodity().symbol()); + if (post.has_xdata() && + post.xdata().has_flags(POST_EXT_COMPOUND)) + return string_value(post.xdata().compound_value.to_amount() + .commodity().symbol()); + else + return string_value(post.amount.commodity().symbol()); } value_t get_commodity_is_primary(post_t& post) { - return post.amount.commodity().has_flags(COMMODITY_PRIMARY); + if (post.has_xdata() && + post.xdata().has_flags(POST_EXT_COMPOUND)) + return post.xdata().compound_value.to_amount() + .commodity().has_flags(COMMODITY_PRIMARY); + else + return post.amount.commodity().has_flags(COMMODITY_PRIMARY); } value_t get_has_cost(post_t& post) { diff --git a/src/report.h b/src/report.h index 783f0026..df2f3469 100644 --- a/src/report.h +++ b/src/report.h @@ -459,11 +459,12 @@ public: OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) { on(none, "%(quoted(date))," + "%(quoted(code))," "%(quoted(payee))," "%(quoted(account))," - "%(quoted(scrub(display_amount)))," + "%(quoted(commodity))," + "%(quoted(quantity(scrub(display_amount))))," "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," - "%(quoted(code))," "%(quoted(join(note | xact.note)))\n"); }); -- cgit v1.2.3 From d397d5a2fc1cae219f8f040a5be99cc21df55d32 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 17:50:13 -0400 Subject: Made the output from --options better looking --- src/option.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/option.h b/src/option.h index 9688171e..cd3cd594 100644 --- a/src/option.h +++ b/src/option.h @@ -90,13 +90,16 @@ public: void report(std::ostream& out) const { if (handled && source) { + out.width(24); + out << std::right << desc(); if (wants_arg) { - out << desc() << " => "; - value.dump(out); + out << " = "; + value.print(out, 42); } else { - out << desc(); + out.width(45); + out << ' '; } - out << " <" << *source << ">" << std::endl; + out << std::left << *source << std::endl; } } -- cgit v1.2.3 From 3e1ec40551184010d6ff3272d68b3ff17ff1ad26 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 18:19:21 -0400 Subject: Report an error for incorrect dates like 2010/04/32 Fixes EF57C685-2C18-49A1-9A8C-FB3BE6F99C41 --- src/times.cc | 54 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/times.cc b/src/times.cc index be488baf..00be1d3b 100644 --- a/src/times.cc +++ b/src/times.cc @@ -199,23 +199,32 @@ namespace { { date_t when; - if (std::strchr(date_str, '/')) { - when = io.parse(date_str); - } else { - char buf[128]; - VERIFY(std::strlen(date_str) < 127); - std::strcpy(buf, date_str); + VERIFY(std::strlen(date_str) < 127); - for (char * p = buf; *p; p++) - if (*p == '.' || *p == '-') - *p = '/'; + char buf[128]; + std::strcpy(buf, date_str); - when = io.parse(buf); - } + for (char * p = buf; *p; p++) + if (*p == '.' || *p == '-') + *p = '/'; + + when = io.parse(buf); if (! when.is_not_a_date()) { - DEBUG("times.parse", "Parsed date string: " << date_str); - DEBUG("times.parse", "Parsed result is: " << when); + DEBUG("times.parse", "Passed date string: " << date_str); + DEBUG("times.parse", "Parsed date string: " << buf); + DEBUG("times.parse", "Parsed result is: " << when); + DEBUG("times.parse", "Formatted result is: " << io.format(when)); + + const char * p = io.format(when).c_str(); + const char * q = buf; + for (; *p != '\0' && *q != '\0'; + p++, q++) { + if (*p != *q && *p == '0') p++; + if (*p != *q) break; + } + if (*p != '\0' || *q != '\0') + throw_(date_error, _("Invalid date: %1") << date_str); if (traits) *traits = io.traits; @@ -1299,14 +1308,14 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() // "2009/08/01", but also dates that fit the user's --input-date-format, // assuming their format fits in one argument and begins with a digit. if (std::isdigit(*begin)) { - try { - string::const_iterator i = begin; - for (i = begin; i != end && ! std::isspace(*i); i++) {} - assert(i != begin); + string::const_iterator i = begin; + for (i = begin; i != end && ! std::isspace(*i); i++) {} + assert(i != begin); - string possible_date(start, i); - date_traits_t traits; + string possible_date(start, i); + try { + date_traits_t traits; date_t when = parse_date_mask(possible_date.c_str(), none, &traits); if (! when.is_not_a_date()) { begin = i; @@ -1314,7 +1323,12 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() token_t::content_t(date_specifier_t(when, traits))); } } - catch (...) {} + catch (date_error&) { + if (contains(possible_date, "/") || + contains(possible_date, "-") || + contains(possible_date, ".")) + throw; + } } start = begin; -- cgit v1.2.3 From a3482606dc33b88d2ae661e49c6b15b902497421 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 18:45:42 -0400 Subject: Improved error reporting in the expression parser Fixes 15A80F68-F233-49D9-AF0C-9908BB6903BA --- src/parser.cc | 9 ++------- src/parser.h | 5 +++-- src/token.cc | 47 ++++++++++++++++++++++++++++++++++++----------- src/token.h | 7 ++++--- 4 files changed, 45 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/parser.cc b/src/parser.cc index e8e987cb..f52949ce 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -79,9 +79,7 @@ expr_t::parser_t::parse_value_term(std::istream& in, case token_t::LPAREN: node = parse_value_expr(in, tflags.plus_flags(PARSE_PARTIAL) .minus_flags(PARSE_SINGLE)); - tok = next_token(in, tflags); - if (tok.kind != token_t::RPAREN) - tok.expected(')'); + tok = next_token(in, tflags, ')'); if (node->kind == op_t::O_CONS) { ptr_op_t prev(node); @@ -383,10 +381,7 @@ expr_t::parser_t::parse_querycolon_expr(std::istream& in, throw_(parse_error, _("%1 operator not followed by argument") << tok.symbol); - token_t& next_tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - if (next_tok.kind != token_t::COLON) - next_tok.expected(':'); - + next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT), ':'); prev = node->right(); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(prev); diff --git a/src/parser.h b/src/parser.h index 5eba4ffd..aab48830 100644 --- a/src/parser.h +++ b/src/parser.h @@ -52,11 +52,12 @@ class expr_t::parser_t : public noncopyable mutable token_t lookahead; mutable bool use_lookahead; - token_t& next_token(std::istream& in, const parse_flags_t& tflags) const { + token_t& next_token(std::istream& in, const parse_flags_t& tflags, + const char expecting = '\0') const { if (use_lookahead) use_lookahead = false; else - lookahead.next(in, tflags); + lookahead.next(in, tflags, expecting); return lookahead; } diff --git a/src/token.cc b/src/token.cc index a34cdcd0..add97b8b 100644 --- a/src/token.cc +++ b/src/token.cc @@ -138,7 +138,8 @@ void expr_t::token_t::parse_ident(std::istream& in) value.set_string(buf); } -void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) +void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, + const char expecting) { if (in.eof()) { kind = TOK_EOF; @@ -423,6 +424,13 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags) expected('\0', c); parse_ident(in); + + if (value.as_string().length() == 0) { + kind = ERROR; + symbol[0] = c; + symbol[1] = '\0'; + unexpected(expecting); + } } else { kind = VALUE; value = temp; @@ -447,21 +455,38 @@ void expr_t::token_t::rewind(std::istream& in) } -void expr_t::token_t::unexpected() +void expr_t::token_t::unexpected(const char wanted) { kind_t prev_kind = kind; kind = ERROR; - switch (prev_kind) { - case TOK_EOF: - throw_(parse_error, _("Unexpected end of expression")); - case IDENT: - throw_(parse_error, _("Unexpected symbol '%1'") << value); - case VALUE: - throw_(parse_error, _("Unexpected value '%1'") << value); - default: - throw_(parse_error, _("Unexpected token '%1'") << symbol); + if (wanted == '\0') { + switch (prev_kind) { + case TOK_EOF: + throw_(parse_error, _("Unexpected end of expression")); + case IDENT: + throw_(parse_error, _("Unexpected symbol '%1'") << value); + case VALUE: + throw_(parse_error, _("Unexpected value '%1'") << value); + default: + throw_(parse_error, _("Unexpected expression token '%1'") << symbol); + } + } else { + switch (prev_kind) { + case TOK_EOF: + throw_(parse_error, + _("Unexpected end of expression (wanted '%1')" << wanted)); + case IDENT: + throw_(parse_error, + _("Unexpected symbol '%1' (wanted '%2')") << value << wanted); + case VALUE: + throw_(parse_error, + _("Unexpected value '%1' (wanted '%2')") << value << wanted); + default: + throw_(parse_error, _("Unexpected expression token '%1' (wanted '%2')") + << symbol << wanted); + } } } diff --git a/src/token.h b/src/token.h index 8d70996b..582373cb 100644 --- a/src/token.h +++ b/src/token.h @@ -124,10 +124,11 @@ struct expr_t::token_t : public noncopyable int parse_reserved_word(std::istream& in); void parse_ident(std::istream& in); - void next(std::istream& in, const parse_flags_t& flags); + void next(std::istream& in, const parse_flags_t& flags, + const char expecting = '\0'); void rewind(std::istream& in); - void unexpected(); - void expected(char wanted, char c = '\0'); + void unexpected(const char wanted = '\0'); + void expected(const char wanted, char c = '\0'); }; } // namespace ledger -- cgit v1.2.3 From 925b70d5c60849a2ca6857fcc5727f0d87d14757 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 18:50:35 -0400 Subject: Ommitting args to the entry command print nothing --- src/draft.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/draft.cc b/src/draft.cc index 18075731..69dc7025 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -240,6 +240,9 @@ void draft_t::parse_args(const value_t& args) xact_t * draft_t::insert(journal_t& journal) { + if (! tmpl) + return NULL; + if (tmpl->payee_mask.empty()) throw std::runtime_error(_("'xact' command requires at least a payee")); @@ -528,7 +531,8 @@ value_t xact_command(call_scope_t& args) // Only consider actual postings for the "xact" command report.HANDLER(limit_).on(string("#xact"), "actual"); - report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact); + if (new_xact) + report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact); return true; } -- cgit v1.2.3 From db5418c8386e4a1a1b72e0bcdba2eeb22f99b96f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 19:16:11 -0400 Subject: Made a peculiar error slightly more verbose --- src/filters.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 0c45d356..13e188b1 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -869,7 +869,8 @@ void budget_posts::report_budget_items(const date_t& date) optional begin = pair.first.start; if (! begin) { if (! pair.first.find_period(date)) - throw_(std::runtime_error, _("Something odd has happened")); + throw_(std::runtime_error, + _("Something odd has happened at date %1") << date); begin = pair.first.start; } assert(begin); -- cgit v1.2.3 From a7c28aa20057525a9247d0ae69eb063b53b21811 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 19:17:13 -0400 Subject: Added new option --prepend-width This is useful for making sure that the column containing the results of --prepend-format is a consistent width throughout the report (including those lines where it is not applied). Fixes 64F9D913-75E1-4830-A3D9-29B72442E68B --- src/output.cc | 23 ++++++++++++++++------- src/output.h | 22 +++++++++++++--------- src/report.cc | 24 +++++++++++++++++------- src/report.h | 15 ++++++++++----- 4 files changed, 56 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/output.cc b/src/output.cc index ec1faba6..183d80b3 100644 --- a/src/output.cc +++ b/src/output.cc @@ -42,8 +42,10 @@ namespace ledger { format_posts::format_posts(report_t& _report, const string& format, - const optional& _prepend_format) - : report(_report), last_xact(NULL), last_post(NULL) + const optional& _prepend_format, + std::size_t _prepend_width) + : report(_report), prepend_width(_prepend_width), + last_xact(NULL), last_post(NULL) { TRACE_CTOR(format_posts, "report&, const string&, bool"); @@ -82,8 +84,10 @@ void format_posts::operator()(post_t& post) ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { bind_scope_t bound_scope(report, post); - if (prepend_format) + if (prepend_format) { + out.width(prepend_width); out << prepend_format(bound_scope); + } if (last_xact != post.xact) { if (last_xact) { @@ -107,8 +111,9 @@ void format_posts::operator()(post_t& post) format_accounts::format_accounts(report_t& _report, const string& format, - const optional& _prepend_format) - : report(_report), disp_pred() + const optional& _prepend_format, + std::size_t _prepend_width) + : report(_report), prepend_width(_prepend_width), disp_pred() { TRACE_CTOR(format_accounts, "report&, const string&"); @@ -144,9 +149,11 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat) bind_scope_t bound_scope(report, account); - if (prepend_format) + if (prepend_format) { + static_cast(report.output_stream).width(prepend_width); static_cast(report.output_stream) << prepend_format(bound_scope); + } static_cast(report.output_stream) << account_line_format(bound_scope); @@ -216,9 +223,11 @@ void format_accounts::flush() bind_scope_t bound_scope(report, *report.session.journal->master); out << separator_format(bound_scope); - if (prepend_format) + if (prepend_format) { + static_cast(report.output_stream).width(prepend_width); static_cast(report.output_stream) << prepend_format(bound_scope); + } out << total_line_format(bound_scope); } diff --git a/src/output.h b/src/output.h index 3a7b68df..00c664c1 100644 --- a/src/output.h +++ b/src/output.h @@ -56,17 +56,19 @@ class report_t; class format_posts : public item_handler { protected: - report_t& report; - format_t first_line_format; - format_t next_lines_format; - format_t between_format; - format_t prepend_format; - xact_t * last_xact; - post_t * last_post; + report_t& report; + format_t first_line_format; + format_t next_lines_format; + format_t between_format; + format_t prepend_format; + std::size_t prepend_width; + xact_t * last_xact; + post_t * last_post; public: format_posts(report_t& _report, const string& format, - const optional& _prepend_format = none); + const optional& _prepend_format = none, + std::size_t _prepend_width = 0); virtual ~format_posts() { TRACE_DTOR(format_posts); } @@ -83,13 +85,15 @@ protected: format_t total_line_format; format_t separator_format; format_t prepend_format; + std::size_t prepend_width; predicate_t disp_pred; std::list posted_accounts; public: format_accounts(report_t& _report, const string& _format, - const optional& _prepend_format = none); + const optional& _prepend_format = none, + std::size_t _prepend_width = 0); virtual ~format_accounts() { TRACE_DTOR(format_accounts); } diff --git a/src/report.cc b/src/report.cc index d2db87b0..4c8f4060 100644 --- a/src/report.cc +++ b/src/report.cc @@ -119,6 +119,8 @@ void report_t::normalize_options(const string& verb) HANDLER(meta_).str() + "\"))"); } } + if (! HANDLED(prepend_width_)) + HANDLER(prepend_width_).on_with(string("?normalize"), static_cast(0)); if (verb == "print" || verb == "xact" || verb == "dump") { HANDLER(related).on_only(string("?normalize")); @@ -937,6 +939,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(pricedb_format_); else OPT(payee_width_); else OPT(prepend_format_); + else OPT(prepend_width_); else OPT(print_virtual); break; case 'q': @@ -1234,7 +1237,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return expr_t::op_t::wrap_functor (reporter (new format_accounts(*this, report_format(HANDLER(balance_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#balance")); } else if (is_eq(p, "budget")) { @@ -1247,7 +1251,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return expr_t::op_t::wrap_functor (reporter (new format_accounts(*this, report_format(HANDLER(budget_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#budget")); } break; @@ -1257,7 +1262,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(csv_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#csv")); } else if (is_eq(p, "cleared")) { @@ -1266,7 +1272,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return expr_t::op_t::wrap_functor (reporter (new format_accounts(*this, report_format(HANDLER(cleared_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#cleared")); } else if (is_eq(p, "convert")) { @@ -1300,13 +1307,15 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return expr_t::op_t::wrap_functor (reporter (new format_posts(*this, report_format(HANDLER(prices_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#prices")); else if (is_eq(p, "pricedb")) return expr_t::op_t::wrap_functor (reporter (new format_posts(*this, report_format(HANDLER(pricedb_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#pricedb")); else if (is_eq(p, "payees")) return WRAP_FUNCTOR(reporter<>(new report_payees(*this), *this, @@ -1318,7 +1327,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(register_format_)), - maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_)), + HANDLER(prepend_width_).value.to_long()), *this, "#register")); else if (is_eq(p, "reload")) return MAKE_FUNCTOR(report_t::reload_command); diff --git a/src/report.h b/src/report.h index df2f3469..64c14858 100644 --- a/src/report.h +++ b/src/report.h @@ -280,6 +280,7 @@ public: HANDLER(plot_amount_format_).report(out); HANDLER(plot_total_format_).report(out); HANDLER(prepend_format_).report(out); + HANDLER(prepend_width_).report(out); HANDLER(price).report(out); HANDLER(prices_format_).report(out); HANDLER(pricedb_format_).report(out); @@ -372,7 +373,7 @@ public: OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { on(none, - "%(justify(scrub(display_total), 20, -1, true, color))" + "%(justify(scrub(display_total), 20, 20 + prepend_width, true, color))" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1\n%/" @@ -432,8 +433,9 @@ public: OPTION__(report_t, cleared_format_, CTOR(report_t, cleared_format_) { on(none, - "%(justify(scrub(get_at(total_expr, 0)), 16, -1, true, color))" - " %(justify(scrub(get_at(total_expr, 1)), 16, -1, true, color))" + "%(justify(scrub(get_at(total_expr, 0)), 16, 16 + prepend_width, " + " true, color)) %(justify(scrub(get_at(total_expr, 1)), 18, " + " 36 + prepend_width, true, color))" " %(latest_cleared ? format_date(latest_cleared) : \" \")" " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" @@ -737,6 +739,9 @@ public: }); OPTION(report_t, prepend_format_); + OPTION_(report_t, prepend_width_, DO_(args) { + value = args[1].to_long(); + }); OPTION_(report_t, price, DO() { // -I parent->HANDLER(display_amount_) @@ -784,10 +789,10 @@ public: " account_width), blue if color))" " %(justify(scrub(display_amount), amount_width, " " 3 + meta_width + date_width + payee_width + account_width" - " + amount_width, true, color))" + " + amount_width + prepend_width, true, color))" " %(justify(scrub(display_total), total_width, " " 4 + meta_width + date_width + payee_width + account_width" - " + amount_width + total_width, true, color))\n%/" + " + amount_width + total_width + prepend_width, true, color))\n%/" "%(justify(\" \", 2 + date_width + payee_width))" "%$3 %$4 %$5\n"); }); -- cgit v1.2.3 From 9557a9d955f4272ac4ad7b727af18ab388b1f92e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 20:50:43 -0400 Subject: A minor fix to transaction auto-balancing It allows transactions like the following to auto-balance: 1999/08/16 Sell AAPL Assets:Broker $585 Expense:Broker:Commissions $15 Assets:Broker -10 AAPL {$30} @ $60 Income:Capital Gains --- src/xact.cc | 85 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 40 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/xact.cc b/src/xact.cc index 3c2505af..f63835c9 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -172,44 +172,8 @@ bool xact_base_t::finalize() add_post(null_post); } - if (null_post != NULL) { - // If one post has no value at all, its value will become the inverse of - // the rest. If multiple commodities are involved, multiple posts are - // generated to balance them all. - - DEBUG("xact.finalize", "there was a null posting"); - - if (balance.is_balance()) { - bool first = true; - const balance_t& bal(balance.as_balance()); - foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { - if (first) { - null_post->amount = pair.second.negated(); - null_post->add_flags(POST_CALCULATED); - first = false; - } else { - post_t * p = new post_t(null_post->account, pair.second.negated(), - ITEM_GENERATED | POST_CALCULATED); - p->set_state(null_post->state()); - add_post(p); - } - } - } - else if (balance.is_amount()) { - null_post->amount = balance.as_amount().negated(); - null_post->add_flags(POST_CALCULATED); - } - else if (balance.is_long()) { - null_post->amount = amount_t(- balance.as_long()); - null_post->add_flags(POST_CALCULATED); - } - else if (! balance.is_null() && ! balance.is_realzero()) { - throw_(balance_error, _("Transaction does not balance")); - } - balance = NULL_VALUE; - } - else if (balance.is_balance() && - balance.as_balance().amounts.size() == 2) { + if (balance.is_balance() && + balance.as_balance().amounts.size() == 2) { // When an xact involves two different commodities (regardless of how // many posts there are) determine the conversion ratio by dividing the // total value of one commodity by the total value of the other. This @@ -293,12 +257,6 @@ bool xact_base_t::finalize() } } - // Now that the post list has its final form, calculate the balance once - // more in terms of total cost, accounting for any possible gain/loss - // amounts. - - DEBUG("xact.finalize", "resolved balance = " << balance); - posts_list copy(posts); foreach (post_t * post, copy) { @@ -340,7 +298,44 @@ bool xact_base_t::finalize() } } - DEBUG("xact.finalize", "final balance = " << balance); + if (null_post != NULL) { + // If one post has no value at all, its value will become the inverse of + // the rest. If multiple commodities are involved, multiple posts are + // generated to balance them all. + + DEBUG("xact.finalize", "there was a null posting"); + + if (balance.is_balance()) { + bool first = true; + const balance_t& bal(balance.as_balance()); + foreach (const balance_t::amounts_map::value_type& pair, bal.amounts) { + if (first) { + null_post->amount = pair.second.negated(); + null_post->add_flags(POST_CALCULATED); + first = false; + } else { + post_t * p = new post_t(null_post->account, pair.second.negated(), + ITEM_GENERATED | POST_CALCULATED); + p->set_state(null_post->state()); + add_post(p); + } + } + } + else if (balance.is_amount()) { + null_post->amount = balance.as_amount().negated(); + null_post->add_flags(POST_CALCULATED); + } + else if (balance.is_long()) { + null_post->amount = amount_t(- balance.as_long()); + null_post->add_flags(POST_CALCULATED); + } + else if (! balance.is_null() && ! balance.is_realzero()) { + throw_(balance_error, _("Transaction does not balance")); + } + balance = NULL_VALUE; + + } + DEBUG("xact.finalize", "resolved balance = " << balance); if (! balance.is_null() && ! balance.is_zero()) { add_error_context(item_context(*this, _("While balancing transaction"))); -- cgit v1.2.3 From b5c9be4d29fab1388e195600d659602ae19b8f4e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 21:05:30 -0400 Subject: Created new valexpr variable display_account Where display_account might be '(Expenses:Food)', account will always be 'Expenses:Food'. account is now used by all matching and query operations, while display_account is used in the various report outputs (besides balance, which never distinguished virtual accounts). Fixes F2832452-4521-49A3-B854-F4E12CC4D82E --- src/post.cc | 32 ++++++++++++++++++++++++-------- src/report.h | 10 +++++----- 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/post.cc b/src/post.cc index 18c566c4..f1f3e96a 100644 --- a/src/post.cc +++ b/src/post.cc @@ -236,7 +236,7 @@ namespace { return 1L; } - value_t get_account(call_scope_t& scope) + value_t account_name(call_scope_t& scope) { in_context_t env(scope, "&v"); @@ -279,14 +279,28 @@ namespace { } else { name = env->reported_account()->fullname(); } + return string_value(name); + } - if (env->has_flags(POST_VIRTUAL)) { - if (env->must_balance()) - name = string("[") + name + "]"; - else - name = string("(") + name + ")"; + value_t get_display_account(call_scope_t& scope) + { + in_context_t env(scope, "&v"); + + value_t acct = account_name(scope); + if (acct.is_string()) { + if (env->has_flags(POST_VIRTUAL)) { + if (env->must_balance()) + acct = string_value(string("[") + acct.as_string() + "]"); + else + acct = string_value(string("(") + acct.as_string() + ")"); + } } - return string_value(name); + return acct; + } + + value_t get_account(call_scope_t& scope) + { + return account_name(scope); } value_t get_account_id(post_t& post) { @@ -398,7 +412,9 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, break; case 'd': - if (name == "depth") + if (name == "display_account") + return WRAP_FUNCTOR(get_display_account); + else if (name == "depth") return WRAP_FUNCTOR(get_wrapper<&get_account_depth>); else if (name == "datetime") return WRAP_FUNCTOR(get_wrapper<&get_datetime>); diff --git a/src/report.h b/src/report.h index 64c14858..aff4af91 100644 --- a/src/report.h +++ b/src/report.h @@ -463,7 +463,7 @@ public: "%(quoted(date))," "%(quoted(code))," "%(quoted(payee))," - "%(quoted(account))," + "%(quoted(display_account))," "%(quoted(commodity))," "%(quoted(quantity(scrub(display_amount))))," "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," @@ -752,13 +752,13 @@ public: OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) { on(none, - "%(date) %-8(account) %(justify(scrub(display_amount), 12, " + "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, " " 2 + 9 + 8 + 12, true, color))\n"); }); OPTION__(report_t, pricedb_format_, CTOR(report_t, pricedb_format_) { on(none, - "P %(datetime) %(account) %(scrub(display_amount))\n"); + "P %(datetime) %(display_account) %(scrub(display_amount))\n"); }); OPTION(report_t, print_virtual); @@ -785,8 +785,8 @@ public: " if color & date > today))" " %(ansify_if(justify(truncated(payee, payee_width), payee_width), " " bold if color & !cleared & actual))" - " %(ansify_if(justify(truncated(account, account_width, abbrev_len), " - " account_width), blue if color))" + " %(ansify_if(justify(truncated(display_account, account_width, " + " abbrev_len), account_width), blue if color))" " %(justify(scrub(display_amount), amount_width, " " 3 + meta_width + date_width + payee_width + account_width" " + amount_width + prepend_width, true, color))" -- cgit v1.2.3 From 37a3f27ef2897a0d2bf129126f45c4ab0b5000bd Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 21:32:50 -0400 Subject: Corrected handling of context stack in the parser Fixes F4A477E6-C4F6-43B1-ABCC-4DC325C2869A --- src/textual.cc | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index d953da26..16a6e228 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -95,7 +95,6 @@ namespace { public: parse_context_t& context; instance_t * parent; - account_t * master; accounts_map account_aliases; const path * original_file; path pathname; @@ -109,7 +108,6 @@ namespace { instance_t(parse_context_t& _context, std::istream& _in, - account_t * _master = NULL, const path * _original_file = NULL, instance_t * _parent = NULL); @@ -200,30 +198,17 @@ namespace { 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) + : context(_context), parent(_parent), original_file(_original_file), + pathname(original_file ? *original_file : "/dev/stdin"), in(_in) { TRACE_CTOR(instance_t, "..."); - - if (! master) - master = context.journal.master; - context.state_stack.push_front(master); - - if (_original_file) - pathname = *_original_file; - else - pathname = "/dev/stdin"; } instance_t::~instance_t() { TRACE_DTOR(instance_t); - - assert(! context.state_stack.empty()); - context.state_stack.pop_front(); } void instance_t::parse() @@ -411,8 +396,7 @@ void instance_t::read_next_directive() #if defined(TIMELOG_SUPPORT) -void instance_t::clock_in_directive(char * line, - bool /*capitalized*/) +void instance_t::clock_in_directive(char * line, bool /*capitalized*/) { string datetime(line, 2, 19); @@ -441,8 +425,7 @@ void instance_t::clock_in_directive(char * line, context.timelog.clock_in(event); } -void instance_t::clock_out_directive(char * line, - bool /*capitalized*/) +void instance_t::clock_out_directive(char * line, bool /*capitalized*/) { string datetime(line, 2, 19); @@ -689,7 +672,7 @@ void instance_t::include_directive(char * line) if (glob.match(base)) { path inner_file(*iter); ifstream stream(inner_file); - instance_t instance(context, stream, master, &inner_file, this); + instance_t instance(context, stream, &inner_file, this); instance.parse(); files_found = true; } @@ -1451,8 +1434,10 @@ std::size_t journal_t::parse(std::istream& in, parse_context_t context(*this, scope); context.strict = strict; + if (master || this->master) + context.state_stack.push_front(master ? master : this->master); - instance_t instance(context, in, master, original_file); + instance_t instance(context, in, original_file); instance.parse(); TRACE_STOP(parsing_total, 1); -- cgit v1.2.3 From c645ac1de7b7b492eb9096e1a16937c9d3539795 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 22:04:26 -0400 Subject: If FMT_CUSTOM is given, and no format, use FMT_PRINTED --- src/times.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/times.cc b/src/times.cc index 00be1d3b..35082f51 100644 --- a/src/times.cc +++ b/src/times.cc @@ -1471,7 +1471,7 @@ std::string format_datetime(const datetime_t& when, if (format_type == FMT_WRITTEN) { return written_datetime_io->format(when); } - else if (format_type == FMT_CUSTOM || format) { + else if (format_type == FMT_CUSTOM && format) { datetime_io_map::iterator i = temp_datetime_io.find(*format); if (i != temp_datetime_io.end()) { return (*i).second->format(when); @@ -1497,7 +1497,7 @@ std::string format_date(const date_t& when, if (format_type == FMT_WRITTEN) { return written_date_io->format(when); } - else if (format_type == FMT_CUSTOM || format) { + else if (format_type == FMT_CUSTOM && format) { date_io_map::iterator i = temp_date_io.find(*format); if (i != temp_date_io.end()) { return (*i).second->format(when); -- cgit v1.2.3 From 0f3e7e3e7fcba2b9f99dcc101b1c937ffa0fd56e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 22:05:03 -0400 Subject: The print command now honors use of --date-format --- src/print.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index 34f5af51..7964b001 100644 --- a/src/print.cc +++ b/src/print.cc @@ -71,11 +71,18 @@ namespace { void print_xact(report_t& report, std::ostream& out, xact_t& xact) { + format_type_t format_type = FMT_WRITTEN; + optional format; + + if (report.HANDLED(date_format_)) { + format_type = FMT_CUSTOM; + format = report.HANDLER(date_format_).str().c_str(); + } + out << format_date(item_t::use_effective_date ? - xact.date() : xact.actual_date(), - FMT_WRITTEN); + xact.date() : xact.actual_date(), format_type, format); if (! item_t::use_effective_date && xact.effective_date()) - out << '=' << format_date(*xact.effective_date(), FMT_WRITTEN); + out << '=' << format_date(*xact.effective_date(), format_type, format); out << ' '; out << (xact.state() == item_t::CLEARED ? "* " : -- cgit v1.2.3 From 7bd0170d51dd3cf20764b6748a9ea4884f9ee7bc Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 22 May 2010 23:22:26 -0600 Subject: print honors --columns, --account,amount-width --- src/print.cc | 81 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/print.cc b/src/print.cc index 7964b001..5c46f4e7 100644 --- a/src/print.cc +++ b/src/print.cc @@ -48,9 +48,14 @@ print_xacts::print_xacts(report_t& _report, } namespace { - void print_note(std::ostream& out, const string& note) + void print_note(std::ostream& out, + const string& note, + const std::size_t columns, + const std::size_t prior_width) { - if (note.length() > 15) + // The 4 is for four leading spaces at the beginning of the posting, and + // the 3 is for two spaces and a semi-colon before the note. + if (columns > 0 && note.length() > columns - (prior_width + 3)) out << "\n ;"; else out << " ;"; @@ -79,22 +84,32 @@ namespace { format = report.HANDLER(date_format_).str().c_str(); } - out << format_date(item_t::use_effective_date ? - xact.date() : xact.actual_date(), format_type, format); + std::ostringstream buf; + + buf << format_date(item_t::use_effective_date ? + xact.date() : xact.actual_date(), + format_type, format); if (! item_t::use_effective_date && xact.effective_date()) - out << '=' << format_date(*xact.effective_date(), format_type, format); - out << ' '; + buf << '=' << format_date(*xact.effective_date(), + format_type, format); + buf << ' '; - out << (xact.state() == item_t::CLEARED ? "* " : + buf << (xact.state() == item_t::CLEARED ? "* " : (xact.state() == item_t::PENDING ? "! " : "")); if (xact.code) - out << '(' << *xact.code << ") "; + buf << '(' << *xact.code << ") "; + + buf << xact.payee; + + string leader = buf.str(); + out << leader; - out << xact.payee; + std::size_t columns = (report.HANDLED(columns_) ? + report.HANDLER(columns_).value.to_long() : 80); if (xact.note) - print_note(out, *xact.note); + print_note(out, *xact.note, columns, unistring(leader).length()); out << '\n'; if (xact.metadata) { @@ -139,20 +154,37 @@ namespace { buf << ')'; } - if (! post->has_flags(POST_CALCULATED) || report.HANDLED(print_virtual)) { - unistring name(buf.str()); + unistring name(buf.str()); + + std::size_t account_width = + (report.HANDLER(account_width_).specified ? + report.HANDLER(account_width_).value.to_long() : 36); + + if (account_width < name.length()) + account_width = name.length(); + if (! post->has_flags(POST_CALCULATED) || report.HANDLED(print_virtual)) { out << name.extract(); - int slip = 36 - static_cast(name.length()); - if (slip > 0) - out << string(slip, ' '); + int slip = (static_cast(account_width) - + static_cast(name.length())); + if (slip > 0) { + out.width(slip); + out << ' '; + } + + std::ostringstream amtbuf; string amt; if (post->amount_expr) { amt = post->amount_expr->text(); } else { + std::size_t amount_width = + (report.HANDLER(amount_width_).specified ? + report.HANDLER(amount_width_).value.to_long() : 12); + std::ostringstream amt_str; - report.scrub(post->amount).print(amt_str, 12, -1, true); + report.scrub(post->amount) + .print(amt_str, static_cast(amount_width), -1, true); amt = amt_str.str(); } @@ -161,24 +193,29 @@ namespace { int amt_slip = (static_cast(amt.length()) - static_cast(trimmed_amt.length())); if (slip + amt_slip < 2) - out << string(2 - (slip + amt_slip), ' '); - out << amt; + amtbuf << string(2 - (slip + amt_slip), ' '); + amtbuf << amt; if (post->cost && ! post->has_flags(POST_CALCULATED)) { if (post->has_flags(POST_COST_IN_FULL)) - out << " @@ " << report.scrub(post->cost->abs()); + amtbuf << " @@ " << report.scrub(post->cost->abs()); else - out << " @ " << report.scrub((*post->cost / post->amount).abs()); + amtbuf << " @ " << report.scrub((*post->cost / post->amount).abs()); } if (post->assigned_amount) - out << " = " << report.scrub(*post->assigned_amount); + amtbuf << " = " << report.scrub(*post->assigned_amount); + + string trailer = amtbuf.str(); + out << trailer; + + account_width += unistring(trailer).length(); } else { out << buf.str(); } if (post->note) - print_note(out, *post->note); + print_note(out, *post->note, columns, 4 + account_width); out << '\n'; } } -- cgit v1.2.3 From 3fab2e1333dc0da4614900f7e231579a12d2e1c8 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 23 May 2010 00:34:26 -0600 Subject: For Boost 1.35 and earlier, use is_regular Fixes 0F17CB7F-A000-4F99-8471-739948AD575F --- src/textual.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/textual.cc b/src/textual.cc index 16a6e228..2b204df0 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -663,7 +663,12 @@ void instance_t::include_directive(char * line) for (filesystem::directory_iterator iter(parent_path); iter != end; ++iter) { - if (is_regular_file(*iter)) { +#if BOOST_VERSION <= 103500 + if (is_regular(*iter)) +#else + if (is_regular_file(*iter)) +#endif + { #if BOOST_VERSION >= 103700 string base = (*iter).filename(); #else // BOOST_VERSION >= 103700 -- cgit v1.2.3 From 834d223c27f8240122dcc0de0e614f1b2e62c08e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 23 May 2010 00:37:45 -0600 Subject: Do not generate names with double underscores Fixes 6A4AFDC2-DE87-48A5-A17D-B04120EE1F62 --- src/option.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/option.h b/src/option.h index cd3cd594..f11497a4 100644 --- a/src/option.h +++ b/src/option.h @@ -205,20 +205,20 @@ public: }; #define BEGIN(type, name) \ - struct name ## _option_t : public option_t + struct name ## option_t : public option_t #define CTOR(type, name) \ - name ## _option_t() : option_t(#name) + name ## option_t() : option_t(#name) #define DECL1(type, name, vartype, var, value) \ vartype var ; \ - name ## _option_t() : option_t(#name), var(value) + name ## option_t() : option_t(#name), var(value) #define DO() virtual void handler_thunk(call_scope_t&) #define DO_(var) virtual void handler_thunk(call_scope_t& var) -#define END(name) name ## _handler +#define END(name) name ## handler -#define COPY_OPT(name, other) name ## _handler(other.name ## _handler) +#define COPY_OPT(name, other) name ## handler(other.name ## handler) #define MAKE_OPT_HANDLER(type, x) \ expr_t::op_t::wrap_functor(bind(&option_t::handler_wrapper, x, _1)) @@ -238,26 +238,26 @@ inline bool is_eq(const char * p, const char * n) { #define OPT(name) \ if (is_eq(p, #name)) \ - return ((name ## _handler).parent = this, &(name ## _handler)) + return ((name ## handler).parent = this, &(name ## handler)) #define OPT_ALT(name, alt) \ if (is_eq(p, #name) || is_eq(p, #alt)) \ - return ((name ## _handler).parent = this, &(name ## _handler)) + return ((name ## handler).parent = this, &(name ## handler)) #define OPT_(name) \ if (! *(p + 1) || \ - ((name ## _handler).wants_arg && \ + ((name ## handler).wants_arg && \ *(p + 1) == '_' && ! *(p + 2)) || \ is_eq(p, #name)) \ - return ((name ## _handler).parent = this, &(name ## _handler)) + return ((name ## handler).parent = this, &(name ## handler)) #define OPT_CH(name) \ if (! *(p + 1) || \ - ((name ## _handler).wants_arg && \ + ((name ## handler).wants_arg && \ *(p + 1) == '_' && ! *(p + 2))) \ - return ((name ## _handler).parent = this, &(name ## _handler)) + return ((name ## handler).parent = this, &(name ## handler)) -#define HANDLER(name) name ## _handler +#define HANDLER(name) name ## handler #define HANDLED(name) HANDLER(name) #define OPTION(type, name) \ -- cgit v1.2.3 From efcede3ca5ce31603ef8454a0bd6c19ef67b2aeb Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 23 May 2010 01:11:16 -0600 Subject: Fix to an interaction between --period and --sort Fixes 3AAB00ED-9904-4380-8988-16506B0AFE08 --- src/report.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 4c8f4060..dfdc77cc 100644 --- a/src/report.cc +++ b/src/report.cc @@ -145,9 +145,6 @@ void report_t::normalize_options(const string& verb) // then ignore the period since the begin/end are the only interesting // details. if (HANDLED(period_)) { - if (! HANDLED(sort_all_)) - HANDLER(sort_xacts_).on_only(string("?normalize")); - date_interval_t interval(HANDLER(period_).str()); optional begin = interval.begin(session.current_year); @@ -164,6 +161,8 @@ void report_t::normalize_options(const string& verb) if (! interval.duration) HANDLER(period_).off(); + else if (! HANDLED(sort_all_)) + HANDLER(sort_xacts_).on_only(string("?normalize")); } // If -j or -J were specified, set the appropriate format string now so as -- cgit v1.2.3 From 847a5e4e73afd1c959f7211ceb67d6b9ab0f95d0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 23 May 2010 15:23:19 -0600 Subject: Optimized several "in_place" function in balance_t --- src/balance.h | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/balance.h b/src/balance.h index f8455d49..5c00c55a 100644 --- a/src/balance.h +++ b/src/balance.h @@ -321,10 +321,8 @@ public: return temp; } void in_place_round() { - balance_t temp; - foreach (const amounts_map::value_type& pair, amounts) - temp += pair.second.rounded(); - *this = temp; + foreach (amounts_map::value_type& pair, amounts) + pair.second.in_place_round(); } balance_t truncated() const { @@ -333,10 +331,8 @@ public: return temp; } void in_place_truncate() { - balance_t temp; - foreach (const amounts_map::value_type& pair, amounts) - temp += pair.second.truncated(); - *this = temp; + foreach (amounts_map::value_type& pair, amounts) + pair.second.in_place_truncate(); } balance_t floored() const { @@ -345,10 +341,8 @@ public: return temp; } void in_place_floor() { - balance_t temp; - foreach (const amounts_map::value_type& pair, amounts) - temp += pair.second.floored(); - *this = temp; + foreach (amounts_map::value_type& pair, amounts) + pair.second.in_place_floor(); } balance_t unrounded() const { @@ -357,10 +351,8 @@ public: return temp; } void in_place_unround() { - balance_t temp; - foreach (const amounts_map::value_type& pair, amounts) - temp += pair.second.unrounded(); - *this = temp; + foreach (amounts_map::value_type& pair, amounts) + pair.second.in_place_unround(); } balance_t reduced() const { -- cgit v1.2.3 From 7bddcd676bc53c6caad7dd283be362fcd53d5721 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 23 May 2010 15:24:02 -0600 Subject: Added --rounding option, which is off by default The purpose of this option is to add special "" postings, to ensure that a regiter's running total is *always* the sum of its postings. Within --rounding, these adjustment postings are missing, which was the behavior in Ledger 2.x. It can be orders of magnitude slower to turn it on for large reports with many commodities. --- src/chain.cc | 3 ++- src/filters.cc | 13 ++++++++----- src/filters.h | 4 +++- src/report.cc | 1 + src/report.h | 2 ++ test/ConfirmTests.py | 2 +- 6 files changed, 17 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 86f639ad..44133391 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -86,7 +86,8 @@ post_handler_ptr chain_post_handlers(report_t& report, report.HANDLED(unrealized))) handler.reset(new changed_value_posts(handler, report, for_accounts_report, - report.HANDLED(unrealized))); + report.HANDLED(unrealized), + report.HANDLED(rounding))); // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the diff --git a/src/filters.cc b/src/filters.cc index 13e188b1..57c95cd3 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -420,10 +420,12 @@ void related_posts::flush() changed_value_posts::changed_value_posts(post_handler_ptr handler, report_t& _report, bool _for_accounts_report, - bool _show_unrealized) + bool _show_unrealized, + bool _show_rounding) : item_handler(handler), report(_report), for_accounts_report(_for_accounts_report), - show_unrealized(_show_unrealized), last_post(NULL), + show_unrealized(_show_unrealized), + show_rounding(_show_rounding), last_post(NULL), revalued_account(temps.create_account(_(""))), rounding_account(temps.create_account(_(""))) { @@ -505,9 +507,10 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) /* total= */ repriced_total, /* direct_amount= */ false, /* mark_visited= */ false, - /* functor= */ (optional + /* functor= */ (show_rounding ? + optional (bind(&changed_value_posts::output_rounding, - this, _1)))); + this, _1)) : none)); } else if (show_unrealized) { handle_value @@ -572,7 +575,7 @@ void changed_value_posts::operator()(post_t& post) if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); - if (! for_accounts_report) + if (! for_accounts_report && show_rounding) output_rounding(post); item_handler::operator()(post); diff --git a/src/filters.h b/src/filters.h index 82fbf687..ced51f3f 100644 --- a/src/filters.h +++ b/src/filters.h @@ -381,6 +381,7 @@ class changed_value_posts : public item_handler bool changed_values_only; bool for_accounts_report; bool show_unrealized; + bool show_rounding; post_t * last_post; value_t last_total; value_t last_display_total; @@ -396,7 +397,8 @@ public: changed_value_posts(post_handler_ptr handler, report_t& _report, bool _for_accounts_report, - bool _show_unrealized); + bool _show_unrealized, + bool _show_rounding); virtual ~changed_value_posts() { TRACE_DTOR(changed_value_posts); diff --git a/src/report.cc b/src/report.cc index dfdc77cc..486c0fdf 100644 --- a/src/report.cc +++ b/src/report.cc @@ -954,6 +954,7 @@ option_t * report_t::lookup_option(const char * p) else OPT(revalued); else OPT(revalued_only); else OPT(revalued_total_); + else OPT(rounding); break; case 's': OPT(sort_); diff --git a/src/report.h b/src/report.h index aff4af91..aa72832c 100644 --- a/src/report.h +++ b/src/report.h @@ -295,6 +295,7 @@ public: HANDLER(revalued).report(out); HANDLER(revalued_only).report(out); HANDLER(revalued_total_).report(out); + HANDLER(rounding).report(out); HANDLER(seed_).report(out); HANDLER(sort_).report(out); HANDLER(sort_all_).report(out); @@ -818,6 +819,7 @@ public: set_expr(args[0].to_string(), args[1].to_string()); }); + OPTION(report_t, rounding); OPTION(report_t, seed_); OPTION_(report_t, sort_, DO_(args) { // -S diff --git a/test/ConfirmTests.py b/test/ConfirmTests.py index 901ae7cd..6fc04336 100755 --- a/test/ConfirmTests.py +++ b/test/ConfirmTests.py @@ -84,7 +84,7 @@ def confirm_report(command): return not failure for cmd in commands: - if confirm_report('$ledger $cmd ' + re.sub('\$tests', tests, cmd)): + if confirm_report('$ledger --rounding $cmd ' + re.sub('\$tests', tests, cmd)): harness.success() else: harness.failure() -- cgit v1.2.3 From 04461f49fdc0f70d74172c77843be3e0a9fe313f Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 26 May 2010 00:58:04 -0600 Subject: Optimized amount_t::in_place_truncate --- src/amount.cc | 38 ++++++++++++++++++++++++++++++++++++++ src/amount.h | 4 +--- test/regress/25A099C9.test | 12 ++++++------ 3 files changed, 45 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index 3a64577f..a16d287e 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -594,6 +594,44 @@ void amount_t::in_place_round() set_keep_precision(false); } +void amount_t::in_place_truncate() +{ +#if 1 + if (! quantity) + throw_(amount_error, _("Cannot truncate an uninitialized amount")); + + _dup(); + + DEBUG("amount.truncate", + "Truncating " << *this << " to precision " << display_precision()); + + std::ostringstream out; + stream_out_mpq(out, MP(quantity), display_precision()); + + scoped_array buf(new char [out.str().length() + 1]); + std::strcpy(buf.get(), out.str().c_str()); + + char * q = buf.get(); + for (char * p = q; *p != '\0'; p++, q++) { + if (*p == '.') p++; + if (p != q) *q = *p; + } + *q = '\0'; + + mpq_set_str(MP(quantity), buf.get(), 10); + + mpz_ui_pow_ui(temp, 10, display_precision()); + mpq_set_z(tempq, temp); + mpq_div(MP(quantity), MP(quantity), tempq); + + DEBUG("amount.truncate", "Truncated = " << *this); +#else + // This naive implementation is straightforward, but extremely inefficient + // as it requires parsing the commodity too, which might be fully annotated. + *this = amount_t(to_string()); +#endif +} + void amount_t::in_place_floor() { if (! quantity) diff --git a/src/amount.h b/src/amount.h index 5c1bca46..ae0e5a69 100644 --- a/src/amount.h +++ b/src/amount.h @@ -346,9 +346,7 @@ public: temp.in_place_truncate(); return temp; } - void in_place_truncate() { - *this = amount_t(to_string()); - } + void in_place_truncate(); /** Yields an amount which has lost all of its extra precision, beyond what the display precision of the commodity would have printed. */ diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test index 4067d005..a74b0601 100644 --- a/test/regress/25A099C9.test +++ b/test/regress/25A099C9.test @@ -4,16 +4,16 @@ >>>2 While parsing file "$sourcepath/src/amount.h", line 67: Error: No quantity specified for amount -While parsing file "$sourcepath/src/amount.h", line 720: +While parsing file "$sourcepath/src/amount.h", line 718: Error: Invalid date/time: line amount_t amoun -While parsing file "$sourcepath/src/amount.h", line 726: +While parsing file "$sourcepath/src/amount.h", line 724: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 732: +While parsing file "$sourcepath/src/amount.h", line 730: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 738: +While parsing file "$sourcepath/src/amount.h", line 736: Error: Invalid date/time: line string amount_ -While parsing file "$sourcepath/src/amount.h", line 744: +While parsing file "$sourcepath/src/amount.h", line 742: Error: Invalid date/time: line std::ostream& -While parsing file "$sourcepath/src/amount.h", line 751: +While parsing file "$sourcepath/src/amount.h", line 749: Error: Invalid date/time: line std::istream& === 7 -- cgit v1.2.3 From 51115f0a91b81453fc0f7b055df5a4ea13ee70a7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 27 May 2010 21:23:50 -0600 Subject: Updated to Boost 1.43 --- acprep | 24 ++++++++++++------------ lib/Makefile | 2 +- src/py_account.cc | 6 +++--- src/py_commodity.cc | 13 ++++++++----- src/py_journal.cc | 19 ++++++++++--------- src/py_xact.cc | 4 ++-- 6 files changed, 36 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/acprep b/acprep index a611e3e9..b7c5e177 100755 --- a/acprep +++ b/acprep @@ -1122,13 +1122,13 @@ class PrepareBuild(CommandLineApp): self.log.debug('We are using GLIBCXX_DEBUG, so setting up flags') self.CPPFLAGS.append('-D_GLIBCXX_DEBUG=1') - if self.boost_info.configure(home_path = '/usr/local/stow/boost_1_42_0', - suffix = '-xgcc44-sd-1_42', - include_path = 'include/boost-1_42'): + if self.boost_info.configure(home_path = '/usr/local/stow/boost_1_43_0', + suffix = '-xgcc44-sd-1_43', + include_path = 'include/boost-1_43'): pass - elif self.boost_info.configure(home_path = '/usr/local/stow/boost_1_42_0', - suffix = '-xgcc44-d-1_42', - include_path = 'include/boost-1_42'): + elif self.boost_info.configure(home_path = '/usr/local/stow/boost_1_43_0', + suffix = '-xgcc44-d-1_43', + include_path = 'include/boost-1_43'): pass elif self.boost_info.configure(suffix = '-d'): pass @@ -1136,13 +1136,13 @@ class PrepareBuild(CommandLineApp): else: if self.boost_info.configure(): pass - elif self.boost_info.configure(home_path = '/usr/local/stow/boost_1_42_0', - suffix = '-xgcc44-s-1_42', - include_path = 'include/boost-1_42'): + elif self.boost_info.configure(home_path = '/usr/local/stow/boost_1_43_0', + suffix = '-xgcc44-s-1_43', + include_path = 'include/boost-1_43'): pass - elif self.boost_info.configure(home_path = '/usr/local/stow/boost_1_42_0', - suffix = '-xgcc44-1_42', - include_path = 'include/boost-1_42'): + elif self.boost_info.configure(home_path = '/usr/local/stow/boost_1_43_0', + suffix = '-xgcc44-1_43', + include_path = 'include/boost-1_43'): pass def setup_flavor_default(self): diff --git a/lib/Makefile b/lib/Makefile index f0f2b3d1..14605176 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -12,7 +12,7 @@ CPPFLAGS = -D_GLIBCXX_DEBUG=1 -D_GLIBCXX_FULLY_DYNAMIC_STRING=1 CFLAGS = $(CPPFLAGS) -g LDFLAGS = -g -BOOST_VERSION = 1_42_0 +BOOST_VERSION = 1_43_0 BOOST_SOURCE = boost_$(BOOST_VERSION) BOOST_TOOLSET = darwin BOOST_DEFINES = define=_GLIBCXX_DEBUG=1 define=_GLIBCXX_FULLY_DYNAMIC_STRING=1 diff --git a/src/py_account.cc b/src/py_account.cc index 3114cc0b..2b860a24 100644 --- a/src/py_account.cc +++ b/src/py_account.cc @@ -207,11 +207,11 @@ void export_account() .def("__len__", accounts_len) .def("__getitem__", accounts_getitem, return_internal_reference<>()) - .def("__iter__", range > + .def("__iter__", python::range > (&account_t::accounts_begin, &account_t::accounts_end)) - .def("accounts", range > + .def("accounts", python::range > (&account_t::accounts_begin, &account_t::accounts_end)) - .def("posts", range > + .def("posts", python::range > (&account_t::posts_begin, &account_t::posts_end)) .def("has_xdata", &account_t::has_xdata) diff --git a/src/py_commodity.cc b/src/py_commodity.cc index 22a4f153..d89a7151 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -295,13 +295,16 @@ void export_commodity() .def("keys", py_pool_keys) .def("has_key", py_pool_contains) .def("__contains__", py_pool_contains) - .def("__iter__", range > + .def("__iter__", + python::range > (py_pool_commodities_begin, py_pool_commodities_end)) - .def("iteritems", range > + .def("iteritems", + python::range > (py_pool_commodities_begin, py_pool_commodities_end)) - .def("iterkeys", range<>(py_pool_commodities_keys_begin, - py_pool_commodities_keys_end)) - .def("itervalues", range > + .def("iterkeys", python::range<>(py_pool_commodities_keys_begin, + py_pool_commodities_keys_end)) + .def("itervalues", + python::range > (py_pool_commodities_values_begin, py_pool_commodities_values_end)) ; diff --git a/src/py_journal.cc b/src/py_journal.cc index 81ce290d..1848adc4 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -226,8 +226,8 @@ void export_journal() class_< collect_posts, bases >, shared_ptr, boost::noncopyable >("PostCollector") .def("__len__", &collect_posts::length) - .def("__iter__", range > > + .def("__iter__", python::range > > (&collect_posts::begin, &collect_posts::end)) ; @@ -236,8 +236,9 @@ void export_journal() .def("__len__", &collector_wrapper::length) .def("__getitem__", posts_getitem, return_internal_reference<1, with_custodian_and_ward_postcall<0, 1> >()) - .def("__iter__", range > > + .def("__iter__", + python::range > > (&collector_wrapper::begin, &collector_wrapper::end)) ; @@ -296,15 +297,15 @@ void export_journal() with_custodian_and_ward_postcall<0, 1> >()) #endif - .def("__iter__", range > + .def("__iter__", python::range > (&journal_t::xacts_begin, &journal_t::xacts_end)) - .def("xacts", range > + .def("xacts", python::range > (&journal_t::xacts_begin, &journal_t::xacts_end)) - .def("auto_xacts", range > + .def("auto_xacts", python::range > (&journal_t::auto_xacts_begin, &journal_t::auto_xacts_end)) - .def("period_xacts", range > + .def("period_xacts", python::range > (&journal_t::period_xacts_begin, &journal_t::period_xacts_end)) - .def("sources", range > + .def("sources", python::range > (&journal_t::sources_begin, &journal_t::sources_end)) .def("read", py_read) diff --git a/src/py_xact.cc b/src/py_xact.cc index c4e1fd32..6553a67f 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -98,9 +98,9 @@ void export_xact() .def("finalize", &xact_base_t::finalize) - .def("__iter__", range > + .def("__iter__", python::range > (&xact_t::posts_begin, &xact_t::posts_end)) - .def("posts", range > + .def("posts", python::range > (&xact_t::posts_begin, &xact_t::posts_end)) .def("valid", &xact_base_t::valid) -- cgit v1.2.3 From 66a26252df84909224ec18b4ca2adaaca3b01268 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:15:45 -0600 Subject: Corrected error message text to be consistent --- src/value.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index ce9b0e6a..2888c335 100644 --- a/src/value.cc +++ b/src/value.cc @@ -832,7 +832,7 @@ bool value_t::is_equal_to(const value_t& val) const break; } - throw_(value_error, _("Cannot compare %1 by %2") << label() << val.label()); + throw_(value_error, _("Cannot compare %1 to %2") << label() << val.label()); return *this; } -- cgit v1.2.3 From 7ec52d2b395bf4cfc656eef52d72b9d83c1c1523 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:16:32 -0600 Subject: Comparison of boolean values is now allowed true > false --- src/value.cc | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index 2888c335..9515de46 100644 --- a/src/value.cc +++ b/src/value.cc @@ -840,6 +840,23 @@ bool value_t::is_equal_to(const value_t& val) const bool value_t::is_less_than(const value_t& val) const { switch (type()) { + case BOOLEAN: + if (val.is_boolean()) { + if (as_boolean()) { + if (! val.as_boolean()) + return false; + else + return false; + } + else if (! as_boolean()) { + if (! val.as_boolean()) + return false; + else + return true; + } + } + break; + case DATETIME: if (val.is_datetime()) return as_datetime() < val.as_datetime(); @@ -935,6 +952,22 @@ bool value_t::is_less_than(const value_t& val) const bool value_t::is_greater_than(const value_t& val) const { switch (type()) { + if (val.is_boolean()) { + if (as_boolean()) { + if (! val.as_boolean()) + return true; + else + return false; + } + else if (! as_boolean()) { + if (! val.as_boolean()) + return false; + else + return false; + } + } + break; + case DATETIME: if (val.is_datetime()) return as_datetime() > val.as_datetime(); -- cgit v1.2.3 From 8f17d01f5e48ae5097f4cb38d481b00577329b8c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:20:34 -0600 Subject: Added new required item_handler_t::clear() method --- src/chain.h | 5 ++ src/filters.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/output.h | 29 ++++++++++ src/temps.cc | 45 +++++++++------- src/temps.h | 6 ++- src/xml.h | 8 +++ 6 files changed, 227 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/chain.h b/src/chain.h index 94d54317..fbde9f0a 100644 --- a/src/chain.h +++ b/src/chain.h @@ -75,6 +75,11 @@ public: (*handler.get())(item); } } + + virtual void clear() { + if (handler) + handler->clear(); + } }; typedef shared_ptr > post_handler_ptr; diff --git a/src/filters.h b/src/filters.h index ced51f3f..3f3e3d34 100644 --- a/src/filters.h +++ b/src/filters.h @@ -88,6 +88,11 @@ public: virtual void operator()(post_t& post) { posts.push_back(&post); } + + virtual void clear() { + posts.clear(); + item_handler::clear(); + } }; class posts_iterator; @@ -149,27 +154,34 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + completed = false; + posts.clear(); + xacts_seen = 0; + last_xact = NULL; + + item_handler::clear(); + } }; class sort_posts : public item_handler { typedef std::deque posts_deque; - posts_deque posts; - const expr_t sort_order; + posts_deque posts; + expr_t sort_order; sort_posts(); public: - sort_posts(post_handler_ptr handler, - const expr_t& _sort_order) + sort_posts(post_handler_ptr handler, const expr_t& _sort_order) : item_handler(handler), sort_order(_sort_order) { TRACE_CTOR(sort_posts, "post_handler_ptr, const value_expr&"); } - sort_posts(post_handler_ptr handler, - const string& _sort_order) + sort_posts(post_handler_ptr handler, const string& _sort_order) : item_handler(handler), sort_order(_sort_order) { TRACE_CTOR(sort_posts, @@ -189,6 +201,13 @@ public: virtual void operator()(post_t& post) { posts.push_back(&post); } + + virtual void clear() { + posts.clear(); + sort_order.mark_uncompiled(); + + item_handler::clear(); + } }; class sort_xacts : public item_handler @@ -199,14 +218,12 @@ class sort_xacts : public item_handler sort_xacts(); public: - sort_xacts(post_handler_ptr handler, - const expr_t& _sort_order) + sort_xacts(post_handler_ptr handler, const expr_t& _sort_order) : sorter(handler, _sort_order) { TRACE_CTOR(sort_xacts, "post_handler_ptr, const value_expr&"); } - sort_xacts(post_handler_ptr handler, - const string& _sort_order) + sort_xacts(post_handler_ptr handler, const string& _sort_order) : sorter(handler, _sort_order) { TRACE_CTOR(sort_xacts, "post_handler_ptr, const string&"); @@ -228,6 +245,13 @@ public: last_xact = post.xact; } + + virtual void clear() { + sorter.clear(); + last_xact = NULL; + + item_handler::clear(); + } }; class filter_posts : public item_handler @@ -255,6 +279,11 @@ public: (*handler)(post); } } + + virtual void clear() { + pred.mark_uncompiled(); + item_handler::clear(); + } }; class anonymize_posts : public item_handler @@ -274,6 +303,13 @@ public: } virtual void operator()(post_t& post); + + virtual void clear() { + temps.clear(); + last_xact = NULL; + + item_handler::clear(); + } }; class calc_posts : public item_handler @@ -297,6 +333,13 @@ public: } virtual void operator()(post_t& post); + + virtual void clear() { + last_post = NULL; + amount_expr.mark_uncompiled(); + + item_handler::clear(); + } }; class collapse_posts : public item_handler @@ -334,13 +377,29 @@ public: } virtual void flush() { - report_subtotal(); + report_subtotal(); item_handler::flush(); } void report_subtotal(); virtual void operator()(post_t& post); + + virtual void clear() { + amount_expr.mark_uncompiled(); + display_predicate.mark_uncompiled(); + only_predicate.mark_uncompiled(); + + subtotal = value_t(); + count = 0; + last_xact = NULL; + last_post = NULL; + + temps.clear(); + component_posts.clear(); + + item_handler::clear(); + } }; class related_posts : public item_handler @@ -367,6 +426,11 @@ public: post.xdata().add_flags(POST_EXT_RECEIVED); posts.push_back(&post); } + + virtual void clear() { + posts.clear(); + item_handler::clear(); + } }; class changed_value_posts : public item_handler @@ -410,6 +474,20 @@ public: void output_rounding(post_t& post); virtual void operator()(post_t& post); + + virtual void clear() { + display_amount_expr.mark_uncompiled(); + total_expr.mark_uncompiled(); + display_total_expr.mark_uncompiled(); + + last_post = NULL; + last_total = value_t(); + last_display_total = value_t(); + + temps.clear(); + + item_handler::clear(); + } }; class subtotal_posts : public item_handler @@ -471,10 +549,20 @@ public: item_handler::flush(); } virtual void operator()(post_t& post); + + virtual void clear() { + amount_expr.mark_uncompiled(); + values.clear(); + temps.clear(); + component_posts.clear(); + + item_handler::clear(); + } }; class interval_posts : public subtotal_posts { + date_interval_t start_interval; date_interval_t interval; date_interval_t last_interval; post_t * last_post; @@ -491,8 +579,9 @@ public: const date_interval_t& _interval, bool _exact_periods = false, bool _generate_empty_posts = false) - : subtotal_posts(_handler, amount_expr), interval(_interval), - last_post(NULL), empty_account(temps.create_account(_(""))), + : subtotal_posts(_handler, amount_expr), start_interval(_interval), + interval(start_interval), last_post(NULL), + empty_account(temps.create_account(_(""))), exact_periods(_exact_periods), generate_empty_posts(_generate_empty_posts) { TRACE_CTOR(interval_posts, @@ -512,6 +601,14 @@ public: } } virtual void operator()(post_t& post); + + virtual void clear() { + interval = start_interval; + last_interval = date_interval_t(); + last_post = NULL; + + item_handler::clear(); + } }; class posts_as_equity : public subtotal_posts @@ -539,6 +636,11 @@ public: report_subtotal(); subtotal_posts::flush(); } + + virtual void clear() { + last_post = NULL; + item_handler::clear(); + } }; class by_payee_posts : public item_handler @@ -562,6 +664,13 @@ class by_payee_posts : public item_handler virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + amount_expr.mark_uncompiled(); + payee_subtotals.clear(); + + item_handler::clear(); + } }; class transfer_details : public item_handler @@ -595,6 +704,13 @@ public: } virtual void operator()(post_t& post); + + virtual void clear() { + expr.mark_uncompiled(); + temps.clear(); + + item_handler::clear(); + } }; class dow_posts : public subtotal_posts @@ -616,6 +732,13 @@ public: virtual void operator()(post_t& post) { days_of_the_week[post.date().day_of_week()].push_back(&post); } + + virtual void clear() { + for (int i = 0; i < 7; i++) + days_of_the_week[i].clear(); + + item_handler::clear(); + } }; class generate_posts : public item_handler @@ -642,6 +765,13 @@ public: void add_period_xacts(period_xacts_list& period_xacts); virtual void add_post(const date_interval_t& period, post_t& post); + + virtual void clear() { + pending_posts.clear(); + temps.clear(); + + item_handler::clear(); + } }; class budget_posts : public generate_posts @@ -692,6 +822,11 @@ class forecast_posts : public generate_posts virtual void add_post(const date_interval_t& period, post_t& post); virtual void flush(); + + virtual void clear() { + pred.mark_uncompiled(); + item_handler::clear(); + } }; ////////////////////////////////////////////////////////////////////// @@ -717,6 +852,13 @@ public: virtual ~pass_down_accounts() { TRACE_DTOR(pass_down_accounts); } + + virtual void clear() { + if (pred) + pred->mark_uncompiled(); + + item_handler::clear(); + } }; } // namespace ledger diff --git a/src/output.h b/src/output.h index 00c664c1..f0e7f9a5 100644 --- a/src/output.h +++ b/src/output.h @@ -75,6 +75,13 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + last_xact = NULL; + last_post = NULL; + + item_handler::clear(); + } }; class format_accounts : public item_handler @@ -105,6 +112,13 @@ public: virtual void flush(); virtual void operator()(account_t& account); + + virtual void clear() { + disp_pred.mark_uncompiled(); + posted_accounts.clear(); + + item_handler::clear(); + } }; class report_accounts : public item_handler @@ -126,6 +140,11 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + accounts.clear(); + item_handler::clear(); + } }; class report_payees : public item_handler @@ -147,6 +166,11 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + payees.clear(); + item_handler::clear(); + } }; class report_commodities : public item_handler @@ -168,6 +192,11 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + commodities.clear(); + item_handler::clear(); + } }; } // namespace ledger diff --git a/src/temps.cc b/src/temps.cc index dcaa9101..7a630176 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -38,26 +38,6 @@ namespace ledger { -temporaries_t::~temporaries_t() -{ - if (post_temps) { - foreach (post_t& post, *post_temps) { - if (! post.xact->has_flags(ITEM_TEMP)) - post.xact->remove_post(&post); - - if (post.account && ! post.account->has_flags(ACCOUNT_TEMP)) - post.account->remove_post(&post); - } - } - - if (acct_temps) { - foreach (account_t& acct, *acct_temps) { - if (acct.parent && ! acct.parent->has_flags(ACCOUNT_TEMP)) - acct.parent->remove_account(&acct); - } - } -} - xact_t& temporaries_t::copy_xact(xact_t& origin) { if (! xact_temps) @@ -134,4 +114,29 @@ account_t& temporaries_t::create_account(const string& name, return temp; } +void temporaries_t::clear() +{ + if (post_temps) { + foreach (post_t& post, *post_temps) { + if (! post.xact->has_flags(ITEM_TEMP)) + post.xact->remove_post(&post); + + if (post.account && ! post.account->has_flags(ACCOUNT_TEMP)) + post.account->remove_post(&post); + } + post_temps->clear(); + } + + if (xact_temps) + xact_temps->clear(); + + if (acct_temps) { + foreach (account_t& acct, *acct_temps) { + if (acct.parent && ! acct.parent->has_flags(ACCOUNT_TEMP)) + acct.parent->remove_account(&acct); + } + acct_temps->clear(); + } +} + } // namespace ledger diff --git a/src/temps.h b/src/temps.h index ac6d08cd..210bbf63 100644 --- a/src/temps.h +++ b/src/temps.h @@ -51,7 +51,9 @@ class temporaries_t optional > acct_temps; public: - ~temporaries_t(); + ~temporaries_t() { + clear(); + } xact_t& copy_xact(xact_t& origin); xact_t& create_xact(); @@ -69,6 +71,8 @@ public: account_t& last_account() { return acct_temps->back(); } + + void clear(); }; } // namespace ledger diff --git a/src/xml.h b/src/xml.h index 320096f8..5d14dab3 100644 --- a/src/xml.h +++ b/src/xml.h @@ -83,6 +83,14 @@ public: virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + commodities.clear(); + transactions_set.clear(); + transactions.clear(); + + item_handler::clear(); + } }; } // namespace ledger -- cgit v1.2.3 From 4d372a8e1ecd4203fe8d9de24e945ef793e4f039 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:26:17 -0600 Subject: Added value_scope_t, for wrapping a value in a scope The value expression "value" may be used to extract the wrapped value. This is currently only used by the upcoming --group-title-format option. --- src/scope.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src') diff --git a/src/scope.h b/src/scope.h index 30ba6823..1e6f24a1 100644 --- a/src/scope.h +++ b/src/scope.h @@ -354,6 +354,30 @@ inline T& find_scope(child_scope_t& scope, bool skip_this = true) return reinterpret_cast(scope); // never executed } +class value_scope_t : public scope_t +{ + value_t value; + + value_t get_value(call_scope_t&) { + return value; + } + +public: + value_scope_t(const value_t& _value) : value(_value) {} + + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, + const string& name) + { + if (kind != symbol_t::FUNCTION) + return NULL; + + if (name == "value") + return MAKE_FUNCTOR(value_scope_t::get_value); + + return NULL; + } +}; + } // namespace ledger #endif // _SCOPE_H -- cgit v1.2.3 From 3215fd71a499648f5ff8e992bd1ff66e963858a0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:27:25 -0600 Subject: Added optional item_handler::title() method --- src/chain.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/chain.h b/src/chain.h index fbde9f0a..935f2935 100644 --- a/src/chain.h +++ b/src/chain.h @@ -65,14 +65,19 @@ public: TRACE_DTOR(item_handler); } + virtual void title(const string& str) { + if (handler) + handler->title(str); + } + virtual void flush() { - if (handler.get()) + if (handler) handler->flush(); } virtual void operator()(T& item) { - if (handler.get()) { + if (handler) { check_for_signal(); - (*handler.get())(item); + (*handler)(item); } } -- cgit v1.2.3 From f491979d5547742aae70b3f6dd5b4aa0eac36605 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:41:06 -0600 Subject: Added new option: --no-titles --- src/report.cc | 1 + src/report.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index 486c0fdf..4dee4bb2 100644 --- a/src/report.cc +++ b/src/report.cc @@ -916,6 +916,7 @@ option_t * report_t::lookup_option(const char * p) case 'n': OPT_CH(collapse); else OPT(no_color); + else OPT(no_titles); else OPT(no_total); else OPT(now_); break; diff --git a/src/report.h b/src/report.h index aa72832c..104cfc73 100644 --- a/src/report.h +++ b/src/report.h @@ -267,6 +267,7 @@ public: HANDLER(market).report(out); HANDLER(meta_).report(out); HANDLER(monthly).report(out); + HANDLER(no_titles).report(out); HANDLER(no_total).report(out); HANDLER(now_).report(out); HANDLER(only_).report(out); @@ -637,6 +638,7 @@ public: parent->HANDLER(color).off(); }); + OPTION(report_t, no_titles); OPTION(report_t, no_total); OPTION_(report_t, now_, DO_(args) { -- cgit v1.2.3 From e86a4767bc93664893faa4d0f03619f57302c7d1 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:27:46 -0600 Subject: Added new post_splitter posting handler --- src/filters.cc | 43 ++++++++++++++++++++++++++++++++++++++++++- src/filters.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 57c95cd3..6915144d 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -39,8 +39,49 @@ namespace ledger { +void post_splitter::print_title(const value_t& val) +{ + if (! report.HANDLED(no_titles)) { + std::ostringstream buf; + val.print(buf); + post_chain->title(buf.str()); + } +} + +void post_splitter::flush() +{ + foreach (value_to_posts_map::value_type pair, posts_map) { + preflush_func(pair.first); + + foreach (post_t * post, pair.second) + (*post_chain)(*post); + + post_chain->flush(); + post_chain->clear(); + + if (postflush_func) + (*postflush_func)(pair.first); + } +} + +void post_splitter::operator()(post_t& post) +{ + bind_scope_t bound_scope(report, post); + value_t result(group_by_expr.calc(bound_scope)); + + value_to_posts_map::iterator i = posts_map.find(result); + if (i != posts_map.end()) { + (*i).second.push_back(&post); + } else { + std::pair inserted + = posts_map.insert(value_to_posts_map::value_type(result, posts_list())); + assert(inserted.second); + (*inserted.first).second.push_back(&post); + } +} + pass_down_posts::pass_down_posts(post_handler_ptr handler, - posts_iterator& iter) + posts_iterator& iter) : item_handler(handler) { TRACE_CTOR(pass_down_posts, "post_handler_ptr, posts_iterator"); diff --git a/src/filters.h b/src/filters.h index 3f3e3d34..dd6b3b1a 100644 --- a/src/filters.h +++ b/src/filters.h @@ -50,6 +50,56 @@ namespace ledger { +////////////////////////////////////////////////////////////////////// +// +// Posting collector +// + +class post_splitter : public item_handler +{ +public: + typedef std::map value_to_posts_map; + typedef function custom_flusher_t; + +protected: + value_to_posts_map posts_map; + report_t& report; + post_handler_ptr post_chain; + expr_t group_by_expr; + custom_flusher_t preflush_func; + optional postflush_func; + +public: + post_splitter(report_t& _report, + post_handler_ptr _post_chain, + expr_t _group_by_expr) + : report(_report), post_chain(_post_chain), + group_by_expr(_group_by_expr), + preflush_func(bind(&post_splitter::print_title, this, _1)) { + TRACE_CTOR(post_splitter, "scope_t&, post_handler_ptr, expr_t"); + } + virtual ~post_splitter() { + TRACE_DTOR(post_splitter); + } + + void set_preflush_func(custom_flusher_t functor) { + preflush_func = functor; + } + void set_postflush_func(custom_flusher_t functor) { + postflush_func = functor; + } + + virtual void print_title(const value_t& val); + + virtual void flush(); + virtual void operator()(post_t& post); + + virtual void clear() { + posts_map.clear(); + post_chain->clear(); + } +}; + ////////////////////////////////////////////////////////////////////// // // Posting filters -- cgit v1.2.3 From a41d33fba37460587f59ea0349ac4947a4de9f3c Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:35:25 -0600 Subject: Option --rounding inverted to --no-rounding --- src/chain.cc | 2 +- src/report.cc | 2 +- src/report.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 44133391..1103fe42 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -87,7 +87,7 @@ post_handler_ptr chain_post_handlers(report_t& report, handler.reset(new changed_value_posts(handler, report, for_accounts_report, report.HANDLED(unrealized), - report.HANDLED(rounding))); + ! report.HANDLED(no_rounding))); // calc_posts computes the running total. When this appears will determine, // for example, whether filtered posts are included or excluded from the diff --git a/src/report.cc b/src/report.cc index 4dee4bb2..f3186e0b 100644 --- a/src/report.cc +++ b/src/report.cc @@ -916,6 +916,7 @@ option_t * report_t::lookup_option(const char * p) case 'n': OPT_CH(collapse); else OPT(no_color); + else OPT(no_rounding); else OPT(no_titles); else OPT(no_total); else OPT(now_); @@ -955,7 +956,6 @@ option_t * report_t::lookup_option(const char * p) else OPT(revalued); else OPT(revalued_only); else OPT(revalued_total_); - else OPT(rounding); break; case 's': OPT(sort_); diff --git a/src/report.h b/src/report.h index 104cfc73..78dc2165 100644 --- a/src/report.h +++ b/src/report.h @@ -267,6 +267,7 @@ public: HANDLER(market).report(out); HANDLER(meta_).report(out); HANDLER(monthly).report(out); + HANDLER(no_rounding).report(out); HANDLER(no_titles).report(out); HANDLER(no_total).report(out); HANDLER(now_).report(out); @@ -296,7 +297,6 @@ public: HANDLER(revalued).report(out); HANDLER(revalued_only).report(out); HANDLER(revalued_total_).report(out); - HANDLER(rounding).report(out); HANDLER(seed_).report(out); HANDLER(sort_).report(out); HANDLER(sort_all_).report(out); @@ -638,6 +638,7 @@ public: parent->HANDLER(color).off(); }); + OPTION(report_t, no_rounding); OPTION(report_t, no_titles); OPTION(report_t, no_total); @@ -821,7 +822,6 @@ public: set_expr(args[0].to_string(), args[1].to_string()); }); - OPTION(report_t, rounding); OPTION(report_t, seed_); OPTION_(report_t, sort_, DO_(args) { // -S -- cgit v1.2.3 From 647d4aac2fa474085d01f7ea1cebdc34fafd64a6 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:28:58 -0600 Subject: New: --group-by=EXPR and --group-title-format=FMT The --group-by option allows for most reports to be split up into sections based on the varying value of EXPR. For example, to see register subtotals by payee, use: ledger reg --group-by=payee -s This works for separated balances too: ledger bal --group-by=payee Another interesting possibility is seeing a register of all the accounts affected by a related account: ledger reg -r --group-by=payee The option --group-title-format can be used to add a separator bar to the group titles. The option --no-titles can be used to drop titles altogether. --- src/chain.cc | 126 +++++++++++++++++++++++++----------------------- src/chain.h | 14 ++++++ src/output.cc | 55 +++++++++++++++++---- src/output.h | 16 +++++++ src/print.cc | 12 ++++- src/print.h | 10 ++++ src/report.cc | 150 +++++++++++++++++++++++++++++++++++++++++++++------------- src/report.h | 18 +++++++ 8 files changed, 298 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/chain.cc b/src/chain.cc index 1103fe42..b8c2eb0a 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -39,6 +39,73 @@ namespace ledger { +post_handler_ptr chain_pre_post_handlers(report_t& report, + post_handler_ptr base_handler) +{ + post_handler_ptr handler(base_handler); + + // anonymize_posts removes all meaningful information from xact payee's and + // account names, for the sake of creating useful bug reports. + if (report.HANDLED(anon)) + handler.reset(new anonymize_posts(handler)); + + // This filter_posts will only pass through posts matching the `predicate'. + if (report.HANDLED(limit_)) { + DEBUG("report.predicate", + "Report predicate expression = " << report.HANDLER(limit_).str()); + handler.reset(new filter_posts + (handler, predicate_t(report.HANDLER(limit_).str(), + report.what_to_keep()), + report)); + } + + // budget_posts takes a set of posts from a data file and uses them to + // generate "budget posts" which balance against the reported posts. + // + // forecast_posts is a lot like budget_posts, except that it adds xacts + // only for the future, and does not balance them against anything but the + // future balance. + + if (report.budget_flags != BUDGET_NO_BUDGET) { + budget_posts * budget_handler = new budget_posts(handler, + report.budget_flags); + budget_handler->add_period_xacts(report.session.journal->period_xacts); + handler.reset(budget_handler); + + // Apply this before the budget handler, so that only matching posts are + // calculated toward the budget. The use of filter_posts above will + // further clean the results so that no automated posts that don't match + // the filter get reported. + if (report.HANDLED(limit_)) + handler.reset(new filter_posts + (handler, predicate_t(report.HANDLER(limit_).str(), + report.what_to_keep()), + report)); + } + else if (report.HANDLED(forecast_while_)) { + forecast_posts * forecast_handler + = new forecast_posts(handler, + predicate_t(report.HANDLER(forecast_while_).str(), + report.what_to_keep()), + report, + report.HANDLED(forecast_years_) ? + static_cast + (report.HANDLER(forecast_years_).value.to_long()) : + 5UL); + forecast_handler->add_period_xacts(report.session.journal->period_xacts); + handler.reset(forecast_handler); + + // See above, under budget_posts. + if (report.HANDLED(limit_)) + handler.reset(new filter_posts + (handler, predicate_t(report.HANDLER(limit_).str(), + report.what_to_keep()), + report)); + } + + return handler; +} + post_handler_ptr chain_post_handlers(report_t& report, post_handler_ptr base_handler, bool for_accounts_report) @@ -189,65 +256,6 @@ post_handler_ptr chain_post_handlers(report_t& report, if (report.HANDLED(related)) handler.reset(new related_posts(handler, report.HANDLED(related_all))); - // anonymize_posts removes all meaningful information from xact payee's and - // account names, for the sake of creating useful bug reports. - if (report.HANDLED(anon)) - handler.reset(new anonymize_posts(handler)); - - // This filter_posts will only pass through posts matching the `predicate'. - if (report.HANDLED(limit_)) { - DEBUG("report.predicate", - "Report predicate expression = " << report.HANDLER(limit_).str()); - handler.reset(new filter_posts - (handler, predicate_t(report.HANDLER(limit_).str(), - report.what_to_keep()), - report)); - } - - // budget_posts takes a set of posts from a data file and uses them to - // generate "budget posts" which balance against the reported posts. - // - // forecast_posts is a lot like budget_posts, except that it adds xacts - // only for the future, and does not balance them against anything but the - // future balance. - - if (report.budget_flags != BUDGET_NO_BUDGET) { - budget_posts * budget_handler = new budget_posts(handler, - report.budget_flags); - budget_handler->add_period_xacts(report.session.journal->period_xacts); - handler.reset(budget_handler); - - // Apply this before the budget handler, so that only matching posts are - // calculated toward the budget. The use of filter_posts above will - // further clean the results so that no automated posts that don't match - // the filter get reported. - if (report.HANDLED(limit_)) - handler.reset(new filter_posts - (handler, predicate_t(report.HANDLER(limit_).str(), - report.what_to_keep()), - report)); - } - else if (report.HANDLED(forecast_while_)) { - forecast_posts * forecast_handler - = new forecast_posts(handler, - predicate_t(report.HANDLER(forecast_while_).str(), - report.what_to_keep()), - report, - report.HANDLED(forecast_years_) ? - static_cast - (report.HANDLER(forecast_years_).value.to_long()) : - 5UL); - forecast_handler->add_period_xacts(report.session.journal->period_xacts); - handler.reset(forecast_handler); - - // See above, under budget_posts. - if (report.HANDLED(limit_)) - handler.reset(new filter_posts - (handler, predicate_t(report.HANDLER(limit_).str(), - report.what_to_keep()), - report)); - } - return handler; } diff --git a/src/chain.h b/src/chain.h index 935f2935..59b04eb8 100644 --- a/src/chain.h +++ b/src/chain.h @@ -91,11 +91,25 @@ typedef shared_ptr > post_handler_ptr; typedef shared_ptr > acct_handler_ptr; class report_t; + +post_handler_ptr +chain_pre_post_handlers(report_t& report, + post_handler_ptr base_handler); + post_handler_ptr chain_post_handlers(report_t& report, post_handler_ptr base_handler, bool for_accounts_report = false); +inline post_handler_ptr +chain_handlers(report_t& report, + post_handler_ptr handler, + bool for_accounts_report = false) { + handler = chain_post_handlers(report, handler, for_accounts_report); + handler = chain_pre_post_handlers(report, handler); + return handler; +} + } // namespace ledger #endif // _CHAIN_H diff --git a/src/output.cc b/src/output.cc index 183d80b3..f697dee4 100644 --- a/src/output.cc +++ b/src/output.cc @@ -45,7 +45,7 @@ format_posts::format_posts(report_t& _report, const optional& _prepend_format, std::size_t _prepend_width) : report(_report), prepend_width(_prepend_width), - last_xact(NULL), last_post(NULL) + last_xact(NULL), last_post(NULL), first_report_title(true) { TRACE_CTOR(format_posts, "report&, const string&, bool"); @@ -78,12 +78,30 @@ void format_posts::flush() void format_posts::operator()(post_t& post) { - std::ostream& out(report.output_stream); - if (! post.has_xdata() || ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { + std::ostream& out(report.output_stream); + bind_scope_t bound_scope(report, post); + if (! report_title.empty()) { + if (first_report_title) + first_report_title = false; + else + out << '\n'; + + value_scope_t val_scope(string_value(report_title)); + bind_scope_t inner_scope(bound_scope, val_scope); + + format_t group_title_format; + group_title_format + .parse_format(report.HANDLER(group_title_format_).str()); + + out << group_title_format(inner_scope); + + report_title = ""; + } + if (prepend_format) { out.width(prepend_width); out << prepend_format(bound_scope); @@ -113,7 +131,8 @@ format_accounts::format_accounts(report_t& _report, const string& format, const optional& _prepend_format, std::size_t _prepend_width) - : report(_report), prepend_width(_prepend_width), disp_pred() + : report(_report), prepend_width(_prepend_width), disp_pred(), + first_report_title(true) { TRACE_CTOR(format_accounts, "report&, const string&"); @@ -144,19 +163,37 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat) if (account.xdata().has_flags(ACCOUNT_EXT_TO_DISPLAY) && ! account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED)) { + std::ostream& out(report.output_stream); + DEBUG("account.display", "Displaying account: " << account.fullname()); account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); bind_scope_t bound_scope(report, account); + if (! report_title.empty()) { + if (first_report_title) + first_report_title = false; + else + out << '\n'; + + value_scope_t val_scope(string_value(report_title)); + bind_scope_t inner_scope(bound_scope, val_scope); + + format_t group_title_format; + group_title_format + .parse_format(report.HANDLER(group_title_format_).str()); + + out << group_title_format(inner_scope); + + report_title = ""; + } + if (prepend_format) { - static_cast(report.output_stream).width(prepend_width); - static_cast(report.output_stream) - << prepend_format(bound_scope); + out.width(prepend_width); + out << prepend_format(bound_scope); } - static_cast(report.output_stream) - << account_line_format(bound_scope); + out << account_line_format(bound_scope); return 1; } diff --git a/src/output.h b/src/output.h index f0e7f9a5..a19c6235 100644 --- a/src/output.h +++ b/src/output.h @@ -64,6 +64,8 @@ protected: std::size_t prepend_width; xact_t * last_xact; post_t * last_post; + bool first_report_title; + string report_title; public: format_posts(report_t& _report, const string& format, @@ -73,6 +75,10 @@ public: TRACE_DTOR(format_posts); } + virtual void title(const string& str) { + report_title = str; + } + virtual void flush(); virtual void operator()(post_t& post); @@ -80,6 +86,8 @@ public: last_xact = NULL; last_post = NULL; + report_title = ""; + item_handler::clear(); } }; @@ -94,6 +102,8 @@ protected: format_t prepend_format; std::size_t prepend_width; predicate_t disp_pred; + bool first_report_title; + string report_title; std::list posted_accounts; @@ -108,6 +118,10 @@ public: std::pair mark_accounts(account_t& account, const bool flat); + virtual void title(const string& str) { + report_title = str; + } + virtual std::size_t post_account(account_t& account, const bool flat); virtual void flush(); @@ -117,6 +131,8 @@ public: disp_pred.mark_uncompiled(); posted_accounts.clear(); + report_title = ""; + item_handler::clear(); } }; diff --git a/src/print.cc b/src/print.cc index 5c46f4e7..a8aa5872 100644 --- a/src/print.cc +++ b/src/print.cc @@ -42,7 +42,7 @@ namespace ledger { print_xacts::print_xacts(report_t& _report, bool _print_raw) - : report(_report), print_raw(_print_raw) + : report(_report), print_raw(_print_raw), first_title(true) { TRACE_CTOR(print_xacts, "report&, bool"); } @@ -221,6 +221,16 @@ namespace { } } +void print_xacts::title(const string&) +{ + if (first_title) { + first_title = false; + } else { + std::ostream& out(report.output_stream); + out << '\n'; + } +} + void print_xacts::flush() { std::ostream& out(report.output_stream); diff --git a/src/print.h b/src/print.h index f323b153..5263ec91 100644 --- a/src/print.h +++ b/src/print.h @@ -62,6 +62,7 @@ protected: xacts_present_map xacts_present; xacts_list xacts; bool print_raw; + bool first_title; public: print_xacts(report_t& _report, bool _print_raw = false); @@ -69,8 +70,17 @@ public: TRACE_DTOR(print_xacts); } + virtual void title(const string&); + virtual void flush(); virtual void operator()(post_t& post); + + virtual void clear() { + xacts_present.clear(); + xacts.clear(); + + item_handler::clear(); + } }; diff --git a/src/report.cc b/src/report.cc index f3186e0b..dcb56319 100644 --- a/src/report.cc +++ b/src/report.cc @@ -274,10 +274,35 @@ void report_t::parse_query_args(const value_t& args, const string& whence) } } +namespace { + struct posts_flusher + { + report_t& report; + post_handler_ptr handler; + + posts_flusher(report_t& _report, post_handler_ptr _handler) + : report(_report), handler(_handler) {} + + void operator()(const value_t&) { + report.session.journal->clear_xdata(); + } + }; +} + void report_t::posts_report(post_handler_ptr handler) { + handler = chain_post_handlers(*this, handler); + if (HANDLED(group_by_)) { + std::auto_ptr + splitter(new post_splitter(*this, handler, HANDLER(group_by_).expr)); + splitter->set_postflush_func(posts_flusher(*this, handler)); + handler = post_handler_ptr(splitter.release()); + } + handler = chain_pre_post_handlers(*this, handler); + journal_posts_iterator walker(*session.journal.get()); - pass_down_posts(chain_post_handlers(*this, handler), walker); + pass_down_posts(handler, walker); + session.journal->clear_xdata(); } @@ -285,66 +310,121 @@ void report_t::generate_report(post_handler_ptr handler) { HANDLER(limit_).on(string("#generate"), "actual"); + handler = chain_handlers(*this, handler); + generate_posts_iterator walker (session, HANDLED(seed_) ? static_cast(HANDLER(seed_).value.to_long()) : 0, HANDLED(head_) ? static_cast(HANDLER(head_).value.to_long()) : 50); - pass_down_posts(chain_post_handlers(*this, handler), walker); + pass_down_posts(handler, walker); } void report_t::xact_report(post_handler_ptr handler, xact_t& xact) { + handler = chain_handlers(*this, handler); + xact_posts_iterator walker(xact); - pass_down_posts(chain_post_handlers(*this, handler), walker); + pass_down_posts(handler, walker); + xact.clear_xdata(); } +namespace { + struct accounts_title_printer + { + report_t& report; + acct_handler_ptr handler; + + accounts_title_printer(report_t& _report, acct_handler_ptr _handler) + : report(_report), handler(_handler) {} + + void operator()(const value_t& val) + { + if (! report.HANDLED(no_titles)) { + std::ostringstream buf; + val.print(buf); + handler->title(buf.str()); + } + } + }; + + struct accounts_flusher + { + report_t& report; + acct_handler_ptr handler; + + accounts_flusher(report_t& _report, acct_handler_ptr _handler) + : report(_report), handler(_handler) {} + + void operator()(const value_t&) + { + report.HANDLER(amount_).expr.mark_uncompiled(); + report.HANDLER(total_).expr.mark_uncompiled(); + report.HANDLER(display_amount_).expr.mark_uncompiled(); + report.HANDLER(display_total_).expr.mark_uncompiled(); + report.HANDLER(revalued_total_).expr.mark_uncompiled(); + + scoped_ptr iter; + if (! report.HANDLED(sort_)) { + iter.reset(new basic_accounts_iterator(*report.session.journal->master)); + } else { + expr_t sort_expr(report.HANDLER(sort_).str()); + sort_expr.set_context(&report); + iter.reset(new sorted_accounts_iterator(*report.session.journal->master, + sort_expr, report.HANDLED(flat))); + } + + if (report.HANDLED(display_)) { + DEBUG("report.predicate", + "Display predicate = " << report.HANDLER(display_).str()); + pass_down_accounts(handler, *iter.get(), + predicate_t(report.HANDLER(display_).str(), + report.what_to_keep()), + report); + } else { + pass_down_accounts(handler, *iter.get()); + } + + report.session.journal->clear_xdata(); + } + }; +} + void report_t::accounts_report(acct_handler_ptr handler) { - journal_posts_iterator walker(*session.journal.get()); - - // The lifetime of the chain object controls the lifetime of all temporary - // objects created within it during the call to pass_down_posts, which will - // be needed later by the pass_down_accounts. post_handler_ptr chain = - chain_post_handlers(*this, post_handler_ptr(new ignore_posts), true); - pass_down_posts(chain, walker); + chain_post_handlers(*this, post_handler_ptr(new ignore_posts), + /* for_accounts_report= */ true); + if (HANDLED(group_by_)) { + std::auto_ptr + splitter(new post_splitter(*this, chain, HANDLER(group_by_).expr)); - 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(); + splitter->set_preflush_func(accounts_title_printer(*this, handler)); + splitter->set_postflush_func(accounts_flusher(*this, handler)); - scoped_ptr iter; - if (! HANDLED(sort_)) { - iter.reset(new basic_accounts_iterator(*session.journal->master)); - } else { - expr_t sort_expr(HANDLER(sort_).str()); - sort_expr.set_context(this); - iter.reset(new sorted_accounts_iterator(*session.journal->master, - sort_expr, HANDLED(flat))); + chain = post_handler_ptr(splitter.release()); } + chain = chain_pre_post_handlers(*this, chain); - 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 { - pass_down_accounts(handler, *iter.get()); - } + // The lifetime of the chain object controls the lifetime of all temporary + // objects created within it during the call to pass_down_posts, which will + // be needed later by the pass_down_accounts. + journal_posts_iterator walker(*session.journal.get()); + pass_down_posts(chain, walker); - session.journal->clear_xdata(); + if (! HANDLED(group_by_)) + accounts_flusher(*this, handler)(value_t()); } void report_t::commodities_report(post_handler_ptr handler) { + handler = chain_handlers(*this, handler); + posts_commodities_iterator walker(*session.journal.get()); - pass_down_posts(chain_post_handlers(*this, handler), walker); + pass_down_posts(handler, walker); + session.journal->clear_xdata(); } @@ -888,6 +968,8 @@ option_t * report_t::lookup_option(const char * p) break; case 'g': OPT(gain); + else OPT(group_by_); + else OPT(group_title_format_); break; case 'h': OPT(head_); diff --git a/src/report.h b/src/report.h index 78dc2165..6fa238f0 100644 --- a/src/report.h +++ b/src/report.h @@ -256,6 +256,8 @@ public: HANDLER(forecast_years_).report(out); HANDLER(format_).report(out); HANDLER(gain).report(out); + HANDLER(group_by_).report(out); + HANDLER(group_title_format_).report(out); HANDLER(head_).report(out); HANDLER(invert).report(out); HANDLER(limit_).report(out); @@ -596,6 +598,22 @@ public: " - get_at(total_expr, 1)"); }); + OPTION__ + (report_t, group_by_, + expr_t expr; + CTOR(report_t, group_by_) {} + void set_expr(const optional& whence, const string& str) { + expr = str; + on(whence, str); + } + DO_(args) { + set_expr(args[0].to_string(), args[1].to_string()); + }); + + OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) { + on(none, "%(value)\n"); + }); + OPTION(report_t, head_); OPTION_(report_t, invert, DO() { -- cgit v1.2.3 From 4f3b39e22c7a7743132ead79b1e092929679de44 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 02:55:02 -0600 Subject: Empty notes and tags now return null values --- src/filters.cc | 84 +++++++++++++++++++----------------- src/item.cc | 9 ++-- src/post.cc | 12 ++++-- src/xact.cc | 2 +- test/baseline/opt-code-as-payee.test | 4 +- 5 files changed, 60 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 6915144d..6e4aeee3 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -69,14 +69,16 @@ void post_splitter::operator()(post_t& post) bind_scope_t bound_scope(report, post); value_t result(group_by_expr.calc(bound_scope)); - value_to_posts_map::iterator i = posts_map.find(result); - if (i != posts_map.end()) { - (*i).second.push_back(&post); - } else { - std::pair inserted - = posts_map.insert(value_to_posts_map::value_type(result, posts_list())); - assert(inserted.second); - (*inserted.first).second.push_back(&post); + if (! result.is_null()) { + value_to_posts_map::iterator i = posts_map.find(result); + if (i != posts_map.end()) { + (*i).second.push_back(&post); + } else { + std::pair inserted + = posts_map.insert(value_to_posts_map::value_type(result, posts_list())); + assert(inserted.second); + (*inserted.first).second.push_back(&post); + } } } @@ -837,41 +839,43 @@ void transfer_details::operator()(post_t& post) bind_scope_t bound_scope(scope, temp); value_t substitute(expr.calc(bound_scope)); - switch (which_element) { - case SET_DATE: - temp.xdata().date = substitute.to_date(); - break; - - case SET_ACCOUNT: { - 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()); + if (! substitute.is_null()) { + switch (which_element) { + case SET_DATE: + temp.xdata().date = substitute.to_date(); + break; + + case SET_ACCOUNT: { + 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; } - break; - } - case SET_PAYEE: - xact.payee = substitute.to_string(); - break; + case SET_PAYEE: + xact.payee = substitute.to_string(); + break; - default: - assert(false); - break; + default: + assert(false); + break; + } } item_handler::operator()(temp); diff --git a/src/item.cc b/src/item.cc index 14a0896f..0a22b260 100644 --- a/src/item.cc +++ b/src/item.cc @@ -227,7 +227,7 @@ namespace { return NULL_VALUE; } value_t get_note(item_t& item) { - return string_value(item.note ? *item.note : empty_string); + return item.note ? string_value(*item.note) : NULL_VALUE; } value_t has_tag(call_scope_t& args) { @@ -260,7 +260,8 @@ namespace { return false; } - value_t get_tag(call_scope_t& args) { + value_t get_tag(call_scope_t& args) + { item_t& item(find_scope(args)); optional str; @@ -292,14 +293,14 @@ namespace { if (str) return string_value(*str); else - return string_value(empty_string); + return NULL_VALUE; } value_t get_pathname(item_t& item) { if (item.pos) return string_value(item.pos->pathname.string()); else - return string_value(empty_string); + return NULL_VALUE; } value_t get_beg_pos(item_t& item) { diff --git a/src/post.cc b/src/post.cc index f1f3e96a..7dc15830 100644 --- a/src/post.cc +++ b/src/post.cc @@ -150,7 +150,7 @@ namespace { if (post.xact->code) return string_value(*post.xact->code); else - return string_value(empty_string); + return NULL_VALUE; } value_t get_payee(post_t& post) { @@ -158,9 +158,13 @@ namespace { } value_t get_note(post_t& post) { - string note = post.note ? *post.note : empty_string; - note += post.xact->note ? *post.xact->note : empty_string; - return string_value(note); + if (post.note || post.xact->note) { + string note = post.note ? *post.note : empty_string; + note += post.xact->note ? *post.xact->note : empty_string; + return string_value(note); + } else { + return NULL_VALUE; + } } value_t get_magnitude(post_t& post) { diff --git a/src/xact.cc b/src/xact.cc index f63835c9..569e5869 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -468,7 +468,7 @@ namespace { if (xact.code) return string_value(*xact.code); else - return string_value(empty_string); + return NULL_VALUE; } value_t get_payee(xact_t& xact) { diff --git a/test/baseline/opt-code-as-payee.test b/test/baseline/opt-code-as-payee.test index c2988626..99aa182e 100644 --- a/test/baseline/opt-code-as-payee.test +++ b/test/baseline/opt-code-as-payee.test @@ -28,7 +28,7 @@ reg --payee=code 08-Feb-01 102 Assets:Cash $-20.00 0 08-Feb-28 103 Expenses:Books $20.00 $20.00 08-Feb-28 103 Assets:Cash $-20.00 0 -08-Mar-01 Expenses:Books $30.00 $30.00 -08-Mar-01 Assets:Cash $-30.00 0 +08-Mar-01 March Expenses:Books $30.00 $30.00 +08-Mar-01 March Assets:Cash $-30.00 0 >>>2 === 0 -- cgit v1.2.3 From d5ea3080a7322730a28009bb3190631f371234db Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 03:19:09 -0600 Subject: Allow null values to be cast to int and string --- src/value.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/value.cc b/src/value.cc index 9515de46..a967eeb8 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1075,8 +1075,27 @@ void value_t::in_place_cast(type_t cast_type) } switch (type()) { + case VOID: + switch (cast_type) { + case INTEGER: + set_long(0L); + return; + case AMOUNT: + set_amount(0L); + return; + case STRING: + set_string(""); + return; + default: + break; + } + break; + case BOOLEAN: switch (cast_type) { + case INTEGER: + set_long(as_boolean() ? 1L : 0L); + return; case AMOUNT: set_amount(as_boolean() ? 1L : 0L); return; -- cgit v1.2.3 From 58621a96a22de5c1453b7fac624e00dc7f3c402e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 20:43:45 -0600 Subject: Made several debug categories more consistent --- src/filters.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 6e4aeee3..eabf9834 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -355,7 +355,7 @@ namespace { if (functor) (*functor)(post); - DEBUG("filter.changed_value.rounding", "post.amount = " << post.amount); + DEBUG("filters.changed_value.rounding", "post.amount = " << post.amount); (*handler)(post); @@ -398,7 +398,7 @@ void collapse_posts::report_subtotal() xact.payee = last_xact->payee; xact._date = (is_valid(earliest_date) ? earliest_date : last_xact->_date); - DEBUG("filter.collapse", "Pseudo-xact date = " << *xact._date); + DEBUG("filters.collapse", "Pseudo-xact date = " << *xact._date); handle_value(subtotal, &totals_account, &xact, temps, handler); } @@ -525,14 +525,14 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } post.xdata().date = date_t(); - DEBUG("filter.changed_value", - "output_revaluation(last_balance) = " << last_total); - DEBUG("filter.changed_value", + DEBUG("filters.changed_value", + "output_revaluation(last_total) = " << last_total); + DEBUG("filters.changed_value", "output_revaluation(repriced_total) = " << repriced_total); if (! last_total.is_null()) { if (value_t diff = repriced_total - last_total) { - DEBUG("filter.changed_value", "output_revaluation(strip(diff)) = " + DEBUG("filters.changed_value", "output_revaluation(strip(diff)) = " << diff.strip_annotations(report.what_to_keep())); xact_t& xact = temps.create_xact(); @@ -578,24 +578,24 @@ void changed_value_posts::output_rounding(post_t& post) bind_scope_t bound_scope(report, post); value_t new_display_total(display_total_expr.calc(bound_scope)); - DEBUG("filter.changed_value.rounding", + DEBUG("filters.changed_value.rounding", "rounding.new_display_total = " << new_display_total); if (! last_display_total.is_null()) { if (value_t repriced_amount = display_amount_expr.calc(bound_scope)) { - DEBUG("filter.changed_value.rounding", + DEBUG("filters.changed_value.rounding", "rounding.repriced_amount = " << repriced_amount); value_t precise_display_total(new_display_total.truncated() - repriced_amount.truncated()); - DEBUG("filter.changed_value.rounding", + DEBUG("filters.changed_value.rounding", "rounding.precise_display_total = " << precise_display_total); - DEBUG("filter.changed_value.rounding", + DEBUG("filters.changed_value.rounding", "rounding.last_display_total = " << last_display_total); if (value_t diff = precise_display_total - last_display_total) { - DEBUG("filter.changed_value.rounding", + DEBUG("filters.changed_value.rounding", "rounding.diff = " << diff); xact_t& xact = temps.create_xact(); -- cgit v1.2.3 From 5a2644c1b7220dc96e4de9cfb82f6d829cf34321 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 20:49:50 -0600 Subject: -V/-X options now take price history into account --- src/filters.cc | 80 ++++++++++++++++++++++++++++++++++++++++++-- src/filters.h | 2 ++ test/baseline/opt-price.test | 3 +- test/regress/D943AE0F.test | 4 +-- 4 files changed, 84 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index eabf9834..bca8516e 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -503,6 +503,8 @@ changed_value_posts::changed_value_posts(post_handler_ptr handler, void changed_value_posts::flush() { if (last_post && last_post->date() <= report.terminus.date()) { + if (! for_accounts_report) + output_intermediate_prices(*last_post, report.terminus.date()); output_revaluation(*last_post, report.terminus.date()); last_post = NULL; } @@ -514,7 +516,6 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) if (is_valid(date)) post.xdata().date = date; - value_t repriced_total; try { bind_scope_t bound_scope(report, post); repriced_total = total_expr.calc(bound_scope); @@ -573,6 +574,78 @@ void changed_value_posts::output_revaluation(post_t& post, const date_t& date) } } +void changed_value_posts::output_intermediate_prices(post_t& post, + const date_t& current) +{ + // To fix BZ#199, examine the balance of last_post and determine whether the + // price of that amount changed after its date and before the new post's + // date. If so, generate an output_revaluation for that price change. + // Mostly this is only going to occur if the user has a series of pricing + // entries, since a posting-based revaluation would be seen here as a post. + assert(! last_total.is_null()); + + switch (last_total.type()) { + case value_t::INTEGER: + case value_t::SEQUENCE: + break; + case value_t::AMOUNT: + last_total.in_place_cast(value_t::BALANCE); + // fall through... + case value_t::BALANCE: { + commodity_t::history_map all_prices; + + foreach (const balance_t::amounts_map::value_type& amt_comm, + last_total.as_balance().amounts) { + if (optional hist = + amt_comm.first->varied_history()) { + foreach + (const commodity_t::history_by_commodity_map::value_type& comm_hist, + hist->histories) { + foreach (const commodity_t::history_map::value_type& price, + comm_hist.second.prices) { + if (price.first.date() > post.date() && + price.first.date() < current) { + DEBUG("filters.revalued", post.date() << " < " + << price.first.date() << " < " << current); + DEBUG("filters.revalued", "inserting " + << price.second << " at " << price.first.date()); + all_prices.insert(price); + } + } + } + } + } + + // Choose the last price from each day as the price to use + typedef std::map > history_by_date_map; + history_by_date_map all_prices_by_date; + + BOOST_REVERSE_FOREACH + (const commodity_t::history_map::value_type& price, all_prices) { + // This insert will fail if a later price has already been inserted + // for that date. + DEBUG("filters.revalued", + "re-inserting " << price.second << " at " << price.first.date()); + all_prices_by_date.insert(history_by_date_map::value_type + (price.first.date(), price)); + } + + // Go through the time-sorted prices list, outputting a revaluation for + // each price difference. + foreach (const history_by_date_map::value_type& price, all_prices_by_date) { + output_revaluation(post, price.first); + last_total = repriced_total; + } + break; + } + default: + assert(false); + break; + } +} + void changed_value_posts::output_rounding(post_t& post) { bind_scope_t bound_scope(report, post); @@ -612,8 +685,11 @@ void changed_value_posts::output_rounding(post_t& post) void changed_value_posts::operator()(post_t& post) { - if (last_post) + if (last_post) { + if (! for_accounts_report) + output_intermediate_prices(*last_post, post.date()); output_revaluation(*last_post, post.date()); + } if (changed_values_only) post.xdata().add_flags(POST_EXT_DISPLAYED); diff --git a/src/filters.h b/src/filters.h index dd6b3b1a..a66d8c47 100644 --- a/src/filters.h +++ b/src/filters.h @@ -499,6 +499,7 @@ class changed_value_posts : public item_handler post_t * last_post; value_t last_total; value_t last_display_total; + value_t repriced_total; temporaries_t temps; account_t& revalued_account; account_t& rounding_account; @@ -521,6 +522,7 @@ public: virtual void flush(); void output_revaluation(post_t& post, const date_t& current); + void output_intermediate_prices(post_t& post, const date_t& current); void output_rounding(post_t& post); virtual void operator()(post_t& post); diff --git a/test/baseline/opt-price.test b/test/baseline/opt-price.test index 06cc7751..bf302264 100644 --- a/test/baseline/opt-price.test +++ b/test/baseline/opt-price.test @@ -28,7 +28,8 @@ reg --end 2009/06/26 -V equities 08-Jan-01 Purchase Apple shares Equities $2000 $2000 08-Jun-30 Commodities revalued $500 $2500 08-Jun-30 Sell some Apple sha.. Equities $-1250 $1250 -09-Jun-26 Commodities revalued $750 $2000 +09-Jan-31 Commodities revalued $250 $1500 +09-Jun-26 Commodities revalued $500 $2000 >>>2 === 0 reg --end 2009/06/26 -G equities diff --git a/test/regress/D943AE0F.test b/test/regress/D943AE0F.test index 94a26df5..7a2e14d8 100644 --- a/test/regress/D943AE0F.test +++ b/test/regress/D943AE0F.test @@ -1,4 +1,4 @@ -reg -V --end=2009/06/16 +reg -V <<< D 1000.00 EUR @@ -10,6 +10,6 @@ P 2008/04/20 00:00:00 CAD 1.20 EUR >>>1 08-Apr-15 Paid expenses back .. Ex:Cie-Reimbursements 2200.00 EUR 2200.00 EUR Assets:Checking -2200.00 EUR 0 -09-Jun-16 Commodities revalued 200.00 EUR 200.00 EUR +08-Apr-20 Commodities revalued 200.00 EUR 200.00 EUR >>>2 === 0 -- cgit v1.2.3 From 654c842348c570a086f31cb79b7b3b4810463707 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sun, 30 May 2010 21:58:05 -0600 Subject: -G option now takes price history into account --- src/filters.cc | 56 ++++++++++++++++++++++++++++++++++++++++---- test/baseline/opt-gain.test | 3 ++- test/baseline/opt-price.test | 3 ++- 3 files changed, 56 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index bca8516e..41e8c16b 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -582,20 +582,68 @@ void changed_value_posts::output_intermediate_prices(post_t& post, // date. If so, generate an output_revaluation for that price change. // Mostly this is only going to occur if the user has a series of pricing // entries, since a posting-based revaluation would be seen here as a post. - assert(! last_total.is_null()); - switch (last_total.type()) { + value_t display_total(last_total); + + if (display_total.type() == value_t::SEQUENCE) { + xact_t& xact(temps.create_xact()); + + xact.payee = _("Commodities revalued"); + xact._date = is_valid(current) ? current : post.date(); + + post_t& temp(temps.copy_post(post, xact)); + temp.add_flags(ITEM_GENERATED); + + post_t::xdata_t& xdata(temp.xdata()); + if (is_valid(current)) + xdata.date = current; + + DEBUG("filters.revalued", "intermediate last_total = " << last_total); + + switch (last_total.type()) { + case value_t::BOOLEAN: + case value_t::INTEGER: + last_total.in_place_cast(value_t::AMOUNT); + // fall through... + + case value_t::AMOUNT: + temp.amount = last_total.as_amount(); + break; + + case value_t::BALANCE: + case value_t::SEQUENCE: + xdata.compound_value = last_total; + xdata.add_flags(POST_EXT_COMPOUND); + break; + + case value_t::DATETIME: + case value_t::DATE: + default: + assert(false); + break; + } + + bind_scope_t inner_scope(report, temp); + display_total = display_total_expr.calc(inner_scope); + + DEBUG("filters.revalued", "intermediate display_total = " << display_total); + } + + switch (display_total.type()) { + case value_t::VOID: case value_t::INTEGER: case value_t::SEQUENCE: break; + case value_t::AMOUNT: - last_total.in_place_cast(value_t::BALANCE); + display_total.in_place_cast(value_t::BALANCE); // fall through... + case value_t::BALANCE: { commodity_t::history_map all_prices; foreach (const balance_t::amounts_map::value_type& amt_comm, - last_total.as_balance().amounts) { + display_total.as_balance().amounts) { if (optional hist = amt_comm.first->varied_history()) { foreach diff --git a/test/baseline/opt-gain.test b/test/baseline/opt-gain.test index 8aeb8bab..63055d5f 100644 --- a/test/baseline/opt-gain.test +++ b/test/baseline/opt-gain.test @@ -50,7 +50,8 @@ P 2010/03/01 00:00:00 S 8 P P 2010/04/01 00:00:00 S 16 P >>>1 09-Jan-01 Sample 1a As:Brokerage:Stocks 0 0 -09-Feb-01 Commodities revalued 300 P 300 P +09-Jan-15 Commodities revalued 100 P 100 P +09-Feb-01 Commodities revalued 200 P 300 P 09-Feb-01 Sample 2a As:Brokerage:Stocks 300 P 600 P 09-Mar-01 Commodities revalued 800 P 1400 P 09-Mar-01 Sample 3a As:Brokerage:Stocks 700 P 2100 P diff --git a/test/baseline/opt-price.test b/test/baseline/opt-price.test index bf302264..133b2155 100644 --- a/test/baseline/opt-price.test +++ b/test/baseline/opt-price.test @@ -37,7 +37,8 @@ reg --end 2009/06/26 -G equities 08-Jan-01 Purchase Apple shares Equities 0 0 08-Jun-30 Commodities revalued $500 $500 08-Jun-30 Sell some Apple sha.. Equities 0 $500 -09-Jun-26 Commodities revalued $750 $1250 +09-Jan-31 Commodities revalued $250 $750 +09-Jun-26 Commodities revalued $500 $1250 >>>2 === 0 reg -I equities -- cgit v1.2.3 From df0edbd2dc416281bffb1fc519aaa3532496d045 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Mon, 31 May 2010 15:13:04 -0600 Subject: Minor optimization --- src/report.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/report.cc b/src/report.cc index dcb56319..90de9a3f 100644 --- a/src/report.cc +++ b/src/report.cc @@ -303,7 +303,8 @@ void report_t::posts_report(post_handler_ptr handler) journal_posts_iterator walker(*session.journal.get()); pass_down_posts(handler, walker); - session.journal->clear_xdata(); + if (! HANDLED(group_by_)) + posts_flusher(*this, handler)(value_t()); } void report_t::generate_report(post_handler_ptr handler) -- cgit v1.2.3 From 651220129219d5581597d0c312ef9fee7b47a358 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 1 Jun 2010 16:55:24 -0400 Subject: Changed a comment --- src/commodity.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/commodity.cc b/src/commodity.cc index 836a4269..b4220354 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -454,7 +454,7 @@ void commodity_t::parse_symbol(std::istream& in, string& symbol) { // Invalid commodity characters: // SPACE, TAB, NEWLINE, RETURN - // 0-9 . , ; - + * / ^ ? : & | ! = + // 0-9 . , ; : ? ! - + * / ^ & | = // < > { } [ ] ( ) @ static int invalid_chars[256] = { -- cgit v1.2.3 From d19745afded63276449bb56b7d24c38c7e32d0a7 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 1 Jun 2010 17:32:41 -0400 Subject: Improve parsing of 'expr' query terms Fixes #157 / 9DF85DF2-4BF5-4931-A30C-2592A10BB5C0 --- src/query.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/query.cc b/src/query.cc index 1f086df8..c79fe1c2 100644 --- a/src/query.cc +++ b/src/query.cc @@ -55,8 +55,9 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() if (consume_next_arg) { consume_next_arg = false; + token_t tok(token_t::TERM, string(arg_i, arg_end)); arg_i = arg_end; - return token_t(token_t::TERM, (*begin).as_string()); + return tok; } resume: @@ -70,23 +71,25 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() return next_token(); goto resume; + case '\'': case '/': { string pat; - bool found_end_slash = false; + char closing = *arg_i; + bool found_closing = false; for (++arg_i; arg_i != arg_end; ++arg_i) { if (*arg_i == '\\') { if (++arg_i == arg_end) throw_(parse_error, _("Unexpected '\\' at end of pattern")); } - else if (*arg_i == '/') { + else if (*arg_i == closing) { ++arg_i; - found_end_slash = true; + found_closing = true; break; } pat.push_back(*arg_i); } - if (! found_end_slash) - throw_(parse_error, _("Expected '/' at end of pattern")); + if (! found_closing) + throw_(parse_error, _("Expected '%1' at end of pattern") << closing); if (pat.empty()) throw_(parse_error, _("Match pattern is empty")); -- cgit v1.2.3 From d513c71236b3e91bb999158829250e7194a9d56e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 1 Jun 2010 17:40:27 -0400 Subject: Minor optimization --- src/filters.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/filters.cc b/src/filters.cc index 41e8c16b..ad4b88a0 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -665,10 +665,8 @@ void changed_value_posts::output_intermediate_prices(post_t& post, } // Choose the last price from each day as the price to use - typedef std::map > history_by_date_map; - history_by_date_map all_prices_by_date; + typedef std::map date_map; + date_map pricing_dates; BOOST_REVERSE_FOREACH (const commodity_t::history_map::value_type& price, all_prices) { @@ -676,13 +674,12 @@ void changed_value_posts::output_intermediate_prices(post_t& post, // for that date. DEBUG("filters.revalued", "re-inserting " << price.second << " at " << price.first.date()); - all_prices_by_date.insert(history_by_date_map::value_type - (price.first.date(), price)); + pricing_dates.insert(date_map::value_type(price.first.date(), true)); } // Go through the time-sorted prices list, outputting a revaluation for // each price difference. - foreach (const history_by_date_map::value_type& price, all_prices_by_date) { + foreach (const date_map::value_type& price, pricing_dates) { output_revaluation(post, price.first); last_total = repriced_total; } -- cgit v1.2.3 From 038c24357e85b3b33460f3b8d41b1ef4ab0ed901 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 1 Jun 2010 17:44:28 -0400 Subject: Changed --european option to --decimal-comma Fixes #211 / 1736ACA5-5DE6-4826-AEB4-DB5B2A2217AC --- src/amount.cc | 39 ++++++++++++++++++++------------------- src/commodity.cc | 10 +++++----- src/commodity.h | 26 +++++++++++++------------- src/py_commodity.cc | 26 +++++++++++++------------- src/session.cc | 4 +--- src/session.h | 6 +++--- test/baseline/cmd-print.test | 2 +- test/unit/t_amount.cc | 6 +++--- 8 files changed, 59 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/amount.cc b/src/amount.cc index a16d287e..13f30755 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -165,8 +165,8 @@ namespace { for (const char * p = buf; *p; p++) { if (*p == '.') { - if (commodity_t::european_by_default || - (comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN))) + if (commodity_t::decimal_comma_by_default || + (comm && comm->has_flags(COMMODITY_STYLE_DECIMAL_COMMA))) out << ','; else out << *p; @@ -179,8 +179,8 @@ namespace { out << *p; if (integer_digits > 3 && --integer_digits % 3 == 0) { - if (commodity_t::european_by_default || - (comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN))) + if (commodity_t::decimal_comma_by_default || + (comm && comm->has_flags(COMMODITY_STYLE_DECIMAL_COMMA))) out << '.'; else out << ','; @@ -1031,8 +1031,9 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) bool no_more_commas = false; bool no_more_periods = false; - bool european_style = (commodity_t::european_by_default || - commodity().has_flags(COMMODITY_STYLE_EUROPEAN)); + bool decimal_comma_style + = (commodity_t::decimal_comma_by_default || + commodity().has_flags(COMMODITY_STYLE_DECIMAL_COMMA)); new_quantity->prec = 0; @@ -1043,16 +1044,16 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) if (no_more_periods) throw_(amount_error, _("Too many periods in amount")); - if (european_style) { + if (decimal_comma_style) { if (decimal_offset % 3 != 0) - throw_(amount_error, _("Incorrect use of european-style period")); + throw_(amount_error, _("Incorrect use of thousand-mark period")); comm_flags |= COMMODITY_STYLE_THOUSANDS; no_more_commas = true; } else { if (last_comma != string::npos) { - european_style = true; + decimal_comma_style = true; if (decimal_offset % 3 != 0) - throw_(amount_error, _("Incorrect use of european-style period")); + throw_(amount_error, _("Incorrect use of thousand-mark period")); } else { no_more_periods = true; new_quantity->prec = decimal_offset; @@ -1067,9 +1068,9 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) if (no_more_commas) throw_(amount_error, _("Too many commas in amount")); - if (european_style) { + if (decimal_comma_style) { if (last_period != string::npos) { - throw_(amount_error, _("Incorrect use of european-style comma")); + throw_(amount_error, _("Incorrect use of decimal comma")); } else { no_more_commas = true; new_quantity->prec = decimal_offset; @@ -1079,12 +1080,12 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) if (decimal_offset % 3 != 0) { if (last_comma != string::npos || last_period != string::npos) { - throw_(amount_error, _("Incorrect use of American-style comma")); + throw_(amount_error, _("Incorrect use of thousand-mark comma")); } else { - european_style = true; - no_more_commas = true; - new_quantity->prec = decimal_offset; - decimal_offset = 0; + decimal_comma_style = true; + no_more_commas = true; + new_quantity->prec = decimal_offset; + decimal_offset = 0; } } else { comm_flags |= COMMODITY_STYLE_THOUSANDS; @@ -1100,8 +1101,8 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) } } - if (european_style) - comm_flags |= COMMODITY_STYLE_EUROPEAN; + if (decimal_comma_style) + comm_flags |= COMMODITY_STYLE_DECIMAL_COMMA; if (flags.has_flags(PARSE_NO_MIGRATE)) { // Can't call set_keep_precision here, because it assumes that `quantity' diff --git a/src/commodity.cc b/src/commodity.cc index b4220354..1b85910f 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -38,7 +38,7 @@ namespace ledger { -bool commodity_t::european_by_default = false; +bool commodity_t::decimal_comma_by_default = false; void commodity_t::history_t::add_price(commodity_t& source, const datetime_t& date, @@ -663,10 +663,10 @@ void to_xml(std::ostream& out, const commodity_t& comm, push_xml x(out, "commodity", true); out << " flags=\""; - if (! (comm.has_flags(COMMODITY_STYLE_SUFFIXED))) out << 'P'; - if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << 'S'; - if (comm.has_flags(COMMODITY_STYLE_THOUSANDS)) out << 'T'; - if (comm.has_flags(COMMODITY_STYLE_EUROPEAN)) out << 'E'; + if (! (comm.has_flags(COMMODITY_STYLE_SUFFIXED))) out << 'P'; + if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) out << 'S'; + if (comm.has_flags(COMMODITY_STYLE_THOUSANDS)) out << 'T'; + if (comm.has_flags(COMMODITY_STYLE_DECIMAL_COMMA)) out << 'D'; out << '"'; x.close_attrs(); diff --git a/src/commodity.h b/src/commodity.h index 10f209fa..53e3033f 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -155,16 +155,16 @@ protected: class base_t : public noncopyable, public supports_flags { public: -#define COMMODITY_STYLE_DEFAULTS 0x000 -#define COMMODITY_STYLE_SUFFIXED 0x001 -#define COMMODITY_STYLE_SEPARATED 0x002 -#define COMMODITY_STYLE_EUROPEAN 0x004 -#define COMMODITY_STYLE_THOUSANDS 0x008 -#define COMMODITY_NOMARKET 0x010 -#define COMMODITY_BUILTIN 0x020 -#define COMMODITY_WALKED 0x040 -#define COMMODITY_KNOWN 0x080 -#define COMMODITY_PRIMARY 0x100 +#define COMMODITY_STYLE_DEFAULTS 0x000 +#define COMMODITY_STYLE_SUFFIXED 0x001 +#define COMMODITY_STYLE_SEPARATED 0x002 +#define COMMODITY_STYLE_DECIMAL_COMMA 0x004 +#define COMMODITY_STYLE_THOUSANDS 0x008 +#define COMMODITY_NOMARKET 0x010 +#define COMMODITY_BUILTIN 0x020 +#define COMMODITY_WALKED 0x040 +#define COMMODITY_KNOWN 0x080 +#define COMMODITY_PRIMARY 0x100 string symbol; amount_t::precision_t precision; @@ -179,8 +179,8 @@ protected: public: explicit base_t(const string& _symbol) : supports_flags - (commodity_t::european_by_default ? - static_cast(COMMODITY_STYLE_EUROPEAN) : + (commodity_t::decimal_comma_by_default ? + static_cast(COMMODITY_STYLE_DECIMAL_COMMA) : static_cast(COMMODITY_STYLE_DEFAULTS)), symbol(_symbol), precision(0), searched(false) { TRACE_CTOR(base_t, "const string&"); @@ -228,7 +228,7 @@ protected: } public: - static bool european_by_default; + static bool decimal_comma_by_default; virtual ~commodity_t() { TRACE_DTOR(commodity_t); diff --git a/src/py_commodity.cc b/src/py_commodity.cc index d89a7151..fc7e8c3e 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -312,16 +312,16 @@ void export_commodity() scope().attr("commodities") = commodity_pool_t::current_pool; - scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS; - scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED; - scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED; - scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN; - scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS; - scope().attr("COMMODITY_NOMARKET") = COMMODITY_NOMARKET; - scope().attr("COMMODITY_BUILTIN") = COMMODITY_BUILTIN; - scope().attr("COMMODITY_WALKED") = COMMODITY_WALKED; - scope().attr("COMMODITY_KNOWN") = COMMODITY_KNOWN; - scope().attr("COMMODITY_PRIMARY") = COMMODITY_PRIMARY; + scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS; + scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED; + scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED; + scope().attr("COMMODITY_STYLE_DECIMAL_COMMA") = COMMODITY_STYLE_DECIMAL_COMMA; + scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS; + scope().attr("COMMODITY_NOMARKET") = COMMODITY_NOMARKET; + scope().attr("COMMODITY_BUILTIN") = COMMODITY_BUILTIN; + scope().attr("COMMODITY_WALKED") = COMMODITY_WALKED; + scope().attr("COMMODITY_KNOWN") = COMMODITY_KNOWN; + scope().attr("COMMODITY_PRIMARY") = COMMODITY_PRIMARY; class_< commodity_t, boost::noncopyable > ("Commodity", no_init) #if 1 @@ -334,9 +334,9 @@ void export_commodity() .def("drop_flags", &delegates_flags::drop_flags) #endif - .add_static_property("european_by_default", - make_getter(&commodity_t::european_by_default), - make_setter(&commodity_t::european_by_default)) + .add_static_property("decimal_comma_by_default", + make_getter(&commodity_t::decimal_comma_by_default), + make_setter(&commodity_t::decimal_comma_by_default)) .def("__str__", &commodity_t::symbol) .def("__unicode__", py_commodity_unicode) diff --git a/src/session.cc b/src/session.cc index 8e5536b0..f8befde4 100644 --- a/src/session.cc +++ b/src/session.cc @@ -196,9 +196,7 @@ option_t * session_t::lookup_option(const char * p) break; case 'd': OPT(download); // -Q - break; - case 'e': - OPT(european); + else OPT(decimal_comma); break; case 'f': OPT_(file_); // -f diff --git a/src/session.h b/src/session.h index de1771ad..10f636bb 100644 --- a/src/session.h +++ b/src/session.h @@ -80,7 +80,7 @@ public: { HANDLER(cache_).report(out); HANDLER(download).report(out); - HANDLER(european).report(out); + HANDLER(decimal_comma).report(out); HANDLER(file_).report(out); HANDLER(input_date_format_).report(out); HANDLER(master_account_).report(out); @@ -101,8 +101,8 @@ public: OPTION(session_t, cache_); OPTION(session_t, download); // -Q - OPTION_(session_t, european, DO() { - commodity_t::european_by_default = true; + OPTION_(session_t, decimal_comma, DO() { + commodity_t::decimal_comma_by_default = true; }); OPTION__ diff --git a/test/baseline/cmd-print.test b/test/baseline/cmd-print.test index 759a2334..6099b39f 100644 --- a/test/baseline/cmd-print.test +++ b/test/baseline/cmd-print.test @@ -1,4 +1,4 @@ -print --european +print --decimal-comma <<< 2008/12/31 Market Expenses:Food ($10,00 + $2,50) diff --git a/test/unit/t_amount.cc b/test/unit/t_amount.cc index 2c91ee98..63d82675 100644 --- a/test/unit/t_amount.cc +++ b/test/unit/t_amount.cc @@ -64,9 +64,9 @@ void AmountTestCase::testParser() x16.parse("$2000,00"); assertEqual(string("$2.000,00"), x16.to_string()); - // Since European-ness is an additive quality, we must switch back - // to American-ness manually - x15.commodity().drop_flags(COMMODITY_STYLE_EUROPEAN); + // Since use of a decimal-comma is an additive quality, we must switch back + // to decimal-period manually. + x15.commodity().drop_flags(COMMODITY_STYLE_DECIMAL_COMMA); amount_t x17("$1,000,000.00"); // parsing this switches back to American -- cgit v1.2.3 From 5cdd36f358dcafddd29e7a6c5e0d8210b65bfd79 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 1 Jun 2010 18:32:27 -0400 Subject: Further improved parsing of query expressions Fixes #210 / D4C2DD6F-8967-4FFC-BBBC-A941F9C53475 --- src/query.cc | 42 ++++++++++++++++++++++++------------------ src/query.h | 27 ++++++++++++++++----------- src/textual.cc | 2 +- test/unit/t_expr.cc | 6 ++---- 4 files changed, 43 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/query.cc b/src/query.cc index c79fe1c2..363c6f73 100644 --- a/src/query.cc +++ b/src/query.cc @@ -53,25 +53,9 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() } } - if (consume_next_arg) { - consume_next_arg = false; - token_t tok(token_t::TERM, string(arg_i, arg_end)); - arg_i = arg_end; - return tok; - } - - resume: - bool consume_next = false; switch (*arg_i) { - case ' ': - case '\t': - case '\r': - case '\n': - if (++arg_i == arg_end) - return next_token(); - goto resume; - case '\'': + case '"': case '/': { string pat; char closing = *arg_i; @@ -95,6 +79,25 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() return token_t(token_t::TERM, pat); } + } + + if (multiple_args && consume_next_arg) { + consume_next_arg = false; + token_t tok(token_t::TERM, string(arg_i, arg_end)); + arg_i = arg_end; + return tok; + } + + resume: + bool consume_next = false; + switch (*arg_i) { + case ' ': + case '\t': + case '\r': + case '\n': + if (++arg_i == arg_end) + return next_token(); + goto resume; case '(': ++arg_i; return token_t(token_t::LPAREN); case ')': ++arg_i; return token_t(token_t::RPAREN); @@ -104,7 +107,10 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token() case '@': ++arg_i; return token_t(token_t::TOK_PAYEE); case '#': ++arg_i; return token_t(token_t::TOK_CODE); case '%': ++arg_i; return token_t(token_t::TOK_META); - case '=': ++arg_i; return token_t(token_t::TOK_EQ); + case '=': + ++arg_i; + consume_next_arg = true; + return token_t(token_t::TOK_EQ); case '\\': consume_next = true; diff --git a/src/query.h b/src/query.h index 2b0bc75d..59adfd72 100644 --- a/src/query.h +++ b/src/query.h @@ -62,6 +62,7 @@ public: bool consume_whitespace; bool consume_next_arg; + bool multiple_args; public: struct token_t @@ -177,10 +178,11 @@ public: token_t token_cache; lexer_t(value_t::sequence_t::const_iterator _begin, - value_t::sequence_t::const_iterator _end) + value_t::sequence_t::const_iterator _end, + bool _multiple_args = true) : begin(_begin), end(_end), - consume_whitespace(false), - consume_next_arg(false) + consume_whitespace(false), consume_next_arg(false), + multiple_args(_multiple_args) { TRACE_CTOR(query_t::lexer_t, ""); assert(begin != end); @@ -192,6 +194,7 @@ public: arg_i(lexer.arg_i), arg_end(lexer.arg_end), consume_whitespace(lexer.consume_whitespace), consume_next_arg(lexer.consume_next_arg), + multiple_args(lexer.multiple_args), token_cache(lexer.token_cache) { TRACE_CTOR(query_t::lexer_t, "copy"); @@ -227,8 +230,8 @@ protected: expr_t::ptr_op_t parse_query_expr(lexer_t::token_t::kind_t tok_context); public: - parser_t(const value_t& _args) - : args(_args), lexer(args.begin(), args.end()) { + parser_t(const value_t& _args, bool multiple_args = true) + : args(_args), lexer(args.begin(), args.end(), multiple_args) { TRACE_CTOR(query_t::parser_t, ""); } parser_t(const parser_t& parser) @@ -261,28 +264,30 @@ public: TRACE_CTOR(query_t, "copy"); } query_t(const string& arg, - const keep_details_t& _what_to_keep = keep_details_t()) + const keep_details_t& _what_to_keep = keep_details_t(), + bool multiple_args = true) : predicate_t(_what_to_keep) { TRACE_CTOR(query_t, "string, keep_details_t"); if (! arg.empty()) { value_t temp(string_value(arg)); - parse_args(temp.to_sequence()); + parse_args(temp.to_sequence(), multiple_args); } } query_t(const value_t& args, - const keep_details_t& _what_to_keep = keep_details_t()) + const keep_details_t& _what_to_keep = keep_details_t(), + bool multiple_args = true) : predicate_t(_what_to_keep) { TRACE_CTOR(query_t, "value_t, keep_details_t"); if (! args.empty()) - parse_args(args); + parse_args(args, multiple_args); } virtual ~query_t() { TRACE_DTOR(query_t); } - void parse_args(const value_t& args) { + void parse_args(const value_t& args, bool multiple_args = true) { if (! parser) - parser = parser_t(args); + parser = parser_t(args, multiple_args); ptr = parser->parse(); // expr_t::ptr } diff --git a/src/textual.cc b/src/textual.cc index 2b204df0..9a49edd4 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -526,7 +526,7 @@ 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)))); + keep_details_t(true, true, true), false))); ae->pos = position_t(); ae->pos->pathname = pathname; ae->pos->beg_pos = line_beg_pos; diff --git a/test/unit/t_expr.cc b/test/unit/t_expr.cc index b5865948..0d88be9e 100644 --- a/test/unit/t_expr.cc +++ b/test/unit/t_expr.cc @@ -158,8 +158,6 @@ void ValueExprTestCase::testPredicateTokenizer7() assertEqual(query_t::lexer_t::token_t::TOK_EQ, tokens.next_token().kind); assertEqual(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); - assertEqual(query_t::lexer_t::token_t::TOK_AND, tokens.next_token().kind); - assertEqual(query_t::lexer_t::token_t::TERM, tokens.next_token().kind); assertEqual(query_t::lexer_t::token_t::END_REACHED, tokens.next_token().kind); #endif } @@ -167,7 +165,7 @@ void ValueExprTestCase::testPredicateTokenizer7() void ValueExprTestCase::testPredicateTokenizer8() { value_t args; - args.push_back(string_value("expr foo and bar")); + args.push_back(string_value("expr 'foo and bar'")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); @@ -182,7 +180,7 @@ void ValueExprTestCase::testPredicateTokenizer9() { value_t args; args.push_back(string_value("expr")); - args.push_back(string_value("foo and bar")); + args.push_back(string_value("'foo and bar'")); #ifndef NOT_FOR_PYTHON query_t::lexer_t tokens(args.begin(), args.end()); -- cgit v1.2.3 From e0c5f6db30382960fa60739c368e3700e5f71b67 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 2 Jun 2010 00:19:07 -0400 Subject: Fixed two memory reference errors --- src/account.cc | 11 +++-------- src/times.cc | 13 ++++++------- 2 files changed, 9 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/account.cc b/src/account.cc index 46f37091..8d4341e7 100644 --- a/src/account.cc +++ b/src/account.cc @@ -42,16 +42,11 @@ account_t::~account_t() { TRACE_DTOR(account_t); - foreach (accounts_map::value_type& pair, accounts) + foreach (accounts_map::value_type& pair, accounts) { if (! pair.second->has_flags(ACCOUNT_TEMP) || - has_flags(ACCOUNT_TEMP)) + has_flags(ACCOUNT_TEMP)) { checked_delete(pair.second); - - foreach (post_t * post, posts) { - if (post->account) { - assert(post->account == this); - post->account = NULL; - } + } } } diff --git a/src/times.cc b/src/times.cc index 35082f51..a7906aee 100644 --- a/src/times.cc +++ b/src/times.cc @@ -197,8 +197,6 @@ namespace { optional_year year, date_traits_t * traits = NULL) { - date_t when; - VERIFY(std::strlen(date_str) < 127); char buf[128]; @@ -208,7 +206,7 @@ namespace { if (*p == '.' || *p == '-') *p = '/'; - when = io.parse(buf); + date_t when = io.parse(buf); if (! when.is_not_a_date()) { DEBUG("times.parse", "Passed date string: " << date_str); @@ -216,12 +214,13 @@ namespace { DEBUG("times.parse", "Parsed result is: " << when); DEBUG("times.parse", "Formatted result is: " << io.format(when)); - const char * p = io.format(when).c_str(); + string when_str = io.format(when); + + const char * p = when_str.c_str(); const char * q = buf; - for (; *p != '\0' && *q != '\0'; - p++, q++) { + for (; *p && *q; p++, q++) { if (*p != *q && *p == '0') p++; - if (*p != *q) break; + if (! *p || *p != *q) break; } if (*p != '\0' || *q != '\0') throw_(date_error, _("Invalid date: %1") << date_str); -- cgit v1.2.3 From 8c61ba013f676b60cc016464363edfea0c70c7e3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 2 Jun 2010 00:27:59 -0400 Subject: Changed two uses of delete to checked_delete() --- src/convert.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/convert.cc b/src/convert.cc index 2e6da2f6..aa9bbb6f 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -117,7 +117,7 @@ value_t convert_command(call_scope_t& scope) if (matched) { DEBUG("convert.csv", "Ignored xact with code: " << *xact->code); - delete xact; // ignore it + checked_delete(xact); // ignore it } else { if (xact->posts.front()->account == NULL) { @@ -135,7 +135,7 @@ value_t convert_command(call_scope_t& scope) } if (! journal.add_xact(xact)) { - delete xact; + checked_delete(xact); throw_(std::runtime_error, _("Failed to finalize derived transaction (check commodities)")); } -- cgit v1.2.3