summaryrefslogtreecommitdiff
path: root/src/post.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2010-05-08 02:06:06 -0400
committerJohn Wiegley <johnw@newartisans.com>2010-05-08 02:06:06 -0400
commit4ec54b86f856c6e85446d065d2940b4244d71b8f (patch)
tree645c11578337991c7de83a4e3443a50b5c8a469a /src/post.cc
parentd5f8c3bc576d98266519911ec05a98cd586d49db (diff)
downloadfork-ledger-4ec54b86f856c6e85446d065d2940b4244d71b8f.tar.gz
fork-ledger-4ec54b86f856c6e85446d065d2940b4244d71b8f.tar.bz2
fork-ledger-4ec54b86f856c6e85446d065d2940b4244d71b8f.zip
Added any() and all() value expression macros
any() matches an expression against every post in a transaction or account, and returns true if any of them are true. all() tests if all are true. For example: ledger -l 'account =~ /Expense/ & any(account =~ /MasterCard/)' reg This reports every posting affecting an Expense account (regex match), but only if some other posting in the same transaction affects the MasterCard account. Both functions also take a second boolean argument. If it is false, the "source" posting is not considered. For example: ledger -l 'any(/x/, false)' This matches any posting where a *different* posting in the same transaction contains the letter 'x'.
Diffstat (limited to 'src/post.cc')
-rw-r--r--src/post.cc48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/post.cc b/src/post.cc
index 41ae04dd..183fb901 100644
--- a/src/post.cc
+++ b/src/post.cc
@@ -291,6 +291,50 @@ namespace {
value_t get_wrapper(call_scope_t& scope) {
return (*Func)(find_scope<post_t>(scope));
}
+
+ value_t fn_any(call_scope_t& scope)
+ {
+ interactive_t args(scope, "X&X");
+
+ post_t& post(find_scope<post_t>(scope));
+ expr_t& expr(args.get<expr_t&>(0));
+
+ foreach (post_t * p, post.xact->posts) {
+ bind_scope_t bound_scope(scope, *p);
+ if (p == &post && args.has(1) &&
+ ! args.get<expr_t&>(1).calc(bound_scope).to_boolean()) {
+ // If the user specifies any(EXPR, false), and the context is a
+ // posting, then that posting isn't considered by the test.
+ ; // skip it
+ }
+ else if (expr.calc(bound_scope).to_boolean()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ value_t fn_all(call_scope_t& scope)
+ {
+ interactive_t args(scope, "X&X");
+
+ post_t& post(find_scope<post_t>(scope));
+ expr_t& expr(args.get<expr_t&>(0));
+
+ foreach (post_t * p, post.xact->posts) {
+ bind_scope_t bound_scope(scope, *p);
+ if (p == &post && args.has(1) &&
+ ! args.get<expr_t&>(1).calc(bound_scope).to_boolean()) {
+ // If the user specifies any(EXPR, false), and the context is a
+ // posting, then that posting isn't considered by the test.
+ ; // skip it
+ }
+ else if (! expr.calc(bound_scope).to_boolean()) {
+ return false;
+ }
+ }
+ return true;
+ }
}
expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind,
@@ -307,6 +351,10 @@ 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 == "any")
+ return WRAP_FUNCTOR(&fn_any);
+ else if (name == "all")
+ return WRAP_FUNCTOR(&fn_all);
break;
case 'b':