diff options
author | John Wiegley <johnw@newartisans.com> | 2009-11-14 20:43:28 -0500 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-11-14 20:43:28 -0500 |
commit | badbeb545b9e700e6656dfbc348bfe1387cb2ffa (patch) | |
tree | 26237a150367e13a24917d994602f5f94f979ec8 /src | |
parent | 73baf9cd727407f6e47b410bbde3b4bcad482ae3 (diff) | |
parent | 8d698e5ccb471d546eee8a5bea6d6c56ec4b08c3 (diff) | |
download | fork-ledger-badbeb545b9e700e6656dfbc348bfe1387cb2ffa.tar.gz fork-ledger-badbeb545b9e700e6656dfbc348bfe1387cb2ffa.tar.bz2 fork-ledger-badbeb545b9e700e6656dfbc348bfe1387cb2ffa.zip |
Merge branch 'next'
Diffstat (limited to 'src')
-rw-r--r-- | src/account.cc | 21 | ||||
-rw-r--r-- | src/amount.cc | 8 | ||||
-rw-r--r-- | src/filters.cc | 21 | ||||
-rw-r--r-- | src/filters.h | 2 | ||||
-rw-r--r-- | src/format.cc | 7 | ||||
-rw-r--r-- | src/item.cc | 5 | ||||
-rw-r--r-- | src/post.cc | 28 | ||||
-rw-r--r-- | src/post.h | 5 | ||||
-rw-r--r-- | src/py_journal.cc | 40 | ||||
-rw-r--r-- | src/py_post.cc | 2 | ||||
-rw-r--r-- | src/pyinterp.cc | 4 | ||||
-rw-r--r-- | src/report.cc | 39 | ||||
-rw-r--r-- | src/report.h | 2 | ||||
-rw-r--r-- | src/value.cc | 5 | ||||
-rw-r--r-- | src/xact.cc | 84 | ||||
-rw-r--r-- | src/xact.h | 10 |
16 files changed, 239 insertions, 44 deletions
diff --git a/src/account.cc b/src/account.cc index da43745a..e6c7af56 100644 --- a/src/account.cc +++ b/src/account.cc @@ -249,7 +249,7 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, switch (name[0]) { case 'a': - if (name == "amount") + if (name[1] == '\0' || name == "amount") return WRAP_FUNCTOR(get_wrapper<&get_amount>); else if (name == "account") return WRAP_FUNCTOR(get_wrapper<&get_account>); @@ -272,11 +272,20 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, case 'i': if (name == "is_account") return WRAP_FUNCTOR(get_wrapper<&get_true>); + else if (name == "is_index") + return WRAP_FUNCTOR(get_wrapper<&get_subcount>); break; case 'l': if (name == "latest_cleared") return WRAP_FUNCTOR(get_wrapper<&get_latest_cleared>); + else if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_depth>); + break; + + case 'n': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_subcount>); break; case 'p': @@ -300,6 +309,16 @@ expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind, if (name == "use_direct_amount") return WRAP_FUNCTOR(get_wrapper<&ignore>); break; + + case 'N': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_count>); + break; + + case 'O': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_total>); + break; } return NULL; diff --git a/src/amount.cc b/src/amount.cc index f8406505..82b93931 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -56,7 +56,7 @@ struct amount_t::bigint_t : public supports_flags<> mpq_t val; precision_t prec; - uint_least16_t refc; + uint_least32_t refc; #define MP(bigint) ((bigint)->val) @@ -80,11 +80,7 @@ struct amount_t::bigint_t : public supports_flags<> bool valid() const { if (prec > 1024) { - DEBUG("ledger.validate", "amount_t::bigint_t: prec > 128"); - return false; - } - if (refc > 16535) { - DEBUG("ledger.validate", "amount_t::bigint_t: refc > 16535"); + DEBUG("ledger.validate", "amount_t::bigint_t: prec > 1024"); return false; } if (flags() & ~(BIGINT_BULK_ALLOC | BIGINT_KEEP_PREC)) { diff --git a/src/filters.cc b/src/filters.cc index 811067fc..bef4dc24 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -350,9 +350,9 @@ void collapse_posts::report_subtotal() component_posts.clear(); last_xact = NULL; - last_post = NULL; - subtotal = 0L; - count = 0; + last_post = NULL; + subtotal = 0L; + count = 0; } void collapse_posts::operator()(post_t& post) @@ -364,12 +364,12 @@ void collapse_posts::operator()(post_t& post) report_subtotal(); post.add_to_value(subtotal, amount_expr); - count++; component_posts.push_back(&post); last_xact = post.xact; - last_post = &post; + last_post = &post; + count++; } void related_posts::flush() @@ -648,8 +648,15 @@ void posts_as_equity::report_subtotal() value_t total = 0L; foreach (values_map::value_type& pair, values) { - handle_value(pair.second.value, pair.second.account, &xact, temps, - *handler); + if (pair.second.value.is_balance()) { + foreach (balance_t::amounts_map::value_type amount_pair, + pair.second.value.as_balance().amounts) + handle_value(amount_pair.second, pair.second.account, &xact, temps, + *handler); + } else { + handle_value(pair.second.value, pair.second.account, &xact, temps, + *handler); + } total += pair.second.value; } values.clear(); diff --git a/src/filters.h b/src/filters.h index 80bbe5b4..57b3edd2 100644 --- a/src/filters.h +++ b/src/filters.h @@ -466,7 +466,7 @@ public: TRACE_DTOR(subtotal_posts); } - void report_subtotal(const char * spec_fmt = NULL, + void report_subtotal(const char * spec_fmt = NULL, const optional<date_interval_t>& interval = none); virtual void flush() { diff --git a/src/format.cc b/src/format.cc index e910ce3b..f26a86a1 100644 --- a/src/format.cc +++ b/src/format.cc @@ -355,8 +355,11 @@ string format_t::real_calc(scope_t& scope) } DEBUG("format.expr", "value = (" << value << ")"); - value.print(out, static_cast<int>(elem->min_width), -1, - ! elem->has_flags(ELEMENT_ALIGN_LEFT)); + if (elem->min_width > 0) + value.print(out, static_cast<int>(elem->min_width), -1, + ! elem->has_flags(ELEMENT_ALIGN_LEFT)); + else + out << value.to_string(); } catch (const calc_error&) { add_error_context(_("While calculating format expression:")); diff --git a/src/item.cc b/src/item.cc index f60db226..43274cfd 100644 --- a/src/item.cc +++ b/src/item.cc @@ -395,6 +395,11 @@ expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_uncleared>); break; + case 'L': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_actual>); + break; + case 'X': if (name[1] == '\0') return WRAP_FUNCTOR(get_wrapper<&get_cleared>); diff --git a/src/post.cc b/src/post.cc index 7c27b6c4..34284e1b 100644 --- a/src/post.cc +++ b/src/post.cc @@ -297,6 +297,11 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(get_wrapper<&get_account_base>); break; + case 'b': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_cost>); + break; + case 'c': if (name == "code") return WRAP_FUNCTOR(get_wrapper<&get_code>); @@ -325,7 +330,9 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, break; case 'i': - if (name == "id") + if (name == "index") + return WRAP_FUNCTOR(get_wrapper<&get_count>); + else if (name == "id") return WRAP_FUNCTOR(get_wrapper<&get_id>); else if (name == "idstring") return WRAP_FUNCTOR(get_wrapper<&get_idstring>); @@ -339,6 +346,8 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, case 'n': if (name == "note") return WRAP_FUNCTOR(get_wrapper<&get_note>); + else if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_count>); break; case 'p': @@ -358,7 +367,7 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, break; case 't': - if (name[1] == '\0' || name == "total") + if (name == "total") return WRAP_FUNCTOR(get_wrapper<&get_total>); break; @@ -376,6 +385,21 @@ expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind, if (name == "xact") return WRAP_FUNCTOR(get_wrapper<&get_xact>); break; + + case 'N': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_count>); + break; + + case 'O': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_total>); + break; + + case 'R': + if (name[1] == '\0') + return WRAP_FUNCTOR(get_wrapper<&get_real>); + break; } return item_t::lookup(kind, name); @@ -135,13 +135,11 @@ public: date_t date; datetime_t datetime; account_t * account; - void * ptr; std::list<sort_value_t> sort_values; xdata_t() - : supports_flags<uint_least16_t>(), count(0), - account(NULL), ptr(NULL) { + : supports_flags<uint_least16_t>(), count(0), account(NULL) { TRACE_CTOR(post_t::xdata_t, ""); } xdata_t(const xdata_t& other) @@ -152,7 +150,6 @@ public: count(other.count), date(other.date), account(other.account), - ptr(NULL), sort_values(other.sort_values) { TRACE_CTOR(post_t::xdata_t, "copy"); diff --git a/src/py_journal.cc b/src/py_journal.cc index bea14d66..7e9f8a1b 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -230,15 +230,18 @@ void export_journal() class_< collect_posts, bases<item_handler<post_t> >, shared_ptr<collect_posts>, boost::noncopyable >("PostCollector") .def("__len__", &collect_posts::length) - .def("__iter__", range<return_internal_reference<> > + .def("__iter__", range<return_internal_reference<1, + with_custodian_and_ward_postcall<1, 0> > > (&collect_posts::begin, &collect_posts::end)) ; class_< collector_wrapper, shared_ptr<collector_wrapper>, boost::noncopyable >("PostCollectorWrapper", no_init) .def("__len__", &collector_wrapper::length) - .def("__getitem__", posts_getitem, return_internal_reference<>()) - .def("__iter__", range<return_internal_reference<> > + .def("__getitem__", posts_getitem, return_internal_reference<1, + with_custodian_and_ward_postcall<0, 1> >()) + .def("__iter__", range<return_value_policy<reference_existing_object, + with_custodian_and_ward_postcall<0, 1> > > (&collector_wrapper::begin, &collector_wrapper::end)) ; @@ -263,30 +266,43 @@ void export_journal() .def(init<path>()) .def(init<string>()) - .add_property("master", make_getter(&journal_t::master, - return_internal_reference<>())) + .add_property("master", + make_getter(&journal_t::master, + return_internal_reference<1, + with_custodian_and_ward_postcall<1, 0> >())) .add_property("bucket", make_getter(&journal_t::bucket, - return_internal_reference<>()), + return_internal_reference<1, + with_custodian_and_ward_postcall<1, 0> >()), make_setter(&journal_t::bucket)) .add_property("was_loaded", make_getter(&journal_t::was_loaded)) .add_property("commodity_pool", make_getter(&journal_t::commodity_pool, - return_internal_reference<>())) + return_internal_reference<1, + with_custodian_and_ward_postcall<1, 0> >())) .def("add_account", &journal_t::add_account) .def("remove_account", &journal_t::remove_account) - .def("find_account", py_find_account_1, return_internal_reference<>()) - .def("find_account", py_find_account_2, return_internal_reference<>()) + .def("find_account", py_find_account_1, + return_internal_reference<1, + with_custodian_and_ward_postcall<0, 1> >()) + .def("find_account", py_find_account_2, + return_internal_reference<1, + with_custodian_and_ward_postcall<0, 1> >()) .def("find_account_re", &journal_t::find_account_re, - return_internal_reference<>()) + return_internal_reference<1, + with_custodian_and_ward_postcall<0, 1> >()) .def("add_xact", &journal_t::add_xact) .def("remove_xact", &journal_t::remove_xact) .def("__len__", xacts_len) - .def("__getitem__", xacts_getitem, return_internal_reference<>()) +#if 0 + .def("__getitem__", xacts_getitem, + return_internal_reference<1, + with_custodian_and_ward_postcall<0, 1> >()) +#endif .def("__iter__", range<return_internal_reference<> > (&journal_t::xacts_begin, &journal_t::xacts_end)) @@ -304,7 +320,7 @@ void export_journal() .def("has_xdata", &journal_t::has_xdata) .def("clear_xdata", &journal_t::clear_xdata) - .def("collect", py_collect) + .def("collect", py_collect, with_custodian_and_ward_postcall<0, 1>()) .def("valid", &journal_t::valid) ; diff --git a/src/py_post.cc b/src/py_post.cc index 8aabea28..20cdba6b 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -116,7 +116,7 @@ void export_post() make_setter(&post_t::xdata_t::datetime)) .add_property("account", make_getter(&post_t::xdata_t::account, - return_internal_reference<>()), + return_value_policy<reference_existing_object>()), make_setter(&post_t::xdata_t::account, with_custodian_and_ward<1, 2>())) .add_property("sort_values", diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 0a56049c..0701176f 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -296,6 +296,10 @@ value_t python_interpreter_t::python_command(call_scope_t& args) try { status = Py_Main(static_cast<int>(args.size()) + 1, argv); } + catch (const error_already_set&) { + PyErr_Print(); + throw_(std::runtime_error, _("Failed to execute Python module")); + } catch (...) { for (std::size_t i = 0; i < args.size() + 1; i++) delete[] argv[i]; diff --git a/src/report.cc b/src/report.cc index 49633350..267a4a3d 100644 --- a/src/report.cc +++ b/src/report.cc @@ -915,6 +915,45 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, switch (kind) { case symbol_t::FUNCTION: + // Support 2.x's single-letter value expression names. + if (*(p + 1) == '\0') { + switch (*p) { + case 'd': + case 'm': + return MAKE_FUNCTOR(report_t::fn_now); + case 'P': + return MAKE_FUNCTOR(report_t::fn_market); + case 't': + return MAKE_FUNCTOR(report_t::fn_display_amount); + case 'T': + return MAKE_FUNCTOR(report_t::fn_display_total); + case 'U': + return MAKE_FUNCTOR(report_t::fn_abs); + case 'S': + return MAKE_FUNCTOR(report_t::fn_strip); + case 'i': + throw_(std::runtime_error, + _("The i value expression variable is no longer supported")); + case 'A': + throw_(std::runtime_error, + _("The A value expression variable is no longer supported")); + case 'v': + case 'V': + throw_(std::runtime_error, + _("The V and v value expression variables are no longer supported")); + case 'I': + case 'B': + throw_(std::runtime_error, + _("The I and B value expression variables are no longer supported")); + case 'g': + case 'G': + throw_(std::runtime_error, + _("The G and g value expression variables are no longer supported")); + default: + return NULL; + } + } + switch (*p) { case 'a': if (is_eq(p, "amount_expr")) diff --git a/src/report.h b/src/report.h index c656829b..93f6e9e0 100644 --- a/src/report.h +++ b/src/report.h @@ -775,7 +775,7 @@ public: "%(ansify_if(justify(format_date(date), date_width), green " " if color & date > today))" " %(ansify_if(justify(truncated(payee, payee_width), payee_width), " - " bold if color & !cleared))" + " bold if color & !cleared & actual))" " %(ansify_if(justify(truncated(account, account_width, abbrev_len), " " account_width), blue if color))" " %(justify(scrub(display_amount), amount_width, " diff --git a/src/value.cc b/src/value.cc index ce852c2d..3f70ab3d 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1618,7 +1618,10 @@ void value_t::print(std::ostream& out, break; case STRING: - justify(out, as_string(), first_width, right_justify); + if (first_width > 0) + justify(out, as_string(), first_width, right_justify); + else + out << as_string(); break; case MASK: diff --git a/src/xact.cc b/src/xact.cc index 1cece187..f2694976 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -412,7 +412,7 @@ bool xact_base_t::verify() continue; if (post->amount.commodity() == post->cost->commodity()) - throw_(balance_error, + throw_(amount_error, _("A posting's cost must be of a different commodity than its amount")); } @@ -538,6 +538,50 @@ bool xact_t::valid() const return true; } +namespace { + + bool post_pred(expr_t::ptr_op_t op, post_t& post) + { + switch (op->kind) { + case expr_t::op_t::VALUE: + return op->as_value().to_boolean(); + break; + + case expr_t::op_t::O_MATCH: + if (op->left()->kind == expr_t::op_t::IDENT && + op->left()->as_ident() == "account" && + op->right()->kind == expr_t::op_t::VALUE && + op->right()->as_value().is_mask()) + return op->right()->as_value().as_mask() + .match(post.reported_account()->fullname()); + else + break; + + case expr_t::op_t::O_NOT: + return ! post_pred(op->left(), post); + + case expr_t::op_t::O_AND: + return post_pred(op->left(), post) && post_pred(op->right(), post); + + case expr_t::op_t::O_OR: + return post_pred(op->left(), post) || post_pred(op->right(), post); + + case expr_t::op_t::O_QUERY: + if (post_pred(op->left(), post)) + return post_pred(op->right()->left(), post); + else + return post_pred(op->right()->right(), post); + + default: + break; + } + + throw_(calc_error, _("Unhandled operator")); + return false; + } + +} // unnamed namespace + void auto_xact_t::extend_xact(xact_base_t& xact) { posts_list initial_posts(xact.posts.begin(), xact.posts.end()); @@ -547,8 +591,42 @@ void auto_xact_t::extend_xact(xact_base_t& xact) bool needs_further_verification = false; foreach (post_t * initial_post, initial_posts) { - if (! initial_post->has_flags(ITEM_GENERATED) && - predicate(*initial_post)) { + if (initial_post->has_flags(ITEM_GENERATED)) + continue; + + bool matches_predicate = false; + if (try_quick_match) { + try { + bool found_memoized_result = false; + if (! memoized_results.empty()) { + std::map<string, bool>::iterator i = + memoized_results.find(initial_post->account->fullname()); + if (i != memoized_results.end()) { + found_memoized_result = true; + matches_predicate = (*i).second; + } + } + + // Since the majority of people who use automated transactions simply + // match against account names, try using a *much* faster version of + // the predicate evaluator. + if (! found_memoized_result) { + matches_predicate = post_pred(predicate.get_op(), *initial_post); + memoized_results.insert + (std::pair<string, bool>(initial_post->account->fullname(), + matches_predicate)); + } + } + catch (...) { + DEBUG("xact.extend.fail", + "The quick matcher failed, going back to regular eval"); + try_quick_match = false; + matches_predicate = predicate(*initial_post); + } + } else { + matches_predicate = predicate(*initial_post); + } + if (matches_predicate) { foreach (post_t * post, posts) { amount_t post_amount; if (post->amount.is_null()) { @@ -146,16 +146,20 @@ class auto_xact_t : public xact_base_t { public: predicate_t predicate; + bool try_quick_match; - auto_xact_t() { + std::map<string, bool> memoized_results; + + auto_xact_t() : try_quick_match(true) { TRACE_CTOR(auto_xact_t, ""); } auto_xact_t(const auto_xact_t& other) - : xact_base_t(), predicate(other.predicate) { + : xact_base_t(), predicate(other.predicate), + try_quick_match(other.try_quick_match) { TRACE_CTOR(auto_xact_t, "copy"); } auto_xact_t(const predicate_t& _predicate) - : predicate(_predicate) + : predicate(_predicate), try_quick_match(true) { TRACE_CTOR(auto_xact_t, "const predicate_t&"); } |