diff options
-rw-r--r-- | config.cc | 12 | ||||
-rw-r--r-- | datetime.cc | 4 | ||||
-rw-r--r-- | datetime.h | 3 | ||||
-rw-r--r-- | ledger.h | 4 | ||||
-rw-r--r-- | main.cc | 113 | ||||
-rw-r--r-- | valexpr.cc | 119 | ||||
-rw-r--r-- | valexpr.h | 12 | ||||
-rw-r--r-- | walk.h | 2 |
8 files changed, 169 insertions, 100 deletions
@@ -26,7 +26,7 @@ config_t::config_t() price_db += "/.pricedb"; value_expr = "a"; - total_expr = "T"; + total_expr = "O"; pricing_leeway = 24 * 3600; show_subtotals = true; show_expanded = false; @@ -301,7 +301,7 @@ OPT_BEGIN(download, "Q") { OPT_BEGIN(quantity, "O") { config->value_expr = "a"; - config->total_expr = "T"; + config->total_expr = "O"; } OPT_END(quantity); OPT_BEGIN(basis, "B") { @@ -326,23 +326,23 @@ OPT_BEGIN(gain, "G") { OPT_BEGIN(average, "A") { config->value_expr = "a"; - config->total_expr = "MT"; + config->total_expr = "MO"; } OPT_END(average); OPT_BEGIN(deviation, "D") { config->value_expr = "a"; - config->total_expr = "DMT"; + config->total_expr = "DMO"; } OPT_END(deviation); OPT_BEGIN(trend, "X") { config->value_expr = "a"; - config->total_expr = "MDMT"; + config->total_expr = "MDMO"; } OPT_END(trend); OPT_BEGIN(weighted_trend, "Z") { config->value_expr = "a"; 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); } // namespace ledger diff --git a/datetime.cc b/datetime.cc index 586722b5..4f6cf0d3 100644 --- a/datetime.cc +++ b/datetime.cc @@ -4,8 +4,8 @@ namespace ledger { -static std::time_t now = std::time(NULL); - struct std::tm * now_tm = std::localtime(&now); +std::time_t now = std::time(NULL); +struct std::tm * now_tm = std::localtime(&now); static std::time_t base = -1; static int base_year = -1; @@ -3,6 +3,8 @@ #include "ledger.h" +#include <ctime> + namespace ledger { struct interval_t @@ -20,6 +22,7 @@ struct interval_t static interval_t * parse(std::istream& in); }; +extern std::time_t now; extern struct std::tm * now_tm; bool parse_date_mask(const char * date_str, struct std::tm * result); @@ -125,6 +125,7 @@ class account_t mutable balance_pair_t value; mutable balance_pair_t total; + mutable unsigned int count; // transactions counted toward total mutable ident_t ident; mutable unsigned short dflags; mutable std::string _fullname; @@ -135,7 +136,8 @@ class account_t const std::string& _name = "", const std::string& _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(); @@ -66,6 +66,66 @@ namespace std { #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[]) { 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"); if (use_cache && ! config->cache_file.empty()) { - journal->sources.clear(); // remove init_file entry_count += parse_journal_file(config->cache_file, journal.get()); - journal->sources.pop_front(); // remove cache_file - - 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) { + if (entry_count == 0) { journal.reset(new journal_t); - entry_count = 0; cache_dirty = true; } else { cache_dirty = false; } } - if (entry_count == 0) + if (entry_count == 0) { for (strings_list::iterator i = config->files.begin(); i != config->files.end(); i++) @@ -153,9 +204,10 @@ int main(int argc, char * argv[], char * envp[]) entry_count += parse_journal_file(*i, journal.get()); } - if (! config->price_db.empty()) - if (parse_journal_file(config->price_db, journal.get())) - throw error("Entries not allowed in price history file"); + if (! config->price_db.empty()) + if (parse_journal_file(config->price_db, journal.get())) + throw error("Entries not allowed in price history file"); + } for (strings_list::iterator i = config->price_settings.begin(); i != config->price_settings.end(); @@ -219,21 +271,11 @@ int main(int argc, char * argv[], char * envp[]) if (*i == "--") break; - const std::string pred = regexps_to_predicate(arg, i); - if (! pred.empty()) { - if (! config->predicate.empty()) - config->predicate += "&"; - config->predicate += pred; - } - - 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; - } - } + regexps_to_predicate(arg, i, config, true, + command == "b" && ! config->show_expanded && + config->display_predicate.empty()); + if (i != args.end()) + regexps_to_predicate(i, args.end(), config); } // Compile the predicates @@ -242,18 +284,23 @@ int main(int argc, char * argv[], char * envp[]) if (command == "b") { if (! config->show_empty) config->display_predicate = "T"; - - if (! config->show_expanded && config->predicate.empty()) { + if (! config->show_expanded) { if (! config->display_predicate.empty()) config->display_predicate += "&"; - config->display_predicate += "!n"; + config->display_predicate += "!l"; } } 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 std::auto_ptr<value_expr_t> sort_order; @@ -1,4 +1,5 @@ #include "valexpr.h" +#include "format.h" #include "error.h" #include "datetime.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; 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: if (details.entry) result = (unsigned int) details.entry->date; else - result = (unsigned int) std::time(NULL); + result = (unsigned int) now; break; case TODAY: - result = (unsigned int) std::time(NULL); + result = (unsigned int) now; break; case CLEARED: @@ -169,6 +187,11 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const case INDEX: if (details.xact) result = details.xact->index + 1; + break; + + case DEPTH: + if (details.xact) + result = details.xact->account->depth - 1; else if (details.account) result = details.account->depth - 1; break; @@ -179,6 +202,11 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const left->compute(result, details); 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; 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()); break; + case F_SHORT_ACCOUNT_MASK: + assert(mask); + if (details.account) + result = mask->match(details.account->name); + break; + case F_VALUE: { assert(left); left->compute(result, details); @@ -225,18 +259,18 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const if (details.entry) moment = details.entry->date; else - moment = std::time(NULL); + moment = now; break; case TODAY: - moment = std::time(NULL); + moment = now; break; default: throw compute_error("Invalid date passed to P(value,date)"); } } else { - moment = std::time(NULL); + moment = now; } result = result.value(moment); 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 'c': node = new value_expr_t(value_expr_t::COST); 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 'R': node = new value_expr_t(value_expr_t::REAL); 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 '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; + // 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 - 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 '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 case '/': { - bool payee_mask = false; + bool payee_mask = false; + bool short_account_mask = false; c = peek_next_nonws(in); if (c == '/') { - payee_mask = true; in.get(c); c = in.peek(); + if (c == '/') { + in.get(c); + c = in.peek(); + short_account_mask = true; + } else { + payee_mask = true; + } } static char buf[4096]; @@ -438,8 +484,10 @@ value_expr_t * parse_value_term(std::istream& in) throw value_expr_error("Missing closing '/'"); in.get(c); - node = new value_expr_t(payee_mask ? value_expr_t::F_PAYEE_MASK : - value_expr_t::F_ACCOUNT_MASK); + node = new value_expr_t(short_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); break; } @@ -664,47 +712,6 @@ value_expr_t * parse_value_expr(std::istream& in) 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 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::REAL: out << "REAL"; 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::COST_BALANCE: out << "COST_BALANCE"; 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 << ")"; 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: out << "VALUE("; dump_value_expr(out, node->left); @@ -52,7 +52,8 @@ struct value_expr_t TODAY, CLEARED, REAL, - INDEX, // for accounts, this is the DEPTH + INDEX, + DEPTH, // Item totals BALANCE, @@ -60,6 +61,10 @@ struct value_expr_t TOTAL, COST_TOTAL, + // Relating to format_t + VALUE_EXPR, + TOTAL_EXPR, + // Functions F_ARITH_MEAN, F_VALUE, @@ -68,6 +73,7 @@ struct value_expr_t F_STRIP, F_PAYEE_MASK, F_ACCOUNT_MASK, + F_SHORT_ACCOUNT_MASK, // Binary operators 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 #endif // _REPORT_H @@ -117,6 +117,7 @@ class add_to_account_value : public item_handler<transaction_t> public: virtual void operator()(transaction_t * xact) { xact->account->value += *xact; + xact->account->count++; } }; @@ -368,6 +369,7 @@ inline void sum_accounts(account_t * account) { i++) { sum_accounts((*i).second); account->total += (*i).second->total; + account->count += (*i).second->count; } account->total += account->value; } |