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";
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

View file

@ -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;

View file

@ -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);

View file

@ -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
View file

@ -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;

View file

@ -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);

View file

@ -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
View file

@ -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;
}