changes to value expressions, and to how the display predicate is computed
This commit is contained in:
parent
de39574e89
commit
fadab59e87
8 changed files with 169 additions and 100 deletions
12
config.cc
12
config.cc
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
4
ledger.h
4
ledger.h
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
113
main.cc
113
main.cc
|
|
@ -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;
|
||||
|
|
|
|||
119
valexpr.cc
119
valexpr.cc
|
|
@ -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);
|
||||
|
|
|
|||
12
valexpr.h
12
valexpr.h
|
|
@ -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
|
||||
|
|
|
|||
2
walk.h
2
walk.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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue