summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account.cc21
-rw-r--r--src/amount.cc8
-rw-r--r--src/filters.cc21
-rw-r--r--src/filters.h2
-rw-r--r--src/format.cc7
-rw-r--r--src/item.cc5
-rw-r--r--src/post.cc28
-rw-r--r--src/post.h5
-rw-r--r--src/py_journal.cc40
-rw-r--r--src/py_post.cc2
-rw-r--r--src/pyinterp.cc4
-rw-r--r--src/report.cc39
-rw-r--r--src/report.h2
-rw-r--r--src/value.cc5
-rw-r--r--src/xact.cc84
-rw-r--r--src/xact.h10
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);
diff --git a/src/post.h b/src/post.h
index 22f8b961..a0ab5ffd 100644
--- a/src/post.h
+++ b/src/post.h
@@ -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()) {
diff --git a/src/xact.h b/src/xact.h
index fe748fcc..c819b2a0 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -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&");
}