changes to value expressions, and to how the display predicate is computed

This commit is contained in:
John Wiegley 2004-08-17 18:19:25 -04:00
parent de39574e89
commit fadab59e87
8 changed files with 169 additions and 100 deletions

View file

@ -26,7 +26,7 @@ config_t::config_t()
price_db += "/.pricedb"; price_db += "/.pricedb";
value_expr = "a"; value_expr = "a";
total_expr = "T"; total_expr = "O";
pricing_leeway = 24 * 3600; pricing_leeway = 24 * 3600;
show_subtotals = true; show_subtotals = true;
show_expanded = false; show_expanded = false;
@ -301,7 +301,7 @@ OPT_BEGIN(download, "Q") {
OPT_BEGIN(quantity, "O") { OPT_BEGIN(quantity, "O") {
config->value_expr = "a"; config->value_expr = "a";
config->total_expr = "T"; config->total_expr = "O";
} OPT_END(quantity); } OPT_END(quantity);
OPT_BEGIN(basis, "B") { OPT_BEGIN(basis, "B") {
@ -326,23 +326,23 @@ OPT_BEGIN(gain, "G") {
OPT_BEGIN(average, "A") { OPT_BEGIN(average, "A") {
config->value_expr = "a"; config->value_expr = "a";
config->total_expr = "MT"; config->total_expr = "MO";
} OPT_END(average); } OPT_END(average);
OPT_BEGIN(deviation, "D") { OPT_BEGIN(deviation, "D") {
config->value_expr = "a"; config->value_expr = "a";
config->total_expr = "DMT"; config->total_expr = "DMO";
} OPT_END(deviation); } OPT_END(deviation);
OPT_BEGIN(trend, "X") { OPT_BEGIN(trend, "X") {
config->value_expr = "a"; config->value_expr = "a";
config->total_expr = "MDMT"; config->total_expr = "MDMO";
} OPT_END(trend); } OPT_END(trend);
OPT_BEGIN(weighted_trend, "Z") { OPT_BEGIN(weighted_trend, "Z") {
config->value_expr = "a"; config->value_expr = "a";
config->total_expr config->total_expr
= "MD(MT/(1+(((t-d)/(30*86400))<0?0:((t-d)/(30*86400)))))"; = "MD(MO/(1+(((t-d)/(30*86400))<0?0:((t-d)/(30*86400)))))";
} OPT_END(weighted_trend); } OPT_END(weighted_trend);
} // namespace ledger } // namespace ledger

View file

@ -4,8 +4,8 @@
namespace ledger { namespace ledger {
static std::time_t now = std::time(NULL); std::time_t now = std::time(NULL);
struct std::tm * now_tm = std::localtime(&now); struct std::tm * now_tm = std::localtime(&now);
static std::time_t base = -1; static std::time_t base = -1;
static int base_year = -1; static int base_year = -1;

View file

@ -3,6 +3,8 @@
#include "ledger.h" #include "ledger.h"
#include <ctime>
namespace ledger { namespace ledger {
struct interval_t struct interval_t
@ -20,6 +22,7 @@ struct interval_t
static interval_t * parse(std::istream& in); static interval_t * parse(std::istream& in);
}; };
extern std::time_t now;
extern struct std::tm * now_tm; extern struct std::tm * now_tm;
bool parse_date_mask(const char * date_str, struct std::tm * result); bool parse_date_mask(const char * date_str, struct std::tm * result);

View file

@ -125,6 +125,7 @@ class account_t
mutable balance_pair_t value; mutable balance_pair_t value;
mutable balance_pair_t total; mutable balance_pair_t total;
mutable unsigned int count; // transactions counted toward total
mutable ident_t ident; mutable ident_t ident;
mutable unsigned short dflags; mutable unsigned short dflags;
mutable std::string _fullname; mutable std::string _fullname;
@ -135,7 +136,8 @@ class account_t
const std::string& _name = "", const std::string& _name = "",
const std::string& _note = "") const std::string& _note = "")
: parent(_parent), name(_name), note(_note), : parent(_parent), name(_name), note(_note),
depth(parent ? parent->depth + 1 : 0), dflags(0) {} depth(parent ? parent->depth + 1 : 0),
count(0), dflags(0) {}
~account_t(); ~account_t();

113
main.cc
View file

@ -66,6 +66,66 @@ namespace std {
#endif // NO_CLEANUP #endif // NO_CLEANUP
static void
regexps_to_predicate(std::list<std::string>::const_iterator begin,
std::list<std::string>::const_iterator end,
config_t * config, const bool account_regexp = false,
const bool add_account_short_masks = false)
{
std::vector<std::string> regexps(2);
// Treat the remaining command-line arguments as regular
// expressions, used for refining report results.
for (std::list<std::string>::const_iterator i = begin;
i != end;
i++)
if ((*i)[0] == '-') {
if (! regexps[1].empty())
regexps[1] += "|";
regexps[1] += (*i).substr(1);
}
else if ((*i)[0] == '+') {
if (! regexps[0].empty())
regexps[0] += "|";
regexps[0] += (*i).substr(1);
}
else {
if (! regexps[0].empty())
regexps[0] += "|";
regexps[0] += *i;
}
for (std::vector<std::string>::const_iterator i = regexps.begin();
i != regexps.end();
i++)
if (! (*i).empty()) {
if (! config->predicate.empty())
config->predicate += "&";
if (i != regexps.begin()) {
config->predicate += "!";
}
else if (add_account_short_masks &&
(*i).find(':') == std::string::npos) {
if (! config->display_predicate.empty())
config->display_predicate += "&";
else if (! config->show_empty)
config->display_predicate += "T&";
config->display_predicate += "///(?:";
config->display_predicate += *i;
config->display_predicate += ")/";
}
if (! account_regexp)
config->predicate += "/";
config->predicate += "/(?:";
config->predicate += *i;
config->predicate += ")/";
}
}
int main(int argc, char * argv[], char * envp[]) int main(int argc, char * argv[], char * envp[])
{ {
std::auto_ptr<journal_t> journal(new journal_t); std::auto_ptr<journal_t> journal(new journal_t);
@ -123,25 +183,16 @@ int main(int argc, char * argv[], char * envp[])
throw error("Entries not allowed in initialization file"); throw error("Entries not allowed in initialization file");
if (use_cache && ! config->cache_file.empty()) { if (use_cache && ! config->cache_file.empty()) {
journal->sources.clear(); // remove init_file
entry_count += parse_journal_file(config->cache_file, journal.get()); entry_count += parse_journal_file(config->cache_file, journal.get());
journal->sources.pop_front(); // remove cache_file if (entry_count == 0) {
strings_list exceptions;
std::set_difference(journal->sources.begin(), journal->sources.end(),
config->files.begin(), config->files.end(),
std::back_insert_iterator<strings_list>(exceptions));
if (entry_count == 0 || exceptions.size() > 0) {
journal.reset(new journal_t); journal.reset(new journal_t);
entry_count = 0;
cache_dirty = true; cache_dirty = true;
} else { } else {
cache_dirty = false; cache_dirty = false;
} }
} }
if (entry_count == 0) if (entry_count == 0) {
for (strings_list::iterator i = config->files.begin(); for (strings_list::iterator i = config->files.begin();
i != config->files.end(); i != config->files.end();
i++) i++)
@ -153,9 +204,10 @@ int main(int argc, char * argv[], char * envp[])
entry_count += parse_journal_file(*i, journal.get()); entry_count += parse_journal_file(*i, journal.get());
} }
if (! config->price_db.empty()) if (! config->price_db.empty())
if (parse_journal_file(config->price_db, journal.get())) if (parse_journal_file(config->price_db, journal.get()))
throw error("Entries not allowed in price history file"); throw error("Entries not allowed in price history file");
}
for (strings_list::iterator i = config->price_settings.begin(); for (strings_list::iterator i = config->price_settings.begin();
i != config->price_settings.end(); i != config->price_settings.end();
@ -219,21 +271,11 @@ int main(int argc, char * argv[], char * envp[])
if (*i == "--") if (*i == "--")
break; break;
const std::string pred = regexps_to_predicate(arg, i); regexps_to_predicate(arg, i, config, true,
if (! pred.empty()) { command == "b" && ! config->show_expanded &&
if (! config->predicate.empty()) config->display_predicate.empty());
config->predicate += "&"; if (i != args.end())
config->predicate += pred; regexps_to_predicate(i, args.end(), config);
}
if (i != args.end()) {
const std::string pred = regexps_to_predicate(i, args.end(), false);
if (! pred.empty()) {
if (! config->predicate.empty())
config->predicate += "&";
config->predicate += pred;
}
}
} }
// Compile the predicates // Compile the predicates
@ -242,18 +284,23 @@ int main(int argc, char * argv[], char * envp[])
if (command == "b") { if (command == "b") {
if (! config->show_empty) if (! config->show_empty)
config->display_predicate = "T"; config->display_predicate = "T";
if (! config->show_expanded) {
if (! config->show_expanded && config->predicate.empty()) {
if (! config->display_predicate.empty()) if (! config->display_predicate.empty())
config->display_predicate += "&"; config->display_predicate += "&";
config->display_predicate += "!n"; config->display_predicate += "!l";
} }
} }
else if (command == "E") { else if (command == "E") {
config->display_predicate = "a"; config->display_predicate = "t";
} }
} }
#ifdef DEBUG_ENABLED
DEBUG_PRINT("ledger.main.predicates", "predicate: " << config->predicate);
DEBUG_PRINT("ledger.main.predicates",
"disp-pred: " << config->display_predicate);
#endif
// Compile the sorting criteria // Compile the sorting criteria
std::auto_ptr<value_expr_t> sort_order; std::auto_ptr<value_expr_t> sort_order;

View file

@ -1,4 +1,5 @@
#include "valexpr.h" #include "valexpr.h"
#include "format.h"
#include "error.h" #include "error.h"
#include "datetime.h" #include "datetime.h"
#include "debug.h" #include "debug.h"
@ -119,15 +120,32 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
result = details.account->total.cost; result = details.account->total.cost;
break; break;
case VALUE_EXPR:
#ifdef NO_CLEANUP
assert(format_t::value_expr);
#else
assert(format_t::value_expr.get());
#endif
format_t::value_expr->compute(result, details);
break;
case TOTAL_EXPR:
#ifdef NO_CLEANUP
assert(format_t::total_expr);
#else
assert(format_t::total_expr.get());
#endif
format_t::total_expr->compute(result, details);
break;
case DATE: case DATE:
if (details.entry) if (details.entry)
result = (unsigned int) details.entry->date; result = (unsigned int) details.entry->date;
else else
result = (unsigned int) std::time(NULL); result = (unsigned int) now;
break; break;
case TODAY: case TODAY:
result = (unsigned int) std::time(NULL); result = (unsigned int) now;
break; break;
case CLEARED: case CLEARED:
@ -169,6 +187,11 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
case INDEX: case INDEX:
if (details.xact) if (details.xact)
result = details.xact->index + 1; result = details.xact->index + 1;
break;
case DEPTH:
if (details.xact)
result = details.xact->account->depth - 1;
else if (details.account) else if (details.account)
result = details.account->depth - 1; result = details.account->depth - 1;
break; break;
@ -179,6 +202,11 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
left->compute(result, details); left->compute(result, details);
result /= amount_t(details.xact->index + 1); result /= amount_t(details.xact->index + 1);
} }
else if (details.account && details.account->count) {
assert(left);
left->compute(result, details);
result /= amount_t(details.account->count);
}
break; break;
case F_NEG: case F_NEG:
@ -214,6 +242,12 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
result = mask->match(details.account->fullname()); result = mask->match(details.account->fullname());
break; break;
case F_SHORT_ACCOUNT_MASK:
assert(mask);
if (details.account)
result = mask->match(details.account->name);
break;
case F_VALUE: { case F_VALUE: {
assert(left); assert(left);
left->compute(result, details); left->compute(result, details);
@ -225,18 +259,18 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
if (details.entry) if (details.entry)
moment = details.entry->date; moment = details.entry->date;
else else
moment = std::time(NULL); moment = now;
break; break;
case TODAY: case TODAY:
moment = std::time(NULL); moment = now;
break; break;
default: default:
throw compute_error("Invalid date passed to P(value,date)"); throw compute_error("Invalid date passed to P(value,date)");
} }
} else { } else {
moment = std::time(NULL); moment = now;
} }
result = result.value(moment); result = result.value(moment);
break; break;
@ -359,16 +393,21 @@ value_expr_t * parse_value_term(std::istream& in)
case 'a': node = new value_expr_t(value_expr_t::AMOUNT); break; case 'a': node = new value_expr_t(value_expr_t::AMOUNT); break;
case 'c': node = new value_expr_t(value_expr_t::COST); break; case 'c': node = new value_expr_t(value_expr_t::COST); break;
case 'd': node = new value_expr_t(value_expr_t::DATE); break; case 'd': node = new value_expr_t(value_expr_t::DATE); break;
case 't': node = new value_expr_t(value_expr_t::TODAY); break; case 'N': node = new value_expr_t(value_expr_t::TODAY); break;
case 'X': node = new value_expr_t(value_expr_t::CLEARED); break; case 'X': node = new value_expr_t(value_expr_t::CLEARED); break;
case 'R': node = new value_expr_t(value_expr_t::REAL); break; case 'R': node = new value_expr_t(value_expr_t::REAL); break;
case 'n': node = new value_expr_t(value_expr_t::INDEX); break; case 'n': node = new value_expr_t(value_expr_t::INDEX); break;
case 'l': node = new value_expr_t(value_expr_t::DEPTH); break;
case 'B': node = new value_expr_t(value_expr_t::BALANCE); break; case 'B': node = new value_expr_t(value_expr_t::BALANCE); break;
case 'T': node = new value_expr_t(value_expr_t::TOTAL); break; case 'O': node = new value_expr_t(value_expr_t::TOTAL); break;
case 'C': node = new value_expr_t(value_expr_t::COST_TOTAL); break; case 'C': node = new value_expr_t(value_expr_t::COST_TOTAL); break;
// Relating to format_t
case 't': node = new value_expr_t(value_expr_t::VALUE_EXPR); break;
case 'T': node = new value_expr_t(value_expr_t::TOTAL_EXPR); break;
// Compound terms // Compound terms
case 'v': node = parse_value_expr("P(a,d)"); break; case 'v': node = parse_value_expr("P(t,d)"); break;
case 'V': node = parse_value_term("P(T,d)"); break; case 'V': node = parse_value_term("P(T,d)"); break;
case 'g': node = parse_value_expr("v-c"); break; case 'g': node = parse_value_expr("v-c"); break;
case 'G': node = parse_value_expr("V-C"); break; case 'G': node = parse_value_expr("V-C"); break;
@ -423,13 +462,20 @@ value_expr_t * parse_value_term(std::istream& in)
// Other // Other
case '/': { case '/': {
bool payee_mask = false; bool payee_mask = false;
bool short_account_mask = false;
c = peek_next_nonws(in); c = peek_next_nonws(in);
if (c == '/') { if (c == '/') {
payee_mask = true;
in.get(c); in.get(c);
c = in.peek(); c = in.peek();
if (c == '/') {
in.get(c);
c = in.peek();
short_account_mask = true;
} else {
payee_mask = true;
}
} }
static char buf[4096]; static char buf[4096];
@ -438,8 +484,10 @@ value_expr_t * parse_value_term(std::istream& in)
throw value_expr_error("Missing closing '/'"); throw value_expr_error("Missing closing '/'");
in.get(c); in.get(c);
node = new value_expr_t(payee_mask ? value_expr_t::F_PAYEE_MASK : node = new value_expr_t(short_account_mask ?
value_expr_t::F_ACCOUNT_MASK); value_expr_t::F_SHORT_ACCOUNT_MASK :
(payee_mask ? value_expr_t::F_PAYEE_MASK :
value_expr_t::F_ACCOUNT_MASK));
node->mask = new mask_t(buf); node->mask = new mask_t(buf);
break; break;
} }
@ -664,47 +712,6 @@ value_expr_t * parse_value_expr(std::istream& in)
return node; return node;
} }
std::string regexps_to_predicate(std::list<std::string>::const_iterator begin,
std::list<std::string>::const_iterator end,
const bool account_regexp)
{
std::vector<std::string> regexps(2);
std::string pred;
// Treat the remaining command-line arguments as regular
// expressions, used for refining report results.
for (std::list<std::string>::const_iterator i = begin;
i != end;
i++)
if ((*i)[0] == '-') {
if (! regexps[1].empty())
regexps[1] += "|";
regexps[1] += (*i).substr(1);
} else {
if (! regexps[0].empty())
regexps[0] += "|";
regexps[0] += *i;
}
for (std::vector<std::string>::const_iterator i = regexps.begin();
i != regexps.end();
i++)
if (! (*i).empty()) {
if (! pred.empty())
pred += "&";
if (i != regexps.begin())
pred += "!";
if (! account_regexp)
pred += "/";
pred += "/(?:";
pred += *i;
pred += ")/";
}
return pred;
}
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
void dump_value_expr(std::ostream& out, const value_expr_t * node) void dump_value_expr(std::ostream& out, const value_expr_t * node)
@ -724,6 +731,7 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
case value_expr_t::CLEARED: out << "CLEARED"; break; case value_expr_t::CLEARED: out << "CLEARED"; break;
case value_expr_t::REAL: out << "REAL"; break; case value_expr_t::REAL: out << "REAL"; break;
case value_expr_t::INDEX: out << "INDEX"; break; case value_expr_t::INDEX: out << "INDEX"; break;
case value_expr_t::DEPTH: out << "DEPTH"; break;
case value_expr_t::BALANCE: out << "BALANCE"; break; case value_expr_t::BALANCE: out << "BALANCE"; break;
case value_expr_t::COST_BALANCE: out << "COST_BALANCE"; break; case value_expr_t::COST_BALANCE: out << "COST_BALANCE"; break;
case value_expr_t::TOTAL: out << "TOTAL"; break; case value_expr_t::TOTAL: out << "TOTAL"; break;
@ -763,6 +771,11 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
out << "A_MASK(" << node->mask->pattern << ")"; out << "A_MASK(" << node->mask->pattern << ")";
break; break;
case value_expr_t::F_SHORT_ACCOUNT_MASK:
assert(node->mask);
out << "A_SMASK(" << node->mask->pattern << ")";
break;
case value_expr_t::F_VALUE: case value_expr_t::F_VALUE:
out << "VALUE("; out << "VALUE(";
dump_value_expr(out, node->left); dump_value_expr(out, node->left);

View file

@ -52,7 +52,8 @@ struct value_expr_t
TODAY, TODAY,
CLEARED, CLEARED,
REAL, REAL,
INDEX, // for accounts, this is the DEPTH INDEX,
DEPTH,
// Item totals // Item totals
BALANCE, BALANCE,
@ -60,6 +61,10 @@ struct value_expr_t
TOTAL, TOTAL,
COST_TOTAL, COST_TOTAL,
// Relating to format_t
VALUE_EXPR,
TOTAL_EXPR,
// Functions // Functions
F_ARITH_MEAN, F_ARITH_MEAN,
F_VALUE, F_VALUE,
@ -68,6 +73,7 @@ struct value_expr_t
F_STRIP, F_STRIP,
F_PAYEE_MASK, F_PAYEE_MASK,
F_ACCOUNT_MASK, F_ACCOUNT_MASK,
F_SHORT_ACCOUNT_MASK,
// Binary operators // Binary operators
O_ADD, O_ADD,
@ -174,10 +180,6 @@ class item_predicate
} }
}; };
std::string regexps_to_predicate(std::list<std::string>::const_iterator begin,
std::list<std::string>::const_iterator end,
const bool account_regexp = true);
} // namespace report } // namespace report
#endif // _REPORT_H #endif // _REPORT_H

2
walk.h
View file

@ -117,6 +117,7 @@ class add_to_account_value : public item_handler<transaction_t>
public: public:
virtual void operator()(transaction_t * xact) { virtual void operator()(transaction_t * xact) {
xact->account->value += *xact; xact->account->value += *xact;
xact->account->count++;
} }
}; };
@ -368,6 +369,7 @@ inline void sum_accounts(account_t * account) {
i++) { i++) {
sum_accounts((*i).second); sum_accounts((*i).second);
account->total += (*i).second->total; account->total += (*i).second->total;
account->count += (*i).second->count;
} }
account->total += account->value; account->total += account->value;
} }